diff options
115 files changed, 2187 insertions, 782 deletions
diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index bb7fbeb78b..d5809c42cf 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -28,7 +28,7 @@ tasks: gmake deps - build: | cd neovim - gmake CMAKE_BUILD_TYPE=Release CMAKE_EXTRA_FLAGS="${CMAKE_EXTRA_FLAGS}" nvim + gmake CMAKE_BUILD_TYPE=RelWithDebInfo CMAKE_EXTRA_FLAGS="${CMAKE_EXTRA_FLAGS}" nvim - functionaltest: | cd neovim gmake functionaltest diff --git a/.builds/openbsd.yml b/.builds/openbsd.yml index 2f0f970dcb..0ffc8aa786 100644 --- a/.builds/openbsd.yml +++ b/.builds/openbsd.yml @@ -28,16 +28,16 @@ tasks: mkdir neovim/.deps cd neovim/.deps cmake -G Ninja ../third-party/ - cmake --build . --config Debug + cmake --build . --config RelWithDebInfo - build: | mkdir neovim/build cd neovim/build cmake -G Ninja $CMAKE_EXTRA_FLAGS .. - cmake --build . --config Debug + cmake --build . --config RelWithDebInfo ./bin/nvim --version - functionaltest: | cd neovim/build - cmake --build . --config Debug --target functionaltest + cmake --build . --config RelWithDebInfo --target functionaltest - oldtest: | cd neovim gmake oldtest diff --git a/.gitignore b/.gitignore index ab301bd336..6004101cce 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ compile_commands.json /.deps/ /tmp/ /.clangd/ +/.cache/clangd/ .DS_Store *.mo diff --git a/ci/build.ps1 b/ci/build.ps1 index db7026ac66..53e4328e02 100644 --- a/ci/build.ps1 +++ b/ci/build.ps1 @@ -88,11 +88,14 @@ elseif ($compiler -eq 'MSVC') { if (-not $NoTests) { # Setup python (use AppVeyor system python) - C:\hostedtoolcache\windows\Python\2.7.18\x64\python.exe -m pip install pynvim ; exitIfFailed - C:\hostedtoolcache\windows\Python\3.5.4\x64\python.exe -m pip install pynvim ; exitIfFailed - # Disambiguate python3 - move C:\hostedtoolcache\windows\Python\3.5.4\x64\python.exe C:\hostedtoolcache\windows\Python\3.5.4\x64\python3.exe - $env:PATH = "C:\hostedtoolcache\windows\Python\3.5.4\x64;C:\hostedtoolcache\windows\Python\2.7.18\x64;$env:PATH" + # Disambiguate python3, if needed + if (-not (Test-Path -Path C:\hostedtoolcache\windows\Python\3.5.4\x64\python3.exe) ) { + move C:\hostedtoolcache\windows\Python\3.5.4\x64\python.exe C:\hostedtoolcache\windows\Python\3.5.4\x64\python3.exe + } + $env:PATH = "C:\hostedtoolcache\windows\Python\2.7.18\x64;C:\hostedtoolcache\windows\Python\3.5.4\x64;$env:PATH" + + python -m pip install pynvim ; exitIfFailed + python3 -m pip install pynvim ; exitIfFailed # Sanity check python -c "import pynvim; print(str(pynvim))" ; exitIfFailed python3 -c "import pynvim; print(str(pynvim))" ; exitIfFailed diff --git a/runtime/autoload/health/nvim.vim b/runtime/autoload/health/nvim.vim index 8b734bbb6f..0bb343e198 100644 --- a/runtime/autoload/health/nvim.vim +++ b/runtime/autoload/health/nvim.vim @@ -42,7 +42,7 @@ function! s:check_config() abort endif let writeable = v:true - let shadafile = substitute(matchstr( + let shadafile = empty(&shada) ? &shada : substitute(matchstr( \ split(&shada, ',')[-1], '^n.\+'), '^n', '', '') let shadafile = empty(&shadafile) ? empty(shadafile) ? \ stdpath('data').'/shada/main.shada' : expand(shadafile) @@ -247,6 +247,10 @@ function! s:check_terminal() abort let kdch1_entry = matchstr(out, 'key_dc=[^,[:space:]]*') if v:shell_error + \ && (!has('win32') + \ || empty(matchstr(out, + \ 'infocmp: couldn''t open terminfo file .\+' + \ ..'\%(conemu\|vtpcon\|win32con\)'))) call health#report_error('command failed: '.cmd."\n".out) else call health#report_info('key_backspace (kbs) terminfo entry: ' diff --git a/runtime/autoload/man.vim b/runtime/autoload/man.vim index 78a86315a3..c629923cd3 100644 --- a/runtime/autoload/man.vim +++ b/runtime/autoload/man.vim @@ -51,7 +51,7 @@ function! man#open_page(count, mods, ...) abort let [l:buf, l:save_tfu] = [bufnr(), &tagfunc] try - set tagfunc=man#goto_tag + setlocal tagfunc=man#goto_tag let l:target = l:name . '(' . l:sect . ')' if a:mods !~# 'tab' && s:find_man() execute 'silent keepalt tag' l:target diff --git a/runtime/autoload/netrw.vim b/runtime/autoload/netrw.vim index b69ad7187a..1fe4ec5a89 100644 --- a/runtime/autoload/netrw.vim +++ b/runtime/autoload/netrw.vim @@ -1,8 +1,8 @@ " netrw.vim: Handles file transfer and remote directory listing across " AUTOLOAD SECTION -" Date: Jul 16, 2019 -" Version: 165 -" Maintainer: Charles E Campbell <NdrOchip@ScampbellPfamily.AbizM-NOSPAM> +" Date: Jan 07, 2020 +" Version: 168 +" Maintainer: Charles E Campbell <NcampObell@SdrPchip.AorgM-NOSPAM> " GetLatestVimScripts: 1075 1 :AutoInstall: netrw.vim " Copyright: Copyright (C) 2016 Charles E. Campbell {{{1 " Permission is hereby granted to use and distribute this code, @@ -13,6 +13,10 @@ " 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. +" +" Note: the code here was started in 1999 under a much earlier version of vim. The directory browsing +" code was written using vim v6, which did not have Lists (Lists were first offered with vim-v7). +" "redraw!|call DechoSep()|call inputsave()|call input("Press <cr> to continue")|call inputrestore() " " But be doers of the Word, and not only hearers, deluding your own selves {{{1 @@ -39,7 +43,7 @@ if exists("s:needspatches") endfor endif -let g:loaded_netrw = "v165" +let g:loaded_netrw = "v168" if !exists("s:NOTE") let s:NOTE = 0 let s:WARNING = 1 @@ -64,7 +68,7 @@ setl cpo&vim " Usage: netrw#ErrorMsg(s:NOTE | s:WARNING | s:ERROR,"some message",error-number) " netrw#ErrorMsg(s:NOTE | s:WARNING | s:ERROR,["message1","message2",...],error-number) " (this function can optionally take a list of messages) -" Mar 21, 2017 : max errnum currently is 105 +" Dec 2, 2019 : max errnum currently is 106 fun! netrw#ErrorMsg(level,msg,errnum) " call Dfunc("netrw#ErrorMsg(level=".a:level." msg<".a:msg."> errnum=".a:errnum.") g:netrw_use_errorwindow=".g:netrw_use_errorwindow) @@ -232,12 +236,12 @@ if !exists("g:netrw_ftp_options") let g:netrw_ftp_options= "-i -n" endif if !exists("g:netrw_http_cmd") - if executable("curl") - let g:netrw_http_cmd = "curl" - call s:NetrwInit("g:netrw_http_xcmd","-L -o") - elseif executable("wget") + if executable("wget") let g:netrw_http_cmd = "wget" call s:NetrwInit("g:netrw_http_xcmd","-q -O") + elseif executable("curl") + let g:netrw_http_cmd = "curl" + call s:NetrwInit("g:netrw_http_xcmd","-L -o") elseif executable("elinks") let g:netrw_http_cmd = "elinks" call s:NetrwInit("g:netrw_http_xcmd","-source >") @@ -443,23 +447,9 @@ if !exists("g:netrw_localmovecmd") let g:netrw_localmovecmd= "" endif endif -if v:version < 704 || (v:version == 704 && !has("patch1107")) - " 1109 provides for delete(tmpdir,"d") which is what will be used - if exists("g:netrw_local_rmdir") - let g:netrw_localrmdir= g:netrw_local_rmdir - call netrw#ErrorMsg(s:NOTE,"g:netrw_local_rmdir is deprecated in favor of g:netrw_localrmdir",86) - endif - if has("win32") || has("win95") || has("win64") || has("win16") - if g:netrw_cygwin - call s:NetrwInit("g:netrw_localrmdir","rmdir") - else - let g:netrw_localrmdir = expand("$COMSPEC") - let g:netrw_localrmdiropt= " /c rmdir" - endif - else - call s:NetrwInit("g:netrw_localrmdir","rmdir") - endif -endif +" following serves as an example for how to insert a version&patch specific test +"if v:version < 704 || (v:version == 704 && !has("patch1107")) +"endif call s:NetrwInit("g:netrw_liststyle" , s:THINLIST) " sanity checks if g:netrw_liststyle < 0 || g:netrw_liststyle >= s:MAXLIST @@ -1500,7 +1490,8 @@ fun! netrw#Obtain(islocal,fname,...) " -i : turns off interactive prompting from ftp " -n unix : DON'T use <.netrc>, even though it exists " -n win32: quit being obnoxious about password - NetrwKeepj norm! 1Gdd + " Note: using "_dd to delete to the black hole register; avoids messing up @@ + NetrwKeepj norm! 1G"_dd call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." ".g:netrw_ftp_options) " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) if getline(1) !~ "^$" @@ -1614,7 +1605,6 @@ fun! s:NetrwOptionsSave(vt) let {a:vt}netrw_cpokeep = &l:cpo let {a:vt}netrw_diffkeep = &l:diff let {a:vt}netrw_fenkeep = &l:fen -" call Decho("saving current settings: got here#1",'~'.expand("<slnum>")) if !exists("g:netrw_ffkeep") || g:netrw_ffkeep let {a:vt}netrw_ffkeep = &l:ff endif @@ -1633,7 +1623,6 @@ fun! s:NetrwOptionsSave(vt) let {a:vt}netrw_rokeep = &l:ro let {a:vt}netrw_selkeep = &l:sel let {a:vt}netrw_spellkeep = &l:spell -" call Decho("saving current settings: got here#2",'~'.expand("<slnum>")) if !g:netrw_use_noswf let {a:vt}netrw_swfkeep = &l:swf endif @@ -1647,6 +1636,7 @@ fun! s:NetrwOptionsSave(vt) " call Decho("saving a few selected netrw-related variables",'~'.expand("<slnum>")) if g:netrw_keepdir let {a:vt}netrw_dirkeep = getcwd() +" call Decho("saving to ".a:vt."netrw_dirkeep<".{a:vt}netrw_dirkeep.">",'~'.expand("<slnum>")) endif sil! let {a:vt}netrw_slashkeep= @/ @@ -1713,6 +1703,7 @@ fun! s:NetrwOptionsRestore(vt) " call Dfunc("s:NetrwOptionsRestore(vt<".a:vt.">) win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> winnr($)=".winnr("$")) " call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt,'~'.expand("<slnum>")) if !exists("{a:vt}netrw_optionsave") +" call Decho("case ".a:vt."netrw_optionsave : doesn't exist",'~'.expand("<slnum>")) " call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt,'~'.expand("<slnum>")) " call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) " call Dret("s:NetrwOptionsRestore : ".a:vt."netrw_optionsave doesn't exist") @@ -1854,7 +1845,7 @@ endfun " Used by s:NetrwOptionsRestore() to restore each netrw-senstive setting " keepvars are set up by s:NetrwOptionsSave fun! s:NetrwRestoreSetting(keepvar,setting) -"" call Dfunc("s:NetrwRestoreSetting(a:keepvar<".a:keepvar."> a:setting<".a:setting.">)") +""" call Dfunc("s:NetrwRestoreSetting(a:keepvar<".a:keepvar."> a:setting<".a:setting.">)") " typically called from s:NetrwOptionsRestore " call s:NetrwRestoreSettings(keep-option-variable-name,'associated-option') @@ -1869,7 +1860,7 @@ fun! s:NetrwRestoreSetting(keepvar,setting) "" call Decho("fyi: a:setting<".a:setting."> setting<".setting.">") if setting != keepvarval -"" call Decho("restore setting<".a:setting."=".setting."> to keepvarval<".keepvarval.">") +"" call Decho("restore setting<".a:setting."> (currently=".setting.") to keepvarval<".keepvarval.">") if type(a:setting) == 0 exe "let ".a:setting."= ".keepvarval elseif type(a:setting) == 1 @@ -2159,7 +2150,7 @@ fun! netrw#NetRead(mode,...) " -i : turns off interactive prompting from ftp " -n unix : DON'T use <.netrc>, even though it exists " -n win32: quit being obnoxious about password - NetrwKeepj norm! 1Gdd + NetrwKeepj norm! 1G"_dd call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." ".g:netrw_ftp_options) " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) if getline(1) !~ "^$" @@ -2259,7 +2250,7 @@ fun! netrw#NetRead(mode,...) NetrwKeepj put ='quit' " perform cadaver operation: - NetrwKeepj norm! 1Gdd + NetrwKeepj norm! 1G"_dd call s:NetrwExe(s:netrw_silentxfer."%!".g:netrw_dav_cmd) keepj bd! endif @@ -2575,7 +2566,7 @@ fun! netrw#NetWrite(...) range " -i : turns off interactive prompting from ftp " -n unix : DON'T use <.netrc>, even though it exists " -n win32: quit being obnoxious about password - NetrwKeepj norm! 1Gdd + NetrwKeepj norm! 1G"_dd call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." ".g:netrw_ftp_options) " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) if getline(1) !~ "^$" @@ -2641,7 +2632,7 @@ fun! netrw#NetWrite(...) range NetrwKeepj put ='put '.tmpfile.' '.netrw_fname " perform cadaver operation: - NetrwKeepj norm! 1Gdd + NetrwKeepj norm! 1G"_dd call s:NetrwExe(s:netrw_silentxfer."%!".g:netrw_dav_cmd) " remove enew buffer (quietly) @@ -2827,7 +2818,7 @@ fun! s:NetrwGetFile(readcmd, tfile, method) " readcmd=='t': simply do nothing if a:readcmd == 't' " call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) -" call Dret("NetrwGetFile : skip read of <".a:tfile.">") +" call Dret("NetrwGetFile : skip read of tfile<".a:tfile.">") return endif @@ -4323,7 +4314,7 @@ fun! s:NetrwGetWord() let curline= getline('.') if curline =~# '"\s*Sorted by\s' - NetrwKeepj norm! s + NetrwKeepj norm! "_s let s:netrw_skipbrowse= 1 echo 'Pressing "s" also works' @@ -5158,17 +5149,31 @@ fun! s:NetrwBrowseUpDir(islocal) endfun " --------------------------------------------------------------------- -" netrw#BrowseX: (implements "x") executes a special "viewer" script or program for the {{{2 +" netrw#BrowseX: (implements "x" and "gx") executes a special "viewer" script or program for the {{{2 " given filename; typically this means given their extension. " 0=local, 1=remote fun! netrw#BrowseX(fname,remote) -" call Dfunc("netrw#BrowseX(fname<".a:fname."> remote=".a:remote.")") - - " if its really just a local directory, then do a "gf" instead - if (a:remote == 0 && isdirectory(a:fname)) || (a:remote == 1 && a:fname =~ '/$' && a:fname !~ '^https\=:') + let use_ctrlo= 1 +" call Dfunc("netrw#BrowseX(fname<".a:fname."> remote=".a:remote.") implements x and gx maps") + + if a:remote == 0 && isdirectory(a:fname) + " if its really just a local directory, then do a "gf" instead +" call Decho("remote≡0 and a:fname<".a:fname."> ".(isdirectory(a:fname)? "is a directory" : "is not a directory"),'~'.expand("<slnum>")) +" call Decho("..appears to be a local directory; using e ".a:fname." instead",'~'.expand("<slnum>")) + exe "e ".a:fname +" call Dret("netrw#BrowseX") + return + elseif a:remote == 1 && a:fname !~ '^https\=:' && a:fname =~ '/$' + " remote directory, not a webpage access, looks like an attempt to do a directory listing +" call Decho("remote≡1 and a:fname<".a:fname.">",'~'.expand("<slnum>")) +" call Decho("..and fname ".((a:fname =~ '^https\=:')? 'matches' : 'does not match').'^https\=:','~'.expand("<slnum>")) +" call Decho("..and fname ".((a:fname =~ '/$')? 'matches' : 'does not match').' /$','~'.expand("<slnum>")) +" call Decho("..appears to be a remote directory listing request; using gf instead",'~'.expand("<slnum>")) norm! gf -" call Dret("(netrw#BrowseX) did gf instead") +" call Dret("netrw#BrowseX") + return endif +" call Decho("not a local file nor a webpage request",'~'.expand("<slnum>")) let ykeep = @@ let screenposn = winsaveview() @@ -5266,10 +5271,9 @@ fun! netrw#BrowseX(fname,remote) endif " call Decho("set up redirection: redir{".redir."} srr{".&srr."}",'~'.expand("<slnum>")) - " extract any viewing options. Assumes that they're set apart by quotes. -" call Decho("extract any viewing options",'~'.expand("<slnum>")) + " extract any viewing options. Assumes that they're set apart by spaces. if exists("g:netrw_browsex_viewer") -" call Decho("g:netrw_browsex_viewer<".g:netrw_browsex_viewer.">",'~'.expand("<slnum>")) +" call Decho("extract any viewing options from g:netrw_browsex_viewer<".g:netrw_browsex_viewer.">",'~'.expand("<slnum>")) if g:netrw_browsex_viewer =~ '\s' let viewer = substitute(g:netrw_browsex_viewer,'\s.*$','','') let viewopt = substitute(g:netrw_browsex_viewer,'^\S\+\s*','','')." " @@ -5292,16 +5296,16 @@ fun! netrw#BrowseX(fname,remote) " execute the file handler " call Decho("execute the file handler (if any)",'~'.expand("<slnum>")) if exists("g:netrw_browsex_viewer") && g:netrw_browsex_viewer == '-' -" call Decho("g:netrw_browsex_viewer<".g:netrw_browsex_viewer.">",'~'.expand("<slnum>")) +" call Decho("(netrw#BrowseX) g:netrw_browsex_viewer<".g:netrw_browsex_viewer.">",'~'.expand("<slnum>")) let ret= netrwFileHandlers#Invoke(exten,fname) elseif exists("g:netrw_browsex_viewer") && executable(viewer) -" call Decho("g:netrw_browsex_viewer<".g:netrw_browsex_viewer.">",'~'.expand("<slnum>")) +" call Decho("(netrw#BrowseX) g:netrw_browsex_viewer<".g:netrw_browsex_viewer.">",'~'.expand("<slnum>")) call s:NetrwExe("sil !".viewer." ".viewopt.s:ShellEscape(fname,1).redir) let ret= v:shell_error elseif has("win32") || has("win64") -" call Decho("win".(has("win32")? "32" : "64")",'~'.expand("<slnum>")) +" call Decho("(netrw#BrowseX) win".(has("win32")? "32" : "64"),'~'.expand("<slnum>")) if executable("start") call s:NetrwExe('sil! !start rundll32 url.dll,FileProtocolHandler '.s:ShellEscape(fname,1)) elseif executable("rundll32") @@ -5309,56 +5313,68 @@ fun! netrw#BrowseX(fname,remote) else call netrw#ErrorMsg(s:WARNING,"rundll32 not on path",74) endif - " call inputsave()|call input("Press <cr> to continue")|call inputrestore() let ret= v:shell_error elseif has("win32unix") let winfname= 'c:\cygwin'.substitute(fname,'/','\\','g') -" call Decho("cygwin: winfname<".s:ShellEscape(winfname,1).">",'~'.expand("<slnum>")) +" call Decho("(netrw#BrowseX) cygwin: winfname<".s:ShellEscape(winfname,1).">",'~'.expand("<slnum>")) if executable("start") +" call Decho("(netrw#BrowseX) win32unix+start",'~'.expand("<slnum>")) call s:NetrwExe('sil !start rundll32 url.dll,FileProtocolHandler '.s:ShellEscape(winfname,1)) elseif executable("rundll32") +" call Decho("(netrw#BrowseX) win32unix+rundll32",'~'.expand("<slnum>")) call s:NetrwExe('sil !rundll32 url.dll,FileProtocolHandler '.s:ShellEscape(winfname,1)) elseif executable("cygstart") +" call Decho("(netrw#BrowseX) win32unix+cygstart",'~'.expand("<slnum>")) call s:NetrwExe('sil !cygstart '.s:ShellEscape(fname,1)) else call netrw#ErrorMsg(s:WARNING,"rundll32 not on path",74) endif - " call inputsave()|call input("Press <cr> to continue")|call inputrestore() let ret= v:shell_error elseif has("unix") && executable("kfmclient") && s:CheckIfKde() -" call Decho("unix and kfmclient",'~'.expand("<slnum>")) +" call Decho("(netrw#BrowseX) unix and kfmclient",'~'.expand("<slnum>")) call s:NetrwExe("sil !kfmclient exec ".s:ShellEscape(fname,1)." ".redir) let ret= v:shell_error elseif has("unix") && executable("exo-open") && executable("xdg-open") && executable("setsid") -" call Decho("unix, exo-open, xdg-open",'~'.expand("<slnum>")) +" call Decho("(netrw#BrowseX) unix, exo-open, xdg-open",'~'.expand("<slnum>")) call s:NetrwExe("sil !setsid xdg-open ".s:ShellEscape(fname,1).redir) let ret= v:shell_error elseif has("unix") && $DESKTOP_SESSION == "mate" && executable("atril") -" call Decho("unix and atril",'~'.expand("<slnum>")) - call s:NetrwExe("sil !atril ".s:ShellEscape(fname,1).redir) +" call Decho("(netrw#BrowseX) unix and atril",'~'.expand("<slnum>")) + if a:fname =~ '^https\=://' + " atril does not appear to understand how to handle html -- so use gvim to edit the document + let use_ctrlo= 0 +" call Decho("(COMBAK) fname<".fname.">") +" call Decho("(COMBAK) a:fname<".a:fname.">") + call s:NetrwExe("sil! !gvim ".fname.' -c "keepj keepalt file '.fnameescape(a:fname).'"') + + else + call s:NetrwExe("sil !atril ".s:ShellEscape(fname,1).redir) + endif let ret= v:shell_error elseif has("unix") && executable("xdg-open") -" call Decho("unix and xdg-open",'~'.expand("<slnum>")) +" call Decho("(netrw#BrowseX) unix and xdg-open",'~'.expand("<slnum>")) call s:NetrwExe("sil !xdg-open ".s:ShellEscape(fname,1).redir) let ret= v:shell_error elseif has("macunix") && executable("open") -" call Decho("macunix and open",'~'.expand("<slnum>")) +" call Decho("(netrw#BrowseX) macunix and open",'~'.expand("<slnum>")) call s:NetrwExe("sil !open ".s:ShellEscape(fname,1)." ".redir) let ret= v:shell_error else " netrwFileHandlers#Invoke() always returns 0 +" call Decho("(netrw#BrowseX) use netrwFileHandlers",'~'.expand("<slnum>")) let ret= netrwFileHandlers#Invoke(exten,fname) endif " if unsuccessful, attempt netrwFileHandlers#Invoke() if ret +" call Decho("(netrw#BrowseX) ret=".ret," indicates unsuccessful thus far",'~'.expand("<slnum>")) let ret= netrwFileHandlers#Invoke(exten,fname) endif @@ -5380,8 +5396,9 @@ fun! netrw#BrowseX(fname,remote) if g:netrw_use_noswf setl noswf endif - exe "sil! NetrwKeepj norm! \<c-o>" -" redraw! + if use_ctrlo + exe "sil! NetrwKeepj norm! \<c-o>" + endif endif " call Decho("restoring posn to screenposn<".string(screenposn).">",'~'.expand("<slnum>")) call winrestview(screenposn) @@ -5410,11 +5427,11 @@ endfun " netrw#BrowseXVis: used by gx in visual mode to select a file for browsing {{{2 fun! netrw#BrowseXVis() " call Dfunc("netrw#BrowseXVis()") - let atkeep = @@ - norm! gvy -" call Decho("@@<".@@.">",'~'.expand("<slnum>")) - call netrw#BrowseX(@@,netrw#CheckIfRemote(@@)) - let @@ = atkeep + let akeep = @a + norm! gv"ay + let gxfile= @a + let @a = akeep + call netrw#BrowseX(gxfile,netrw#CheckIfRemote(gxfile)) " call Dret("netrw#BrowseXVis") endfun @@ -6039,13 +6056,12 @@ fun! s:NetrwSLeftrelease(islocal) endfun " --------------------------------------------------------------------- -" s:NetrwListHide: uses [range]g~...~d to delete files that match comma {{{2 -" separated patterns given in g:netrw_list_hide +" s:NetrwListHide: uses [range]g~...~d to delete files that match {{{2 +" comma-separated patterns given in g:netrw_list_hide fun! s:NetrwListHide() " call Dfunc("s:NetrwListHide() g:netrw_hide=".g:netrw_hide." g:netrw_list_hide<".g:netrw_list_hide.">") " call Decho("initial: ".string(getline(w:netrw_bannercnt,'$'))) let ykeep= @@ -" call DechoBuf(bufnr("%"),"COMBAK#3") " find a character not in the "hide" string to use as a separator for :g and :v commands " How-it-works: take the hiding command, convert it into a range. @@ -6054,8 +6070,8 @@ fun! s:NetrwListHide() " Use the first character left as a separator character. " call Decho("find a character not in the hide string to use as a separator") let listhide= g:netrw_list_hide - let sep = strpart(substitute('/~@#$%^&*{};:,<.>?|1234567890','['.escape(listhide,'-]^\').']','','ge'),1,1) -" call Decho("sep=".sep,'~'.expand("<slnum>")) + let sep = strpart(substitute('~@#$%^&*{};:,<.>?|1234567890','['.escape(listhide,'-]^\').']','','ge'),1,1) +" call Decho("sep=".sep," (sep not in hide string)'~'.expand("<slnum>")) while listhide != "" if listhide =~ ',' @@ -6065,10 +6081,19 @@ fun! s:NetrwListHide() let hide = listhide let listhide = "" endif -" call Decho("hide<".hide."> listhide<".listhide.'>','~'.expand("<slnum>")) +" call Decho("..extracted from listhide: hide<".hide."> g:netrw_sort_by<".g:netrw_sort_by.'>','~'.expand("<slnum>")) + if g:netrw_sort_by =~ '^[ts]' + if hide =~ '^\^' +" call Decho("..modify hide to handle a \"^...\" pattern",'~'.expand("<slnum>")) + let hide= substitute(hide,'^\^','^\(\\d\\+/\)','') + elseif hide =~ '^\\(\^' + let hide= substitute(hide,'^\\(\^','\\(^\\(\\d\\+/\\)','') + endif +" call Decho("..hide<".hide."> listhide<".listhide.'>','~'.expand("<slnum>")) + endif " Prune the list by hiding any files which match -" call Decho("prune the list by hiding any files which ",((g:netrw_hide == 1)? "" : "don't")." match hide<".hide.">") +" call Decho("..prune the list by hiding any files which ",((g:netrw_hide == 1)? "" : "don't")." match hide<".hide.">") if g:netrw_hide == 1 " call Decho("..hiding<".hide.">",'~'.expand("<slnum>")) exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$g'.sep.hide.sep.'d' @@ -6089,7 +6114,6 @@ fun! s:NetrwListHide() " remove any blank lines that have somehow remained. " This seems to happen under Windows. exe 'sil! NetrwKeepj 1,$g@^\s*$@d' -" call DechoBuf(bufnr("%"),"COMBAK#4") let @@= ykeep " call Dret("s:NetrwListHide") @@ -6297,7 +6321,7 @@ fun! s:NetrwMaps(islocal) " generate default <Plug> maps {{{3 if !hasmapto('<Plug>NetrwHide') |nmap <buffer> <silent> <nowait> a <Plug>NetrwHide_a|endif - if !hasmapto('<Plug>NetrwBrowseUpDir') |nmap <buffer> <silent> <nowait> - <Plug>NetrwBrowseUpDir |endif + if !hasmapto('<Plug>NetrwBrowseUpDir') |nmap <buffer> <silent> <nowait> - <Plug>NetrwBrowseUpDir|endif if !hasmapto('<Plug>NetrwOpenFile') |nmap <buffer> <silent> <nowait> % <Plug>NetrwOpenFile|endif if !hasmapto('<Plug>NetrwBadd_cb') |nmap <buffer> <silent> <nowait> cb <Plug>NetrwBadd_cb|endif if !hasmapto('<Plug>NetrwBadd_cB') |nmap <buffer> <silent> <nowait> cB <Plug>NetrwBadd_cB|endif @@ -6614,9 +6638,9 @@ fun! s:NetrwMarkFiles(islocal,...) while i <= a:0 if a:islocal if v:version > 704 || (v:version == 704 && has("patch656")) - let mffiles= glob(fnameescape(a:{i}),0,1,1) + let mffiles= glob(a:{i},0,1,1) else - let mffiles= glob(fnameescape(a:{i}),0,1) + let mffiles= glob(a:{i},0,1) endif else let mffiles= [a:{i}] @@ -6757,13 +6781,15 @@ fun! s:NetrwMarkFile(islocal,fname) if index(s:netrwmarkfilelist,dname) == -1 " append new filename to global markfilelist call add(s:netrwmarkfilelist,s:ComposePath(b:netrw_curdir,a:fname)) -" call Decho("append filename<".a:fname."> to global markfilelist<".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>")) +" call Decho("append filename<".a:fname."> to global s:markfilelist<".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>")) else " remove new filename from global markfilelist -" call Decho("filter(".string(s:netrwmarkfilelist).",'v:val != '.".dname.")",'~'.expand("<slnum>")) +" call Decho("remove new filename from global s:markfilelist",'~'.expand("<slnum>")) +" call Decho("..filter(".string(s:netrwmarkfilelist).",'v:val != '.".dname.")",'~'.expand("<slnum>")) call filter(s:netrwmarkfilelist,'v:val != "'.dname.'"') -" call Decho("ending s:netrwmarkfilelist <".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>")) +" call Decho("..ending s:netrwmarkfilelist <".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>")) if s:netrwmarkfilelist == [] +" call Decho("s:netrwmarkfilelist is empty; unlet it",'~'.expand("<slnum>")) unlet s:netrwmarkfilelist endif endif @@ -6787,7 +6813,8 @@ fun! s:NetrwMarkFile(islocal,fname) endif endif let @@= ykeep -" call Dret("s:NetrwMarkFile : s:netrwmarkfilelist_".curbufnr."<".(exists("s:netrwmarkfilelist_{curbufnr}")? string(s:netrwmarkfilelist_{curbufnr}) : " doesn't exist").">") +" call Decho("s:netrwmarkfilelist[".(exists("s:netrwmarkfilelist")? string(s:netrwmarkfilelist) : "")."] (avail in all buffers)",'~'.expand("<slnum>")) +" call Dret("s:NetrwMarkFile : s:netrwmarkfilelist_".curbufnr."<".(exists("s:netrwmarkfilelist_{curbufnr}")? string(s:netrwmarkfilelist_{curbufnr}) : " doesn't exist")."> (buf#".curbufnr."list)") endfun " --------------------------------------------------------------------- @@ -7078,17 +7105,8 @@ fun! s:NetrwMarkFileCopy(islocal,...) " call Dret("s:NetrwMarkFileCopy : lcd failure") return endif - if v:version < 704 || (v:version == 704 && !has("patch1107")) - call s:NetrwExe("sil !".g:netrw_localrmdir.g:netrw_localrmdiropt." ".s:ShellEscape(tmpdir,1)) - if v:shell_error != 0 - call netrw#ErrorMsg(s:WARNING,"consider setting g:netrw_localrmdir<".g:netrw_localrmdir."> to something that works",80) -" " call Dret("s:NetrwMarkFileCopy : failed: sil !".g:netrw_localrmdir." ".s:ShellEscape(tmpdir,1) ) - return - endif - else - if delete(tmpdir,"d") - call netrw#ErrorMsg(s:ERROR,"unable to delete directory <".tmpdir.">!",103) - endif + if delete(tmpdir,"d") + call netrw#ErrorMsg(s:ERROR,"unable to delete directory <".tmpdir.">!",103) endif else if s:NetrwLcd(curdir) @@ -7542,8 +7560,9 @@ fun! s:NetrwMarkFileGrep(islocal) let curdir = s:NetrwGetCurdir(a:islocal) if exists("s:netrwmarkfilelist") -" call Decho("s:netrwmarkfilelist".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>")) +" call Decho("using s:netrwmarkfilelist".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>")) let netrwmarkfilelist= join(map(deepcopy(s:netrwmarkfilelist), "fnameescape(v:val)")) +" call Decho("keeping copy of s:netrwmarkfilelist in function-local variable,'~'.expand("<slnum>"))" call s:NetrwUnmarkAll() else " call Decho('no marked files, using "*"','~'.expand("<slnum>")) @@ -7551,6 +7570,7 @@ fun! s:NetrwMarkFileGrep(islocal) endif " ask user for pattern +" call Decho("ask user for search pattern",'~'.expand("<slnum>")) call inputsave() let pat= input("Enter pattern: ","") call inputrestore() @@ -8663,7 +8683,7 @@ fun! s:NetrwUpload(fname,tgt,...) " -i : turns off interactive prompting from ftp " -n unix : DON'T use <.netrc>, even though it exists " -n win32: quit being obnoxious about password - NetrwKeepj norm! 1Gdd + NetrwKeepj norm! 1G"_dd call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." ".g:netrw_ftp_options) " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) sil NetrwKeepj g/Local directory now/d @@ -9501,15 +9521,19 @@ fun! s:NetrwWideListing() " fpl: filenames per line " fpc: filenames per column setl ma noro + let keepa= @a " call Decho("setl ma noro",'~'.expand("<slnum>")) let b:netrw_cpf= 0 if line("$") >= w:netrw_bannercnt + " determine the maximum filename size; use that to set cpf exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g/^./if virtcol("$") > b:netrw_cpf|let b:netrw_cpf= virtcol("$")|endif' NetrwKeepj call histdel("/",-1) else + let @a= keepa " call Dret("NetrwWideListing") return endif + " allow for two spaces to separate columns let b:netrw_cpf= b:netrw_cpf + 2 " call Decho("b:netrw_cpf=max_filename_length+2=".b:netrw_cpf,'~'.expand("<slnum>")) @@ -9532,10 +9556,11 @@ fun! s:NetrwWideListing() if newcolend > line("$") | let newcolend= line("$") | endif let newcolqty= newcolend - newcolstart exe newcolstart + " COMBAK: both of the visual-mode using lines below are problematic vis-a-vis @* if newcolqty == 0 - exe "sil! NetrwKeepj norm! 0\<c-v>$hx".w:netrw_bannercnt."G$p" + exe "sil! NetrwKeepj norm! 0\<c-v>$h\"ax".w:netrw_bannercnt."G$\"ap" else - exe "sil! NetrwKeepj norm! 0\<c-v>".newcolqty.'j$hx'.w:netrw_bannercnt.'G$p' + exe "sil! NetrwKeepj norm! 0\<c-v>".newcolqty.'j$h"ax'.w:netrw_bannercnt.'G$"ap' endif exe "sil! NetrwKeepj ".newcolstart.','.newcolend.'d _' exe 'sil! NetrwKeepj '.w:netrw_bannercnt @@ -9546,6 +9571,7 @@ fun! s:NetrwWideListing() exe 'nno <buffer> <silent> b :call search(''^.\\|\s\s\zs\S'',''bW'')'."\<cr>" " call Decho("NetrwWideListing) setl noma nomod ro",'~'.expand("<slnum>")) exe "setl ".g:netrw_bufsettings + let @a= keepa " call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) " call Dret("NetrwWideListing") return @@ -9743,6 +9769,7 @@ fun! s:PerformListing(islocal) exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$sort!'.' '.g:netrw_sort_options endif endif + " remove priority pattern prefix " call Decho("remove priority pattern prefix",'~'.expand("<slnum>")) exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\d\{3}'.g:netrw_sepchr.'//e' @@ -9781,6 +9808,7 @@ fun! s:PerformListing(islocal) " call Decho('exe sil NetrwKeepj '.w:netrw_bannercnt.',$sort!','~'.expand("<slnum>")) exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$sort!'.' '.g:netrw_sort_options endif +" call Decho("remove leading digits/ (sorting) information from listing",'~'.expand("<slnum>")) exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\d\{-}\///e' NetrwKeepj call histdel("/",-1) endif @@ -9843,6 +9871,7 @@ fun! s:PerformListing(islocal) " call Decho("exe setl ts=".(g:netrw_maxfilenamelen+1),'~'.expand("<slnum>")) exe "setl ts=".(g:netrw_maxfilenamelen+1) endif +" call Decho("PerformListing buffer:",'~'.expand("<slnum>")) " call DechoBuf(bufnr("%")) if exists("s:treecurpos") @@ -10810,7 +10839,6 @@ fun! s:LocalListing() for filename in filelist " call Decho(" ",'~'.expand("<slnum>")) " call Decho("for filename in filelist: filename<".filename.">",'~'.expand("<slnum>")) -" call DechoBuf(bufnr("%"),"COMBAK#1") if getftype(filename) == "link" " indicate a symbolic link @@ -10868,10 +10896,10 @@ fun! s:LocalListing() if w:netrw_liststyle == s:LONGLIST let sz = getfsize(filename) + let fsz = strpart(" ",1,15-strlen(sz)).sz if g:netrw_sizestyle =~# "[hH]" let sz= s:NetrwHumanReadable(sz) endif - let fsz = strpart(" ",1,15-strlen(sz)).sz let longfile= printf("%-".(g:netrw_maxfilenamelen+1)."s",pfile) let pfile = longfile.fsz." ".strftime(g:netrw_timefmt,getftime(filename)) " call Decho("longlist support: sz=".sz." fsz=".fsz,'~'.expand("<slnum>")) @@ -10879,10 +10907,11 @@ fun! s:LocalListing() if g:netrw_sort_by =~# "^t" " sort by time (handles time up to 1 quintillion seconds, US) + " Decorate listing by prepending a timestamp/ . Sorting will then be done based on time. " call Decho("getftime(".filename.")=".getftime(filename),'~'.expand("<slnum>")) let t = getftime(filename) let ft = strpart("000000000000000000",1,18-strlen(t)).t -" call Decho("exe NetrwKeepj put ='".ft.'/'.filename."'",'~'.expand("<slnum>")) +" call Decho("exe NetrwKeepj put ='".ft.'/'.pfile."'",'~'.expand("<slnum>")) let ftpfile= ft.'/'.pfile sil! NetrwKeepj put=ftpfile @@ -10903,7 +10932,7 @@ fun! s:LocalListing() " call Decho("exe NetrwKeepj put ='".pfile."'",'~'.expand("<slnum>")) sil! NetrwKeepj put=pfile endif -" call DechoBuf(bufnr("%"),"COMBAK#2") +" call DechoBuf(bufnr("%"),"bufnr(%)") endfor " cleanup any windows mess at end-of-line @@ -10950,9 +10979,10 @@ fun! s:NetrwLocalRename(path) range " call Dfunc("NetrwLocalRename(path<".a:path.">)") " preparation for removing multiple files/directories - let ykeep = @@ - let ctr = a:firstline - let svpos = winsaveview() + let ykeep = @@ + let ctr = a:firstline + let svpos = winsaveview() + let all = 0 " call Decho("saving posn to svpos<".string(svpos).">",'~'.expand("<slnum>")) " rename files given by the markfilelist @@ -10980,6 +11010,23 @@ fun! s:NetrwLocalRename(path) range let newname = substitute(oldname,subfrom,subto,'') endif endif + if !all && filereadable(newname) + call inputsave() + let response= input("File<".newname."> already exists; do you want to overwrite it? (y/all/n) ") + call inputrestore() + if response == "all" + let all= 1 + elseif response != "y" && response != "yes" + " refresh the directory +" call Decho("refresh the directory listing",'~'.expand("<slnum>")) + NetrwKeepj call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./')) +" call Decho("restoring posn to svpos<".string(svpos).">",'~'.expand("<slnum>")) + NetrwKeepj call winrestview(svpos) + let @@= ykeep +" call Dret("NetrwLocalRename") + return + endif + endif call rename(oldname,newname) endfor call s:NetrwUnmarkList(bufnr("%"),b:netrw_curdir) @@ -11003,14 +11050,14 @@ fun! s:NetrwLocalRename(path) range NetrwKeepj norm! 0 let oldname= s:ComposePath(a:path,curword) -" call Decho("oldname<".oldname.">",'~'.expand("<slnum>")) +" call Decho("oldname<".oldname.">",'~'.expand("<slnum>")) call inputsave() let newname= input("Moving ".oldname." to : ",substitute(oldname,'/*$','','e')) call inputrestore() call rename(oldname,newname) -" call Decho("renaming <".oldname."> to <".newname.">",'~'.expand("<slnum>")) +" call Decho("renaming <".oldname."> to <".newname.">",'~'.expand("<slnum>")) let ctr= ctr + 1 endwhile @@ -11220,7 +11267,9 @@ fun! netrw#Expose(varname) " call Dfunc("netrw#Expose(varname<".a:varname.">)") if exists("s:".a:varname) exe "let retval= s:".a:varname +" call Decho("retval=".retval,'~'.expand("<slnum>")) if exists("g:netrw_pchk") +" call Decho("type(g:netrw_pchk=".g:netrw_pchk.")=".type(retval),'~'.expand("<slnum>")) if type(retval) == 3 let retval = copy(retval) let i = 0 @@ -11229,10 +11278,13 @@ fun! netrw#Expose(varname) let i = i + 1 endwhile endif -" call Dret("netrw#Expose ".string(retval)) +" call Dret("netrw#Expose ".string(retval)),'~'.expand("<slnum>")) return string(retval) + else +" call Decho("g:netrw_pchk doesn't exist",'~'.expand("<slnum>")) endif else +" call Decho("s:".a:varname." doesn't exist",'~'.expand("<slnum>")) let retval= "n/a" endif @@ -11793,6 +11845,9 @@ fun! s:NetrwExe(cmd) " call Decho("exe ".a:cmd,'~'.expand("<slnum>")) exe a:cmd endif + if v:shell_error + call netrw#ErrorMsg(s:WARNING,"shell signalled an error",106) + endif " call Dret("s:NetrwExe : v:shell_error=".v:shell_error) endfun diff --git a/runtime/autoload/netrwSettings.vim b/runtime/autoload/netrwSettings.vim index 327db6a540..bed5cfc455 100644 --- a/runtime/autoload/netrwSettings.vim +++ b/runtime/autoload/netrwSettings.vim @@ -1,6 +1,6 @@ " netrwSettings.vim: makes netrw settings simpler " Date: Nov 09, 2016 -" Maintainer: Charles E Campbell <drchipNOSPAM at campbellfamily dot biz> +" Maintainer: Charles E Campbell <NcampObell@SdrPchip.AorgM-NOSPAM> " Version: 16 " Copyright: Copyright (C) 1999-2007 Charles E. Campbell {{{1 " Permission is hereby granted to use and distribute this code, diff --git a/runtime/autoload/netrw_gitignore.vim b/runtime/autoload/netrw_gitignore.vim index da3f50bca0..1b55e2488a 100644 --- a/runtime/autoload/netrw_gitignore.vim +++ b/runtime/autoload/netrw_gitignore.vim @@ -18,61 +18,5 @@ " holder be liable for any damages resulting from the use " of this software. function! netrw_gitignore#Hide(...) - let additional_files = a:000 - - let default_files = ['.gitignore', '.git/info/exclude'] - - " get existing global/system gitignore files - let global_gitignore = expand(substitute(system("git config --global core.excludesfile"), '\n', '', 'g')) - if global_gitignore !=# '' - let default_files = add(default_files, global_gitignore) - endif - let system_gitignore = expand(substitute(system("git config --system core.excludesfile"), '\n', '', 'g')) - if system_gitignore !=# '' - let default_files = add(default_files, system_gitignore) - endif - - " append additional files if given as function arguments - if additional_files !=# [] - let files = extend(default_files, additional_files) - else - let files = default_files - endif - - " keep only existing/readable files - let gitignore_files = [] - for file in files - if filereadable(file) - let gitignore_files = add(gitignore_files, file) - endif - endfor - - " get contents of gitignore patterns from those files - let gitignore_lines = [] - for file in gitignore_files - for line in readfile(file) - " filter empty lines and comments - if line !~# '^#' && line !~# '^$' - let gitignore_lines = add(gitignore_lines, line) - endif - endfor - endfor - - " convert gitignore patterns to Netrw/Vim regex patterns - let escaped_lines = [] - for line in gitignore_lines - let escaped = line - let escaped = substitute(escaped, '\*\*', '*', 'g') - let escaped = substitute(escaped, '\.', '\\.', 'g') - let escaped = substitute(escaped, '\$', '\\$', 'g') - let escaped = substitute(escaped, '*', '.*', 'g') - " correction: dot, dollar and asterisks chars shouldn't be escaped when - " within regex matching groups. - let escaped = substitute(escaped, '\(\[[^]]*\)\zs\\\.', '\.', 'g') - let escaped = substitute(escaped, '\(\[[^]]*\)\zs\\\$', '\$', 'g') - let escaped = substitute(escaped, '\(\[[^]]*\)\zs\.\*', '*', 'g') - let escaped_lines = add(escaped_lines, escaped) - endfor - - return join(escaped_lines, ',') + return substitute(substitute(system('git ls-files --other --ignored --exclude-standard --directory'), '\n', ',', 'g'), ',$', '', '') endfunction diff --git a/runtime/doc/diff.txt b/runtime/doc/diff.txt index 2a972483ff..3544882ceb 100644 --- a/runtime/doc/diff.txt +++ b/runtime/doc/diff.txt @@ -48,7 +48,7 @@ In each of the edited files these options are set: 'scrollbind' on 'cursorbind' on 'scrollopt' includes "hor" - 'wrap' off + 'wrap' off, or leave as-is if 'diffopt' includes "followwrap" 'foldmethod' "diff" 'foldcolumn' value from 'diffopt', default is 2 @@ -132,7 +132,7 @@ Otherwise they are set to their default value: 'scrollbind' off 'cursorbind' off 'scrollopt' without "hor" - 'wrap' on + 'wrap' on, or leave as-is if 'diffopt' includes "followwrap" 'foldmethod' "manual" 'foldcolumn' 0 diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 384bdd63a4..b96fc4ac01 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -4449,11 +4449,12 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()* augroup autocmd groups buffer buffer names behave :behave suboptions - cmdline |cmdline-completion| + cmdline |cmdline-completion| result color color schemes command Ex command (and arguments) compiler compilers cscope |:cscope| suboptions + diff_buffer |:diffget| and |:diffput| completion dir directory names environment environment variable names event autocommand events @@ -4481,14 +4482,19 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()* user user names var user variables - If {pat} is an empty string then all matches are returned. - Otherwise only items matching {pat} are returned. See - |wildcards| for the use of special characters in {pat}. + If {pat} is an empty string, then all the matches are + returned. Otherwise only items matching {pat} are returned. + See |wildcards| for the use of special characters in {pat}. If the optional {filtered} flag is set to 1, then 'wildignore' is applied to filter the results. Otherwise all the matches are returned. The 'wildignorecase' option always applies. + If {type} is "cmdline", then the |cmdline-completion| result is + returned. For example, to complete the possible values after + a ":call" command: > + echo getcompletion('call ', 'cmdline') +< If there are no matches, an empty list is returned. An invalid value for {type} produces an error. @@ -5607,7 +5613,6 @@ jobstart({cmd}[, {opts}]) *jobstart()* before invoking `on_stderr`. |channel-buffered| stdout_buffered: (boolean) Collect data until EOF (stream closed) before invoking `on_stdout`. |channel-buffered| - TERM: (string) Sets the `pty` $TERM environment variable. width: (number) Width of the `pty` terminal. {opts} is passed as |self| dictionary to the callback; the diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index 172821ac28..c824a9f9f6 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1144,6 +1144,7 @@ tag command action ~ |:bNext| :bN[ext] go to previous buffer in the buffer list |:ball| :ba[ll] open a window for each buffer in the buffer list |:badd| :bad[d] add buffer to the buffer list +|:balt| :balt like ":badd" but also set the alternate file |:bdelete| :bd[elete] remove a buffer from the buffer list |:behave| :be[have] set mouse and selection behavior |:belowright| :bel[owright] make split window appear right or below diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index a497efa47e..ce3788e0fc 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1971,6 +1971,8 @@ A jump table for the options with a short description can be found at |Q_op|. foldcolumn:{n} Set the 'foldcolumn' option to {n} when starting diff mode. Without this 2 is used. + followwrap Follow the 'wrap' option and leave as it is. + internal Use the internal diff library. This is ignored when 'diffexpr' is set. *E960* When running out of memory when writing a @@ -6289,6 +6291,29 @@ A jump table for the options with a short description can be found at |Q_op|. attributes instead of "cterm" attributes. |highlight-guifg| Requires an ISO-8613-3 compatible terminal. + *'termpastefilter'* *'tpf'* +'termpastefilter' 'tpf' string (default: "BS,HT,ESC,DEL") + global + A comma separated list of options for specifying control characters + to be removed from the text pasted into the terminal window. The + supported values are: + + BS Backspace + + HT TAB + + FF Form feed + + ESC Escape + + DEL DEL + + C0 Other control characters, excluding Line feed and + Carriage return < ' ' + + C1 Control characters 0x80...0x9F + + *'terse'* *'noterse'* 'terse' boolean (default off) global diff --git a/runtime/doc/pi_netrw.txt b/runtime/doc/pi_netrw.txt index 9a75a95f23..d5dfd37fab 100644 --- a/runtime/doc/pi_netrw.txt +++ b/runtime/doc/pi_netrw.txt @@ -1,9 +1,9 @@ -*pi_netrw.txt* For Vim version 8.1. Last change: 2019 Jul 17 +*pi_netrw.txt* For Vim version 8.2. Last change: 2020 Aug 15 ------------------------------------------------ NETRW REFERENCE MANUAL by Charles E. Campbell ------------------------------------------------ -Author: Charles E. Campbell <NdrOchip@ScampbellPfamily.AbizM> +Author: Charles E. Campbell <NcampObell@SdrPchip.AorgM-NOSPAM> (remove NOSPAM from Campbell's email first) Copyright: Copyright (C) 2017 Charles E Campbell *netrw-copyright* @@ -1565,8 +1565,8 @@ So, for example: > file.rcs,v -> NFH_rcsCOMMAv() < If more such translations are necessary, please send me email: > - NdrOchip at ScampbellPfamily.AbizM - NOSPAM -with a request. + NcampObell@SdrPchip.AorgM-NOSPAM +with a request. (remove the embedded NOSPAM first) Associated setting variable: |g:netrw_browsex_viewer| @@ -3054,7 +3054,7 @@ your browsing preferences. (see also: |netrw-settings|) (see |netrw-c-tab|). *g:netrw_xstrlen* Controls how netrw computes string lengths, - including multi-byte characters' string + including multibyte characters' string length. (thanks to N Weibull, T Mechelynck) =0: uses Vim's built-in strlen() =1: number of codepoints (Latin a + combining @@ -3229,7 +3229,8 @@ If there are marked files: (see |netrw-mf|) R [query: reply with s/^\(.*\)\.c$/\1.cpp/] < This example will mark all *.c files and then rename them to *.cpp - files. + files. Netrw will protect you from overwriting local files without + confirmation, but not remote ones. The ctrl-X character has special meaning for renaming files: > @@ -3512,7 +3513,7 @@ Example: Clear netrw's marked file list via a mapping on gu > - Click "Add..." - Set External Editor (adjust path as needed, include the quotes and !.! at the end): - "c:\Program Files\Vim\vim70\gvim.exe" !.! + "c:\Program Files\Vim\vim82\gvim.exe" !.! - Check that the filetype in the box below is {asterisk}.{asterisk} (all files), or whatever types you want (cec: change {asterisk} to * ; I had to @@ -3762,8 +3763,8 @@ by obtaining a copy of the latest (often developmental) netrw at: The <netrw.vim> script is typically installed on systems as something like: > - /usr/local/share/vim/vim7x/plugin/netrwPlugin.vim - /usr/local/share/vim/vim7x/autoload/netrw.vim + /usr/local/share/vim/vim8x/plugin/netrwPlugin.vim + /usr/local/share/vim/vim8x/autoload/netrw.vim (see output of :echo &rtp) < which is loaded automatically at startup (assuming :set nocp). If you @@ -3836,12 +3837,30 @@ netrw: Please send that information to <netrw.vim>'s maintainer along with the o/s you're using and the vim version that you're using - (see |:version|) > - NdrOchip at ScampbellPfamily.AbizM - NOSPAM + (see |:version|) (remove the embedded NOSPAM first) > + NcampObell@SdrPchip.AorgM-NOSPAM < ============================================================================== 12. History *netrw-history* {{{1 + v169: Dec 20, 2019 * (reported by amkarthik) that netrw's x + (|netrw-x|) would throw an error when + attempting to open a local directory. + v168: Dec 12, 2019 * scp timeout error message not reported, + hopefully now fixed (Shane Xb Qian) + + v167: Nov 29, 2019 * netrw does a save&restore on @* and @+. + That causes problems with the clipboard. + Now restores occurs only if @* or @+ have + been changed. + * netrw will change @* or @+ less often. + Never if I happen to have caught all the + operations that modify the unnamed + register (which also writes @*). + * Modified hiding behavior so that "s" + will not ignore hiding. + v166: Nov 06, 2019 * Removed a space from a nmap for "-" + * Numerous debugging statement changes v163: Dec 05, 2017 * (Cristi Balan) reported that a setting ('sel') was left changed * (Holger Mitschke) reported a problem with @@ -3852,6 +3871,8 @@ netrw: * (Holger Mitschke) amended this help file with additional |g:netrw_special_syntax| items + * Prioritized wget over curl for + g:netrw_http_cmd v162: Sep 19, 2016 * (haya14busa) pointed out two syntax errors with a patch; these are now fixed. Oct 26, 2016 * I started using mate-terminal and found that @@ -4265,7 +4286,7 @@ netrw: Vim editor by Bram Moolenaar (Thanks, Bram!) dav support by C Campbell fetch support by Bram Moolenaar and C Campbell - ftp support by C Campbell <NdrOchip@ScampbellPfamily.AbizM> + ftp support by C Campbell <NcampObell@SdrPchip.AorgM-NOSPAM> http support by Bram Moolenaar <bram@moolenaar.net> rcp rsync support by C Campbell (suggested by Erik Warendorph) diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index 2c3ffcbe9a..35efb1bbce 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -1039,6 +1039,11 @@ list of buffers. |unlisted-buffer| line when the buffer is first entered. Note that other commands after the + will be ignored. + *:balt* +:balt [+lnum] {fname} + Like `:badd` and also set the alternate file for the current + window to {fname}. + :[N]bd[elete][!] *:bd* *:bdel* *:bdelete* *E516* :bd[elete][!] [N] Unload buffer [N] (default: current buffer) and delete it from diff --git a/runtime/lua/vim/lsp/log.lua b/runtime/lua/vim/lsp/log.lua index b6e91e37b9..4e8d2d4202 100644 --- a/runtime/lua/vim/lsp/log.lua +++ b/runtime/lua/vim/lsp/log.lua @@ -10,13 +10,7 @@ local log = {} -- Can be used to lookup the number from the name or the name from the number. -- Levels by name: 'trace', 'debug', 'info', 'warn', 'error' -- Level numbers begin with 'trace' at 0 -log.levels = { - TRACE = 0; - DEBUG = 1; - INFO = 2; - WARN = 3; - ERROR = 4; -} +log.levels = vim.log.levels -- Default log level is warn. local current_log_level = log.levels.WARN diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index e33e0109b6..00bdeecef3 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -726,7 +726,7 @@ function M.focusable_float(unique_name, fn) local bufnr = api.nvim_get_current_buf() do local win = find_window_by_var(unique_name, bufnr) - if win and api.nvim_win_is_valid(win) and not vim.fn.pumvisible() then + if win and api.nvim_win_is_valid(win) and vim.fn.pumvisible() == 0 then api.nvim_set_current_win(win) api.nvim_command("stopinsert") return diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index 38ac182e32..3af66b134c 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -44,13 +44,18 @@ function M._create_parser(bufnr, lang, opts) self:_on_bytes(...) end - local function detach_cb() + local function detach_cb(_, ...) if parsers[bufnr] == self then parsers[bufnr] = nil end + self:_on_detach(...) end - a.nvim_buf_attach(self:source(), false, {on_bytes=bytes_cb, on_detach=detach_cb, preview=true}) + local function reload_cb(_, ...) + self:_on_reload(...) + end + + a.nvim_buf_attach(self:source(), false, {on_bytes=bytes_cb, on_detach=detach_cb, on_reload=reload_cb, preview=true}) self:parse() diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index 275e960e28..6b833c6d35 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -113,8 +113,9 @@ function TSHighlighter.new(tree, opts) opts = opts or {} self.tree = tree tree:register_cbs { - on_changedtree = function(...) self:on_changedtree(...) end, - on_bytes = function(...) self:on_bytes(...) end + on_changedtree = function(...) self:on_changedtree(...) end; + on_bytes = function(...) self:on_bytes(...) end; + on_detach = function(...) self:on_detach(...) end; } self.bufnr = tree:source() @@ -176,6 +177,10 @@ function TSHighlighter:on_bytes(_, _, start_row, _, _, _, _, _, new_end) a.nvim__buf_redraw_range(self.bufnr, start_row, start_row + new_end + 1) end +function TSHighlighter:on_detach() + self:destroy() +end + function TSHighlighter:on_changedtree(changes) for _, ch in ipairs(changes or {}) do a.nvim__buf_redraw_range(self.bufnr, ch[1], ch[3]+1) diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua index d60cd2d0c7..eed28e0e41 100644 --- a/runtime/lua/vim/treesitter/language.lua +++ b/runtime/lua/vim/treesitter/language.lua @@ -2,12 +2,12 @@ local a = vim.api local M = {} ---- Asserts that the provided language is installed, and optionnaly provide a path for the parser +--- Asserts that the provided language is installed, and optionally provide a path for the parser -- -- Parsers are searched in the `parser` runtime directory. -- -- @param lang The language the parser should parse --- @param path Optionnal path the parser is located at +-- @param path Optional path the parser is located at -- @param silent Don't throw an error if language not found function M.require_language(lang, path, silent) if vim._ts_has_language(lang) then diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index c864fe5878..28a87f26c7 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -6,14 +6,14 @@ local LanguageTree = {} LanguageTree.__index = LanguageTree -- Represents a single treesitter parser for a language. --- The language can contain child languages with in it's range, +-- The language can contain child languages with in its range, -- hence the tree. -- -- @param source Can be a bufnr or a string of text to parse -- @param lang The language this tree represents -- @param opts Options table --- @param opts.queries A table of language to injection query strings --- This is useful for overridding the built in runtime file +-- @param opts.queries A table of language to injection query strings. +-- This is useful for overriding the built-in runtime file -- searching for the injection language query per language. function LanguageTree.new(source, lang, opts) language.require_language(lang) @@ -21,8 +21,8 @@ function LanguageTree.new(source, lang, opts) local custom_queries = opts.queries or {} local self = setmetatable({ - _source=source, - _lang=lang, + _source = source, + _lang = lang, _children = {}, _regions = {}, _trees = {}, @@ -35,6 +35,7 @@ function LanguageTree.new(source, lang, opts) _callbacks = { changedtree = {}, bytes = {}, + detach = {}, child_added = {}, child_removed = {} }, @@ -44,12 +45,17 @@ function LanguageTree.new(source, lang, opts) return self end --- Invalidates this parser and all it's children -function LanguageTree:invalidate() +-- Invalidates this parser and all its children +function LanguageTree:invalidate(reload) self._valid = false + -- buffer was reloaded, reparse all trees + if reload then + self._trees = {} + end + for _, child in ipairs(self._children) do - child:invalidate() + child:invalidate(reload) end end @@ -97,7 +103,7 @@ function LanguageTree:parse() self._trees = {} -- If there are no ranges, set to an empty list - -- so the included ranges in the parser ar cleared. + -- so the included ranges in the parser are cleared. if self._regions and #self._regions > 0 then for i, ranges in ipairs(self._regions) do local old_tree = old_trees[i] @@ -214,7 +220,7 @@ function LanguageTree:remove_child(lang) end end --- Destroys this language tree and all it's children. +-- Destroys this language tree and all its children. -- Any cleanup logic should be performed here. -- Note, this DOES NOT remove this tree from a parent. -- `remove_child` must be called on the parent to remove it. @@ -241,7 +247,7 @@ end -- -- Note, this call invalidates the tree and requires it to be parsed again. -- --- @param regions A list of regions this tree should manange and parse. +-- @param regions A list of regions this tree should manage and parse. function LanguageTree:set_included_regions(regions) -- Transform the tables from 4 element long to 6 element long (with byte offset) for _, region in ipairs(regions) do @@ -397,14 +403,24 @@ function LanguageTree:_on_bytes(bufnr, changed_tick, new_row, new_col, new_byte) end +function LanguageTree:_on_reload() + self:invalidate(true) +end + + +function LanguageTree:_on_detach(...) + self:invalidate(true) + self:_do_callback('detach', ...) +end + --- Registers callbacks for the parser -- @param cbs An `nvim_buf_attach`-like table argument with the following keys : -- `on_bytes` : see `nvim_buf_attach`, but this will be called _after_ the parsers callback. --- `on_changedtree` : a callback that will be called everytime the tree has syntactical changes. +-- `on_changedtree` : a callback that will be called every time the tree has syntactical changes. -- it will only be passed one argument, that is a table of the ranges (as node ranges) that -- changed. -- `on_child_added` : emitted when a child is added to the tree. --- `on_child_removed` : emitted when a child is remvoed from the tree. +-- `on_child_removed` : emitted when a child is removed from the tree. function LanguageTree:register_cbs(cbs) if not cbs then return end @@ -416,6 +432,10 @@ function LanguageTree:register_cbs(cbs) table.insert(self._callbacks.bytes, cbs.on_bytes) end + if cbs.on_detach then + table.insert(self._callbacks.detach, cbs.on_detach) + end + if cbs.on_child_added then table.insert(self._callbacks.child_added, cbs.on_child_added) end diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index e49f54681d..8b94348994 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -111,7 +111,7 @@ end --- Gets the text corresponding to a given node -- @param node the node --- @param bufnr the buffer from which the node in extracted. +-- @param bufnr the buffer from which the node is extracted. function M.get_node_text(node, source) local start_row, start_col, start_byte = node:start() local end_row, end_col, end_byte = node:end_() @@ -215,10 +215,10 @@ predicate_handlers["vim-match?"] = predicate_handlers["match?"] local directive_handlers = { ["set!"] = function(_, _, _, pred, metadata) if #pred == 4 then - -- (set! @capture "key" "value") + -- (#set! @capture "key" "value") metadata[pred[2]][pred[3]] = pred[4] else - -- (set! "key" "value") + -- (#set! "key" "value") metadata[pred[2]] = pred[3] end end, @@ -245,7 +245,7 @@ local directive_handlers = { end } ---- Adds a new predicates to be used in queries +--- Adds a new predicate to be used in queries -- -- @param name the name of the predicate, without leading # -- @param handler the handler function to be used @@ -355,10 +355,10 @@ end --- Iterates of the captures of self on a given range. -- --- @param node The node under witch the search will occur +-- @param node The node under which the search will occur -- @param buffer The source buffer to search -- @param start The starting line of the search --- @param stop The stoping line of the search (end-exclusive) +-- @param stop The stopping line of the search (end-exclusive) -- -- @returns The matching capture id -- @returns The captured node @@ -388,12 +388,12 @@ function Query:iter_captures(node, source, start, stop) return iter end ---- Iterates of the matches of self on a given range. +--- Iterates the matches of self on a given range. -- --- @param node The node under witch the search will occur +-- @param node The node under which the search will occur -- @param buffer The source buffer to search -- @param start The starting line of the search --- @param stop The stoping line of the search (end-exclusive) +-- @param stop The stopping line of the search (end-exclusive) -- -- @returns The matching pattern id -- @returns The matching match diff --git a/runtime/plugin/netrwPlugin.vim b/runtime/plugin/netrwPlugin.vim index 2d67f6a4e4..87302cf23b 100644 --- a/runtime/plugin/netrwPlugin.vim +++ b/runtime/plugin/netrwPlugin.vim @@ -1,7 +1,7 @@ " netrwPlugin.vim: Handles file transfer and remote directory listing across a network " PLUGIN SECTION -" Date: Feb 08, 2016 -" Maintainer: Charles E Campbell <NdrOchip@ScampbellPfamily.AbizM-NOSPAM> +" Date: Feb 08, 2016 - Jan 07, 2020 +" Maintainer: Charles E Campbell <NcampObell@SdrPchip.AorgM-NOSPAM> " GetLatestVimScripts: 1075 1 :AutoInstall: netrw.vim " Copyright: Copyright (C) 1999-2013 Charles E. Campbell {{{1 " Permission is hereby granted to use and distribute this code, @@ -20,7 +20,7 @@ if &cp || exists("g:loaded_netrwPlugin") finish endif -let g:loaded_netrwPlugin = "v165" +let g:loaded_netrwPlugin = "v168" let s:keepcpo = &cpo set cpo&vim "DechoRemOn @@ -81,7 +81,7 @@ if !exists("g:netrw_nogx") if !hasmapto('<Plug>NetrwBrowseX') nmap <unique> gx <Plug>NetrwBrowseX endif - nno <silent> <Plug>NetrwBrowseX :call netrw#BrowseX(expand((exists("g:netrw_gx")? g:netrw_gx : '<cfile>')),netrw#CheckIfRemote())<cr> + nno <silent> <Plug>NetrwBrowseX :call netrw#BrowseX(netrw#GX(),netrw#CheckIfRemote(netrw#GX()))<cr> endif if maparg('gx','v') == "" if !hasmapto('<Plug>NetrwBrowseXVis') diff --git a/runtime/syntax/netrw.vim b/runtime/syntax/netrw.vim index c4d3cf5fda..83ae17445d 100644 --- a/runtime/syntax/netrw.vim +++ b/runtime/syntax/netrw.vim @@ -1,7 +1,7 @@ " Language : Netrw Listing Syntax " Maintainer : Charles E. Campbell -" Last change: Oct 31, 2016 -" Version : 20 NOT RELEASED +" Last change: Nov 07, 2019 +" Version : 20 " --------------------------------------------------------------------- if exists("b:current_syntax") finish diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh index 551b8fb691..f583b2fdea 100755 --- a/scripts/vim-patch.sh +++ b/scripts/vim-patch.sh @@ -180,8 +180,12 @@ preprocess_patch() { local file="$1" local nvim="nvim -u NORC -i NONE --headless" - # Remove *.proto, Make*, gui_*, some if_* - local na_src='proto\|Make*\|gui_*\|if_lua\|if_mzsch\|if_olepp\|if_ole\|if_perl\|if_py\|if_ruby\|if_tcl\|if_xcmdsrv' + # Remove Filelist, README + local na_files='Filelist\|README.*' + 2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/\<\%('"${na_files}"'\)\>@norm! d/\v(^diff)|%$
' +w +q "$file" + + # Remove *.proto, Make*, INSTALL*, gui_*, beval.*, some if_*, gvim, libvterm, tee, VisVim, xpm, xxd + local na_src='auto\|configure.*\|GvimExt\|libvterm\|proto\|tee\|VisVim\|xpm\|xxd\|Make*\|INSTALL*\|beval.*\|gui_*\|if_lua\|if_mzsch\|if_olepp\|if_ole\|if_perl\|if_py\|if_ruby\|if_tcl\|if_xcmdsrv' 2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/\S*\<\%(testdir/\)\@<!\%('"${na_src}"'\)@norm! d/\v(^diff)|%$
' +w +q "$file" # Remove unwanted Vim doc files. @@ -191,10 +195,14 @@ preprocess_patch() { # Remove "Last change ..." changes in doc files. 2>/dev/null $nvim --cmd 'set dir=/tmp' +'%s/^@@.*\n.*For Vim version.*Last change.*\n.*For Vim version.*Last change.*//' +w +q "$file" - # Remove screen dumps, testdir/Make_*.mak files - local na_src_testdir='Make_amiga.mak\|Make_dos.mak\|Make_ming.mak\|Make_vms.mms\|dumps/.*.dump' + # Remove gui, option, setup, screen dumps, testdir/Make_*.mak files + local na_src_testdir='gen_opt_test.vim\|gui_.*\|Make_amiga.mak\|Make_dos.mak\|Make_ming.mak\|Make_vms.mms\|dumps/.*.dump\|setup_gui.vim' 2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/testdir/\<\%('"${na_src_testdir}"'\)\>@norm! d/\v(^diff)|%$
' +w +q "$file" + # Remove testdir/test_*.vim files + local na_src_testdir='balloon.*\|channel.*\|crypt.vim\|gui.*\|job_fails.vim\|json.vim\|mzscheme.vim\|netbeans.*\|paste.vim\|popupwin.*\|restricted.vim\|shortpathname.vim\|tcl.vim\|terminal.*\|xxd.vim' + 2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/testdir/\<test_\%('"${na_src_testdir}"'\)\>@norm! d/\v(^diff)|%$
' +w +q "$file" + # Remove version.c #7555 local na_po='version.c' 2>/dev/null $nvim --cmd 'set dir=/tmp' +'g@^diff --git a/src/\<\%('${na_po}'\)\>@norm! d/\v(^diff)|%$
' +w +q "$file" diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index db77931c16..1a1a178620 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -284,7 +284,7 @@ foreach(sfile ${NVIM_SOURCES} endif() add_custom_command( OUTPUT "${gf_c_h}" "${gf_h_h}" - COMMAND ${CMAKE_C_COMPILER} ${sfile} ${PREPROC_OUTPUT} ${gen_cflags} ${C_FLAGS_ARRAY} + COMMAND ${CMAKE_C_COMPILER} ${sfile} ${PREPROC_OUTPUT} ${gen_cflags} COMMAND "${LUA_PRG}" "${HEADER_GENERATOR}" "${sfile}" "${gf_c_h}" "${gf_h_h}" "${gf_i}" DEPENDS ${depends}) list(APPEND NVIM_GENERATED_FOR_SOURCES "${gf_c_h}") diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index a83b0ea859..0e97b36506 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -119,6 +119,10 @@ Integer nvim_buf_line_count(Buffer buffer, Error *err) /// - on_detach: Lua callback invoked on detach. Args: /// - the string "detach" /// - buffer handle +/// - on_reload: Lua callback invoked on reload. The entire buffer +/// content should be considered changed. Args: +/// - the string "detach" +/// - buffer handle /// - utf_sizes: include UTF-32 and UTF-16 size of the replaced /// region, as args to `on_lines`. /// - preview: also attach to command preview (i.e. 'inccommand') @@ -141,50 +145,57 @@ Boolean nvim_buf_attach(uint64_t channel_id, bool is_lua = (channel_id == LUA_INTERNAL_CALL); BufUpdateCallbacks cb = BUF_UPDATE_CALLBACKS_INIT; + struct { + const char *name; + LuaRef *dest; + } cbs[] = { + { "on_lines", &cb.on_lines }, + { "on_bytes", &cb.on_bytes }, + { "on_changedtick", &cb.on_changedtick }, + { "on_detach", &cb.on_detach }, + { "on_reload", &cb.on_reload }, + { NULL, NULL }, + }; + for (size_t i = 0; i < opts.size; i++) { String k = opts.items[i].key; Object *v = &opts.items[i].value; - if (is_lua && strequal("on_lines", k.data)) { - if (v->type != kObjectTypeLuaRef) { - api_set_error(err, kErrorTypeValidation, "callback is not a function"); - goto error; - } - cb.on_lines = v->data.luaref; - v->data.luaref = LUA_NOREF; - } else if (is_lua && strequal("on_bytes", k.data)) { - if (v->type != kObjectTypeLuaRef) { - api_set_error(err, kErrorTypeValidation, "callback is not a function"); - goto error; - } - cb.on_bytes = v->data.luaref; - v->data.luaref = LUA_NOREF; - } else if (is_lua && strequal("on_changedtick", k.data)) { - if (v->type != kObjectTypeLuaRef) { - api_set_error(err, kErrorTypeValidation, "callback is not a function"); - goto error; - } - cb.on_changedtick = v->data.luaref; - v->data.luaref = LUA_NOREF; - } else if (is_lua && strequal("on_detach", k.data)) { - if (v->type != kObjectTypeLuaRef) { - api_set_error(err, kErrorTypeValidation, "callback is not a function"); - goto error; - } - cb.on_detach = v->data.luaref; - v->data.luaref = LUA_NOREF; - } else if (is_lua && strequal("utf_sizes", k.data)) { - if (v->type != kObjectTypeBoolean) { - api_set_error(err, kErrorTypeValidation, "utf_sizes must be boolean"); - goto error; + bool key_used = false; + if (is_lua) { + for (size_t j = 0; cbs[j].name; j++) { + if (strequal(cbs[j].name, k.data)) { + if (v->type != kObjectTypeLuaRef) { + api_set_error(err, kErrorTypeValidation, + "%s is not a function", cbs[j].name); + goto error; + } + *(cbs[j].dest) = v->data.luaref; + v->data.luaref = LUA_NOREF; + key_used = true; + break; + } } - cb.utf_sizes = v->data.boolean; - } else if (is_lua && strequal("preview", k.data)) { - if (v->type != kObjectTypeBoolean) { - api_set_error(err, kErrorTypeValidation, "preview must be boolean"); - goto error; + + if (key_used) { + continue; + } else if (strequal("utf_sizes", k.data)) { + if (v->type != kObjectTypeBoolean) { + api_set_error(err, kErrorTypeValidation, "utf_sizes must be boolean"); + goto error; + } + cb.utf_sizes = v->data.boolean; + key_used = true; + } else if (strequal("preview", k.data)) { + if (v->type != kObjectTypeBoolean) { + api_set_error(err, kErrorTypeValidation, "preview must be boolean"); + goto error; + } + cb.preview = v->data.boolean; + key_used = true; } - cb.preview = v->data.boolean; - } else { + } + + if (!key_used) { api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data); goto error; } @@ -722,7 +733,8 @@ void nvim_buf_set_text(uint64_t channel_id, Buffer buffer, kExtmarkUndo); - changed_lines((linenr_T)start_row, 0, (linenr_T)end_row, (long)extra, true); + changed_lines((linenr_T)start_row, 0, (linenr_T)end_row + 1, + (long)extra, true); // adjust cursor like an extmark ( i e it was inside last_part_len) if (curwin->w_cursor.lnum == end_row && curwin->w_cursor.col > end_col) { diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 09895a2119..b94c99dc5e 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -545,6 +545,26 @@ Object nvim_exec_lua(String code, Array args, Error *err) return nlua_exec(code, args, err); } +/// Notify the user with a message +/// +/// Relays the call to vim.notify . By default forwards your message in the +/// echo area but can be overriden to trigger desktop notifications. +/// +/// @param msg Message to display to the user +/// @param log_level The log level +/// @param opts Reserved for future use. +/// @param[out] err Error details, if any +Object nvim_notify(String msg, Integer log_level, Dictionary opts, Error *err) + FUNC_API_SINCE(7) +{ + FIXED_TEMP_ARRAY(args, 3); + args.items[0] = STRING_OBJ(msg); + args.items[1] = INTEGER_OBJ(log_level); + args.items[2] = DICTIONARY_OBJ(opts); + + return nlua_exec(STATIC_CSTR_AS_STRING("return vim.notify(...)"), args, err); +} + /// Calls a VimL function. /// /// @param fn Function name diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 140a9c6bcb..3de2e0f342 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -544,7 +544,7 @@ char_u *au_event_disable(char *what) } else { STRCAT(new_ei, what); } - set_string_option_direct((char_u *)"ei", -1, new_ei, OPT_FREE, SID_NONE); + set_string_option_direct("ei", -1, new_ei, OPT_FREE, SID_NONE); xfree(new_ei); return save_ei; @@ -553,7 +553,7 @@ char_u *au_event_disable(char *what) void au_event_restore(char_u *old_ei) { if (old_ei != NULL) { - set_string_option_direct((char_u *)"ei", -1, old_ei, OPT_FREE, SID_NONE); + set_string_option_direct("ei", -1, old_ei, OPT_FREE, SID_NONE); xfree(old_ei); } } diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 0134ee838d..c42a0e2dad 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -596,7 +596,7 @@ void close_buffer(win_T *win, buf_T *buf, int action, bool abort_if_last) // Disable buffer-updates for the current buffer. // No need to check `unload_buf`: in that case the function returned above. - buf_updates_unregister_all(buf); + buf_updates_unload(buf, false); /* * Remove the buffer from the list. @@ -821,7 +821,7 @@ static void free_buffer_stuff(buf_T *buf, int free_flags) map_clear_int(buf, MAP_ALL_MODES, true, true); // clear local abbrevs XFREE_CLEAR(buf->b_start_fenc); - buf_updates_unregister_all(buf); + buf_updates_unload(buf, false); } /* @@ -1726,7 +1726,8 @@ buf_T *buflist_new(char_u *ffname_arg, char_u *sfname_arg, linenr_T lnum, file_id_valid)) != NULL) { xfree(ffname); if (lnum != 0) { - buflist_setfpos(buf, curwin, lnum, (colnr_T)0, false); + buflist_setfpos(buf, (flags & BLN_NOCURWIN) ? NULL : curwin, + lnum, (colnr_T)0, false); } if ((flags & BLN_NOOPT) == 0) { // Copy the options now, if 'cpo' doesn't have 's' and not done already. @@ -2308,6 +2309,10 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options) *num_file = 0; // return values in case of FAIL *file = NULL; + if ((options & BUF_DIFF_FILTER) && !curwin->w_p_diff) { + return FAIL; + } + // Make a copy of "pat" and change "^" to "\(^\|[\/]\)". if (*pat == '^') { patc = xmalloc(STRLEN(pat) + 11); @@ -2344,6 +2349,13 @@ int ExpandBufnames(char_u *pat, int *num_file, char_u ***file, int options) if (!buf->b_p_bl) { // skip unlisted buffers continue; } + if (options & BUF_DIFF_FILTER) { + // Skip buffers not suitable for + // :diffget or :diffput completion. + if (buf == curbuf || !diff_mode_buf(buf)) { + continue; + } + } p = buflist_match(®match, buf, p_wic); if (p != NULL) { if (round == 1) { @@ -2486,6 +2498,7 @@ buflist_nr2name( /// /// @param[in,out] buf Buffer for which line and column are set. /// @param[in,out] win Window for which line and column are set. +/// May be NULL when using :badd. /// @param[in] lnum Line number to be set. If it is zero then only /// options are touched. /// @param[in] col Column number to be set. @@ -2493,7 +2506,7 @@ buflist_nr2name( void buflist_setfpos(buf_T *const buf, win_T *const win, linenr_T lnum, colnr_T col, bool copy_options) - FUNC_ATTR_NONNULL_ALL + FUNC_ATTR_NONNULL_ARG(1) { wininfo_T *wip; @@ -2528,7 +2541,7 @@ void buflist_setfpos(buf_T *const buf, win_T *const win, wip->wi_fpos.lnum = lnum; wip->wi_fpos.col = col; } - if (copy_options) { + if (copy_options && win != NULL) { // Save the window-specific option values. copy_winopt(&win->w_onebuf_opt, &wip->wi_opt); wip->wi_fold_manual = win->w_fold_manual; @@ -2566,29 +2579,39 @@ static bool wininfo_other_tab_diff(wininfo_T *wip) return false; } -/* - * Find info for the current window in buffer "buf". - * If not found, return the info for the most recently used window. - * When "skip_diff_buffer" is true avoid windows with 'diff' set that is in - * another tab page. - * Returns NULL when there isn't any info. - */ -static wininfo_T *find_wininfo(buf_T *buf, int skip_diff_buffer) +// Find info for the current window in buffer "buf". +// If not found, return the info for the most recently used window. +// When "need_options" is true skip entries where wi_optset is false. +// When "skip_diff_buffer" is true avoid windows with 'diff' set that is in +// another tab page. +// Returns NULL when there isn't any info. +static wininfo_T *find_wininfo(buf_T *buf, bool need_options, + bool skip_diff_buffer) + FUNC_ATTR_NONNULL_ALL { wininfo_T *wip; - for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) + for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) { if (wip->wi_win == curwin && (!skip_diff_buffer || !wininfo_other_tab_diff(wip)) - ) + && (!need_options || wip->wi_optset)) { break; + } + } - /* If no wininfo for curwin, use the first in the list (that doesn't have - * 'diff' set and is in another tab page). */ + // If no wininfo for curwin, use the first in the list (that doesn't have + // 'diff' set and is in another tab page). + // If "need_options" is true skip entries that don't have options set, + // unless the window is editing "buf", so we can copy from the window + // itself. if (wip == NULL) { if (skip_diff_buffer) { for (wip = buf->b_wininfo; wip != NULL; wip = wip->wi_next) { - if (!wininfo_other_tab_diff(wip)) { + if (!wininfo_other_tab_diff(wip) + && (!need_options + || wip->wi_optset + || (wip->wi_win != NULL + && wip->wi_win->w_buffer == buf))) { break; } } @@ -2607,12 +2630,10 @@ static wininfo_T *find_wininfo(buf_T *buf, int skip_diff_buffer) */ void get_winopts(buf_T *buf) { - wininfo_T *wip; - clear_winopt(&curwin->w_onebuf_opt); clearFolding(curwin); - wip = find_wininfo(buf, true); + wininfo_T *const wip = find_wininfo(buf, true, true); if (wip != NULL && wip->wi_win != curwin && wip->wi_win != NULL && wip->wi_win->w_buffer == buf) { win_T *wp = wip->wi_win; @@ -2649,7 +2670,7 @@ pos_T *buflist_findfpos(buf_T *buf) { static pos_T no_position = { 1, 0, 0 }; - wininfo_T *wip = find_wininfo(buf, false); + wininfo_T *const wip = find_wininfo(buf, false, false); return (wip == NULL) ? &no_position : &(wip->wi_fpos); } @@ -3236,7 +3257,7 @@ void maketitle(void) 0, maxlen, NULL, NULL); title_str = (char_u *)buf; if (called_emsg) { - set_string_option_direct((char_u *)"titlestring", -1, (char_u *)"", + set_string_option_direct("titlestring", -1, (char_u *)"", OPT_FREE, SID_ERROR); } called_emsg |= save_called_emsg; @@ -3270,7 +3291,7 @@ void maketitle(void) case 6: buf_p = strappend(buf_p, " -"); break; case 5: case 7: buf_p = strappend(buf_p, " -+"); break; - default: assert(false); + default: abort(); } if (curbuf->b_fname != NULL) { @@ -3346,7 +3367,7 @@ void maketitle(void) p_iconstring, use_sandbox, 0, 0, NULL, NULL); if (called_emsg) { - set_string_option_direct((char_u *)"iconstring", -1, + set_string_option_direct("iconstring", -1, (char_u *)"", OPT_FREE, SID_ERROR); } called_emsg |= save_called_emsg; @@ -3486,7 +3507,7 @@ int build_stl_str_hl( stl_items = xmalloc(sizeof(stl_item_t) * stl_items_len); stl_groupitems = xmalloc(sizeof(int) * stl_items_len); stl_hltab = xmalloc(sizeof(stl_hlrec_t) * stl_items_len); - stl_tabtab = xmalloc(sizeof(stl_hlrec_t) * stl_items_len); + stl_tabtab = xmalloc(sizeof(StlClickRecord) * stl_items_len); stl_separator_locations = xmalloc(sizeof(int) * stl_items_len); } @@ -3940,9 +3961,9 @@ int build_stl_str_hl( // Store the current buffer number as a string variable vim_snprintf((char *)buf_tmp, sizeof(buf_tmp), "%d", curbuf->b_fnum); - set_internal_string_var((char_u *)"g:actual_curbuf", buf_tmp); + set_internal_string_var("g:actual_curbuf", buf_tmp); vim_snprintf((char *)win_tmp, sizeof(win_tmp), "%d", curwin->handle); - set_internal_string_var((char_u *)"g:actual_curwin", win_tmp); + set_internal_string_var("g:actual_curwin", win_tmp); buf_T *const save_curbuf = curbuf; win_T *const save_curwin = curwin; @@ -4482,22 +4503,15 @@ int build_stl_str_hl( int num_separators = 0; for (int i = 0; i < itemcnt; i++) { if (stl_items[i].type == Separate) { + // Create an array of the start location for each + // separator mark. + stl_separator_locations[num_separators] = i; num_separators++; } } // If we have separated groups, then we deal with it now if (num_separators) { - // Create an array of the start location for each - // separator mark. - int index = 0; - for (int i = 0; i < itemcnt; i++) { - if (stl_items[i].type == Separate) { - stl_separator_locations[index] = i; - index++; - } - } - int standard_spaces = (maxwidth - width) / num_separators; int final_spaces = (maxwidth - width) - standard_spaces * (num_separators - 1); diff --git a/src/nvim/buffer.h b/src/nvim/buffer.h index ee3fda5f6d..ac7ead5f92 100644 --- a/src/nvim/buffer.h +++ b/src/nvim/buffer.h @@ -33,6 +33,9 @@ enum bln_values { BLN_DUMMY = 4, // Allocating dummy buffer BLN_NEW = 8, // create a new buffer BLN_NOOPT = 16, // Don't copy options to existing buffer + // BLN_DUMMY_OK = 32, // also find an existing dummy buffer + // BLN_REUSE = 64, // may re-use number from buf_reuse + BLN_NOCURWIN = 128, // buffer is not associated with curwin }; // Values for action argument for do_buffer() diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index cc09b7e989..a05bd6fcc7 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -296,13 +296,11 @@ typedef struct arglist { int id; ///< id of this arglist } alist_T; -/* - * For each argument remember the file name as it was given, and the buffer - * number that contains the expanded file name (required for when ":cd" is - * used. - * - * TODO: move aentry_T to another header - */ +// For each argument remember the file name as it was given, and the buffer +// number that contains the expanded file name (required for when ":cd" is +// used). +// +// TODO(Felipe): move aentry_T to another header typedef struct argentry { char_u *ae_fname; // file name as specified int ae_fnum; // buffer number with expanded file name @@ -490,11 +488,12 @@ typedef struct { LuaRef on_bytes; LuaRef on_changedtick; LuaRef on_detach; + LuaRef on_reload; bool utf_sizes; bool preview; } BufUpdateCallbacks; #define BUF_UPDATE_CALLBACKS_INIT { LUA_NOREF, LUA_NOREF, LUA_NOREF, \ - LUA_NOREF, false, false } + LUA_NOREF, LUA_NOREF, false, false } EXTERN int curbuf_splice_pending INIT(= 0); @@ -1036,10 +1035,10 @@ struct matchitem { int id; ///< match ID int priority; ///< match priority char_u *pattern; ///< pattern to highlight - int hlg_id; ///< highlight group ID regmmatch_T match; ///< regexp program for pattern posmatch_T pos; ///< position matches match_T hl; ///< struct for doing the actual highlighting + int hlg_id; ///< highlight group ID int conceal_char; ///< cchar for Conceal highlighting }; diff --git a/src/nvim/buffer_updates.c b/src/nvim/buffer_updates.c index 68e123896b..97562eace6 100644 --- a/src/nvim/buffer_updates.c +++ b/src/nvim/buffer_updates.c @@ -135,7 +135,7 @@ void buf_updates_unregister(buf_T *buf, uint64_t channelid) } } -void buf_updates_unregister_all(buf_T *buf) +void buf_updates_unload(buf_T *buf, bool can_reload) { size_t size = kv_size(buf->update_channels); if (size) { @@ -146,9 +146,20 @@ void buf_updates_unregister_all(buf_T *buf) kv_init(buf->update_channels); } + size_t j = 0; for (size_t i = 0; i < kv_size(buf->update_callbacks); i++) { BufUpdateCallbacks cb = kv_A(buf->update_callbacks, i); - if (cb.on_detach != LUA_NOREF) { + LuaRef thecb = LUA_NOREF; + + bool keep = false; + if (can_reload && cb.on_reload != LUA_NOREF) { + keep = true; + thecb = cb.on_reload; + } else if (cb.on_detach != LUA_NOREF) { + thecb = cb.on_detach; + } + + if (thecb != LUA_NOREF) { Array args = ARRAY_DICT_INIT; Object items[1]; args.size = 1; @@ -158,15 +169,24 @@ void buf_updates_unregister_all(buf_T *buf) args.items[0] = BUFFER_OBJ(buf->handle); textlock++; - nlua_call_ref(cb.on_detach, "detach", args, false, NULL); + nlua_call_ref(thecb, keep ? "reload" : "detach", args, false, NULL); textlock--; } - free_update_callbacks(cb); + + if (keep) { + kv_A(buf->update_callbacks, j++) = kv_A(buf->update_callbacks, i); + } else { + free_update_callbacks(cb); + } + } + kv_size(buf->update_callbacks) = j; + if (kv_size(buf->update_callbacks) == 0) { + kv_destroy(buf->update_callbacks); + kv_init(buf->update_callbacks); } - kv_destroy(buf->update_callbacks); - kv_init(buf->update_callbacks); } + void buf_updates_send_changes(buf_T *buf, linenr_T firstline, int64_t num_added, diff --git a/src/nvim/channel.c b/src/nvim/channel.c index 37cbfb968b..09a34ca9fe 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -292,7 +292,6 @@ static void close_cb(Stream *stream, void *data) /// directory if `cwd` is NULL /// @param[in] pty_width Width of the pty, ignored if `pty` is false /// @param[in] pty_height Height of the pty, ignored if `pty` is false -/// @param[in] term_name `$TERM` for the pty /// @param[in] env Nvim's configured environment is used if this is NULL, /// otherwise defines all environment variables /// @param[out] status_out 0 for invalid arguments, > 0 for the channel id, @@ -304,7 +303,7 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout, bool pty, bool rpc, bool overlapped, bool detach, const char *cwd, uint16_t pty_width, uint16_t pty_height, - char *term_name, char **env, varnumber_T *status_out) + dict_T *env, varnumber_T *status_out) { assert(cwd == NULL || os_isdir_executable(cwd)); @@ -317,7 +316,9 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout, if (detach) { EMSG2(_(e_invarg2), "terminal/pty job cannot be detached"); shell_free_argv(argv); - xfree(term_name); + if (env) { + tv_dict_free(env); + } channel_destroy_early(chan); *status_out = 0; return NULL; @@ -329,9 +330,6 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout, if (pty_height > 0) { chan->stream.pty.height = pty_height; } - if (term_name) { - chan->stream.pty.term_name = term_name; - } } else { chan->stream.uv = libuv_process_init(&main_loop, chan); } @@ -358,17 +356,17 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout, if (status) { EMSG3(_(e_jobspawn), os_strerror(status), cmd); xfree(cmd); - os_free_fullenv(proc->env); - if (proc->type == kProcessTypePty) { - xfree(chan->stream.pty.term_name); + if (proc->env) { + tv_dict_free(proc->env); } channel_destroy_early(chan); *status_out = proc->status; return NULL; } xfree(cmd); - os_free_fullenv(proc->env); - + if (proc->env) { + tv_dict_free(proc->env); + } wstream_init(&proc->in, 0); if (has_out) { diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 3e52b3e3ce..be265e3f27 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -1747,7 +1747,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, goto vim_str2nr_dec; } default: { - assert(false); + abort(); } } } else if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN)) @@ -1788,7 +1788,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, } // Do the string-to-numeric conversion "manually" to avoid sscanf quirks. - assert(false); // Should’ve used goto earlier. + abort(); // Should’ve used goto earlier. #define PARSE_NUMBER(base, cond, conv) \ do { \ while (!STRING_ENDED(ptr) && (cond)) { \ diff --git a/src/nvim/context.c b/src/nvim/context.c index 1ae0510762..4162daa6ca 100644 --- a/src/nvim/context.c +++ b/src/nvim/context.c @@ -126,7 +126,7 @@ bool ctx_restore(Context *ctx, const int flags) } char_u *op_shada; - get_option_value((char_u *)"shada", NULL, &op_shada, OPT_GLOBAL); + get_option_value("shada", NULL, &op_shada, OPT_GLOBAL); set_option_value("shada", 0L, "!,'100,%", OPT_GLOBAL); if (flags & kCtxRegs) { diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 93bc34fa4b..31b7b1bd8f 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -58,6 +58,7 @@ static bool diff_need_update = false; // ex_diffupdate needs to be called #define DIFF_HIDDEN_OFF 0x100 // diffoff when hidden #define DIFF_INTERNAL 0x200 // use internal xdiff algorithm #define DIFF_CLOSE_OFF 0x400 // diffoff when closing window +#define DIFF_FOLLOWWRAP 0x800 // follow the wrap option #define ALL_WHITE_DIFF (DIFF_IWHITE | DIFF_IWHITEALL | DIFF_IWHITEEOL) static int diff_flags = DIFF_INTERNAL | DIFF_FILLER | DIFF_CLOSE_OFF; @@ -1361,11 +1362,12 @@ void diff_win_options(win_T *wp, int addbuf) wp->w_p_crb_save = wp->w_p_crb; } wp->w_p_crb = true; - - if (!wp->w_p_diff) { - wp->w_p_wrap_save = wp->w_p_wrap; + if (!(diff_flags & DIFF_FOLLOWWRAP)) { + if (!wp->w_p_diff) { + wp->w_p_wrap_save = wp->w_p_wrap; + } + wp->w_p_wrap = false; } - wp->w_p_wrap = false; curwin = wp; // -V519 curbuf = curwin->w_buffer; @@ -1375,7 +1377,7 @@ void diff_win_options(win_T *wp, int addbuf) } wp->w_p_fdm_save = vim_strsave(wp->w_p_fdm); } - set_string_option_direct((char_u *)"fdm", -1, (char_u *)"diff", + set_string_option_direct("fdm", -1, (char_u *)"diff", OPT_LOCAL | OPT_FREE, 0); curwin = old_curwin; curbuf = curwin->w_buffer; @@ -1437,11 +1439,11 @@ void ex_diffoff(exarg_T *eap) if (wp->w_p_crb) { wp->w_p_crb = wp->w_p_crb_save; } - - if (!wp->w_p_wrap) { - wp->w_p_wrap = wp->w_p_wrap_save; + if (!(diff_flags & DIFF_FOLLOWWRAP)) { + if (!wp->w_p_wrap) { + wp->w_p_wrap = wp->w_p_wrap_save; + } } - free_string_option(wp->w_p_fdm); wp->w_p_fdm = vim_strsave(*wp->w_p_fdm_save ? wp->w_p_fdm_save @@ -2158,6 +2160,9 @@ int diffopt_changed(void) } else if (STRNCMP(p, "closeoff", 8) == 0) { p += 8; diff_flags_new |= DIFF_CLOSE_OFF; + } else if (STRNCMP(p, "followwrap", 10) == 0) { + p += 10; + diff_flags_new |= DIFF_FOLLOWWRAP; } else if (STRNCMP(p, "indent-heuristic", 16) == 0) { p += 16; diff_indent_heuristic = XDF_INDENT_HEURISTIC; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 8a1556320c..d07618d2c0 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -365,7 +365,7 @@ void eval_init(void) eval_msgpack_type_lists[i] = type_list; if (tv_dict_add(msgpack_types_dict, di) == FAIL) { // There must not be duplicate items in this dictionary by definition. - assert(false); + abort(); } } msgpack_types_dict->dv_lock = VAR_FIXED; @@ -455,14 +455,15 @@ void eval_clear(void) * Set an internal variable to a string value. Creates the variable if it does * not already exist. */ -void set_internal_string_var(char_u *name, char_u *value) +void set_internal_string_var(const char *name, char_u *value) + FUNC_ATTR_NONNULL_ARG(1) { - const typval_T tv = { + typval_T tv = { .v_type = VAR_STRING, .vval.v_string = value, }; - set_var((const char *)name, STRLEN(name), (typval_T *)&tv, true); + set_var(name, strlen(name), &tv, true); } static lval_T *redir_lval = NULL; @@ -522,9 +523,9 @@ var_redir_start( tv.v_type = VAR_STRING; tv.vval.v_string = (char_u *)""; if (append) { - set_var_lval(redir_lval, redir_endp, &tv, true, false, (char_u *)"."); + set_var_lval(redir_lval, redir_endp, &tv, true, false, "."); } else { - set_var_lval(redir_lval, redir_endp, &tv, true, false, (char_u *)"="); + set_var_lval(redir_lval, redir_endp, &tv, true, false, "="); } clear_lval(redir_lval); err = did_emsg; @@ -584,7 +585,7 @@ void var_redir_stop(void) redir_endp = (char_u *)get_lval(redir_varname, NULL, redir_lval, false, false, 0, FNE_CHECK_START); if (redir_endp != NULL && redir_lval->ll_name != NULL) { - set_var_lval(redir_lval, redir_endp, &tv, false, false, (char_u *)"."); + set_var_lval(redir_lval, redir_endp, &tv, false, false, "."); } clear_lval(redir_lval); } @@ -1847,7 +1848,7 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, s = tv_get_string_chk(tv); // != NULL if number or string. } if (s != NULL && op != NULL && *op != '=') { - opt_type = get_option_value(arg, &numval, (char_u **)&stringval, + opt_type = get_option_value((char *)arg, &numval, (char_u **)&stringval, opt_flags); if ((opt_type == 1 && *op == '.') || (opt_type == 0 && *op != '.')) { @@ -1924,7 +1925,7 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, if (endchars != NULL && vim_strchr(endchars, *skipwhite(p)) == NULL) { EMSG(_(e_letunexp)); } else { - set_var_lval(&lv, p, tv, copy, is_const, op); + set_var_lval(&lv, p, tv, copy, is_const, (const char *)op); arg_end = p; } } @@ -2121,9 +2122,10 @@ char_u *get_lval(char_u *const name, typval_T *const rettv, return NULL; } } - lp->ll_range = TRUE; - } else - lp->ll_range = FALSE; + lp->ll_range = true; + } else { + lp->ll_range = false; + } if (*p != ']') { if (!quiet) { @@ -2240,12 +2242,10 @@ char_u *get_lval(char_u *const name, typval_T *const rettv, return NULL; } - /* - * May need to find the item or absolute index for the second - * index of a range. - * When no index given: "lp->ll_empty2" is TRUE. - * Otherwise "lp->ll_n2" is set to the second index. - */ + // May need to find the item or absolute index for the second + // index of a range. + // When no index given: "lp->ll_empty2" is true. + // Otherwise "lp->ll_n2" is set to the second index. if (lp->ll_range && !lp->ll_empty2) { lp->ll_n2 = (long)tv_get_number(&var2); // Is number or string. tv_clear(&var2); @@ -2299,7 +2299,7 @@ void clear_lval(lval_T *lp) * "%" for "%=", "." for ".=" or "=" for "=". */ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, - int copy, const bool is_const, const char_u *op) + int copy, const bool is_const, const char *op) { int cc; listitem_T *ri; @@ -2326,7 +2326,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, TV_CSTRING) && !tv_check_lock(di->di_tv.v_lock, (const char *)lp->ll_name, TV_CSTRING))) - && eexe_mod_op(&tv, rettv, (const char *)op) == OK) { + && eexe_mod_op(&tv, rettv, op) == OK) { set_var(lp->ll_name, lp->ll_name_len, &tv, false); } tv_clear(&tv); @@ -2369,8 +2369,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, */ for (ri = tv_list_first(rettv->vval.v_list); ri != NULL; ) { if (op != NULL && *op != '=') { - eexe_mod_op(TV_LIST_ITEM_TV(lp->ll_li), TV_LIST_ITEM_TV(ri), - (const char *)op); + eexe_mod_op(TV_LIST_ITEM_TV(lp->ll_li), TV_LIST_ITEM_TV(ri), op); } else { tv_clear(TV_LIST_ITEM_TV(lp->ll_li)); tv_copy(TV_LIST_ITEM_TV(ri), TV_LIST_ITEM_TV(lp->ll_li)); @@ -2428,7 +2427,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, } if (op != NULL && *op != '=') { - eexe_mod_op(lp->ll_tv, rettv, (const char *)op); + eexe_mod_op(lp->ll_tv, rettv, op); goto notify; } else { tv_clear(lp->ll_tv); @@ -4538,7 +4537,7 @@ int get_option_tv(const char **const arg, typval_T *const rettv, c = *option_end; *option_end = NUL; - opt_type = get_option_value((char_u *)(*arg), &numval, + opt_type = get_option_value(*arg, &numval, rettv == NULL ? NULL : &stringval, opt_flags); if (opt_type == -3) { // invalid name diff --git a/src/nvim/eval.h b/src/nvim/eval.h index 06b7f9e21d..c1891758ea 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -56,10 +56,10 @@ typedef struct lval_S { ///< isn't NULL it's the Dict to which to add the item. listitem_T *ll_li; ///< The list item or NULL. list_T *ll_list; ///< The list or NULL. - int ll_range; ///< TRUE when a [i:j] range was used. + bool ll_range; ///< true when a [i:j] range was used. + bool ll_empty2; ///< Second index is empty: [i:]. long ll_n1; ///< First index for list. long ll_n2; ///< Second index for list range. - int ll_empty2; ///< Second index is empty: [i:]. dict_T *ll_dict; ///< The Dictionary or NULL. dictitem_T *ll_di; ///< The dictitem or NULL. char_u *ll_newkey; ///< New key for Dict in allocated memory or NULL. diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c index 638fef331a..bd4dc87d31 100644 --- a/src/nvim/eval/decode.c +++ b/src/nvim/eval/decode.c @@ -147,7 +147,7 @@ static inline int json_decoder_pop(ValuesStackItem obj, tv_clear(&key.val); if (tv_dict_add(last_container.container.vval.v_dict, obj_di) == FAIL) { - assert(false); + abort(); } obj_di->di_tv = obj.val; } else { @@ -480,7 +480,7 @@ static inline int parse_json_string(const char *const buf, const size_t buf_len, break; } default: { - assert(false); + abort(); } } } else { diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index 9a9f2e4287..a4d7af7971 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -174,7 +174,7 @@ static int conv_error(const char *const msg, const MPConvStack *const mpstack, case kMPConvPartial: { switch (v.data.p.stage) { case kMPConvPartialArgs: { - assert(false); + abort(); break; } case kMPConvPartialSelf: { @@ -237,7 +237,7 @@ bool encode_vim_list_to_buf(const list_T *const list, size_t *const ret_len, char *const buf = xmalloc(len); size_t read_bytes; if (encode_read_from_list(&lrstate, buf, len, &read_bytes) != OK) { - assert(false); + abort(); } assert(len == read_bytes); *ret_buf = buf; diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c index da05ecda43..bbba9d12f2 100644 --- a/src/nvim/eval/executor.c +++ b/src/nvim/eval/executor.c @@ -118,7 +118,7 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, return OK; } case VAR_UNKNOWN: { - assert(false); + abort(); } } } diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 8235d74cbb..d54e49bbd4 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -1629,27 +1629,26 @@ static void f_deletebufline(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (u_save(first - 1, last + 1) == FAIL) { rettv->vval.v_number = 1; // FAIL - return; - } - - for (linenr_T lnum = first; lnum <= last; lnum++) { - ml_delete(first, true); - } + } else { + for (linenr_T lnum = first; lnum <= last; lnum++) { + ml_delete(first, true); + } - FOR_ALL_TAB_WINDOWS(tp, wp) { - if (wp->w_buffer == buf) { - if (wp->w_cursor.lnum > last) { - wp->w_cursor.lnum -= count; - } else if (wp->w_cursor.lnum> first) { - wp->w_cursor.lnum = first; - } - if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count) { - wp->w_cursor.lnum = wp->w_buffer->b_ml.ml_line_count; + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (wp->w_buffer == buf) { + if (wp->w_cursor.lnum > last) { + wp->w_cursor.lnum -= count; + } else if (wp->w_cursor.lnum> first) { + wp->w_cursor.lnum = first; + } + if (wp->w_cursor.lnum > wp->w_buffer->b_ml.ml_line_count) { + wp->w_cursor.lnum = wp->w_buffer->b_ml.ml_line_count; + } } } + check_cursor_col(); + deleted_lines_mark(first, count); } - check_cursor_col(); - deleted_lines_mark(first, count); if (!is_curbuf) { curbuf = curbuf_save; @@ -1798,7 +1797,7 @@ static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr) os_copy_fullenv(env, env_size); - for (size_t i = 0; i < env_size; i++) { + for (ssize_t i = env_size - 1; i >= 0; i--) { const char * str = env[i]; const char * const end = strchr(str + (str[0] == '=' ? 1 : 0), '='); @@ -1806,6 +1805,12 @@ static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr) ptrdiff_t len = end - str; assert(len > 0); const char * value = str + len + 1; + if (tv_dict_find(rettv->vval.v_dict, str, len) != NULL) { + // Since we're traversing from the end of the env block to the front, any + // duplicate names encountered should be ignored. This preserves the + // semantics of env vars defined later in the env block taking precedence. + continue; + } tv_dict_add_str(rettv->vval.v_dict, str, len, value); @@ -2143,7 +2148,7 @@ static void f_menu_get(typval_T *argvars, typval_T *rettv, FunPtr fptr) tv_list_alloc_ret(rettv, kListLenMayKnow); int modes = MENU_ALL_MODES; if (argvars[1].v_type == VAR_STRING) { - const char_u *const strmodes = (char_u *)tv_get_string(&argvars[1]); + const char *const strmodes = tv_get_string(&argvars[1]); modes = get_menu_cmd_modes(strmodes, false, NULL, NULL); } menu_get((char_u *)tv_get_string(&argvars[0]), modes, rettv->vval.v_list); @@ -3129,6 +3134,12 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) int options = WILD_SILENT | WILD_USE_NL | WILD_ADD_SLASH | WILD_NO_BEEP; + if (argvars[1].v_type != VAR_STRING) { + EMSG2(_(e_invarg2), "type must be a string"); + return; + } + const char *const type = tv_get_string(&argvars[1]); + if (argvars[2].v_type != VAR_UNKNOWN) { filtered = (bool)tv_get_number_chk(&argvars[2], NULL); } @@ -3142,12 +3153,12 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) options |= WILD_KEEP_ALL; } - if (argvars[0].v_type != VAR_STRING || argvars[1].v_type != VAR_STRING) { + if (argvars[0].v_type != VAR_STRING) { EMSG(_(e_invarg)); return; } - if (strcmp(tv_get_string(&argvars[1]), "cmdline") == 0) { + if (strcmp(type, "cmdline") == 0) { set_one_cmd_context(&xpc, tv_get_string(&argvars[0])); xpc.xp_pattern_len = STRLEN(xpc.xp_pattern); goto theend; @@ -3156,15 +3167,14 @@ static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) ExpandInit(&xpc); xpc.xp_pattern = (char_u *)tv_get_string(&argvars[0]); xpc.xp_pattern_len = STRLEN(xpc.xp_pattern); - xpc.xp_context = cmdcomplete_str_to_type( - (char_u *)tv_get_string(&argvars[1])); + xpc.xp_context = cmdcomplete_str_to_type(type); if (xpc.xp_context == EXPAND_NOTHING) { - EMSG2(_(e_invarg2), argvars[1].vval.v_string); + EMSG2(_(e_invarg2), type); return; } if (xpc.xp_context == EXPAND_MENUS) { - set_context_in_menu_cmd(&xpc, (char_u *)"menu", xpc.xp_pattern, false); + set_context_in_menu_cmd(&xpc, "menu", xpc.xp_pattern, false); xpc.xp_pattern_len = STRLEN(xpc.xp_pattern); } @@ -3301,7 +3311,7 @@ static void f_getcwd(typval_T *argvars, typval_T *rettv, FunPtr fptr) } break; case kCdScopeInvalid: // We should never get here - assert(false); + abort(); } if (from) { @@ -4354,7 +4364,7 @@ static void f_haslocaldir(typval_T *argvars, typval_T *rettv, FunPtr fptr) break; case kCdScopeInvalid: // We should never get here - assert(false); + abort(); } } @@ -4875,6 +4885,108 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = 1; } +static const char *ignored_env_vars[] = { +#ifndef WIN32 + "COLUMNS", + "LINES", + "TERMCAP", + "COLORFGBG", +#endif + NULL +}; + +/// According to comments in src/win/process.c of libuv, Windows has a few +/// "essential" environment variables. +static const char *required_env_vars[] = { +#ifdef WIN32 + "HOMEDRIVE", + "HOMEPATH", + "LOGONSERVER", + "PATH", + "SYSTEMDRIVE", + "SYSTEMROOT", + "TEMP", + "USERDOMAIN", + "USERNAME", + "USERPROFILE", + "WINDIR", +#endif + NULL +}; + +static dict_T *create_environment(const dictitem_T *job_env, + const bool clear_env, + const bool pty, + const char * const pty_term_name) +{ + dict_T * env = tv_dict_alloc(); + + if (!clear_env) { + typval_T temp_env = TV_INITIAL_VALUE; + f_environ(NULL, &temp_env, NULL); + tv_dict_extend(env, temp_env.vval.v_dict, "force"); + tv_dict_free(temp_env.vval.v_dict); + + if (pty) { + // These environment variables generally shouldn't be propagated to the + // child process. We're removing them here so the user can still decide + // they want to explicitly set them. + for (size_t i = 0; + i < ARRAY_SIZE(ignored_env_vars) && ignored_env_vars[i]; + i++) { + dictitem_T *dv = tv_dict_find(env, ignored_env_vars[i], -1); + if (dv) { + tv_dict_item_remove(env, dv); + } + } +#ifndef WIN32 + // Set COLORTERM to "truecolor" if termguicolors is set and 256 + // otherwise, but only if it was set in the parent terminal at all + dictitem_T *dv = tv_dict_find(env, S_LEN("COLORTERM")); + if (dv) { + tv_dict_item_remove(env, dv); + tv_dict_add_str(env, S_LEN("COLORTERM"), p_tgc ? "truecolor" : "256"); + } +#endif + } + } + + // For a pty, we need a sane $TERM set. We can't rely on nvim's environment, + // because the child process is going to be communicating with nvim, not the + // parent terminal. Set a sane default, but let the user override it in the + // job's environment if they want. + if (pty) { + dictitem_T *dv = tv_dict_find(env, S_LEN("TERM")); + if (dv) { + tv_dict_item_remove(env, dv); + } + tv_dict_add_str(env, S_LEN("TERM"), pty_term_name); + } + + if (job_env) { + tv_dict_extend(env, job_env->di_tv.vval.v_dict, "force"); + } + + if (pty) { + // Now that the custom environment is configured, we need to ensure certain + // environment variables are present. + for (size_t i = 0; + i < ARRAY_SIZE(required_env_vars) && required_env_vars[i]; + i++) { + size_t len = strlen(required_env_vars[i]); + dictitem_T *dv = tv_dict_find(env, required_env_vars[i], len); + if (!dv) { + const char *env_var = os_getenv(required_env_vars[i]); + if (env_var) { + tv_dict_add_str(env, required_env_vars[i], len, env_var); + } + } + } + } + + return env; +} + // "jobstart()" function static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -4887,7 +4999,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool executable = true; char **argv = tv_to_argv(&argvars[0], NULL, &executable); - char **env = NULL; + dict_T *env = NULL; if (!argv) { rettv->vval.v_number = executable ? 0 : -1; return; // Did error message in tv_to_argv. @@ -4911,6 +5023,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) on_stderr = CALLBACK_READER_INIT; Callback on_exit = CALLBACK_NONE; char *cwd = NULL; + dictitem_T *job_env = NULL; if (argvars[1].v_type == VAR_DICT) { job_opts = argvars[1].vval.v_dict; @@ -4936,7 +5049,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) #endif char *new_cwd = tv_dict_get_string(job_opts, "cwd", false); - if (new_cwd && strlen(new_cwd) > 0) { + if (new_cwd && *new_cwd != NUL) { cwd = new_cwd; // The new cwd must be a directory. if (!os_isdir_executable((const char *)cwd)) { @@ -4945,46 +5058,14 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } } - dictitem_T *job_env = tv_dict_find(job_opts, S_LEN("env")); - if (job_env) { - if (job_env->di_tv.v_type != VAR_DICT) { - EMSG2(_(e_invarg2), "env"); - shell_free_argv(argv); - return; - } - - size_t custom_env_size = (size_t)tv_dict_len(job_env->di_tv.vval.v_dict); - size_t i = 0; - size_t env_size = 0; - - if (clear_env) { - // + 1 for last null entry - env = xmalloc((custom_env_size + 1) * sizeof(*env)); - env_size = 0; - } else { - env_size = os_get_fullenv_size(); - - env = xmalloc((custom_env_size + env_size + 1) * sizeof(*env)); - os_copy_fullenv(env, env_size); - i = env_size; - } - assert(env); // env must be allocated at this point - - TV_DICT_ITER(job_env->di_tv.vval.v_dict, var, { - const char *str = tv_get_string(&var->di_tv); - assert(str); - size_t len = STRLEN(var->di_key) + strlen(str) + strlen("=") + 1; - env[i] = xmalloc(len); - snprintf(env[i], len, "%s=%s", (char *)var->di_key, str); - i++; - }); - - // must be null terminated - env[env_size + custom_env_size] = NULL; + job_env = tv_dict_find(job_opts, S_LEN("env")); + if (job_env && job_env->di_tv.v_type != VAR_DICT) { + EMSG2(_(e_invarg2), "env"); + shell_free_argv(argv); + return; } - if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) { shell_free_argv(argv); return; @@ -4997,12 +5078,19 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (pty) { width = (uint16_t)tv_dict_get_number(job_opts, "width"); height = (uint16_t)tv_dict_get_number(job_opts, "height"); - term_name = tv_dict_get_string(job_opts, "TERM", true); + // Legacy method, before env option existed, to specify $TERM. No longer + // documented, but still usable to avoid breaking scripts. + term_name = tv_dict_get_string(job_opts, "TERM", false); + if (!term_name) { + term_name = "ansi"; + } } + env = create_environment(job_env, clear_env, pty, term_name); + Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, pty, rpc, overlapped, detach, cwd, width, height, - term_name, env, &rettv->vval.v_number); + env, &rettv->vval.v_number); if (chan) { channel_create_event(chan, NULL); } @@ -7445,7 +7533,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) Channel *chan = channel_job_start(argv, CALLBACK_READER_INIT, CALLBACK_READER_INIT, CALLBACK_NONE, false, true, false, false, NULL, 0, 0, - NULL, NULL, &rettv->vval.v_number); + NULL, &rettv->vval.v_number); if (chan) { channel_create_event(chan, NULL); } @@ -10518,6 +10606,11 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) Callback on_exit = CALLBACK_NONE; dict_T *job_opts = NULL; const char *cwd = "."; + dict_T *env = NULL; + const bool pty = true; + bool clear_env = false; + dictitem_T *job_env = NULL; + if (argvars[1].v_type == VAR_DICT) { job_opts = argvars[1].vval.v_dict; @@ -10532,18 +10625,31 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } + job_env = tv_dict_find(job_opts, S_LEN("env")); + if (job_env && job_env->di_tv.v_type != VAR_DICT) { + EMSG2(_(e_invarg2), "env"); + shell_free_argv(argv); + return; + } + + clear_env = tv_dict_get_number(job_opts, "clear_env") != 0; + if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) { shell_free_argv(argv); return; } } + env = create_environment(job_env, clear_env, pty, "xterm-256color"); + + const bool rpc = false; + const bool overlapped = false; + const bool detach = false; uint16_t term_width = MAX(0, curwin->w_width_inner - win_col_off(curwin)); Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, - true, false, false, false, cwd, + pty, rpc, overlapped, detach, cwd, term_width, curwin->w_height_inner, - xstrdup("xterm-256color"), NULL, - &rettv->vval.v_number); + env, &rettv->vval.v_number); if (rettv->vval.v_number <= 0) { return; } diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 02d32a4f86..9be487f4fd 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -1523,6 +1523,33 @@ varnumber_T tv_dict_get_number(const dict_T *const d, const char *const key) return tv_get_number(&di->di_tv); } +/// Converts a dict to an environment +/// +/// +char **tv_dict_to_env(dict_T *denv) +{ + size_t env_size = (size_t)tv_dict_len(denv); + + size_t i = 0; + char **env = NULL; + + // + 1 for NULL + env = xmalloc((env_size + 1) * sizeof(*env)); + + TV_DICT_ITER(denv, var, { + const char *str = tv_get_string(&var->di_tv); + assert(str); + size_t len = STRLEN(var->di_key) + strlen(str) + strlen("=") + 1; + env[i] = xmalloc(len); + snprintf(env[i], len, "%s=%s", (char *)var->di_key, str); + i++; + }); + + // must be null terminated + env[env_size] = NULL; + return env; +} + /// Get a string item from a dictionary /// /// @param[in] d Dictionary to get item from. @@ -2494,7 +2521,7 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock) break; } case VAR_UNKNOWN: { - assert(false); + abort(); } } #undef CHANGE_LOCK @@ -2666,7 +2693,7 @@ bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic, } } - assert(false); + abort(); return false; } @@ -2719,7 +2746,7 @@ bool tv_check_str_or_nr(const typval_T *const tv) return false; } } - assert(false); + abort(); return false; } @@ -2764,7 +2791,7 @@ bool tv_check_num(const typval_T *const tv) return false; } } - assert(false); + abort(); return false; } @@ -2809,7 +2836,7 @@ bool tv_check_str(const typval_T *const tv) return false; } } - assert(false); + abort(); return false; } diff --git a/src/nvim/event/libuv_process.c b/src/nvim/event/libuv_process.c index 13517d3df1..0b1ecb12e2 100644 --- a/src/nvim/event/libuv_process.c +++ b/src/nvim/event/libuv_process.c @@ -41,7 +41,6 @@ int libuv_process_spawn(LibuvProcess *uvproc) #endif uvproc->uvopts.exit_cb = exit_cb; uvproc->uvopts.cwd = proc->cwd; - uvproc->uvopts.env = proc->env; uvproc->uvopts.stdio = uvproc->uvstdio; uvproc->uvopts.stdio_count = 3; uvproc->uvstdio[0].flags = UV_IGNORE; @@ -49,6 +48,12 @@ int libuv_process_spawn(LibuvProcess *uvproc) uvproc->uvstdio[2].flags = UV_IGNORE; uvproc->uv.data = proc; + if (proc->env) { + uvproc->uvopts.env = tv_dict_to_env(proc->env); + } else { + uvproc->uvopts.env = NULL; + } + if (!proc->in.closed) { uvproc->uvstdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; #ifdef WIN32 @@ -78,6 +83,9 @@ int libuv_process_spawn(LibuvProcess *uvproc) int status; if ((status = uv_spawn(&proc->loop->uv, &uvproc->uv, &uvproc->uvopts))) { ELOG("uv_spawn failed: %s", uv_strerror(status)); + if (uvproc->uvopts.env) { + os_free_fullenv(uvproc->uvopts.env); + } return status; } @@ -97,6 +105,10 @@ static void close_cb(uv_handle_t *handle) if (proc->internal_close_cb) { proc->internal_close_cb(proc); } + LibuvProcess *uvproc = (LibuvProcess *)proc; + if (uvproc->uvopts.env) { + os_free_fullenv(uvproc->uvopts.env); + } } static void exit_cb(uv_process_t *handle, int64_t status, int term_signal) diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c index 8e9964bd37..b93d6cc0ab 100644 --- a/src/nvim/event/process.c +++ b/src/nvim/event/process.c @@ -270,9 +270,6 @@ static void process_close_event(void **argv) { Process *proc = argv[0]; shell_free_argv(proc->argv); - if (proc->type == kProcessTypePty) { - xfree(((PtyProcess *)proc)->term_name); - } if (proc->cb) { // "on_exit" for jobstart(). See channel_job_start(). proc->cb(proc, proc->status, proc->data); } diff --git a/src/nvim/event/process.h b/src/nvim/event/process.h index 84e81238e9..24debdb276 100644 --- a/src/nvim/event/process.h +++ b/src/nvim/event/process.h @@ -4,6 +4,7 @@ #include "nvim/event/loop.h" #include "nvim/event/rstream.h" #include "nvim/event/wstream.h" +#include "nvim/eval/typval.h" typedef enum { kProcessTypeUv, @@ -23,7 +24,7 @@ struct process { uint64_t stopped_time; // process_stop() timestamp const char *cwd; char **argv; - char **env; + dict_T *env; Stream in, out, err; process_exit_cb cb; internal_process_cb internal_exit_cb, internal_close_cb; diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 2da8c205c1..b220b034dd 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -790,7 +790,10 @@ void ex_retab(exarg_T *eap) for (col = 0; col < len; col++) { ptr[col] = (col < num_tabs) ? '\t' : ' '; } - ml_replace(lnum, new_line, false); + if (ml_replace(lnum, new_line, false) == OK) { + // "new_line" may have been copied + new_line = curbuf->b_ml.ml_line_ptr; + } if (first_line == 0) { first_line = lnum; } @@ -2178,6 +2181,8 @@ theend: /// ECMD_OLDBUF: use existing buffer if it exists /// ECMD_FORCEIT: ! used for Ex command /// ECMD_ADDBUF: don't edit, just add to buffer list +/// ECMD_ALTBUF: like ECMD_ADDBUF and also set the alternate +/// file /// @param oldwin Should be "curwin" when editing a new buffer in the current /// window, NULL when splitting the window first. When not NULL /// info of the previous buffer for "oldwin" is stored. @@ -2234,8 +2239,10 @@ int do_ecmd( path_fix_case(sfname); // set correct case for sfname #endif - if ((flags & ECMD_ADDBUF) && (ffname == NULL || *ffname == NUL)) + if ((flags & (ECMD_ADDBUF | ECMD_ALTBUF)) + && (ffname == NULL || *ffname == NUL)) { goto theend; + } if (ffname == NULL) other_file = TRUE; @@ -2265,15 +2272,16 @@ int do_ecmd( // If the file was changed we may not be allowed to abandon it: // - if we are going to re-edit the same file // - or if we are the only window on this file and if ECMD_HIDE is FALSE - if ( ((!other_file && !(flags & ECMD_OLDBUF)) - || (curbuf->b_nwindows == 1 - && !(flags & (ECMD_HIDE | ECMD_ADDBUF)))) - && check_changed(curbuf, (p_awa ? CCGD_AW : 0) - | (other_file ? 0 : CCGD_MULTWIN) - | ((flags & ECMD_FORCEIT) ? CCGD_FORCEIT : 0) - | (eap == NULL ? 0 : CCGD_EXCMD))) { - if (fnum == 0 && other_file && ffname != NULL) + if (((!other_file && !(flags & ECMD_OLDBUF)) + || (curbuf->b_nwindows == 1 + && !(flags & (ECMD_HIDE | ECMD_ADDBUF | ECMD_ALTBUF)))) + && check_changed(curbuf, (p_awa ? CCGD_AW : 0) + | (other_file ? 0 : CCGD_MULTWIN) + | ((flags & ECMD_FORCEIT) ? CCGD_FORCEIT : 0) + | (eap == NULL ? 0 : CCGD_EXCMD))) { + if (fnum == 0 && other_file && ffname != NULL) { (void)setaltfname(ffname, sfname, newlnum < 0 ? 0 : newlnum); + } goto theend; } @@ -2303,25 +2311,35 @@ int do_ecmd( * Otherwise we re-use the current buffer. */ if (other_file) { - if (!(flags & ECMD_ADDBUF)) { - if (!cmdmod.keepalt) + if (!(flags & (ECMD_ADDBUF | ECMD_ALTBUF))) { + if (!cmdmod.keepalt) { curwin->w_alt_fnum = curbuf->b_fnum; - if (oldwin != NULL) + } + if (oldwin != NULL) { buflist_altfpos(oldwin); + } } if (fnum) { buf = buflist_findnr(fnum); } else { - if (flags & ECMD_ADDBUF) { - linenr_T tlnum = 1L; + if (flags & (ECMD_ADDBUF | ECMD_ALTBUF)) { + // Default the line number to zero to avoid that a wininfo item + // is added for the current window. + linenr_T tlnum = 0; if (command != NULL) { tlnum = atol((char *)command); if (tlnum <= 0) tlnum = 1L; } - (void)buflist_new(ffname, sfname, tlnum, BLN_LISTED); + // Add BLN_NOCURWIN to avoid a new wininfo items are associated + // with the current window. + const buf_T *const newbuf + = buflist_new(ffname, sfname, tlnum, BLN_LISTED | BLN_NOCURWIN); + if (newbuf != NULL && (flags & ECMD_ALTBUF)) { + curwin->w_alt_fnum = newbuf->b_fnum; + } goto theend; } buf = buflist_new(ffname, sfname, 0L, @@ -2413,7 +2431,10 @@ int do_ecmd( (flags & ECMD_HIDE) || curbuf->terminal ? 0 : DOBUF_UNLOAD, false); - the_curwin->w_closing = false; + // Autocommands may have closed the window. + if (win_valid(the_curwin)) { + the_curwin->w_closing = false; + } buf->b_locked--; // autocmds may abort script processing @@ -2464,8 +2485,7 @@ int do_ecmd( curwin->w_pcmark.lnum = 1; curwin->w_pcmark.col = 0; } else { // !other_file - if ((flags & ECMD_ADDBUF) - || check_fname() == FAIL) { + if ((flags & (ECMD_ADDBUF | ECMD_ALTBUF)) || check_fname() == FAIL) { goto theend; } oldbuf = (flags & ECMD_OLDBUF); @@ -2531,13 +2551,13 @@ int do_ecmd( goto theend; } u_unchanged(curbuf); - buf_updates_unregister_all(curbuf); + buf_updates_unload(curbuf, false); buf_freeall(curbuf, BFA_KEEP_UNDO); // Tell readfile() not to clear or reload undo info. readfile_flags = READ_KEEP_UNDO; } else { - buf_updates_unregister_all(curbuf); + buf_updates_unload(curbuf, false); buf_freeall(curbuf, 0); // Free all things for buffer. } // If autocommands deleted the buffer we were going to re-edit, give @@ -3310,11 +3330,7 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, int save_b_changed = curbuf->b_changed; bool preview = (State & CMDPREVIEW); - // inccommand tests fail without this check - if (!preview) { - // Required for Undo to work for extmarks. - u_save_cursor(); - } + bool did_save = false; if (!global_busy) { sub_nsubs = 0; @@ -3991,6 +4007,11 @@ static buf_T *do_sub(exarg_T *eap, proftime_T timeout, int matchcols = end.col - ((end.lnum == start.lnum) ? start.col : 0); int subcols = new_endcol - ((lnum == lnum_start) ? start_col : 0); + if (!did_save) { + // Required for Undo to work for extmarks. + u_save_cursor(); + did_save = true; + } extmark_splice(curbuf, lnum_start-1, start_col, end.lnum-start.lnum, matchcols, replaced_bytes, lnum-lnum_start, subcols, sublen-1, kExtmarkUndo); @@ -4220,7 +4241,7 @@ skip: size_t subsize = preview_lines.subresults.size; if (preview && !aborting()) { if (got_quit || profile_passed_limit(timeout)) { // Too slow, disable. - set_string_option_direct((char_u *)"icm", -1, (char_u *)"", OPT_FREE, + set_string_option_direct("icm", -1, (char_u *)"", OPT_FREE, SID_NONE); } else if (*p_icm != NUL && pat != NULL) { if (pre_src_id == 0) { @@ -4521,7 +4542,7 @@ prepare_tagpreview ( RESET_BINDING(curwin); /* don't take over 'scrollbind' and 'cursorbind' */ curwin->w_p_diff = false; // no 'diff' - set_string_option_direct((char_u *)"fdc", -1, // no 'foldcolumn' + set_string_option_direct("fdc", -1, // no 'foldcolumn' (char_u *)"0", OPT_FREE, SID_NONE); return true; } @@ -5016,7 +5037,7 @@ int find_help_tags(const char_u *arg, int *num_matches, char_u ***matches, static void prepare_help_buffer(void) { curbuf->b_help = true; - set_string_option_direct((char_u *)"buftype", -1, (char_u *)"help", + set_string_option_direct("buftype", -1, (char_u *)"help", OPT_FREE|OPT_LOCAL, 0); // Always set these options after jumping to a help tag, because the @@ -5026,13 +5047,13 @@ static void prepare_help_buffer(void) // Only set it when needed, buf_init_chartab() is some work. char_u *p = (char_u *)"!-~,^*,^|,^\",192-255"; if (STRCMP(curbuf->b_p_isk, p) != 0) { - set_string_option_direct((char_u *)"isk", -1, p, OPT_FREE|OPT_LOCAL, 0); + set_string_option_direct("isk", -1, p, OPT_FREE|OPT_LOCAL, 0); check_buf_options(curbuf); (void)buf_init_chartab(curbuf, FALSE); } // Don't use the global foldmethod. - set_string_option_direct((char_u *)"fdm", -1, (char_u *)"manual", + set_string_option_direct("fdm", -1, (char_u *)"manual", OPT_FREE|OPT_LOCAL, 0); curbuf->b_p_ts = 8; // 'tabstop' is 8. @@ -5652,7 +5673,7 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, cmdmod.tab = 0; // disable :tab modifier cmdmod.noswapfile = true; // disable swap for preview buffer // disable file info message - set_string_option_direct((char_u *)"shm", -1, (char_u *)"F", OPT_FREE, + set_string_option_direct("shm", -1, (char_u *)"F", OPT_FREE, SID_NONE); bool outside_curline = (eap->line1 != old_cusr.lnum @@ -5775,7 +5796,7 @@ static buf_T *show_sub(exarg_T *eap, pos_T old_cusr, update_screen(SOME_VALID); RedrawingDisabled = save_rd; - set_string_option_direct((char_u *)"shm", -1, save_shm_p, OPT_FREE, SID_NONE); + set_string_option_direct("shm", -1, save_shm_p, OPT_FREE, SID_NONE); xfree(save_shm_p); cmdmod = save_cmdmod; diff --git a/src/nvim/ex_cmds.h b/src/nvim/ex_cmds.h index b564cde56c..1b54b3a898 100644 --- a/src/nvim/ex_cmds.h +++ b/src/nvim/ex_cmds.h @@ -16,7 +16,7 @@ #define ECMD_OLDBUF 0x04 // use existing buffer if it exists #define ECMD_FORCEIT 0x08 // ! used in Ex command #define ECMD_ADDBUF 0x10 // don't edit, just add to buffer list - +#define ECMD_ALTBUF 0x20 // like ECMD_ADDBUF and set the alternate file /* for lnum argument in do_ecmd() */ #define ECMD_LASTL (linenr_T)0 /* use last position in loaded file */ diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index e9046da800..10dd7d68ca 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -176,6 +176,12 @@ module.cmds = { func='ex_edit', }, { + command='balt', + flags=bit.bor(NEEDARG, FILE1, CMDARG, TRLBAR, CMDWIN), + addr_type='ADDR_NONE', + func='ex_edit', + }, + { command='bdelete', flags=bit.bor(BANG, RANGE, BUFNAME, COUNT, EXTRA, TRLBAR), addr_type='ADDR_BUFFERS', diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index e49bb99aa0..bb53f4d373 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -1657,9 +1657,11 @@ int get_arglist_exp(char_u *str, int *fcountp, char_u ***fnamesp, bool wig) /// AL_DEL: remove files in 'str' from the argument list. /// @param after /// 0 means before first one +/// @param will_edit will edit added argument /// /// @return FAIL for failure, OK otherwise. -static int do_arglist(char_u *str, int what, int after) +static int do_arglist(char_u *str, int what, int after, bool will_edit) + FUNC_ATTR_NONNULL_ALL { garray_T new_ga; int exp_count; @@ -1733,10 +1735,11 @@ static int do_arglist(char_u *str, int what, int after) } if (what == AL_ADD) { - (void)alist_add_list(exp_count, exp_files, after); + alist_add_list(exp_count, exp_files, after, will_edit); xfree(exp_files); - } else { // what == AL_SET - alist_set(ALIST(curwin), exp_count, exp_files, false, NULL, 0); + } else { + assert(what == AL_SET); + alist_set(ALIST(curwin), exp_count, exp_files, will_edit, NULL, 0); } } @@ -1956,7 +1959,7 @@ void ex_next(exarg_T *eap) | (eap->forceit ? CCGD_FORCEIT : 0) | CCGD_EXCMD)) { if (*eap->arg != NUL) { // redefine file list - if (do_arglist(eap->arg, AL_SET, 0) == FAIL) { + if (do_arglist(eap->arg, AL_SET, 0, true) == FAIL) { return; } i = 0; @@ -1974,7 +1977,7 @@ void ex_argedit(exarg_T *eap) // Whether curbuf will be reused, curbuf->b_ffname will be set. bool curbuf_is_reusable = curbuf_reusable(); - if (do_arglist(eap->arg, AL_ADD, i) == FAIL) { + if (do_arglist(eap->arg, AL_ADD, i, true) == FAIL) { return; } maketitle(); @@ -1994,7 +1997,8 @@ void ex_argedit(exarg_T *eap) void ex_argadd(exarg_T *eap) { do_arglist(eap->arg, AL_ADD, - eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1); + eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1, + false); maketitle(); } @@ -2041,7 +2045,7 @@ void ex_argdelete(exarg_T *eap) } } } else { - do_arglist(eap->arg, AL_DEL, 0); + do_arglist(eap->arg, AL_DEL, 0, false); } maketitle(); } @@ -2292,9 +2296,9 @@ void ex_listdo(exarg_T *eap) /// Files[] itself is not taken over. /// /// @param after: where to add: 0 = before first one -/// -/// @return index of first added argument -static int alist_add_list(int count, char_u **files, int after) +/// @param will_edit will edit adding argument +static void alist_add_list(int count, char_u **files, int after, bool will_edit) + FUNC_ATTR_NONNULL_ALL { int old_argcount = ARGCOUNT; ga_grow(&ALIST(curwin)->al_ga, count); @@ -2310,15 +2314,15 @@ static int alist_add_list(int count, char_u **files, int after) (size_t)(ARGCOUNT - after) * sizeof(aentry_T)); } for (int i = 0; i < count; i++) { + const int flags = BLN_LISTED | (will_edit ? BLN_CURBUF : 0); ARGLIST[after + i].ae_fname = files[i]; - ARGLIST[after + i].ae_fnum = buflist_add(files[i], - BLN_LISTED | BLN_CURBUF); + ARGLIST[after + i].ae_fnum = buflist_add(files[i], flags); } ALIST(curwin)->al_ga.ga_len += count; if (old_argcount > 0 && curwin->w_arg_idx >= after) { curwin->w_arg_idx += count; } - return after; + return; } } @@ -2375,13 +2379,13 @@ void ex_compiler(exarg_T *eap) // Set "b:current_compiler" from "current_compiler". p = get_var_value("g:current_compiler"); if (p != NULL) { - set_internal_string_var((char_u *)"b:current_compiler", p); + set_internal_string_var("b:current_compiler", p); } // Restore "current_compiler" for ":compiler {name}". if (!eap->forceit) { if (old_cur_comp != NULL) { - set_internal_string_var((char_u *)"g:current_compiler", + set_internal_string_var("g:current_compiler", old_cur_comp); xfree(old_cur_comp); } else { @@ -3766,7 +3770,7 @@ void ex_drop(exarg_T *eap) // and mostly only one file is dropped. // This also ignores wildcards, since it is very unlikely the user is // editing a file name with a wildcard character. - do_arglist(eap->arg, AL_SET, 0); + do_arglist(eap->arg, AL_SET, 0, false); // Expanding wildcards may result in an empty argument list. E.g. when // editing "foo.pyc" and ".pyc" is in 'wildignore'. Assume that we diff --git a/src/nvim/ex_cmds_defs.h b/src/nvim/ex_cmds_defs.h index ca84d375ce..f928c61ea4 100644 --- a/src/nvim/ex_cmds_defs.h +++ b/src/nvim/ex_cmds_defs.h @@ -188,8 +188,8 @@ struct exarg { // used for completion on the command line struct expand { - int xp_context; // type of expansion char_u *xp_pattern; // start of item to expand + int xp_context; // type of expansion size_t xp_pattern_len; // bytes in xp_pattern before cursor char_u *xp_arg; // completion function sctx_T xp_script_ctx; // SCTX for completion function @@ -199,9 +199,9 @@ struct expand { // characters need to be escaped #endif int xp_numfiles; // number of files found by file name completion + int xp_col; // cursor position in line char_u **xp_files; // list of files char_u *xp_line; // text being completed - int xp_col; // cursor position in line }; // values for xp_backslash diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 8b00417ce3..bc3d29a03f 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -2179,7 +2179,7 @@ int parse_command_modifiers(exarg_T *eap, char_u **errormsg, bool skip_only) // Set 'eventignore' to "all". Restore the // existing option value later. cmdmod.save_ei = vim_strsave(p_ei); - set_string_option_direct((char_u *)"ei", -1, + set_string_option_direct("ei", -1, (char_u *)"all", OPT_FREE, SID_NONE); } continue; @@ -2291,9 +2291,8 @@ static void undo_cmdmod(const exarg_T *eap, int save_msg_scroll) } if (cmdmod.save_ei != NULL) { - /* Restore 'eventignore' to the value before ":noautocmd". */ - set_string_option_direct((char_u *)"ei", -1, cmdmod.save_ei, - OPT_FREE, SID_NONE); + // Restore 'eventignore' to the value before ":noautocmd". + set_string_option_direct("ei", -1, cmdmod.save_ei, OPT_FREE, SID_NONE); free_string_option(cmdmod.save_ei); } @@ -3513,13 +3512,20 @@ const char * set_one_cmd_context( xp->xp_context = EXPAND_BUFFERS; xp->xp_pattern = (char_u *)arg; break; + case CMD_diffget: + case CMD_diffput: + // If current buffer is in diff mode, complete buffer names + // which are in diff mode, and different than current buffer. + xp->xp_context = EXPAND_DIFF_BUFFERS; + xp->xp_pattern = (char_u *)arg; + break; case CMD_USER: case CMD_USER_BUF: if (context != EXPAND_NOTHING) { // EX_XFILE: file names are handled above. if (!(ea.argt & EX_XFILE)) { if (context == EXPAND_MENUS) { - return (const char *)set_context_in_menu_cmd(xp, (char_u *)cmd, + return (const char *)set_context_in_menu_cmd(xp, cmd, (char_u *)arg, forceit); } else if (context == EXPAND_COMMANDS) { return arg; @@ -3599,7 +3605,7 @@ const char * set_one_cmd_context( case CMD_tmenu: case CMD_tunmenu: case CMD_popup: case CMD_emenu: return (const char *)set_context_in_menu_cmd( - xp, (char_u *)cmd, (char_u *)arg, forceit); + xp, cmd, (char_u *)arg, forceit); case CMD_colorscheme: xp->xp_context = EXPAND_COLORS; @@ -5175,6 +5181,7 @@ static const char *command_complete[] = [EXPAND_CSCOPE] = "cscope", [EXPAND_USER_DEFINED] = "custom", [EXPAND_USER_LIST] = "customlist", + [EXPAND_DIFF_BUFFERS] = "diff_buffer", [EXPAND_DIRECTORIES] = "dir", [EXPAND_ENV_VARS] = "environment", [EXPAND_EVENTS] = "event", @@ -6275,14 +6282,14 @@ int parse_compl_arg(const char_u *value, int vallen, int *complp, return OK; } -int cmdcomplete_str_to_type(char_u *complete_str) +int cmdcomplete_str_to_type(const char *complete_str) { for (int i = 0; i < (int)(ARRAY_SIZE(command_complete)); i++) { char *cmd_compl = get_command_complete(i); if (cmd_compl == NULL) { continue; } - if (STRCMP(complete_str, command_complete[i]) == 0) { + if (strcmp(complete_str, command_complete[i]) == 0) { return i; } } @@ -7272,9 +7279,7 @@ static void ex_find(exarg_T *eap) } } -/* - * ":edit", ":badd", ":visual". - */ +/// ":edit", ":badd", ":balt", ":visual". static void ex_edit(exarg_T *eap) { do_exedit(eap, NULL); @@ -7348,7 +7353,9 @@ do_exedit( else if (eap->cmdidx == CMD_enew) readonlymode = FALSE; /* 'readonly' doesn't make sense in an empty buffer */ - setpcmark(); + if (eap->cmdidx != CMD_balt && eap->cmdidx != CMD_badd) { + setpcmark(); + } if (do_ecmd(0, (eap->cmdidx == CMD_enew ? NULL : eap->arg), NULL, eap, eap->do_ecmd_lnum, (buf_hide(curbuf) ? ECMD_HIDE : 0) @@ -7356,6 +7363,7 @@ do_exedit( // After a split we can use an existing buffer. + (old_curwin != NULL ? ECMD_OLDBUF : 0) + (eap->cmdidx == CMD_badd ? ECMD_ADDBUF : 0) + + (eap->cmdidx == CMD_balt ? ECMD_ALTBUF : 0) , old_curwin == NULL ? curwin : NULL) == FAIL) { // Editing the file failed. If the window was split, close it. if (old_curwin != NULL) { @@ -7591,7 +7599,7 @@ void post_chdir(CdScope scope, bool trigger_dirchanged) curwin->w_localdir = (char_u *)xstrdup(cwd); break; case kCdScopeInvalid: - assert(false); + abort(); } shorten_fnames(true); @@ -7756,6 +7764,11 @@ static void do_exmap(exarg_T *eap, int isabbrev) static void ex_winsize(exarg_T *eap) { char_u *arg = eap->arg; + + if (!ascii_isdigit(*arg)) { + EMSG2(_(e_invarg2), arg); + return; + } int w = getdigits_int(&arg, false, 10); arg = skipwhite(arg); char_u *p = arg; @@ -8721,7 +8734,7 @@ ssize_t find_cmdline_var(const char_u *src, size_t *usedlen) * Evaluate cmdline variables. * * change '%' to curbuf->b_ffname - * '#' to curwin->w_altfile + * '#' to curwin->w_alt_fnum * '<cword>' to word under the cursor * '<cWORD>' to WORD under the cursor * '<cexpr>' to C-expression under the cursor diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 8d10e98259..c66ae13f53 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -886,7 +886,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) need_wait_return = false; } - set_string_option_direct((char_u *)"icm", -1, s->save_p_icm, OPT_FREE, + set_string_option_direct("icm", -1, s->save_p_icm, OPT_FREE, SID_NONE); State = s->save_State; setmouse(); @@ -5084,9 +5084,13 @@ ExpandFromContext ( } if (xp->xp_context == EXPAND_BUFFERS) return ExpandBufnames(pat, num_file, file, options); + if (xp->xp_context == EXPAND_DIFF_BUFFERS) { + return ExpandBufnames(pat, num_file, file, options | BUF_DIFF_FILTER); + } if (xp->xp_context == EXPAND_TAGS - || xp->xp_context == EXPAND_TAGS_LISTFILES) + || xp->xp_context == EXPAND_TAGS_LISTFILES) { return expand_tags(xp->xp_context == EXPAND_TAGS, pat, num_file, file); + } if (xp->xp_context == EXPAND_COLORS) { char *directories[] = { "colors", NULL }; return ExpandRTDir(pat, DIP_START + DIP_OPT, num_file, file, directories); @@ -6383,7 +6387,7 @@ int hist_type2char(int type) return '>'; } default: { - assert(false); + abort(); } } return NUL; @@ -6441,7 +6445,7 @@ static int open_cmdwin(void) cmdwin_level = ccline.level; // Create empty command-line buffer. - buf_open_scratch(0, "[Command Line]"); + buf_open_scratch(0, _("[Command Line]")); // Command-line buffer has bufhidden=wipe, unlike a true "scratch" buffer. set_option_value("bh", 0L, "wipe", OPT_LOCAL); curwin->w_p_rl = cmdmsg_rl; diff --git a/src/nvim/ex_getln.h b/src/nvim/ex_getln.h index dc4395e081..3727aa5e62 100644 --- a/src/nvim/ex_getln.h +++ b/src/nvim/ex_getln.h @@ -32,6 +32,7 @@ #define WILD_IGNORE_COMPLETESLASH 0x400 #define WILD_NOERROR 0x800 // sets EW_NOERROR #define WILD_BUFLASTUSED 0x1000 +#define BUF_DIFF_FILTER 0x2000 /// Present history tables typedef enum { diff --git a/src/nvim/ex_session.c b/src/nvim/ex_session.c index 14dac9a126..63789b3981 100644 --- a/src/nvim/ex_session.c +++ b/src/nvim/ex_session.c @@ -384,6 +384,18 @@ static int put_view( xfree(fname_esc); } + if (wp->w_alt_fnum) { + buf_T *const alt = buflist_findnr(wp->w_alt_fnum); + + // Set the alternate file. + if ((flagp == &ssop_flags) && alt != NULL && alt->b_fname != NULL + && *alt->b_fname != NUL + && (fputs("balt ", fd) < 0 + || ses_fname(fd, alt, flagp, true) == FAIL)) { + return FAIL; + } + } + // // Local mappings and abbreviations. // @@ -438,9 +450,9 @@ static int put_view( "let s:l = %" PRId64 " - ((%" PRId64 " * winheight(0) + %" PRId64 ") / %" PRId64 ")\n" "if s:l < 1 | let s:l = 1 | endif\n" - "exe s:l\n" + "keepjumps exe s:l\n" "normal! zt\n" - "%" PRId64 "\n", + "keepjumps %" PRId64 "\n", (int64_t)wp->w_cursor.lnum, (int64_t)(wp->w_cursor.lnum - wp->w_topline), (int64_t)(wp->w_height_inner / 2), diff --git a/src/nvim/file_search.c b/src/nvim/file_search.c index b1fa0b6779..8beba38509 100644 --- a/src/nvim/file_search.c +++ b/src/nvim/file_search.c @@ -1595,7 +1595,7 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, bool changed_window) } case kCdScopeInvalid: { // Should never happen. - assert(false); + abort(); } } diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index a542bb19dd..714bbb5780 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -1644,8 +1644,7 @@ failed: save_file_ff(curbuf); // If editing a new file: set 'fenc' for the current buffer. // Also for ":read ++edit file". - set_string_option_direct((char_u *)"fenc", -1, fenc, - OPT_FREE | OPT_LOCAL, 0); + set_string_option_direct("fenc", -1, fenc, OPT_FREE | OPT_LOCAL, 0); } if (fenc_alloced) xfree(fenc); @@ -2002,7 +2001,7 @@ void set_forced_fenc(exarg_T *eap) { if (eap->force_enc != 0) { char_u *fenc = enc_canonize(eap->cmd + eap->force_enc); - set_string_option_direct((char_u *)"fenc", -1, fenc, OPT_FREE|OPT_LOCAL, 0); + set_string_option_direct("fenc", -1, fenc, OPT_FREE|OPT_LOCAL, 0); xfree(fenc); } } @@ -5077,7 +5076,8 @@ void buf_reload(buf_T *buf, int orig_mode) // Mark all undo states as changed. u_unchanged(curbuf); } - buf_updates_unregister_all(curbuf); + buf_updates_unload(curbuf, true); + curbuf->b_mod_set = true; } } xfree(ea.cmd); diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 0593c16999..5032646d7e 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -2999,7 +2999,6 @@ static void foldlevelDiff(fline_T *flp) static void foldlevelExpr(fline_T *flp) { win_T *win; - int n; int c; linenr_T lnum = flp->lnum + flp->off; @@ -3017,7 +3016,7 @@ static void foldlevelExpr(fline_T *flp) /* KeyTyped may be reset to 0 when calling a function which invokes * do_cmdline(). To make 'foldopen' work correctly restore KeyTyped. */ const bool save_keytyped = KeyTyped; - n = (int)eval_foldexpr(flp->wp->w_p_fde, &c); + const int n = eval_foldexpr(flp->wp->w_p_fde, &c); KeyTyped = save_keytyped; switch (c) { @@ -3202,8 +3201,10 @@ int put_folds(FILE *fd, win_T *wp) { if (foldmethodIsManual(wp)) { if (put_line(fd, "silent! normal! zE") == FAIL - || put_folds_recurse(fd, &wp->w_folds, (linenr_T)0) == FAIL) + || put_folds_recurse(fd, &wp->w_folds, (linenr_T)0) == FAIL + || put_line(fd, "let &fdl = &fdl") == FAIL) { return FAIL; + } } /* If some folds are manually opened/closed, need to restore that. */ diff --git a/src/nvim/log.h b/src/nvim/log.h index f2e74df031..17d754c033 100644 --- a/src/nvim/log.h +++ b/src/nvim/log.h @@ -17,10 +17,11 @@ #endif -#define DEBUG_LOG_LEVEL 0 -#define INFO_LOG_LEVEL 1 -#define WARN_LOG_LEVEL 2 -#define ERROR_LOG_LEVEL 3 +#define TRACE_LOG_LEVEL 0 +#define DEBUG_LOG_LEVEL 1 +#define INFO_LOG_LEVEL 2 +#define WARN_LOG_LEVEL 3 +#define ERROR_LOG_LEVEL 4 #define DLOG(...) #define DLOGN(...) diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 030df69caa..83b3729ad3 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -245,7 +245,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) } else { dictitem_T *const di = tv_dict_item_alloc_len(s, len); if (tv_dict_add(cur.tv->vval.v_dict, di) == FAIL) { - assert(false); + abort(); } kv_push(stack, cur); cur = (TVPopStackItem) { &di->di_tv, false, false, 0 }; @@ -391,7 +391,7 @@ bool nlua_pop_typval(lua_State *lstate, typval_T *ret_tv) break; } default: { - assert(false); + abort(); } } nlua_pop_typval_table_processing_end: @@ -1200,7 +1200,7 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) break; } default: { - assert(false); + abort(); } } break; diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index dbf4f6014c..e13b9745a8 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -39,6 +39,16 @@ assert(vim) vim.inspect = package.loaded['vim.inspect'] assert(vim.inspect) +vim.log = { + levels = { + TRACE = 0; + DEBUG = 1; + INFO = 2; + WARN = 3; + ERROR = 4; + } +} + -- Internal-only until comments in #8107 are addressed. -- Returns: -- {errcode}, {output} @@ -478,6 +488,23 @@ function vim.defer_fn(fn, timeout) return timer end + +--- Notification provider +--- without a runtime, writes to :Messages +-- see :help nvim_notify +--@param msg Content of the notification to show to the user +--@param log_level Optional log level +--@param opts Dictionary with optional options (timeout, etc) +function vim.notify(msg, log_level, _opts) + + if log_level == vim.log.levels.ERROR then + vim.api.nvim_err_writeln(msg) + else + vim.api.nvim_echo({{msg}}, true, {}) + end +end + + local on_keystroke_callbacks = {} --- Register a lua {fn} with an {id} to be run after every keystroke. diff --git a/src/nvim/main.c b/src/nvim/main.c index 9f71df3a46..7064f2a068 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1404,9 +1404,9 @@ static void load_plugins(void) static void handle_quickfix(mparm_T *paramp) { if (paramp->edit_type == EDIT_QF) { - if (paramp->use_ef != NULL) - set_string_option_direct((char_u *)"ef", -1, - paramp->use_ef, OPT_FREE, SID_CARG); + if (paramp->use_ef != NULL) { + set_string_option_direct("ef", -1, paramp->use_ef, OPT_FREE, SID_CARG); + } vim_snprintf((char *)IObuff, IOSIZE, "cfile %s", p_ef); if (qf_init(NULL, p_ef, p_efm, true, IObuff, p_menc) < 0) { msg_putchar('\n'); diff --git a/src/nvim/memline_defs.h b/src/nvim/memline_defs.h index 9a6f29a908..dc4755f83d 100644 --- a/src/nvim/memline_defs.h +++ b/src/nvim/memline_defs.h @@ -45,16 +45,16 @@ typedef struct memline { memfile_T *ml_mfp; // pointer to associated memfile + infoptr_T *ml_stack; // stack of pointer blocks (array of IPTRs) + int ml_stack_top; // current top of ml_stack + int ml_stack_size; // total number of entries in ml_stack + #define ML_EMPTY 1 // empty buffer #define ML_LINE_DIRTY 2 // cached line was changed and allocated #define ML_LOCKED_DIRTY 4 // ml_locked was changed #define ML_LOCKED_POS 8 // ml_locked needs positive block number int ml_flags; - infoptr_T *ml_stack; // stack of pointer blocks (array of IPTRs) - int ml_stack_top; // current top of ml_stack - int ml_stack_size; // total number of entries in ml_stack - linenr_T ml_line_lnum; // line number of cached line, 0 if not valid char_u *ml_line_ptr; // pointer to cached line size_t ml_line_offset; // cached byte offset of ml_line_lnum diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 7094d3be90..ac3b7768e6 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -81,7 +81,7 @@ ex_menu(exarg_T *eap) // kFalse for "menu disable vimmenu_T menuarg; - modes = get_menu_cmd_modes(eap->cmd, eap->forceit, &noremap, &unmenu); + modes = get_menu_cmd_modes((char *)eap->cmd, eap->forceit, &noremap, &unmenu); arg = eap->arg; for (;; ) { @@ -912,7 +912,9 @@ static int expand_emenu; /* TRUE for ":emenu" command */ /* * Work out what to complete when doing command line completion of menu names. */ -char_u *set_context_in_menu_cmd(expand_T *xp, char_u *cmd, char_u *arg, int forceit) +char_u *set_context_in_menu_cmd(expand_T *xp, const char *cmd, char_u *arg, + bool forceit) + FUNC_ATTR_NONNULL_ALL { char_u *after_dot; char_u *p; @@ -1178,7 +1180,7 @@ static bool menu_namecmp(const char_u *const name, const char_u *const mname) /// to whether the command is an "unmenu" command. int get_menu_cmd_modes( - const char_u * cmd, + const char *cmd, bool forceit, int *noremap, int *unmenu diff --git a/src/nvim/move.c b/src/nvim/move.c index a6afdc27d9..1210a3365a 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -888,11 +888,11 @@ void curs_columns( } else { n = plines; } - if ((colnr_T)n >= wp->w_height_inner + wp->w_skipcol / width) { + if ((colnr_T)n >= wp->w_height_inner + wp->w_skipcol / width - so) { extra += 2; } - if (extra == 3 || plines < so * 2) { + if (extra == 3 || plines <= so * 2) { // not enough room for 'scrolloff', put cursor in the middle n = wp->w_virtcol / width; if (n > wp->w_height_inner / 2) { diff --git a/src/nvim/ops.c b/src/nvim/ops.c index 052b07ed44..ea52d6a3d3 100644 --- a/src/nvim/ops.c +++ b/src/nvim/ops.c @@ -2623,7 +2623,7 @@ static void op_yank_reg(oparg_T *oap, bool message, yankreg_T *reg, bool append) } // NOTREACHED case kMTUnknown: - assert(false); + abort(); } } @@ -3422,15 +3422,11 @@ error: if (dir == FORWARD) curbuf->b_op_start.lnum++; } - // Skip mark_adjust when adding lines after the last one, there - // can't be marks there. - if (curbuf->b_op_start.lnum + (y_type == kMTCharWise) - 1 + nr_lines - < curbuf->b_ml.ml_line_count) { - ExtmarkOp kind = (y_type == kMTLineWise && !(flags & PUT_LINE_SPLIT)) - ? kExtmarkUndo : kExtmarkNOOP; - mark_adjust(curbuf->b_op_start.lnum + (y_type == kMTCharWise), - (linenr_T)MAXLNUM, nr_lines, 0L, kind); - } + + ExtmarkOp kind = (y_type == kMTLineWise && !(flags & PUT_LINE_SPLIT)) + ? kExtmarkUndo : kExtmarkNOOP; + mark_adjust(curbuf->b_op_start.lnum + (y_type == kMTCharWise), + (linenr_T)MAXLNUM, nr_lines, 0L, kind); // note changed text for displaying and folding if (y_type == kMTCharWise) { @@ -6092,7 +6088,7 @@ static void set_clipboard(int name, yankreg_T *reg) break; } case kMTUnknown: { - assert(false); + abort(); } } diff --git a/src/nvim/option.c b/src/nvim/option.c index 74bf6f0590..ac25c86b5f 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -1942,6 +1942,7 @@ static void didset_options(void) (void)opt_strings_flags(p_dy, p_dy_values, &dy_flags, true); (void)opt_strings_flags(p_rdb, p_rdb_values, &rdb_flags, true); (void)opt_strings_flags(p_tc, p_tc_values, &tc_flags, false); + (void)opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true); (void)opt_strings_flags(p_ve, p_ve_values, &ve_flags, true); (void)opt_strings_flags(p_wop, p_wop_values, &wop_flags, true); (void)opt_strings_flags(p_jop, p_jop_values, &jop_flags, true); @@ -2119,9 +2120,9 @@ static int shada_idx = -1; // "set_sid". void set_string_option_direct( - char_u *name, + const char *name, int opt_idx, - char_u *val, + const char_u *val, int opt_flags, // OPT_FREE, OPT_LOCAL and/or OPT_GLOBAL int set_sid ) @@ -2132,7 +2133,7 @@ set_string_option_direct( int idx = opt_idx; if (idx == -1) { // Use name. - idx = findoption((const char *)name); + idx = findoption(name); if (idx < 0) { // Not found (should not happen). internal_error("set_string_option_direct()"); IEMSG2(_("For option %s"), name); @@ -3077,6 +3078,10 @@ ambw_end: if (!parse_winhl_opt(curwin)) { errmsg = e_invarg; } + } else if (varp == &p_tpf) { + if (opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true) != OK) { + errmsg = e_invarg; + } } else { // Options that are a list of flags. p = NULL; @@ -3786,7 +3791,7 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, if (p_terse && p == NULL) { STRCPY(IObuff, p_shm); STRCAT(IObuff, "s"); - set_string_option_direct((char_u *)"shm", -1, IObuff, OPT_FREE, 0); + set_string_option_direct("shm", -1, IObuff, OPT_FREE, 0); } else if (!p_terse && p != NULL) { // remove 's' from p_shm STRMOVE(p, p + 1); } @@ -4526,7 +4531,7 @@ bool is_tty_option(const char *name) #define TCO_BUFFER_SIZE 8 /// @param name TUI-related option /// @param[out,allocated] value option string value -bool get_tty_option(char *name, char **value) +bool get_tty_option(const char *name, char **value) { if (strequal(name, "t_Co")) { if (value) { @@ -4592,6 +4597,7 @@ bool set_tty_option(const char *name, char *value) /// /// @return Option index or -1 if option was not found. static int findoption(const char *const arg) + FUNC_ATTR_NONNULL_ALL { return findoption_len(arg, strlen(arg)); } @@ -4605,17 +4611,17 @@ static int findoption(const char *const arg) /// hidden String option: -2. /// unknown option: -3. int get_option_value( - char_u *name, + const char *name, long *numval, char_u **stringval, ///< NULL when only checking existence int opt_flags ) { - if (get_tty_option((char *)name, (char **)stringval)) { + if (get_tty_option(name, (char **)stringval)) { return 0; } - int opt_idx = findoption((const char *)name); + int opt_idx = findoption(name); if (opt_idx < 0) { // Unknown option. return -3; } @@ -7049,7 +7055,7 @@ void set_fileformat(int eol_style, int opt_flags) // p is NULL if "eol_style" is EOL_UNKNOWN. if (p != NULL) { - set_string_option_direct((char_u *)"ff", + set_string_option_direct("ff", -1, (char_u *)p, OPT_FREE | opt_flags, diff --git a/src/nvim/option_defs.h b/src/nvim/option_defs.h index 683afc670e..43b0107800 100644 --- a/src/nvim/option_defs.h +++ b/src/nvim/option_defs.h @@ -621,6 +621,19 @@ EXTERN int p_sta; // 'smarttab' EXTERN int p_sb; // 'splitbelow' EXTERN long p_tpm; // 'tabpagemax' EXTERN char_u *p_tal; // 'tabline' +EXTERN char_u *p_tpf; // 'termpastefilter' +EXTERN unsigned int tpf_flags; ///< flags from 'termpastefilter' +#ifdef IN_OPTION_C +static char *(p_tpf_values[]) = + { "BS", "HT", "FF", "ESC", "DEL", "C0", "C1", NULL }; +#endif +# define TPF_BS 0x001 +# define TPF_HT 0x002 +# define TPF_FF 0x004 +# define TPF_ESC 0x008 +# define TPF_DEL 0x010 +# define TPF_C0 0x020 +# define TPF_C1 0x040 EXTERN char_u *p_sps; // 'spellsuggest' EXTERN int p_spr; // 'splitright' EXTERN int p_sol; // 'startofline' diff --git a/src/nvim/options.lua b/src/nvim/options.lua index df2bfbce34..fe108ef1cc 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -2821,6 +2821,14 @@ return { defaults={if_true={vi=false}} }, { + full_name='termpastefilter', abbreviation='tpf', + type='string', list='onecomma', scope={'global'}, + deny_duplicates=true, + vim=true, + varname='p_tpf', + defaults={if_true={vi="", vim="BS,HT,ESC,DEL"}} + }, + { full_name='terse', short_desc=N_("hides notification of search wrap"), type='bool', scope={'global'}, diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c index 4d7d9a45df..d794969ab5 100644 --- a/src/nvim/os/pty_process_unix.c +++ b/src/nvim/os/pty_process_unix.c @@ -20,6 +20,10 @@ # include <pty.h> #endif +#ifdef __APPLE__ +# include <crt_externs.h> +#endif + #include <uv.h> #include "nvim/lib/klist.h" @@ -154,28 +158,14 @@ void pty_process_teardown(Loop *loop) static void init_child(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL { +#if defined(HAVE__NSGETENVIRON) +#define environ (*_NSGetEnviron()) +#else + extern char **environ; +#endif // New session/process-group. #6530 setsid(); - os_unsetenv("COLUMNS"); - os_unsetenv("LINES"); - os_unsetenv("TERMCAP"); - os_unsetenv("COLORFGBG"); - // setting COLORTERM to "truecolor" if termguicolors is set and 256 - // otherwise, but only if it was set in the parent terminal at all - if (os_env_exists("COLORTERM")) { - const char *colorterm = os_getenv("COLORTERM"); - if (colorterm != NULL) { - if (p_tgc) { - os_setenv("COLORTERM", "truecolor", 1); - } else { - os_setenv("COLORTERM", "256", 1); - } - } else { - os_unsetenv("COLORTERM"); - } - } - signal(SIGCHLD, SIG_DFL); signal(SIGHUP, SIG_DFL); signal(SIGINT, SIG_DFL); @@ -190,9 +180,12 @@ static void init_child(PtyProcess *ptyproc) } char *prog = ptyproc->process.argv[0]; - os_setenv("TERM", ptyproc->term_name ? ptyproc->term_name : "ansi", 1); - execvp(prog, ptyproc->process.argv); + + assert(proc->env); + environ = tv_dict_to_env(proc->env); + execvp(prog, proc->argv); ELOG("execvp failed: %s: %s", strerror(errno), prog); + _exit(122); // 122 is EXEC_FAILED in the Vim source. } diff --git a/src/nvim/os/pty_process_unix.h b/src/nvim/os/pty_process_unix.h index f7c57b3839..8c822eafad 100644 --- a/src/nvim/os/pty_process_unix.h +++ b/src/nvim/os/pty_process_unix.h @@ -17,7 +17,6 @@ static inline PtyProcess pty_process_init(Loop *loop, void *data) { PtyProcess rv; rv.process = process_init(loop, kProcessTypePty, data); - rv.term_name = NULL; rv.width = 80; rv.height = 24; rv.tty_fd = -1; diff --git a/src/nvim/os/pty_process_win.c b/src/nvim/os/pty_process_win.c index 6f7100e846..52d2f84ace 100644 --- a/src/nvim/os/pty_process_win.c +++ b/src/nvim/os/pty_process_win.c @@ -52,6 +52,7 @@ int pty_process_spawn(PtyProcess *ptyproc) uv_connect_t *out_req = NULL; wchar_t *cmd_line = NULL; wchar_t *cwd = NULL; + wchar_t *env = NULL; const char *emsg = NULL; assert(proc->err.closed); @@ -124,13 +125,22 @@ int pty_process_spawn(PtyProcess *ptyproc) goto cleanup; } + if (proc->env != NULL) { + status = build_env_block(proc->env, &env); + } + + if (status != 0) { + emsg = "build_env_block failed"; + goto cleanup; + } + if (ptyproc->type == kConpty) { if (!os_conpty_spawn(conpty_object, &process_handle, NULL, cmd_line, cwd, - NULL)) { + env)) { emsg = "os_conpty_spawn failed"; status = (int)GetLastError(); goto cleanup; @@ -141,7 +151,7 @@ int pty_process_spawn(PtyProcess *ptyproc) NULL, // Optional application name cmd_line, cwd, - NULL, // Optional environment variables + env, &err); if (spawncfg == NULL) { emsg = "winpty_spawn_config_new failed"; @@ -213,6 +223,7 @@ cleanup: xfree(in_req); xfree(out_req); xfree(cmd_line); + xfree(env); xfree(cwd); return status; } @@ -454,3 +465,66 @@ int translate_winpty_error(int winpty_errno) default: return UV_UNKNOWN; } } + +typedef struct EnvNode { + wchar_t *str; + size_t len; + QUEUE node; +} EnvNode; + +/// Build the environment block to pass to CreateProcessW. +/// +/// @param[in] denv Dict of environment name/value pairs +/// @param[out] env Allocated environment block +/// +/// @returns zero on success or error code of MultiByteToWideChar function. +static int build_env_block(dict_T *denv, wchar_t **env_block) +{ + const size_t denv_size = (size_t)tv_dict_len(denv); + size_t env_block_len = 0; + int rc; + char **env = tv_dict_to_env(denv); + + QUEUE *q; + QUEUE env_q; + QUEUE_INIT(&env_q); + // Convert env vars to wchar_t and calculate how big the final env block + // needs to be + for (size_t i = 0; i < denv_size; i++) { + EnvNode *env_node = xmalloc(sizeof(*env_node)); + rc = utf8_to_utf16(env[i], -1, &env_node->str); + if (rc != 0) { + goto cleanup; + } + env_node->len = wcslen(env_node->str) + 1; + env_block_len += env_node->len; + QUEUE_INSERT_TAIL(&env_q, &env_node->node); + } + + // Additional '\0' after the final entry + env_block_len++; + + *env_block = xmalloc(sizeof(**env_block) * env_block_len); + wchar_t *pos = *env_block; + + QUEUE_FOREACH(q, &env_q) { + EnvNode *env_node = QUEUE_DATA(q, EnvNode, node); + memcpy(pos, env_node->str, env_node->len * sizeof(*pos)); + pos += env_node->len; + } + + *pos = L'\0'; + +cleanup: + q = QUEUE_HEAD(&env_q); + while (q != &env_q) { + QUEUE *next = q->next; + EnvNode *env_node = QUEUE_DATA(q, EnvNode, node); + XFREE_CLEAR(env_node->str); + QUEUE_REMOVE(q); + xfree(env_node); + q = next; + } + + return rc; +} diff --git a/src/nvim/os/pty_process_win.h b/src/nvim/os/pty_process_win.h index 8ad5ba7286..f8ec79a3d6 100644 --- a/src/nvim/os/pty_process_win.h +++ b/src/nvim/os/pty_process_win.h @@ -37,7 +37,6 @@ static inline PtyProcess pty_process_init(Loop *loop, void *data) { PtyProcess rv; rv.process = process_init(loop, kProcessTypePty, data); - rv.term_name = NULL; rv.width = 80; rv.height = 24; rv.object.winpty = NULL; diff --git a/src/nvim/os/time.c b/src/nvim/os/time.c index 4b6533cd0c..5cf628935f 100644 --- a/src/nvim/os/time.c +++ b/src/nvim/os/time.c @@ -97,7 +97,7 @@ void os_microdelay(uint64_t us, bool ignoreinput) const int rv = uv_cond_timedwait(&delay_cond, &delay_mutex, ns_delta); if (0 != rv && UV_ETIMEDOUT != rv) { - assert(false); + abort(); break; } // Else: Timeout proceeded normally. diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 3d639d7db5..dfd38a6eca 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -3665,7 +3665,7 @@ static int qf_open_new_cwindow(qf_info_T *qi, int height) static void qf_set_title_var(qf_list_T *qfl) { if (qfl->qf_title != NULL) { - set_internal_string_var((char_u *)"w:quickfix_title", qfl->qf_title); + set_internal_string_var("w:quickfix_title", qfl->qf_title); } } @@ -4951,7 +4951,7 @@ void ex_cfile(exarg_T *eap) } } if (*eap->arg != NUL) { - set_string_option_direct((char_u *)"ef", -1, eap->arg, OPT_FREE, 0); + set_string_option_direct("ef", -1, eap->arg, OPT_FREE, 0); } char_u *enc = (*curbuf->b_p_menc != NUL) ? curbuf->b_p_menc : p_menc; @@ -5648,7 +5648,7 @@ static int get_qfline_items(qfline_T *qfp, list_T *list) == FAIL)) { // tv_dict_add* fail only if key already exist, but this is a newly // allocated dictionary which is thus guaranteed to have no existing keys. - assert(false); + abort(); } return OK; diff --git a/src/nvim/screen.c b/src/nvim/screen.c index a78f905a70..c16fe46955 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -2082,7 +2082,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int line_attr_lowprio = 0; // low-priority attribute for the line matchitem_T *cur; // points to the match list match_T *shl; // points to search_hl or a match - int shl_flag; // flag to indicate whether search_hl + bool shl_flag; // flag to indicate whether search_hl // has been processed or not bool prevcol_hl_flag; // flag to indicate whether prevcol // equals startcol of search_hl or one @@ -2950,16 +2950,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, */ v = (long)(ptr - line); cur = wp->w_match_head; - shl_flag = FALSE; - while (cur != NULL || shl_flag == FALSE) { - if (shl_flag == FALSE - && ((cur != NULL - && cur->priority > SEARCH_HL_PRIORITY) - || cur == NULL)) { + shl_flag = false; + while (cur != NULL || !shl_flag) { + if (!shl_flag + && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) { shl = &search_hl; - shl_flag = TRUE; - } else + shl_flag = true; + } else { shl = &cur->hl; + } if (cur != NULL) { cur->pos.cur = 0; } @@ -2984,7 +2983,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, has_match_conc = v == (long)shl->startcol ? 2 : 1; match_conc = cur->conceal_char; } else { - has_match_conc = match_conc = 0; + has_match_conc = 0; } } else if (v == (long)shl->endcol) { shl->attr_cur = 0; @@ -3026,16 +3025,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, search_attr_from_match = false; search_attr = search_hl.attr_cur; cur = wp->w_match_head; - shl_flag = FALSE; - while (cur != NULL || shl_flag == FALSE) { - if (shl_flag == FALSE - && ((cur != NULL - && cur->priority > SEARCH_HL_PRIORITY) - || cur == NULL)) { + shl_flag = false; + while (cur != NULL || !shl_flag) { + if (!shl_flag + && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) { shl = &search_hl; - shl_flag = TRUE; - } else + shl_flag = true; + } else { shl = &cur->hl; + } if (shl->attr_cur != 0) { search_attr = shl->attr_cur; search_attr_from_match = shl != &search_hl; @@ -3048,6 +3046,12 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, && (wp->w_p_list && lcs_eol_one == -1)) { search_attr = 0; } + + // Do not allow a conceal over EOL otherwise EOL will be missed + // and bad things happen. + if (*ptr == NUL) { + has_match_conc = 0; + } } if (diff_hlf != (hlf_T)0) { @@ -3672,12 +3676,13 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, && vim_strchr(wp->w_p_cocu, 'v') == NULL)) { char_attr = conceal_attr; if ((prev_syntax_id != syntax_seqnr || has_match_conc > 1) - && (syn_get_sub_char() != NUL || match_conc + && (syn_get_sub_char() != NUL + || (has_match_conc && match_conc) || wp->w_p_cole == 1) && wp->w_p_cole != 3) { // First time at this concealed item: display one // character. - if (match_conc) { + if (has_match_conc && match_conc) { c = match_conc; } else if (syn_get_sub_char() != NUL) { c = syn_get_sub_char(); @@ -3837,16 +3842,15 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, // 'search_hl' and the match list. char_attr = search_hl.attr; cur = wp->w_match_head; - shl_flag = FALSE; - while (cur != NULL || shl_flag == FALSE) { - if (shl_flag == FALSE - && ((cur != NULL - && cur->priority > SEARCH_HL_PRIORITY) - || cur == NULL)) { + shl_flag = false; + while (cur != NULL || !shl_flag) { + if (!shl_flag + && (cur == NULL || cur->priority > SEARCH_HL_PRIORITY)) { shl = &search_hl; - shl_flag = TRUE; - } else + shl_flag = true; + } else { shl = &cur->hl; + } if ((ptr - line) - 1 == (long)shl->startcol && (shl == &search_hl || !shl->is_addpos)) { char_attr = shl->attr; @@ -5092,9 +5096,9 @@ static void redraw_custom_statusline(win_T *wp) // When there is an error disable the statusline, otherwise the // display is messed up with errors and a redraw triggers the problem // again and again. - set_string_option_direct((char_u *)"statusline", -1, - (char_u *)"", OPT_FREE | (*wp->w_p_stl != NUL - ? OPT_LOCAL : OPT_GLOBAL), SID_ERROR); + set_string_option_direct("statusline", -1, (char_u *)"", + OPT_FREE | (*wp->w_p_stl != NUL + ? OPT_LOCAL : OPT_GLOBAL), SID_ERROR); } did_emsg |= saved_did_emsg; entered = false; @@ -5175,7 +5179,7 @@ get_keymap_str ( static void win_redr_custom ( win_T *wp, - int draw_ruler /* TRUE or FALSE */ + bool draw_ruler ) { static int entered = FALSE; @@ -6852,7 +6856,7 @@ void draw_tabline(void) did_emsg = false; win_redr_custom(NULL, false); if (did_emsg) { - set_string_option_direct((char_u *)"tabline", -1, + set_string_option_direct("tabline", -1, (char_u *)"", OPT_FREE, SID_ERROR); } did_emsg |= saved_did_emsg; @@ -7114,11 +7118,12 @@ static void win_redr_ruler(win_T *wp, int always) if (*p_ruf) { int save_called_emsg = called_emsg; - called_emsg = FALSE; - win_redr_custom(wp, TRUE); - if (called_emsg) - set_string_option_direct((char_u *)"rulerformat", -1, - (char_u *)"", OPT_FREE, SID_ERROR); + called_emsg = false; + win_redr_custom(wp, true); + if (called_emsg) { + set_string_option_direct("rulerformat", -1, (char_u *)"", + OPT_FREE, SID_ERROR); + } called_emsg |= save_called_emsg; return; } diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 2444910bb3..c0e787380f 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -765,7 +765,7 @@ static ShaDaReadResult sd_reader_skip(ShaDaReadDef *const sd_reader, (uint64_t) offset); return kSDReadStatusNotShaDa; } - assert(false); + abort(); } return kSDReadStatusSuccess; } @@ -1224,7 +1224,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) } case kSDReadStatusFinished: { // Should be handled by the while condition. - assert(false); + abort(); } case kSDReadStatusNotShaDa: case kSDReadStatusReadError: { @@ -1236,7 +1236,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) } switch (cur_entry.type) { case kSDItemMissing: { - assert(false); + abort(); } case kSDItemUnknown: { break; @@ -1628,7 +1628,7 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ((size_t) (!CHECK_DEFAULT(entry, attr))) switch (entry.type) { case kSDItemMissing: { - assert(false); + abort(); } case kSDItemUnknown: { if (spacker->callback(spacker->data, entry.data.unknown_item.contents, @@ -1850,7 +1850,7 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, break; } default: { - assert(false); + abort(); } } } @@ -2147,7 +2147,7 @@ static inline ShaDaWriteResult shada_read_when_writing( } case kSDReadStatusFinished: { // Should be handled by the while condition. - assert(false); + abort(); } case kSDReadStatusNotShaDa: { ret = kSDWriteReadNotShada; @@ -2184,7 +2184,7 @@ static inline ShaDaWriteResult shada_read_when_writing( } case kSDItemHeader: case kSDItemBufferList: { - assert(false); + abort(); } case kSDItemUnknown: { ret = shada_pack_entry(packer, entry, 0); @@ -4044,7 +4044,7 @@ shada_read_next_item_start: } case kSDItemMissing: case kSDItemUnknown: { - assert(false); + abort(); } } entry->type = (ShadaEntryType) type_u64; diff --git a/src/nvim/sign_defs.h b/src/nvim/sign_defs.h index c898dba890..19c0263cf1 100644 --- a/src/nvim/sign_defs.h +++ b/src/nvim/sign_defs.h @@ -24,11 +24,11 @@ typedef struct signlist signlist_T; struct signlist { int id; // unique identifier for each placed sign - linenr_T lnum; // line number which has this sign int typenr; // typenr of sign + int priority; // priority for highlighting bool has_text_or_icon; // has text or icon + linenr_T lnum; // line number which has this sign signgroup_T *group; // sign group - int priority; // priority for highlighting signlist_T *next; // next signlist entry signlist_T *prev; // previous entry -- for easy reordering }; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 6425c9fed5..55f9594de2 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -112,6 +112,7 @@ #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/undo.h" +#include "nvim/ui.h" #include "nvim/os/os.h" #include "nvim/os/input.h" @@ -2889,8 +2890,14 @@ void spell_suggest(int count) msg_col = 0; // Ask for choice. selected = prompt_for_number(&mouse_used); - if (mouse_used) + + if (ui_has(kUIMessages)) { + ui_call_msg_clear(); + } + + if (mouse_used) { selected -= lines_left; + } lines_left = Rows; // avoid more prompt // don't delay for 'smd' in normal_cmd() msg_scroll = msg_scroll_save; @@ -6624,7 +6631,7 @@ void ex_spelldump(exarg_T *eap) if (no_spell_checking(curwin)) { return; } - get_option_value((char_u *)"spl", &dummy, &spl, OPT_LOCAL); + get_option_value("spl", &dummy, &spl, OPT_LOCAL); // Create a new empty buffer in a new window. do_cmdline_cmd("new"); diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 90af010164..3c125959a9 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -5387,7 +5387,8 @@ spell_add_word ( len, word, NameBuff); } } - if (fseek(fd, fpos_next, SEEK_SET) <= 0) { + if (fseek(fd, fpos_next, SEEK_SET) != 0) { + PERROR(_("Seek error in spellfile")); break; } } diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index f99eca7953..547d953be9 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -3419,7 +3419,7 @@ static void syn_cmd_on(exarg_T *eap, int syncing) */ static void syn_cmd_enable(exarg_T *eap, int syncing) { - set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"enable"); + set_internal_string_var("syntax_cmd", (char_u *)"enable"); syn_cmd_onoff(eap, "syntax"); do_unlet(S_LEN("g:syntax_cmd"), true); } @@ -3432,7 +3432,7 @@ static void syn_cmd_reset(exarg_T *eap, int syncing) { eap->nextcmd = check_nextcmd(eap->arg); if (!eap->skip) { - set_internal_string_var((char_u *)"syntax_cmd", (char_u *)"reset"); + set_internal_string_var("syntax_cmd", (char_u *)"reset"); do_cmdline_cmd("runtime! syntax/syncolor.vim"); do_unlet(S_LEN("g:syntax_cmd"), true); } @@ -5614,14 +5614,14 @@ void ex_ownsyntax(exarg_T *eap) // Move value of b:current_syntax to w:current_syntax. new_value = get_var_value("b:current_syntax"); if (new_value != NULL) { - set_internal_string_var((char_u *)"w:current_syntax", new_value); + set_internal_string_var("w:current_syntax", new_value); } // Restore value of b:current_syntax. if (old_value == NULL) { do_unlet(S_LEN("b:current_syntax"), true); } else { - set_internal_string_var((char_u *)"b:current_syntax", old_value); + set_internal_string_var("b:current_syntax", old_value); xfree(old_value); } } diff --git a/src/nvim/tag.c b/src/nvim/tag.c index c6b1a0d04c..84ca240734 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -1461,7 +1461,7 @@ find_tags( p_ic = ignorecase_opt(pat, true, true); break; default: - assert(false); + abort(); } help_save = curbuf->b_help; diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 39e2ca6171..642c443318 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -535,8 +535,44 @@ void terminal_send(Terminal *term, char *data, size_t size) term->opts.write_cb(data, size, term->opts.data); } +static bool is_filter_char(int c) +{ + unsigned int flag = 0; + switch (c) { + case 0x08: + flag = TPF_BS; + break; + case 0x09: + flag = TPF_HT; + break; + case 0x0A: + case 0x0D: + break; + case 0x0C: + flag = TPF_FF; + break; + case 0x1b: + flag = TPF_ESC; + break; + case 0x7F: + flag = TPF_DEL; + break; + default: + if (c < ' ') { + flag = TPF_C0; + } else if (c >= 0x80 && c <= 0x9F) { + flag = TPF_C1; + } + } + return !!(tpf_flags & flag); +} + void terminal_paste(long count, char_u **y_array, size_t y_size) { + vterm_keyboard_start_paste(curbuf->terminal->vt); + terminal_flush_output(curbuf->terminal); + size_t buff_len = STRLEN(y_array[0]); + char_u *buff = xmalloc(buff_len); for (int i = 0; i < count; i++) { // -V756 // feed the lines to the terminal for (size_t j = 0; j < y_size; j++) { @@ -544,9 +580,28 @@ void terminal_paste(long count, char_u **y_array, size_t y_size) // terminate the previous line terminal_send(curbuf->terminal, "\n", 1); } - terminal_send(curbuf->terminal, (char *)y_array[j], STRLEN(y_array[j])); + size_t len = STRLEN(y_array[j]); + if (len > buff_len) { + buff = xrealloc(buff, len); + buff_len = len; + } + char_u *dst = buff; + char_u *src = y_array[j]; + while (*src != '\0') { + len = (size_t)utf_ptr2len(src); + int c = utf_ptr2char(src); + if (!is_filter_char(c)) { + memcpy(dst, src, len); + dst += len; + } + src += len; + } + terminal_send(curbuf->terminal, (char *)buff, (size_t)(dst - buff)); } } + xfree(buff); + vterm_keyboard_end_paste(curbuf->terminal->vt); + terminal_flush_output(curbuf->terminal); } void terminal_flush_output(Terminal *term) diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim index 92fedf9bfb..a1ef8325ec 100644 --- a/src/nvim/testdir/test_arglist.vim +++ b/src/nvim/testdir/test_arglist.vim @@ -80,6 +80,24 @@ func Test_argadd() call assert_equal(0, len(argv())) endfunc +func Test_argadd_empty_curbuf() + new + let curbuf = bufnr('%') + call writefile(['test', 'Xargadd'], 'Xargadd') + " must not re-use the current buffer. + argadd Xargadd + call assert_equal(curbuf, bufnr('%')) + call assert_equal('', bufname('%')) + call assert_equal(1, line('$')) + rew + call assert_notequal(curbuf, bufnr('%')) + call assert_equal('Xargadd', bufname('%')) + call assert_equal(2, line('$')) + + %argd + bwipe! +endfunc + func Init_abc() args a b c next diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 374ad65aa9..1f3a45a9ab 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -1939,4 +1939,15 @@ func Test_autocmd_window() %bw! endfunc +func Test_autocmd_closes_window() + au BufNew,BufWinLeave * e %e + file yyy + au BufNew,BufWinLeave * ball + call assert_fails('n xxx', 'E143:') + + bwipe % + au! BufNew + au! BufWinLeave +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_breakindent.vim b/src/nvim/testdir/test_breakindent.vim index a4c1f62a43..d53acb77d7 100644 --- a/src/nvim/testdir/test_breakindent.vim +++ b/src/nvim/testdir/test_breakindent.vim @@ -353,14 +353,19 @@ func Test_breakindent19_sbr_nextpage() " Scroll down one screen line setl scrolloff=5 norm! 5gj - redraw! let lines = s:screen_lines(1, 20) let expect = [ - \ "> aaaaaaaaaaaaaaaaaa", + \ "aaaaaaaaaaaaaaaaaaaa", \ "> aaaaaaaaaaaaaaaaaa", \ "> aaaaaaaaaaaaaaaaaa", \ ] call s:compare_lines(expect, lines) + redraw! + " moving the cursor doesn't change the text offset + norm! l + redraw! + let lines = s:screen_lines(1, 20) + call s:compare_lines(expect, lines) setl breakindent briopt=min:18 sbr=> norm! 5gj diff --git a/src/nvim/testdir/test_buffer.vim b/src/nvim/testdir/test_buffer.vim new file mode 100644 index 0000000000..40111fdf06 --- /dev/null +++ b/src/nvim/testdir/test_buffer.vim @@ -0,0 +1,33 @@ +" Tests for Vim buffer + +func Test_buffer_error() + new foo1 + new foo2 + + call assert_fails('buffer foo', 'E93:') + call assert_fails('buffer bar', 'E94:') + call assert_fails('buffer 0', 'E939:') + + %bwipe +endfunc + +func Test_badd_options() + new SomeNewBuffer + setlocal numberwidth=3 + wincmd p + badd +1 SomeNewBuffer + new SomeNewBuffer + call assert_equal(3, &numberwidth) + close + close + bwipe! SomeNewBuffer +endfunc + +func Test_balt() + new SomeNewBuffer + balt +3 OtherBuffer + e # + call assert_equal('OtherBuffer', bufname()) +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_bufline.vim b/src/nvim/testdir/test_bufline.vim index 076f03fdd8..e038bce08e 100644 --- a/src/nvim/testdir/test_bufline.vim +++ b/src/nvim/testdir/test_bufline.vim @@ -112,6 +112,17 @@ func Test_deletebufline() call assert_equal(0, deletebufline(b, 1)) call assert_equal(['b', 'c'], getbufline(b, 1, 2)) exe "bwipe! " . b + + edit XbufOne + let one = bufnr() + call setline(1, ['a', 'b', 'c']) + setlocal nomodifiable + split XbufTwo + let two = bufnr() + call assert_fails('call deletebufline(one, 1)', 'E21:') + call assert_equal(two, bufnr()) + bwipe! XbufTwo + bwipe! XbufOne endfunc func Test_appendbufline_redraw() diff --git a/src/nvim/testdir/test_cmdline.vim b/src/nvim/testdir/test_cmdline.vim index 39f865144a..a66aee5e02 100644 --- a/src/nvim/testdir/test_cmdline.vim +++ b/src/nvim/testdir/test_cmdline.vim @@ -442,6 +442,7 @@ func Test_getcompletion() set tags& call assert_fails('call getcompletion("", "burp")', 'E475:') + call assert_fails('call getcompletion("abc", [])', 'E475:') endfunc func Test_shellcmd_completion() diff --git a/src/nvim/testdir/test_command_count.vim b/src/nvim/testdir/test_command_count.vim index 7262789ab4..55b230373f 100644 --- a/src/nvim/testdir/test_command_count.vim +++ b/src/nvim/testdir/test_command_count.vim @@ -158,7 +158,9 @@ endfunc func Test_command_count_4() %argd let bufnr = bufnr('$') - arga aa bb cc dd ee ff + next aa bb cc dd ee ff + call assert_equal(bufnr, bufnr('%')) + 3argu let args = [] .,$-argdo call add(args, expand('%')) diff --git a/src/nvim/testdir/test_conceal.vim b/src/nvim/testdir/test_conceal.vim new file mode 100644 index 0000000000..1306dbe5cf --- /dev/null +++ b/src/nvim/testdir/test_conceal.vim @@ -0,0 +1,282 @@ +" Tests for 'conceal'. + +source check.vim +CheckFeature conceal + +source screendump.vim +" CheckScreendump + +func Test_conceal_two_windows() + CheckScreendump + let code =<< trim [CODE] + let lines = ["one one one one one", "two |hidden| here", "three |hidden| three"] + call setline(1, lines) + syntax match test /|hidden|/ conceal + set conceallevel=2 + set concealcursor= + exe "normal /here\r" + new + call setline(1, lines) + call setline(4, "Second window") + syntax match test /|hidden|/ conceal + set conceallevel=2 + set concealcursor=nc + exe "normal /here\r" + [CODE] + + call writefile(code, 'XTest_conceal') + " Check that cursor line is concealed + let buf = RunVimInTerminal('-S XTest_conceal', {}) + call VerifyScreenDump(buf, 'Test_conceal_two_windows_01', {}) + + " Check that with concealed text vertical cursor movement is correct. + call term_sendkeys(buf, "k") + call VerifyScreenDump(buf, 'Test_conceal_two_windows_02', {}) + + " Check that with cursor line is not concealed + call term_sendkeys(buf, "j") + call term_sendkeys(buf, ":set concealcursor=\r") + call VerifyScreenDump(buf, 'Test_conceal_two_windows_03', {}) + + " Check that with cursor line is not concealed when moving cursor down + call term_sendkeys(buf, "j") + call VerifyScreenDump(buf, 'Test_conceal_two_windows_04', {}) + + " Check that with cursor line is not concealed when switching windows + call term_sendkeys(buf, "\<C-W>\<C-W>") + call VerifyScreenDump(buf, 'Test_conceal_two_windows_05', {}) + + " Check that with cursor line is only concealed in Normal mode + call term_sendkeys(buf, ":set concealcursor=n\r") + call VerifyScreenDump(buf, 'Test_conceal_two_windows_06n', {}) + call term_sendkeys(buf, "a") + call VerifyScreenDump(buf, 'Test_conceal_two_windows_06i', {}) + call term_sendkeys(buf, "\<Esc>/e") + call VerifyScreenDump(buf, 'Test_conceal_two_windows_06c', {}) + call term_sendkeys(buf, "\<Esc>v") + call VerifyScreenDump(buf, 'Test_conceal_two_windows_06v', {}) + call term_sendkeys(buf, "\<Esc>") + + " Check that with cursor line is only concealed in Insert mode + call term_sendkeys(buf, ":set concealcursor=i\r") + call VerifyScreenDump(buf, 'Test_conceal_two_windows_07n', {}) + call term_sendkeys(buf, "a") + call VerifyScreenDump(buf, 'Test_conceal_two_windows_07i', {}) + call term_sendkeys(buf, "\<Esc>/e") + call VerifyScreenDump(buf, 'Test_conceal_two_windows_07c', {}) + call term_sendkeys(buf, "\<Esc>v") + call VerifyScreenDump(buf, 'Test_conceal_two_windows_07v', {}) + call term_sendkeys(buf, "\<Esc>") + + " Check that with cursor line is only concealed in Command mode + call term_sendkeys(buf, ":set concealcursor=c\r") + call VerifyScreenDump(buf, 'Test_conceal_two_windows_08n', {}) + call term_sendkeys(buf, "a") + call VerifyScreenDump(buf, 'Test_conceal_two_windows_08i', {}) + call term_sendkeys(buf, "\<Esc>/e") + call VerifyScreenDump(buf, 'Test_conceal_two_windows_08c', {}) + call term_sendkeys(buf, "\<Esc>v") + call VerifyScreenDump(buf, 'Test_conceal_two_windows_08v', {}) + call term_sendkeys(buf, "\<Esc>") + + " Check that with cursor line is only concealed in Visual mode + call term_sendkeys(buf, ":set concealcursor=v\r") + call VerifyScreenDump(buf, 'Test_conceal_two_windows_09n', {}) + call term_sendkeys(buf, "a") + call VerifyScreenDump(buf, 'Test_conceal_two_windows_09i', {}) + call term_sendkeys(buf, "\<Esc>/e") + call VerifyScreenDump(buf, 'Test_conceal_two_windows_09c', {}) + call term_sendkeys(buf, "\<Esc>v") + call VerifyScreenDump(buf, 'Test_conceal_two_windows_09v', {}) + call term_sendkeys(buf, "\<Esc>") + + " Check moving the cursor while in insert mode. + call term_sendkeys(buf, ":set concealcursor=\r") + call term_sendkeys(buf, "a") + call VerifyScreenDump(buf, 'Test_conceal_two_windows_10', {}) + call term_sendkeys(buf, "\<Down>") + call VerifyScreenDump(buf, 'Test_conceal_two_windows_11', {}) + call term_sendkeys(buf, "\<Esc>") + + " Check the "o" command + call VerifyScreenDump(buf, 'Test_conceal_two_windows_12', {}) + call term_sendkeys(buf, "o") + call VerifyScreenDump(buf, 'Test_conceal_two_windows_13', {}) + call term_sendkeys(buf, "\<Esc>") + + " clean up + call StopVimInTerminal(buf) + call delete('XTest_conceal') +endfunc + +func Test_conceal_with_cursorline() + CheckScreendump + " Opens a help window, where 'conceal' is set, switches to the other window + " where 'cursorline' needs to be updated when the cursor moves. + let code =<< trim [CODE] + set cursorline + normal othis is a test + new + call setline(1, ["one", "two", "three", "four", "five"]) + set ft=help + normal M + [CODE] + + call writefile(code, 'XTest_conceal_cul') + let buf = RunVimInTerminal('-S XTest_conceal_cul', {}) + call VerifyScreenDump(buf, 'Test_conceal_cul_01', {}) + + call term_sendkeys(buf, ":wincmd w\r") + call VerifyScreenDump(buf, 'Test_conceal_cul_02', {}) + + call term_sendkeys(buf, "k") + call VerifyScreenDump(buf, 'Test_conceal_cul_03', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('XTest_conceal_cul') +endfunc + +func Test_conceal_resize_term() + CheckScreendump + let code =<< trim [CODE] + call setline(1, '`one` `two` `three` `four` `five`, the backticks should be concealed') + setl cocu=n cole=3 + syn region CommentCodeSpan matchgroup=Comment start=/`/ end=/`/ concealends + normal fb + [CODE] + call writefile(code, 'XTest_conceal_resize') + let buf = RunVimInTerminal('-S XTest_conceal_resize', {'rows': 6}) + call VerifyScreenDump(buf, 'Test_conceal_resize_01', {}) + + call win_execute(buf->win_findbuf()[0], 'wincmd +') + call TermWait(buf) + call VerifyScreenDump(buf, 'Test_conceal_resize_02', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('XTest_conceal_resize') +endfunc + +" Tests for correct display (cursor column position) with +conceal and +" tabulators. Need to run this test in a separate Vim instance. Otherwise the +" screen is not updated (lazy redraw) and the cursor position is wrong. +func Test_conceal_cursor_pos() + let code =<< trim [CODE] + :let l = ['start:', '.concealed. text', "|concealed|\ttext"] + :let l += ['', "\t.concealed.\ttext", "\t|concealed|\ttext", ''] + :let l += [".a.\t.b.\t.c.\t.d.", "|a|\t|b|\t|c|\t|d|"] + :call append(0, l) + :call cursor(1, 1) + :" Conceal settings. + :set conceallevel=2 + :set concealcursor=nc + :syntax match test /|/ conceal + :" Save current cursor position. Only works in <expr> mode, can't be used + :" with :normal because it moves the cursor to the command line. Thanks + :" to ZyX <zyx.vim@gmail.com> for the idea to use an <expr> mapping. + :let curpos = [] + :nnoremap <expr> GG ":let curpos += ['".screenrow().":".screencol()."']\n" + :normal ztj + GGk + :" We should end up in the same column when running these commands on the + :" two lines. + :normal ft + GGk + :normal $ + GGk + :normal 0j + GGk + :normal ft + GGk + :normal $ + GGk + :normal 0j0j + GGk + :" Same for next test block. + :normal ft + GGk + :normal $ + GGk + :normal 0j + GGk + :normal ft + GGk + :normal $ + GGk + :normal 0j0j + GGk + :" And check W with multiple tabs and conceals in a line. + :normal W + GGk + :normal W + GGk + :normal W + GGk + :normal $ + GGk + :normal 0j + GGk + :normal W + GGk + :normal W + GGk + :normal W + GGk + :normal $ + GGk + :set lbr + :normal $ + GGk + :set list listchars=tab:>- + :normal 0 + GGk + :normal W + GGk + :normal W + GGk + :normal W + GGk + :normal $ + GGk + :call writefile(curpos, 'Xconceal_curpos.out') + :q! + + [CODE] + call writefile(code, 'XTest_conceal_curpos') + + if RunVim([], [], '-s XTest_conceal_curpos') + call assert_equal([ + \ '2:1', '2:17', '2:20', '3:1', '3:17', '3:20', '5:8', '5:25', + \ '5:28', '6:8', '6:25', '6:28', '8:1', '8:9', '8:17', '8:25', + \ '8:27', '9:1', '9:9', '9:17', '9:25', '9:26', '9:26', '9:1', + \ '9:9', '9:17', '9:25', '9:26'], readfile('Xconceal_curpos.out')) + endif + + call delete('Xconceal_curpos.out') + call delete('XTest_conceal_curpos') +endfunc + +func Test_conceal_eol() + new! + setlocal concealcursor=n conceallevel=1 + call setline(1, ["x", ""]) + call matchaddpos('Conceal', [[2, 1, 1]], 2, -1, {'conceal': 1}) + redraw! + + call assert_notequal(screenchar(1, 1), screenchar(2, 2)) + call assert_equal(screenattr(1, 1), screenattr(1, 2)) + call assert_equal(screenattr(1, 2), screenattr(2, 2)) + call assert_equal(screenattr(2, 1), screenattr(2, 2)) + + set list + redraw! + + call assert_equal(screenattr(1, 1), screenattr(2, 2)) + call assert_notequal(screenattr(1, 1), screenattr(1, 2)) + call assert_notequal(screenattr(1, 2), screenattr(2, 1)) + + set nolist +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index f09a64c329..21c1f98283 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -242,6 +242,63 @@ func Test_diffput_two() bwipe! b endfunc +" :diffput and :diffget completes names of buffers which +" are in diff mode and which are different then current buffer. +" No completion when the current window is not in diff mode. +func Test_diffget_diffput_completion() + e Xdiff1 | diffthis + botright new Xdiff2 + botright new Xdiff3 | split | diffthis + botright new Xdiff4 | diffthis + + wincmd t + call assert_equal('Xdiff1', bufname('%')) + call feedkeys(":diffput \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"diffput Xdiff3 Xdiff4', @:) + call feedkeys(":diffget \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"diffget Xdiff3 Xdiff4', @:) + call assert_equal(['Xdiff3', 'Xdiff4'], getcompletion('', 'diff_buffer')) + + " Xdiff2 is not in diff mode, so no completion for :diffput, :diffget + wincmd j + call assert_equal('Xdiff2', bufname('%')) + call feedkeys(":diffput \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"diffput ', @:) + call feedkeys(":diffget \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"diffget ', @:) + call assert_equal([], getcompletion('', 'diff_buffer')) + + " Xdiff3 is split in 2 windows, only the top one is in diff mode. + " So completion of :diffput :diffget only happens in the top window. + wincmd j + call assert_equal('Xdiff3', bufname('%')) + call assert_equal(1, &diff) + call feedkeys(":diffput \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"diffput Xdiff1 Xdiff4', @:) + call feedkeys(":diffget \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"diffget Xdiff1 Xdiff4', @:) + call assert_equal(['Xdiff1', 'Xdiff4'], getcompletion('', 'diff_buffer')) + + wincmd j + call assert_equal('Xdiff3', bufname('%')) + call assert_equal(0, &diff) + call feedkeys(":diffput \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"diffput ', @:) + call feedkeys(":diffget \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"diffget ', @:) + call assert_equal([], getcompletion('', 'diff_buffer')) + + wincmd j + call assert_equal('Xdiff4', bufname('%')) + call feedkeys(":diffput \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"diffput Xdiff1 Xdiff3', @:) + call feedkeys(":diffget \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"diffget Xdiff1 Xdiff3', @:) + call assert_equal(['Xdiff1', 'Xdiff3'], getcompletion('', 'diff_buffer')) + + %bwipe +endfunc + func Test_dp_do_buffer() e! one let bn1=bufnr('%') @@ -964,6 +1021,21 @@ func Test_diff_closeoff() enew! endfunc +func Test_diff_followwrap() + new + set diffopt+=followwrap + set wrap + diffthis + call assert_equal(1, &wrap) + diffoff + set nowrap + diffthis + call assert_equal(0, &wrap) + diffoff + set diffopt& + bwipe! +endfunc + func Test_diff_rnu() CheckScreendump @@ -992,6 +1064,18 @@ func Test_diff_rnu() call delete('Xtest_diff_rnu') endfunc +func Test_diff_multilineconceal() + new + diffthis + + new + call matchadd('Conceal', 'a\nb', 9, -1, {'conceal': 'Y'}) + set cole=2 cocu=n + call setline(1, ["a", "b"]) + diffthis + redraw +endfunc + func Test_diff_and_scroll() " this was causing an ml_get error set ls=2 diff --git a/src/nvim/testdir/test_digraph.vim b/src/nvim/testdir/test_digraph.vim index b6d9687560..d23748a3e3 100644 --- a/src/nvim/testdir/test_digraph.vim +++ b/src/nvim/testdir/test_digraph.vim @@ -211,6 +211,8 @@ func Test_digraphs() call Put_Dig("00") call Put_Dig("el") call assert_equal(['␀', 'ü', '∞', 'l'], getline(line('.')-3,line('.'))) + call assert_fails('digraph xy z', 'E39:') + call assert_fails('digraph x', 'E474:') bw! endfunc diff --git a/src/nvim/testdir/test_excmd.vim b/src/nvim/testdir/test_excmd.vim index 20508b12d3..98a3e60368 100644 --- a/src/nvim/testdir/test_excmd.vim +++ b/src/nvim/testdir/test_excmd.vim @@ -131,3 +131,11 @@ func Test_confirm_cmd_cancel() \ term_getline(buf, 20))}, 1000) call StopVimInTerminal(buf) endfunc + +" Test for the :winsize command +func Test_winsize_cmd() + call assert_fails('winsize 1', 'E465:') + call assert_fails('winsize 1 x', 'E465:') + call assert_fails('win_getid(1)', 'E475: Invalid argument: _getid(1)') + " Actually changing the window size would be flaky. +endfunc diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index 7b90ba56e0..09d79979ce 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -399,7 +399,11 @@ function Test_printf_errors() call assert_fails('echo printf("%d", [])', 'E745:') call assert_fails('echo printf("%d", 1, 2)', 'E767:') call assert_fails('echo printf("%*d", 1)', 'E766:') - call assert_fails('echo printf("%d", 1.2)', 'E805:') + call assert_fails('echo printf("%s")', 'E766:') + if has('float') + call assert_fails('echo printf("%d", 1.2)', 'E805:') + call assert_fails('echo printf("%f")') + endif endfunc function Test_max_min_errors() diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 917a5e8eca..f0c1a1c7f9 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -930,10 +930,20 @@ func Test_Executable() " get "cat" path and remove the leading / let catcmd = exepath('cat')[1:] new + " check that the relative path works in / lcd / call assert_equal(1, executable(catcmd)) - call assert_equal('/' .. catcmd, exepath(catcmd)) + " let result = catcmd->exepath() + let result = exepath(catcmd) + " when using chroot looking for sbin/cat can return bin/cat, that is OK + if catcmd =~ '\<sbin\>' && result =~ '\<bin\>' + call assert_equal('/' .. substitute(catcmd, '\<sbin\>', 'bin', ''), result) + else + call assert_equal('/' .. catcmd, result) + endif bwipe + else + throw 'Skipped: does not work on this platform' endif endfunc diff --git a/src/nvim/testdir/test_matchadd_conceal.vim b/src/nvim/testdir/test_matchadd_conceal.vim index f9e40a9b43..2cbaf5cb76 100644 --- a/src/nvim/testdir/test_matchadd_conceal.vim +++ b/src/nvim/testdir/test_matchadd_conceal.vim @@ -59,9 +59,9 @@ func Test_matchadd_and_conceallevel_3() setlocal filetype=conf syntax on - 1put='# This is a Test' - " 1234567890123456 - let expect = '#ThisisaTest' + 1put='# This is a Test $' + " 1234567890123 + let expect = '#ThisisaTest$' call cursor(1, 1) call matchadd('Conceal', '\%2l ', 10, -1, {'conceal': 'X'}) @@ -69,22 +69,25 @@ func Test_matchadd_and_conceallevel_3() let lnum = 2 call assert_equal(expect, Screenline(lnum)) call assert_equal(screenattr(lnum, 1), screenattr(lnum, 2)) - call assert_equal(screenattr(lnum, 2), screenattr(lnum, 7)) - call assert_equal(screenattr(lnum, 2), screenattr(lnum, 10)) - call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 7)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 10)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 12)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 13)) + call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 14)) call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 16)) " more matchadd() - " 1234567890123456 - let expect = '#Thisisa Test' + " 12345678901234 + let expect = '#Thisisa Test$' call matchadd('ErrorMsg', '\%2l Test', 20, -1, {'conceal': 'X'}) redraw! call assert_equal(expect, Screenline(lnum)) call assert_equal(screenattr(lnum, 1) , screenattr(lnum, 2)) - call assert_equal(screenattr(lnum, 2) , screenattr(lnum, 7)) + call assert_equal(screenattr(lnum, 1) , screenattr(lnum, 7)) call assert_notequal(screenattr(lnum, 1) , screenattr(lnum, 10)) - call assert_equal(screenattr(lnum, 10), screenattr(lnum, 12)) + call assert_equal(screenattr(lnum, 10), screenattr(lnum, 13)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 14)) call assert_notequal(screenattr(lnum, 1) , screenattr(lnum, 16)) call assert_notequal(screenattr(lnum, 10), screenattr(lnum, 16)) @@ -132,15 +135,29 @@ func Test_syn_and_match_conceal() new setlocal concealcursor=n conceallevel=1 - 1put='# This is a Test' - " 1234567890123456 - let expect = '#ZThisZisZaZTest' + 1put='# This is a Test ' + let lnum = 2 call cursor(1, 1) + + " 123456789012345678 + let expect = '#ZThisZisZaZTestZZ' call matchadd('Conceal', '\%2l ', 10, -1, {'conceal': 'Z'}) + syntax match MyConceal /\%2l / conceal containedin=ALL + hi MyConceal ctermbg=4 ctermfg=2 + redraw! + + call assert_equal(expect, Screenline(lnum)) + call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 2)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 7)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 10)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 16)) + + syntax clear MyConceal syntax match MyConceal /\%2l / conceal containedin=ALL cchar=* redraw! - let lnum = 2 + call assert_equal(expect, Screenline(lnum)) call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 2)) call assert_equal(screenattr(lnum, 2), screenattr(lnum, 7)) @@ -148,8 +165,8 @@ func Test_syn_and_match_conceal() call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12)) call assert_equal(screenattr(lnum, 1), screenattr(lnum, 16)) - " 1234567890123456 - let expect = '#*This*is*a*Test' + " 123456789012345678 + let expect = '#*This*is*a*Test**' call clearmatches() redraw! @@ -160,6 +177,48 @@ func Test_syn_and_match_conceal() call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12)) call assert_equal(screenattr(lnum, 1), screenattr(lnum, 16)) + " 123456789012345678 + let expect = '#*ThisXis*a*Test**' + call matchadd('Conceal', '\%2l\%7c ', 10, -1, {'conceal': 'X'}) + redraw! + + call assert_equal(expect, Screenline(lnum)) + call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 2)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 7)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 10)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12)) + call assert_equal(screenattr(lnum, 1), screenattr(lnum, 16)) + + " 123456789012345678 + let expect = '#*ThisXis*a*Test**' + call matchadd('ErrorMsg', '\%2l Test', 20, -1) + redraw! + + call assert_equal(expect, Screenline(lnum)) + call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 2)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12)) + call assert_notequal(screenattr(lnum, 12), screenattr(lnum, 13)) + call assert_equal(screenattr(lnum, 13), screenattr(lnum, 16)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 17)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 18)) + call assert_notequal(screenattr(lnum, 18), screenattr(lnum, 19)) + + " 123456789012345678 + let expect = '# ThisXis a Test' + syntax clear MyConceal + syntax match MyConceal /\%2l / conceal containedin=ALL + redraw! + + call assert_equal(expect, Screenline(lnum)) + call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 2)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 12)) + call assert_notequal(screenattr(lnum, 1), screenattr(lnum, 12)) + call assert_notequal(screenattr(lnum, 12), screenattr(lnum, 13)) + call assert_equal(screenattr(lnum, 13), screenattr(lnum, 16)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 17)) + call assert_equal(screenattr(lnum, 2), screenattr(lnum, 18)) + call assert_notequal(screenattr(lnum, 18), screenattr(lnum, 19)) + syntax off quit! endfunc diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim index 30239a90c2..3ebd048f46 100644 --- a/src/nvim/testdir/test_messages.vim +++ b/src/nvim/testdir/test_messages.vim @@ -31,6 +31,8 @@ func Test_messages() finally let &more = oldmore endtry + + call assert_fails('message 1', 'E474:') endfunc " Patch 7.4.1696 defined the "clearmode()" command for clearing the mode diff --git a/src/nvim/testdir/test_mksession.vim b/src/nvim/testdir/test_mksession.vim index f71da92bf8..7c7804212b 100644 --- a/src/nvim/testdir/test_mksession.vim +++ b/src/nvim/testdir/test_mksession.vim @@ -291,6 +291,46 @@ endfunc endif +func Test_mkview_open_folds() + enew! + + call append(0, ['a', 'b', 'c']) + 1,3fold + " zR affects 'foldlevel', make sure the option is applied after the folds + " have been recreated. + normal zR + write! Xtestfile + + call assert_equal(-1, foldclosed(1)) + call assert_equal(-1, foldclosed(2)) + call assert_equal(-1, foldclosed(3)) + + mkview! Xtestview + source Xtestview + + call assert_equal(-1, foldclosed(1)) + call assert_equal(-1, foldclosed(2)) + call assert_equal(-1, foldclosed(3)) + + call delete('Xtestview') + call delete('Xtestfile') + %bwipe +endfunc + +func Test_mkview_no_balt() + edit Xtestfile1 + edit Xtestfile2 + + mkview! Xtestview + bdelete Xtestfile1 + + source Xtestview + call assert_equal(0, buflisted('Xtestfile1')) + + call delete('Xtestview') + %bwipe +endfunc + " Test :mkview with a file argument. func Test_mkview_file() " Create a view with line number and a fold. @@ -373,6 +413,58 @@ func Test_mkview_no_file_name() %bwipe endfunc +func Test_mkview_loadview_jumplist() + set viewdir=Xviewdir + au BufWinLeave * silent mkview + " au BufWinEnter * silent loadview + + edit Xfile1 + call setline(1, ['a', 'bbbbbbb', 'c']) + normal j3l + call assert_equal([2, 4], getcurpos()[1:2]) + write + + edit Xfile2 + call setline(1, ['d', 'eeeeeee', 'f']) + normal j5l + call assert_equal([2, 6], getcurpos()[1:2]) + write + + edit Xfile3 + call setline(1, ['g', 'h', 'iiiii']) + normal jj3l + call assert_equal([3, 4], getcurpos()[1:2]) + write + + " The commented :au above was moved here so that :mkview (on BufWinLeave) can + " run before :loadview. This is needed because Nvim's :loadview raises E484 if + " the view can't be opened, while Vim's silently fails instead. + au BufWinEnter * silent loadview + + edit Xfile1 + call assert_equal([2, 4], getcurpos()[1:2]) + edit Xfile2 + call assert_equal([2, 6], getcurpos()[1:2]) + edit Xfile3 + call assert_equal([3, 4], getcurpos()[1:2]) + + exe "normal \<C-O>" + call assert_equal('Xfile2', expand('%')) + call assert_equal([2, 6], getcurpos()[1:2]) + exe "normal \<C-O>" + call assert_equal('Xfile1', expand('%')) + call assert_equal([2, 4], getcurpos()[1:2]) + + au! BufWinLeave + au! BufWinEnter + bwipe! + call delete('Xviewdir', 'rf') + call delete('Xfile1') + call delete('Xfile2') + call delete('Xfile3') + set viewdir& +endfunc + " A clean session (one empty buffer, one window, and one tab) should not " set any error messages when sourced because no commands should fail. func Test_mksession_no_errmsg() @@ -663,4 +755,27 @@ func Test_scrolloff() set sessionoptions& endfunc +func Test_altfile() + edit Xone + split Xtwo + edit Xtwoalt + edit # + wincmd w + edit Xonealt + edit # + mksession! Xtest_altfile + only + bwipe Xonealt + bwipe Xtwoalt + bwipe! + source Xtest_altfile + call assert_equal('Xone', bufname()) + call assert_equal('Xonealt', bufname('#')) + wincmd w + call assert_equal('Xtwo', bufname()) + call assert_equal('Xtwoalt', bufname('#')) + only + bwipe! +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_options.vim b/src/nvim/testdir/test_options.vim index 84d99ebb74..5a10c9baa6 100644 --- a/src/nvim/testdir/test_options.vim +++ b/src/nvim/testdir/test_options.vim @@ -330,12 +330,10 @@ func Test_set_ttytype() set ttytype=xterm call assert_equal('xterm', &ttytype) call assert_equal(&ttytype, &term) - " "set ttytype=" gives E522 instead of E529 - " in travis on some builds. Why? Catch both for now try set ttytype= call assert_report('set ttytype= did not fail') - catch /E529\|E522/ + catch /E529/ endtry " Some systems accept any terminal name and return dumb settings, diff --git a/src/nvim/testdir/test_version.vim b/src/nvim/testdir/test_version.vim index 46cf34979f..5fd38f7cdc 100644 --- a/src/nvim/testdir/test_version.vim +++ b/src/nvim/testdir/test_version.vim @@ -1,5 +1,8 @@ " Test :version Ex command +so check.vim +so shared.vim + func Test_version() " version should always return the same string. let v1 = execute('version') @@ -9,4 +12,15 @@ func Test_version() call assert_match("^\n\nNVIM v[0-9]\\+\\.[0-9]\\+\\.[0-9]\\+.*", v1) endfunc +func Test_version_redirect() + CheckNotGui + CheckCanRunGui + CheckUnix + + call RunVim([], [], '--clean -g --version >Xversion 2>&1') + call assert_match('Features included', readfile('Xversion')->join()) + + call delete('Xversion') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/tui/input.c b/src/nvim/tui/input.c index 124f96e039..3e683a4926 100644 --- a/src/nvim/tui/input.c +++ b/src/nvim/tui/input.c @@ -297,7 +297,7 @@ static void forward_mouse_event(TermInput *input, TermKeyKey *key) len += (size_t)snprintf(buf + len, sizeof(buf) - len, "Release"); break; case TERMKEY_MOUSE_UNKNOWN: - assert(false); + abort(); } len += (size_t)snprintf(buf + len, sizeof(buf) - len, "><%d,%d>", col, row); diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c index 9d3ec21949..06efc9fa99 100644 --- a/src/nvim/ui_compositor.c +++ b/src/nvim/ui_compositor.c @@ -157,7 +157,7 @@ bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width, #ifndef NDEBUG for (size_t i = 0; i < kv_size(layers); i++) { if (kv_A(layers, i) == grid) { - assert(false); + abort(); } } #endif diff --git a/src/nvim/undo.c b/src/nvim/undo.c index da464c56dc..8c2ae3bc35 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -580,6 +580,10 @@ int u_savecommon(linenr_T top, linenr_T bot, linenr_T newbot, int reload) uep->ue_array = NULL; uep->ue_next = curbuf->b_u_newhead->uh_entry; curbuf->b_u_newhead->uh_entry = uep; + if (reload) { + // buffer was reloaded, notify text change subscribers + curbuf->b_u_newhead->uh_flags |= UH_RELOAD; + } curbuf->b_u_synced = false; undo_undoes = false; @@ -2157,8 +2161,9 @@ static void u_undoredo(int undo, bool do_buf_event) u_check(FALSE); #endif old_flags = curhead->uh_flags; - new_flags = (curbuf->b_changed ? UH_CHANGED : 0) + - ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0); + new_flags = (curbuf->b_changed ? UH_CHANGED : 0) + | ((curbuf->b_ml.ml_flags & ML_EMPTY) ? UH_EMPTYBUF : 0) + | (old_flags & UH_RELOAD); setpcmark(); /* @@ -2299,6 +2304,11 @@ static void u_undoredo(int undo, bool do_buf_event) extmark_apply_undo(undo_info, undo); } } + if (curhead->uh_flags & UH_RELOAD) { + // TODO(bfredl): this is a bit crude. When 'undoreload' is used we + // should have all info to send a buffer-reloaing on_lines/on_bytes event + buf_updates_unload(curbuf, true); + } // finish Adjusting extmarks diff --git a/src/nvim/undo_defs.h b/src/nvim/undo_defs.h index cc2c39a711..b46295a15d 100644 --- a/src/nvim/undo_defs.h +++ b/src/nvim/undo_defs.h @@ -69,9 +69,10 @@ struct u_header { #endif }; -/* values for uh_flags */ -#define UH_CHANGED 0x01 /* b_changed flag before undo/after redo */ -#define UH_EMPTYBUF 0x02 /* buffer was empty */ +// values for uh_flags +#define UH_CHANGED 0x01 // b_changed flag before undo/after redo +#define UH_EMPTYBUF 0x02 // buffer was empty +#define UH_RELOAD 0x04 // buffer was reloaded /// Structure passed around between undofile functions. typedef struct { diff --git a/src/nvim/vim.h b/src/nvim/vim.h index e70749795b..0245c472ef 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -158,6 +158,7 @@ enum { EXPAND_MESSAGES, EXPAND_MAPCLEAR, EXPAND_ARGLIST, + EXPAND_DIFF_BUFFERS, EXPAND_CHECKHEALTH, EXPAND_LUA, }; diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c index 44b6ab5f5a..e9d82ca87d 100644 --- a/src/nvim/viml/parser/expressions.c +++ b/src/nvim/viml/parser/expressions.c @@ -2078,7 +2078,7 @@ viml_pexpr_parse_process_token: case kExprLexMissing: case kExprLexSpacing: case kExprLexEOC: { - assert(false); + abort(); } case kExprLexInvalid: { ERROR_FROM_TOKEN(cur_token); @@ -3028,7 +3028,7 @@ viml_pexpr_parse_end: // Until trailing "}" it is impossible to distinguish curly braces // identifier and dictionary, so it must not appear in the stack like // this. - assert(false); + abort(); } case kExprNodeInteger: case kExprNodeFloat: @@ -3042,7 +3042,7 @@ viml_pexpr_parse_end: // These are plain values and not containers, for them it should only // be possible to show up in the topmost stack element, but it was // unconditionally popped at the start. - assert(false); + abort(); } case kExprNodeComma: case kExprNodeColon: diff --git a/test/functional/api/buffer_spec.lua b/test/functional/api/buffer_spec.lua index fb8ed6a9d7..81fad206da 100644 --- a/test/functional/api/buffer_spec.lua +++ b/test/functional/api/buffer_spec.lua @@ -513,6 +513,28 @@ describe('api/buf', function() eq({0, 8}, curbufmeths.get_extmark_by_id(ns, id2, {})) eq({0, 8}, curbufmeths.get_extmark_by_id(ns, id3, {})) end) + + it("correctly marks changed region for redraw #13890", function() + local screen = Screen.new(20, 5) + screen:attach() + + insert([[ + AAA + BBB + ]]) + + curbufmeths.set_text(0, 0, 1, 3, {'XXX', 'YYY'}) + + screen:expect([[ + XXX | + YYY | + ^ | + ~ | + | + + ]]) + + end) end) describe('nvim_buf_get_offset', function() diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index 3ff3efb8c9..437a1858f3 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -475,6 +475,18 @@ describe('API', function() end) end) + describe('nvim_notify', function() + it('can notify a info message', function() + nvim("notify", "hello world", 2, {}) + end) + + it('can be overriden', function() + command("lua vim.notify = function(...) return 42 end") + eq(42, meths.exec_lua("return vim.notify('Hello world')", {})) + nvim("notify", "hello world", 4, {}) + end) + end) + describe('nvim_input', function() it("VimL error: does NOT fail, updates v:errmsg", function() local status, _ = pcall(nvim, "input", ":call bogus_fn()<CR>") diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 6d1182478a..b59d87eb12 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -31,9 +31,9 @@ describe('jobs', function() nvim('set_var', 'channel', channel) source([[ function! Normalize(data) abort - " Windows: remove ^M + " Windows: remove ^M and term escape sequences return type([]) == type(a:data) - \ ? map(a:data, 'substitute(v:val, "\r", "", "g")') + \ ? map(a:data, 'substitute(substitute(v:val, "\r", "", "g"), "\x1b\\%(\\]\\d\\+;.\\{-}\x07\\|\\[.\\{-}[\x40-\x7E]\\)", "", "g")') \ : a:data endfunction function! OnEvent(id, data, event) dict @@ -63,6 +63,7 @@ describe('jobs', function() it('append environment #env', function() nvim('command', "let $VAR = 'abc'") + nvim('command', "let $TOTO = 'goodbye world'") nvim('command', "let g:job_opts.env = {'TOTO': 'hello world'}") if iswin() then nvim('command', [[call jobstart('echo %TOTO% %VAR%', g:job_opts)]]) @@ -75,8 +76,24 @@ describe('jobs', function() }) end) + it('append environment with pty #env', function() + nvim('command', "let $VAR = 'abc'") + nvim('command', "let $TOTO = 'goodbye world'") + nvim('command', "let g:job_opts.pty = v:true") + nvim('command', "let g:job_opts.env = {'TOTO': 'hello world'}") + if iswin() then + nvim('command', [[call jobstart('echo %TOTO% %VAR%', g:job_opts)]]) + else + nvim('command', [[call jobstart('echo $TOTO $VAR', g:job_opts)]]) + end + expect_msg_seq({ + {'notification', 'stdout', {0, {'hello world abc', ''}}}, + }) + end) + it('replace environment #env', function() nvim('command', "let $VAR = 'abc'") + nvim('command', "let $TOTO = 'goodbye world'") nvim('command', "let g:job_opts.env = {'TOTO': 'hello world'}") nvim('command', "let g:job_opts.clear_env = 1") diff --git a/test/functional/lua/buffer_updates_spec.lua b/test/functional/lua/buffer_updates_spec.lua index 680b74873d..8c5e936906 100644 --- a/test/functional/lua/buffer_updates_spec.lua +++ b/test/functional/lua/buffer_updates_spec.lua @@ -1,5 +1,6 @@ -- Test suite for testing interactions with API bindings local helpers = require('test.functional.helpers')(after_each) +local lfs = require('lfs') local command = helpers.command local meths = helpers.meths @@ -9,8 +10,9 @@ local eq = helpers.eq local fail = helpers.fail local exec_lua = helpers.exec_lua local feed = helpers.feed -local deepcopy = helpers.deepcopy local expect_events = helpers.expect_events +local write_file = helpers.write_file +local dedent = helpers.dedent local origlines = {"original line 1", "original line 2", @@ -20,19 +22,20 @@ local origlines = {"original line 1", "original line 6", " indented line"} -local function attach_buffer(evname) - exec_lua([[ +before_each(function () + clear() + exec_lua [[ local evname = ... local events = {} - function test_register(bufnr, id, changedtick, utf_sizes, preview) + function test_register(bufnr, evname, id, changedtick, utf_sizes, preview) local function callback(...) table.insert(events, {id, ...}) if test_unreg == id then return true end end - local opts = {[evname]=callback, on_detach=callback, utf_sizes=utf_sizes, preview=preview} + local opts = {[evname]=callback, on_detach=callback, on_reload=callback, utf_sizes=utf_sizes, preview=preview} if changedtick then opts.on_changedtick = callback end @@ -44,41 +47,30 @@ local function attach_buffer(evname) events = {} return ret_events end - ]], evname) -end + ]] +end) describe('lua buffer event callbacks: on_lines', function() - before_each(function() - clear() - attach_buffer('on_lines') - end) - - - -- verifying the sizes with nvim_buf_get_offset is nice (checks we cannot - -- assert the wrong thing), but masks errors with unflushed lines (as - -- nvim_buf_get_offset forces a flush of the memline). To be safe run the - -- test both ways. - local function check(verify,utf_sizes) + local function setup_eventcheck(verify, utf_sizes, lines) local lastsize - meths.buf_set_lines(0, 0, -1, true, origlines) + meths.buf_set_lines(0, 0, -1, true, lines) if verify then lastsize = meths.buf_get_offset(0, meths.buf_line_count(0)) end - exec_lua("return test_register(...)", 0, "test1",false,utf_sizes) - local tick = meths.buf_get_changedtick(0) - + exec_lua("return test_register(...)", 0, "on_lines", "test1",false,utf_sizes) local verify_name = "test1" + local function check_events(expected) local events = exec_lua("return get_events(...)" ) if utf_sizes then -- this test case uses ASCII only, so sizes should be the same. -- Unicode is tested below. for _, event in ipairs(expected) do - event[9] = event[8] - event[10] = event[8] + event[9] = event[9] or event[8] + event[10] = event[10] or event[9] end end - eq(expected, events) + expect_events(expected, events, "line updates") if verify then for _, event in ipairs(events) do if event[1] == verify_name and event[2] == "lines" then @@ -92,25 +84,38 @@ describe('lua buffer event callbacks: on_lines', function() end end end + return check_events, function(new) verify_name = new end + end + + + -- verifying the sizes with nvim_buf_get_offset is nice (checks we cannot + -- assert the wrong thing), but masks errors with unflushed lines (as + -- nvim_buf_get_offset forces a flush of the memline). To be safe run the + -- test both ways. + local function check(verify,utf_sizes) + local check_events, verify_name = setup_eventcheck(verify, utf_sizes, origlines) + local tick = meths.buf_get_changedtick(0) command('set autoindent') command('normal! GyyggP') tick = tick + 1 - check_events({{ "test1", "lines", 1, tick, 0, 0, 1, 0}}) + check_events {{ "test1", "lines", 1, tick, 0, 0, 1, 0}} meths.buf_set_lines(0, 3, 5, true, {"changed line"}) tick = tick + 1 - check_events({{ "test1", "lines", 1, tick, 3, 5, 4, 32 }}) + check_events {{ "test1", "lines", 1, tick, 3, 5, 4, 32 }} - exec_lua("return test_register(...)", 0, "test2", true, utf_sizes) + exec_lua("return test_register(...)", 0, "on_lines", "test2", true, utf_sizes) tick = tick + 1 command('undo') -- plugins can opt in to receive changedtick events, or choose -- to only receive actual changes. - check_events({{ "test1", "lines", 1, tick, 3, 4, 5, 13 }, - { "test2", "lines", 1, tick, 3, 4, 5, 13 }, - { "test2", "changedtick", 1, tick+1 } }) + check_events { + { "test1", "lines", 1, tick, 3, 4, 5, 13 }; + { "test2", "lines", 1, tick, 3, 4, 5, 13 }; + { "test2", "changedtick", 1, tick+1 }; + } tick = tick + 1 -- simulate next callback returning true @@ -121,38 +126,40 @@ describe('lua buffer event callbacks: on_lines', function() -- plugins can opt in to receive changedtick events, or choose -- to only receive actual changes. - check_events({{ "test1", "lines", 1, tick, 6, 7, 9, 16 }, - { "test2", "lines", 1, tick, 6, 7, 9, 16 }}) + check_events { + { "test1", "lines", 1, tick, 6, 7, 9, 16 }; + { "test2", "lines", 1, tick, 6, 7, 9, 16 }; + } - verify_name = "test2" + verify_name "test2" meths.buf_set_lines(0, 1, 1, true, {"added"}) tick = tick + 1 - check_events({{ "test2", "lines", 1, tick, 1, 1, 2, 0 }}) + check_events {{ "test2", "lines", 1, tick, 1, 1, 2, 0 }} feed('wix') tick = tick + 1 - check_events({{ "test2", "lines", 1, tick, 4, 5, 5, 16 }}) + check_events {{ "test2", "lines", 1, tick, 4, 5, 5, 16 }} -- check hot path for multiple insert feed('yz') tick = tick + 1 - check_events({{ "test2", "lines", 1, tick, 4, 5, 5, 17 }}) + check_events {{ "test2", "lines", 1, tick, 4, 5, 5, 17 }} feed('<bs>') tick = tick + 1 - check_events({{ "test2", "lines", 1, tick, 4, 5, 5, 19 }}) + check_events {{ "test2", "lines", 1, tick, 4, 5, 5, 19 }} feed('<esc>Go') tick = tick + 1 - check_events({{ "test2", "lines", 1, tick, 11, 11, 12, 0 }}) + check_events {{ "test2", "lines", 1, tick, 11, 11, 12, 0 }} feed('x') tick = tick + 1 - check_events({{ "test2", "lines", 1, tick, 11, 12, 12, 5 }}) + check_events {{ "test2", "lines", 1, tick, 11, 12, 12, 5 }} command('bwipe!') - check_events({{ "test2", "detach", 1 }}) + check_events {{ "test2", "detach", 1 }} end it('works', function() @@ -167,51 +174,63 @@ describe('lua buffer event callbacks: on_lines', function() check(false,true) end) - it('works with utf_sizes and unicode text', function() + local function check_unicode(verify) local unicode_text = {"ascii text", "latin text åäö", "BMP text ɧ αλφά", "BMP text 汉语 ↥↧", "SMP 🤦 🦄🦃", "combining å بِيَّة"} - meths.buf_set_lines(0, 0, -1, true, unicode_text) - feed('gg') - exec_lua("return test_register(...)", 0, "test1", false, true) + local check_events, verify_name = setup_eventcheck(verify, true, unicode_text) + local tick = meths.buf_get_changedtick(0) - feed('dd') + feed('ggdd') tick = tick + 1 - eq({{ "test1", "lines", 1, tick, 0, 1, 0, 11, 11, 11 }}, exec_lua("return get_events(...)" )) + check_events {{ "test1", "lines", 1, tick, 0, 1, 0, 11, 11, 11 }} feed('A<bs>') tick = tick + 1 - eq({{ "test1", "lines", 1, tick, 0, 1, 1, 18, 15, 15 }}, exec_lua("return get_events(...)" )) + check_events {{ "test1", "lines", 1, tick, 0, 1, 1, 18, 15, 15 }} feed('<esc>jylp') tick = tick + 1 - eq({{ "test1", "lines", 1, tick, 1, 2, 2, 21, 16, 16 }}, exec_lua("return get_events(...)" )) + check_events {{ "test1", "lines", 1, tick, 1, 2, 2, 21, 16, 16 }} feed('+eea<cr>') tick = tick + 1 - eq({{ "test1", "lines", 1, tick, 2, 3, 4, 23, 15, 15 }}, exec_lua("return get_events(...)" )) + check_events {{ "test1", "lines", 1, tick, 2, 3, 4, 23, 15, 15 }} feed('<esc>jdw') tick = tick + 1 -- non-BMP chars count as 2 UTF-2 codeunits - eq({{ "test1", "lines", 1, tick, 4, 5, 5, 18, 9, 12 }}, exec_lua("return get_events(...)" )) + check_events {{ "test1", "lines", 1, tick, 4, 5, 5, 18, 9, 12 }} feed('+rx') tick = tick + 1 -- count the individual codepoints of a composed character. - eq({{ "test1", "lines", 1, tick, 5, 6, 6, 27, 20, 20 }}, exec_lua("return get_events(...)" )) + check_events {{ "test1", "lines", 1, tick, 5, 6, 6, 27, 20, 20 }} feed('kJ') tick = tick + 1 + -- verification fails with multiple line updates, sorry about that + verify_name "" -- NB: this is inefficient (but not really wrong). - eq({{ "test1", "lines", 1, tick, 4, 5, 5, 14, 5, 8 }, - { "test1", "lines", 1, tick+1, 5, 6, 5, 27, 20, 20 }}, exec_lua("return get_events(...)" )) + check_events { + { "test1", "lines", 1, tick, 4, 5, 5, 14, 5, 8 }; + { "test1", "lines", 1, tick+1, 5, 6, 5, 27, 20, 20 }; + } + end + + it('works with utf_sizes and unicode text', function() + check_unicode(false) end) + it('works with utf_sizes and unicode text with verify', function() + check_unicode(true) + end) + + it('has valid cursor position while shifting', function() meths.buf_set_lines(0, 0, -1, true, {'line1'}) exec_lua([[ @@ -280,26 +299,24 @@ describe('lua buffer event callbacks: on_lines', function() end) describe('lua: nvim_buf_attach on_bytes', function() - before_each(function() - clear() - attach_buffer('on_bytes') - end) - -- verifying the sizes with nvim_buf_get_offset is nice (checks we cannot -- assert the wrong thing), but masks errors with unflushed lines (as -- nvim_buf_get_offset forces a flush of the memline). To be safe run the -- test both ways. local function setup_eventcheck(verify, start_txt) - meths.buf_set_lines(0, 0, -1, true, start_txt) - local shadow = deepcopy(start_txt) - local shadowbytes = table.concat(shadow, '\n') .. '\n' + if start_txt then + meths.buf_set_lines(0, 0, -1, true, start_txt) + else + start_txt = meths.buf_get_lines(0, 0, -1, true) + end + local shadowbytes = table.concat(start_txt, '\n') .. '\n' -- TODO: while we are brewing the real strong coffe, -- verify should check buf_get_offset after every check_events if verify then local len = meths.buf_get_offset(0, meths.buf_line_count(0)) eq(len == -1 and 1 or len, string.len(shadowbytes)) end - exec_lua("return test_register(...)", 0, "test1", false, false, true) + exec_lua("return test_register(...)", 0, "on_bytes", "test1", false, false, true) meths.buf_get_changedtick(0) local verify_name = "test1" @@ -326,6 +343,8 @@ describe('lua: nvim_buf_attach on_bytes', function() local unknown = string.rep('\255', new_byte) local after = string.sub(shadowbytes, start_byte + old_byte + 1) shadowbytes = before .. unknown .. after + elseif event[1] == verify_name and event[2] == "reload" then + shadowbytes = table.concat(meths.buf_get_lines(0, 0, -1, true), '\n') .. '\n' end end @@ -503,20 +522,34 @@ describe('lua: nvim_buf_attach on_bytes', function() end) - it('inccomand=nosplit and substitute', function() - if verify then pending("Verification can't be done when previewing") end + it("linewise paste", function() + local check_events = setup_eventcheck(verify, origlines) + feed'yyp' + check_events { + { "test1", "bytes", 1, 3, 1, 0, 16, 0, 0, 0, 1, 0, 16 }; + } + + feed'Gyyp' + check_events { + { "test1", "bytes", 1, 4, 8, 0, 130, 0, 0, 0, 1, 0, 18 }; + } + end) + + it('inccomand=nosplit and substitute', function() local check_events = setup_eventcheck(verify, {"abcde"}) meths.set_option('inccommand', 'nosplit') feed ':%s/bcd/' check_events { { "test1", "bytes", 1, 3, 0, 1, 1, 0, 3, 3, 0, 0, 0 }; + { "test1", "bytes", 1, 5, 0, 1, 1, 0, 0, 0, 0, 3, 3 }; } feed 'a' check_events { { "test1", "bytes", 1, 3, 0, 1, 1, 0, 3, 3, 0, 1, 1 }; + { "test1", "bytes", 1, 5, 0, 1, 1, 0, 1, 1, 0, 3, 3 }; } end) @@ -583,6 +616,59 @@ describe('lua: nvim_buf_attach on_bytes', function() "original line 5", "original line 6" }, meths.buf_get_lines(0, 0, -1, true)) end) + + it('checktime autoread', function() + write_file("Xtest-reload", dedent [[ + old line 1 + old line 2]]) + lfs.touch("Xtest-reload", os.time() - 10) + command "e Xtest-reload" + command "set autoread" + + local check_events = setup_eventcheck(verify, nil) + + write_file("Xtest-reload", dedent [[ + new line 1 + new line 2 + new line 3]]) + + command "checktime" + check_events { + { "test1", "reload", 1 }; + } + + feed 'ggJ' + check_events { + { "test1", "bytes", 1, 5, 0, 10, 10, 1, 0, 1, 0, 1, 1 }; + } + + eq({'new line 1 new line 2', 'new line 3'}, meths.buf_get_lines(0, 0, -1, true)) + + -- check we can undo and redo a reload event. + feed 'u' + check_events { + { "test1", "bytes", 1, 8, 0, 10, 10, 0, 1, 1, 1, 0, 1 }; + } + + feed 'u' + check_events { + { "test1", "reload", 1 }; + } + + feed '<c-r>' + check_events { + { "test1", "reload", 1 }; + } + + feed '<c-r>' + check_events { + { "test1", "bytes", 1, 14, 0, 10, 10, 1, 0, 1, 0, 1, 1 }; + } + end) + + teardown(function() + os.remove "Xtest-reload" + end) end describe('(with verify) handles', function() diff --git a/test/functional/ui/messages_spec.lua b/test/functional/ui/messages_spec.lua index 5df4a1d533..1fe3a4128e 100644 --- a/test/functional/ui/messages_spec.lua +++ b/test/functional/ui/messages_spec.lua @@ -29,6 +29,7 @@ describe('ui/ext_messages', function() [6] = {bold = true, reverse = true}, [7] = {background = Screen.colors.Yellow}, [8] = {foreground = Screen.colors.Red}, + [9] = {special = Screen.colors.Red, undercurl = true}, }) end) after_each(function() @@ -795,6 +796,45 @@ describe('ui/ext_messages', function() pos = 9, }}} end) + + it('hides prompt_for_number messages', function() + command('set spell') + feed('ihelllo<esc>') + + feed('z=') + screen:expect{grid=[[ + {9:helllo} | + {1:~ }| + {1:~ }| + {1:~ }| + {1:^~ }| + ]], messages={ + {content = { { 'Change "helllo" to:\n 1 "Hello"\n 2 "Hallo"\n 3 "Helli"\nType number and <Enter> or click with mouse (empty cancels): ' } }, kind = ""} + }} + + feed('1') + screen:expect{grid=[[ + {9:helllo} | + {1:~ }| + {1:~ }| + {1:~ }| + {1:^~ }| + ]], messages={ + {content = { { 'Change "helllo" to:\n 1 "Hello"\n 2 "Hallo"\n 3 "Helli"\nType number and <Enter> or click with mouse (empty cancels): ' } }, kind = ""}, + { content = { { "1" } }, kind = "" } + }} + + feed('<cr>') + screen:expect{grid=[[ + ^Hello | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + ]]} + + end) + end) describe('ui/builtin messages', function() diff --git a/test/symbolic/klee/nvim/charset.c b/test/symbolic/klee/nvim/charset.c index f9bc3fabc4..fd1c5ee4b9 100644 --- a/test/symbolic/klee/nvim/charset.c +++ b/test/symbolic/klee/nvim/charset.c @@ -62,7 +62,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, goto vim_str2nr_dec; } default: { - assert(false); + abort(); } } } else if ((what & (STR2NR_HEX|STR2NR_OCT|STR2NR_BIN)) @@ -102,7 +102,7 @@ void vim_str2nr(const char_u *const start, int *const prep, int *const len, } // Do the string-to-numeric conversion "manually" to avoid sscanf quirks. - assert(false); // Should’ve used goto earlier. + abort(); // Should’ve used goto earlier. #define PARSE_NUMBER(base, cond, conv) \ do { \ while (!STRING_ENDED(ptr) && (cond)) { \ diff --git a/test/symbolic/klee/nvim/memory.c b/test/symbolic/klee/nvim/memory.c index df422cea3e..1614f813d7 100644 --- a/test/symbolic/klee/nvim/memory.c +++ b/test/symbolic/klee/nvim/memory.c @@ -45,7 +45,7 @@ void xfree(void *const p) return; } } - assert(false); + abort(); } void *xrealloc(void *const p, size_t new_size) @@ -63,7 +63,7 @@ void *xrealloc(void *const p, size_t new_size) return ret; } } - assert(false); + abort(); return (void *)(intptr_t)1; } |