aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.builds/freebsd.yml2
-rw-r--r--.builds/openbsd.yml6
-rw-r--r--.gitignore1
-rw-r--r--ci/build.ps113
-rw-r--r--runtime/autoload/health/nvim.vim6
-rw-r--r--runtime/autoload/man.vim2
-rw-r--r--runtime/autoload/netrw.vim265
-rw-r--r--runtime/autoload/netrwSettings.vim2
-rw-r--r--runtime/autoload/netrw_gitignore.vim58
-rw-r--r--runtime/doc/diff.txt4
-rw-r--r--runtime/doc/eval.txt15
-rw-r--r--runtime/doc/index.txt1
-rw-r--r--runtime/doc/options.txt25
-rw-r--r--runtime/doc/pi_netrw.txt45
-rw-r--r--runtime/doc/windows.txt5
-rw-r--r--runtime/lua/vim/lsp/log.lua8
-rw-r--r--runtime/lua/vim/lsp/util.lua2
-rw-r--r--runtime/lua/vim/treesitter.lua9
-rw-r--r--runtime/lua/vim/treesitter/highlighter.lua9
-rw-r--r--runtime/lua/vim/treesitter/language.lua4
-rw-r--r--runtime/lua/vim/treesitter/languagetree.lua46
-rw-r--r--runtime/lua/vim/treesitter/query.lua18
-rw-r--r--runtime/plugin/netrwPlugin.vim8
-rw-r--r--runtime/syntax/netrw.vim4
-rwxr-xr-xscripts/vim-patch.sh16
-rw-r--r--src/nvim/CMakeLists.txt2
-rw-r--r--src/nvim/api/buffer.c92
-rw-r--r--src/nvim/api/vim.c20
-rw-r--r--src/nvim/autocmd.c4
-rw-r--r--src/nvim/buffer.c90
-rw-r--r--src/nvim/buffer.h3
-rw-r--r--src/nvim/buffer_defs.h17
-rw-r--r--src/nvim/buffer_updates.c32
-rw-r--r--src/nvim/channel.c20
-rw-r--r--src/nvim/charset.c4
-rw-r--r--src/nvim/context.c2
-rw-r--r--src/nvim/diff.c23
-rw-r--r--src/nvim/eval.c47
-rw-r--r--src/nvim/eval.h4
-rw-r--r--src/nvim/eval/decode.c4
-rw-r--r--src/nvim/eval/encode.c4
-rw-r--r--src/nvim/eval/executor.c2
-rw-r--r--src/nvim/eval/funcs.c250
-rw-r--r--src/nvim/eval/typval.c37
-rw-r--r--src/nvim/event/libuv_process.c14
-rw-r--r--src/nvim/event/process.c3
-rw-r--r--src/nvim/event/process.h3
-rw-r--r--src/nvim/ex_cmds.c87
-rw-r--r--src/nvim/ex_cmds.h2
-rw-r--r--src/nvim/ex_cmds.lua6
-rw-r--r--src/nvim/ex_cmds2.c38
-rw-r--r--src/nvim/ex_cmds_defs.h4
-rw-r--r--src/nvim/ex_docmd.c41
-rw-r--r--src/nvim/ex_getln.c12
-rw-r--r--src/nvim/ex_getln.h1
-rw-r--r--src/nvim/ex_session.c16
-rw-r--r--src/nvim/file_search.c2
-rw-r--r--src/nvim/fileio.c8
-rw-r--r--src/nvim/fold.c7
-rw-r--r--src/nvim/log.h9
-rw-r--r--src/nvim/lua/converter.c6
-rw-r--r--src/nvim/lua/vim.lua27
-rw-r--r--src/nvim/main.c6
-rw-r--r--src/nvim/memline_defs.h8
-rw-r--r--src/nvim/menu.c8
-rw-r--r--src/nvim/move.c4
-rw-r--r--src/nvim/ops.c18
-rw-r--r--src/nvim/option.c24
-rw-r--r--src/nvim/option_defs.h13
-rw-r--r--src/nvim/options.lua8
-rw-r--r--src/nvim/os/pty_process_unix.c35
-rw-r--r--src/nvim/os/pty_process_unix.h1
-rw-r--r--src/nvim/os/pty_process_win.c78
-rw-r--r--src/nvim/os/pty_process_win.h1
-rw-r--r--src/nvim/os/time.c2
-rw-r--r--src/nvim/quickfix.c6
-rw-r--r--src/nvim/screen.c81
-rw-r--r--src/nvim/shada.c16
-rw-r--r--src/nvim/sign_defs.h4
-rw-r--r--src/nvim/spell.c11
-rw-r--r--src/nvim/spellfile.c3
-rw-r--r--src/nvim/syntax.c8
-rw-r--r--src/nvim/tag.c2
-rw-r--r--src/nvim/terminal.c57
-rw-r--r--src/nvim/testdir/test_arglist.vim18
-rw-r--r--src/nvim/testdir/test_autocmd.vim11
-rw-r--r--src/nvim/testdir/test_breakindent.vim9
-rw-r--r--src/nvim/testdir/test_buffer.vim33
-rw-r--r--src/nvim/testdir/test_bufline.vim11
-rw-r--r--src/nvim/testdir/test_cmdline.vim1
-rw-r--r--src/nvim/testdir/test_command_count.vim4
-rw-r--r--src/nvim/testdir/test_conceal.vim282
-rw-r--r--src/nvim/testdir/test_diffmode.vim84
-rw-r--r--src/nvim/testdir/test_digraph.vim2
-rw-r--r--src/nvim/testdir/test_excmd.vim8
-rw-r--r--src/nvim/testdir/test_expr.vim6
-rw-r--r--src/nvim/testdir/test_functions.vim12
-rw-r--r--src/nvim/testdir/test_matchadd_conceal.vim91
-rw-r--r--src/nvim/testdir/test_messages.vim2
-rw-r--r--src/nvim/testdir/test_mksession.vim115
-rw-r--r--src/nvim/testdir/test_options.vim4
-rw-r--r--src/nvim/testdir/test_version.vim14
-rw-r--r--src/nvim/tui/input.c2
-rw-r--r--src/nvim/ui_compositor.c2
-rw-r--r--src/nvim/undo.c14
-rw-r--r--src/nvim/undo_defs.h7
-rw-r--r--src/nvim/vim.h1
-rw-r--r--src/nvim/viml/parser/expressions.c6
-rw-r--r--test/functional/api/buffer_spec.lua22
-rw-r--r--test/functional/api/vim_spec.lua12
-rw-r--r--test/functional/core/job_spec.lua21
-rw-r--r--test/functional/lua/buffer_updates_spec.lua216
-rw-r--r--test/functional/ui/messages_spec.lua40
-rw-r--r--test/symbolic/klee/nvim/charset.c4
-rw-r--r--test/symbolic/klee/nvim/memory.c4
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(&regmatch, 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;
}