diff options
106 files changed, 2247 insertions, 1024 deletions
diff --git a/.ci/build.bat b/.ci/build.bat index c2f560fb7c..87a171b994 100644 --- a/.ci/build.bat +++ b/.ci/build.bat @@ -3,11 +3,11 @@ :: in MSYS2, but we cannot build inside the MSYS2 shell. echo on if "%CONFIGURATION%" == "MINGW_32" ( - set ARCH=i686 - set BITS=32 + set ARCH=i686 + set BITS=32 ) else ( - set ARCH=x86_64 - set BITS=64 + set ARCH=x86_64 + set BITS=64 ) :: We cannot have sh.exe in the PATH (MinGW) set PATH=%PATH:C:\Program Files\Git\usr\bin;=% @@ -17,7 +17,17 @@ set PATH=C:\Program Files (x86)\CMake\bin\cpack.exe;%PATH% :: Build third-party dependencies C:\msys64\usr\bin\bash -lc "pacman --verbose --noconfirm -Su" || goto :error -C:\msys64\usr\bin\bash -lc "pacman --verbose --noconfirm --needed -S mingw-w64-%ARCH%-cmake mingw-w64-%ARCH%-perl mingw-w64-%ARCH%-python2 mingw-w64-%ARCH%-diffutils gperf" || goto :error +C:\msys64\usr\bin\bash -lc "pacman --verbose --noconfirm --needed -S mingw-w64-%ARCH%-cmake mingw-w64-%ARCH%-perl mingw-w64-%ARCH%-diffutils gperf" || goto :error + +:: Setup python (use AppVeyor system python) +C:\Python27\python.exe -m pip install neovim || goto :error +C:\Python35\python.exe -m pip install neovim || goto :error +:: Disambiguate python3 +move c:\Python35\python.exe c:\Python35\python3.exe +set PATH=C:\Python35;C:\Python27;%PATH% +:: Sanity check +python -c "import neovim; print(str(neovim))" || goto :error +python3 -c "import neovim; print(str(neovim))" || goto :error mkdir .deps cd .deps diff --git a/CMakeLists.txt b/CMakeLists.txt index cb8302deb7..fdd6f0ed79 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -489,10 +489,9 @@ if(BUSTED_PRG) ${CMAKE_BINARY_DIR}/test/config/paths.lua) set(UNITTEST_PREREQS nvim-test unittest-headers) - if(WIN32) - set(FUNCTIONALTEST_PREREQS nvim shell-test) - else() - set(FUNCTIONALTEST_PREREQS nvim tty-test shell-test) + set(FUNCTIONALTEST_PREREQS nvim printargs-test shell-test) + if(NOT WIN32) + list(APPEND FUNCTIONALTEST_PREREQS tty-test) endif() set(BENCHMARK_PREREQS nvim tty-test) diff --git a/runtime/autoload/health.vim b/runtime/autoload/health.vim index a63eebae13..93ca4dfc54 100644 --- a/runtime/autoload/health.vim +++ b/runtime/autoload/health.vim @@ -39,7 +39,7 @@ function! health#check(plugin_names) abort tabnew setlocal wrap breakindent - setlocal filetype=markdown bufhidden=wipe + setlocal filetype=markdown setlocal conceallevel=2 concealcursor=nc setlocal keywordprg=:help call s:enhance_syntax() @@ -152,8 +152,8 @@ function! health#report_error(msg, ...) abort " {{{ endfunction " }}} function! s:filepath_to_function(name) abort - return substitute(substitute(substitute(a:name, ".*autoload/", "", ""), - \ "\\.vim", "#check", ""), "/", "#", "g") + return substitute(substitute(substitute(a:name, '.*autoload[\/]', '', ''), + \ '\.vim', '#check', ''), '[\/]', '#', 'g') endfunction function! s:discover_health_checks() abort diff --git a/runtime/autoload/health/nvim.vim b/runtime/autoload/health/nvim.vim index f5dacfebcc..e2ad9f7ccb 100644 --- a/runtime/autoload/health/nvim.vim +++ b/runtime/autoload/health/nvim.vim @@ -41,7 +41,7 @@ function! s:check_rplugin_manifest() abort \ + glob(python_dir.'/*/__init__.py', 1, 1) let contents = join(readfile(script)) if contents =~# '\<\%(from\|import\)\s\+neovim\>' - if script =~# '/__init__\.py$' + if script =~# '[\/]__init__\.py$' let script = fnamemodify(script, ':h') endif diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index 9cf540ba09..417426c101 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -37,6 +37,7 @@ endfunction function! s:system(cmd, ...) abort let stdin = a:0 ? a:1 : '' let ignore_stderr = a:0 > 1 ? a:2 : 0 + let ignore_error = a:0 > 2 ? a:3 : 0 let opts = { \ 'output': '', \ 'on_stdout': function('s:system_handler'), @@ -63,7 +64,7 @@ function! s:system(cmd, ...) abort call health#report_error(printf('Command timed out: %s', \ type(a:cmd) == type([]) ? join(a:cmd) : a:cmd)) call jobstop(jobid) - elseif s:shell_error != 0 + elseif s:shell_error != 0 && !ignore_error call health#report_error(printf("Command error (%d) %s: %s", jobid, \ type(a:cmd) == type([]) ? join(a:cmd) : a:cmd, \ opts.output)) @@ -83,8 +84,8 @@ endfunction " Fetch the contents of a URL. function! s:download(url) abort if executable('curl') - let rv = s:system(['curl', '-sL', a:url]) - return s:shell_error ? 'curl error: '.s:shell_error : rv + let rv = s:system(['curl', '-sL', a:url], '', 1, 1) + return s:shell_error ? 'curl error with '.a:url.': '.s:shell_error : rv elseif executable('python') let script = " \try:\n @@ -110,7 +111,7 @@ function! s:check_clipboard() abort let clipboard_tool = provider#clipboard#Executable() if empty(clipboard_tool) call health#report_warn( - \ "No clipboard tool found. Using the system clipboard won't work.", + \ "No clipboard tool found. Clipboard registers will not work.", \ ['See ":help clipboard".']) else call health#report_ok('Clipboard tool found: '. clipboard_tool) @@ -155,13 +156,10 @@ function! s:version_info(python) abort endif let nvim_path = s:trim(s:system([ - \ a:python, - \ '-c', - \ 'import neovim; print(neovim.__file__)'])) - let nvim_path = s:shell_error ? '' : nvim_path - - if empty(nvim_path) - return [python_version, 'unable to find nvim executable', pypi_version, 'unable to get nvim executable'] + \ a:python, '-c', 'import neovim; print(neovim.__file__)'])) + if s:shell_error || empty(nvim_path) + return [python_version, 'unable to load neovim Python module', pypi_version, + \ nvim_path] endif " Assuming that multiple versions of a package are installed, sort them @@ -172,24 +170,34 @@ function! s:version_info(python) abort return a == b ? 0 : a > b ? 1 : -1 endfunction - let nvim_version = 'unable to find nvim version' - let base = fnamemodify(nvim_path, ':h') - let metas = glob(base.'-*/METADATA', 1, 1) + glob(base.'-*/PKG-INFO', 1, 1) - let metas = sort(metas, 's:compare') - - if !empty(metas) - for meta_line in readfile(metas[0]) - if meta_line =~# '^Version:' - let nvim_version = matchstr(meta_line, '^Version: \zs\S\+') - break - endif - endfor + " Try to get neovim.VERSION (added in 0.1.11dev). + let nvim_version = s:system(['python', '-c', + \ 'from neovim import VERSION as v; '. + \ 'print("{}.{}.{}{}".format(v.major, v.minor, v.patch, v.prerelease))'], + \ '', 1, 1) + if empty(nvim_version) + let nvim_version = 'unable to find neovim Python module version' + let base = fnamemodify(nvim_path, ':h') + let metas = glob(base.'-*/METADATA', 1, 1) + \ + glob(base.'-*/PKG-INFO', 1, 1) + \ + glob(base.'.egg-info/PKG-INFO', 1, 1) + let metas = sort(metas, 's:compare') + + if !empty(metas) + for meta_line in readfile(metas[0]) + if meta_line =~# '^Version:' + let nvim_version = matchstr(meta_line, '^Version: \zs\S\+') + break + endif + endfor + endif endif - let version_status = 'unknown' + let nvim_path_base = fnamemodify(nvim_path, ':~:h') + let version_status = 'unknown; '.nvim_path_base if !s:is_bad_response(nvim_version) && !s:is_bad_response(pypi_version) if s:version_cmp(nvim_version, pypi_version) == -1 - let version_status = 'outdated' + let version_status = 'outdated; from '.nvim_path_base else let version_status = 'up to date' endif @@ -264,7 +272,7 @@ function! s:check_python(version) abort let python_bin = s:trim(s:system([pyenv, 'which', python_bin_name], '', 1)) if empty(python_bin) - call health#report_warn(printf('pyenv couldn''t find %s.', python_bin_name)) + call health#report_warn(printf('pyenv could not find %s.', python_bin_name)) endif endif @@ -283,15 +291,15 @@ function! s:check_python(version) abort if len(python_multiple) " This is worth noting since the user may install something " that changes $PATH, like homebrew. - call health#report_info(printf('There are multiple %s executables found. ' - \ . 'Set "g:%s" to avoid surprises.', python_bin_name, host_prog_var)) + call health#report_info(printf('Multiple %s executables found. ' + \ . 'Set `g:%s` to avoid surprises.', python_bin_name, host_prog_var)) endif if python_bin =~# '\<shims\>' - call health#report_warn(printf('"%s" appears to be a pyenv shim.', python_bin), [ - \ 'The "pyenv" executable is not in $PATH,', + call health#report_warn(printf('`%s` appears to be a pyenv shim.', python_bin), [ + \ 'The `pyenv` executable is not in $PATH,', \ 'Your pyenv installation is broken. You should set ' - \ . '"g:'.host_prog_var.'" to avoid surprises.', + \ . '`g:'.host_prog_var.'` to avoid surprises.', \ ]) endif endif @@ -302,9 +310,9 @@ function! s:check_python(version) abort if empty(venv) && !empty(pyenv) && !exists('g:'.host_prog_var) \ && !empty(pyenv_root) && resolve(python_bin) !~# '^'.pyenv_root.'/' call health#report_warn('pyenv is not set up optimally.', [ - \ printf('Suggestion: Create a virtualenv specifically ' - \ . 'for Neovim using pyenv and use "g:%s". This will avoid ' - \ . 'the need to install Neovim''s Python client in each ' + \ printf('Create a virtualenv specifically ' + \ . 'for Neovim using pyenv, and set `g:%s`. This will avoid ' + \ . 'the need to install Neovim''s Python module in each ' \ . 'version/virtualenv.', host_prog_var) \ ]) elseif !empty(venv) && exists('g:'.host_prog_var) @@ -316,9 +324,9 @@ function! s:check_python(version) abort if resolve(python_bin) !~# '^'.venv_root.'/' call health#report_warn('Your virtualenv is not set up optimally.', [ - \ printf('Suggestion: Create a virtualenv specifically ' - \ . 'for Neovim and use "g:%s". This will avoid ' - \ . 'the need to install Neovim''s Python client in each ' + \ printf('Create a virtualenv specifically ' + \ . 'for Neovim and use `g:%s`. This will avoid ' + \ . 'the need to install Neovim''s Python module in each ' \ . 'virtualenv.', host_prog_var) \ ]) endif @@ -327,7 +335,7 @@ function! s:check_python(version) abort if empty(python_bin) && !empty(python_bin_name) " An error message should have already printed. - call health#report_error(printf('"%s" was not found.', python_bin_name)) + call health#report_error(printf('`%s` was not found.', python_bin_name)) elseif !empty(python_bin) && !s:check_bin(python_bin) let python_bin = '' endif @@ -347,13 +355,10 @@ function! s:check_python(version) abort endif if virtualenv_inactive - let suggestions = [ - \ 'If you are using Zsh, see: http://vi.stackexchange.com/a/7654/5229', - \ ] call health#report_warn( - \ '$VIRTUAL_ENV exists but appears to be inactive. ' - \ . 'This could lead to unexpected results.', - \ suggestions) + \ '$VIRTUAL_ENV exists but appears to be inactive. ' + \ . 'This could lead to unexpected results.', + \ [ 'If you are using Zsh, see: http://vi.stackexchange.com/a/7654/5229' ]) endif " Diagnostic output @@ -367,7 +372,7 @@ function! s:check_python(version) abort if !empty(python_bin) let [pyversion, current, latest, status] = s:version_info(python_bin) if a:version != str2nr(pyversion) - call health#report_warn('Got an unexpected version of Python.' . + call health#report_warn('Unexpected Python version.' . \ ' This could lead to confusing error messages.') endif if a:version == 3 && str2float(pyversion) < 3.3 @@ -375,27 +380,25 @@ function! s:check_python(version) abort endif call health#report_info('Python'.a:version.' version: ' . pyversion) - call health#report_info(printf('%s-neovim version: %s', python_bin_name, current)) + if s:is_bad_response(status) + call health#report_info(printf('%s-neovim version: %s (%s)', python_bin_name, current, status)) + else + call health#report_info(printf('%s-neovim version: %s', python_bin_name, current)) + endif if s:is_bad_response(current) - let suggestions = [ - \ 'Error found was: ' . current, - \ 'Use the command `$ pip' . a:version . ' install neovim`', - \ ] call health#report_error( - \ 'Neovim Python client is not installed.', - \ suggestions) + \ "Neovim Python client is not installed.\nError: ".current, + \ ['Run in shell: pip' . a:version . ' install neovim']) endif if s:is_bad_response(latest) - call health#report_warn('Unable to contact PyPI.') + call health#report_warn('Could not contact PyPI to get latest version.') call health#report_error('HTTP request failed: '.latest) - endif - - if s:is_bad_response(status) + elseif s:is_bad_response(status) call health#report_warn(printf('Latest %s-neovim is NOT installed: %s', \ python_bin_name, latest)) - elseif !s:is_bad_response(latest) + elseif !s:is_bad_response(current) call health#report_ok(printf('Latest %s-neovim is installed: %s', \ python_bin_name, latest)) endif @@ -405,38 +408,49 @@ endfunction function! s:check_ruby() abort call health#report_start('Ruby provider') - let ruby_version = 'not found' - if executable('ruby') - let ruby_version = s:systemlist('ruby -v')[0] + + if !executable('ruby') || !executable('gem') + call health#report_warn( + \ "`ruby` and `gem` must be in $PATH.", + \ ["Install Ruby and verify that `ruby` and `gem` commands work."]) + return endif - let ruby_prog = provider#ruby#Detect() - let suggestions = - \ ['Install or upgrade the neovim RubyGem using `gem install neovim`.'] - - if empty(ruby_prog) - let ruby_prog = 'not found' - let prog_vers = 'not found' - call health#report_error('Missing Neovim RubyGem', suggestions) - else - silent let latest_gem = get(split(s:system(['gem', 'list', '-ra', '^neovim$']), - \ ' (\|, \|)$' ), 1, 'not found') - let latest_desc = ' (latest: ' . latest_gem . ')' - - silent let prog_vers = s:systemlist(ruby_prog . ' --version')[0] - if s:shell_error - let prog_vers = 'not found' . latest_desc - call health#report_warn('Neovim RubyGem is not up-to-date.', suggestions) - elseif s:version_cmp(prog_vers, latest_gem) == -1 - let prog_vers .= latest_desc - call health#report_warn('Neovim RubyGem is not up-to-date.', suggestions) - else - call health#report_ok('Found up-to-date neovim RubyGem') - endif + call health#report_info('Ruby: '. s:system('ruby -v')) + + let host = provider#ruby#Detect() + if empty(host) + call health#report_warn('Missing "neovim" gem.', + \ ['Run in shell: gem install neovim']) + return + endif + call health#report_info('Host: '. host) + + let latest_gem_cmd = 'gem list -ra ^neovim$' + let latest_gem = s:system(split(latest_gem_cmd)) + if s:shell_error || empty(latest_gem) + call health#report_error('Failed to run: '. latest_gem_cmd, + \ ["Make sure you're connected to the internet.", + \ "Are you behind a firewall or proxy?"]) + return + endif + let latest_gem = get(split(latest_gem, ' (\|, \|)$' ), 1, 'not found') + + let current_gem_cmd = host .' --version' + let current_gem = s:system(current_gem_cmd) + if s:shell_error + call health#report_error('Failed to run: '. current_gem_cmd, + \ ["Report this issue with the output of: ", current_gem_cmd]) + return endif - call health#report_info('Ruby Version: ' . ruby_version) - call health#report_info('Host Executable: ' . ruby_prog) - call health#report_info('Host Version: ' . prog_vers) + if s:version_cmp(current_gem, latest_gem) == -1 + call health#report_warn( + \ printf('Gem "neovim" is out-of-date. Installed: %s, latest: %s', + \ current_gem, latest_gem), + \ ['Run in shell: gem update neovim']) + else + call health#report_ok('Gem "neovim" is up-to-date: '. current_gem) + endif endfunction function! health#provider#check() abort diff --git a/runtime/autoload/netrw.vim b/runtime/autoload/netrw.vim index 64c08e98fa..de85844d5d 100644 --- a/runtime/autoload/netrw.vim +++ b/runtime/autoload/netrw.vim @@ -1,7 +1,7 @@ " netrw.vim: Handles file transfer and remote directory listing across " AUTOLOAD SECTION -" Date: Feb 16, 2016 -" Version: 155 ASTRO-ONLY +" Date: Apr 20, 2016 +" Version: 156 " Maintainer: Charles E Campbell <NdrOchip@ScampbellPfamily.AbizM-NOSPAM> " GetLatestVimScripts: 1075 1 :AutoInstall: netrw.vim " Copyright: Copyright (C) 2016 Charles E. Campbell {{{1 @@ -13,7 +13,7 @@ " expressed or implied. By using this plugin, you agree that " in no event will the copyright holder be liable for any damages " resulting from the use of this software. -"redraw!|call DechoSep()|call inputsave()|call input("Press <cr> to continue")|call inputrestore(,'~'.expand("<slnum>")) +"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 " (James 1:22 RSV) @@ -30,7 +30,7 @@ if v:version < 704 || !has("patch213") let s:needpatch213= 1 finish endif -let g:loaded_netrw = "v155" +let g:loaded_netrw = "v156" if !exists("s:NOTE") let s:NOTE = 0 let s:WARNING = 1 @@ -444,7 +444,7 @@ call s:NetrwInit("g:netrw_markfileesc" , '*./[\~') call s:NetrwInit("g:netrw_maxfilenamelen", 32) call s:NetrwInit("g:netrw_menu" , 1) call s:NetrwInit("g:netrw_mkdir_cmd" , g:netrw_ssh_cmd." USEPORT HOSTNAME mkdir") -call s:NetrwInit("g:netrw_mousemaps" , (exists("+mouse") && &mouse =~ '[anh]')) +call s:NetrwInit("g:netrw_mousemaps" , (exists("+mouse") && &mouse =~# '[anh]')) call s:NetrwInit("g:netrw_retmap" , 0) if has("unix") || (exists("g:netrw_cygwin") && g:netrw_cygwin) call s:NetrwInit("g:netrw_chgperm" , "chmod PERM FILENAME") @@ -490,6 +490,7 @@ if !exists("g:netrw_sort_sequence") endif call s:NetrwInit("g:netrw_special_syntax" , 0) call s:NetrwInit("g:netrw_ssh_browse_reject", '^total\s\+\d\+$') +call s:NetrwInit("g:netrw_suppress_gx_mesg", 1) call s:NetrwInit("g:netrw_use_noswf" , 1) call s:NetrwInit("g:netrw_sizestyle" ,"b") " Default values - t-w ---------- {{{3 @@ -526,6 +527,7 @@ if has("gui_running") && (&enc == 'utf-8' || &enc == 'utf-16' || &enc == 'ucs-4' else let s:treedepthstring= "| " endif +call s:NetrwInit("s:netrw_nbcd",'{}') " BufEnter event ignored by decho when following variable is true " Has a side effect that doau BufReadPost doesn't work, so @@ -551,7 +553,7 @@ if v:version >= 700 && has("balloon_eval") && !exists("s:initbeval") && !exists( endif au WinEnter * if &ft == "netrw"|call s:NetrwInsureWinVars()|endif -if g:netrw_keepj =~ "keepj" +if g:netrw_keepj =~# "keepj" com! -nargs=* NetrwKeepj keepj <args> else let g:netrw_keepj= "" @@ -821,7 +823,7 @@ fun! netrw#Explore(indx,dosplit,style,...) let dirname= curfiledir " call Decho("..empty dirname, using current file's directory<".dirname.">",'~'.expand("<slnum>")) endif - if dirname =~ '^scp://' || dirname =~ '^ftp://' + if dirname =~# '^scp://' || dirname =~ '^ftp://' call netrw#Nread(2,dirname) else if dirname == "" @@ -1560,15 +1562,7 @@ fun! s:NetrwOptionRestore(vt) " call Dfunc("s:NetrwOptionRestore(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") - if exists("s:nbcd_curpos_{bufnr('%')}") -" call Decho("restoring posn to s:nbcd_curpos_".bufnr('%')."<".string(s:nbcd_curpos_{bufnr('%')}).">",'~'.expand("<slnum>")) - NetrwKeepj call winrestview(s:nbcd_curpos_{bufnr('%')}) -" call Decho("win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> winnr($)=".winnr("$"),'~'.expand("<slnum>")) -" call Decho("unlet s:nbcd_curpos_".bufnr('%'),'~'.expand("<slnum>")) - unlet s:nbcd_curpos_{bufnr('%')} - else -" call Decho("no previous position",'~'.expand("<slnum>")) - endif + call s:RestorePosn(s:netrw_nbcd) " 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:NetrwOptionRestore : ".a:vt."netrw_optionsave doesn't exist") @@ -1656,16 +1650,7 @@ fun! s:NetrwOptionRestore(vt) if exists("{a:vt}netrw_regstar") |sil! let @*= {a:vt}netrw_regstar |unlet {a:vt}netrw_regstar |endif endif if exists("{a:vt}netrw_regslash")|sil! let @/= {a:vt}netrw_regslash|unlet {a:vt}netrw_regslash|endif - if exists("s:nbcd_curpos_{bufnr('%')}") -" call Decho("restoring posn to s:nbcd_curpos_".bufnr('%')."<".string(s:nbcd_curpos_{bufnr('%')}).">",'~'.expand("<slnum>")) - NetrwKeepj call winrestview(s:nbcd_curpos_{bufnr('%')}) -" call Decho("unlet s:nbcd_curpos_".bufnr('%'),'~'.expand("<slnum>")) - if exists("s:nbcd_curpos_".bufnr('%')) - unlet s:nbcd_curpos_{bufnr('%')} - endif - else -" call Decho("no previous position",'~'.expand("<slnum>")) - endif + call s:RestorePosn(s:netrw_nbcd) " call Decho("g:netrw_keepdir=".g:netrw_keepdir.": getcwd<".getcwd()."> acd=".&acd,'~'.expand("<slnum>")) " call Decho("fo=".&fo.(exists("+acd")? " acd=".&acd : " acd doesn't exist"),'~'.expand("<slnum>")) @@ -3016,10 +3001,10 @@ fun! s:NetrwMethod(choice) if exists("s:netrw_hup[host]") call NetUserPass("ftp:".host) - elseif (has("win32") || has("win95") || has("win64") || has("win16")) && s:netrw_ftp_cmd =~ '-[sS]:' + elseif (has("win32") || has("win95") || has("win64") || has("win16")) && s:netrw_ftp_cmd =~# '-[sS]:' " call Decho("has -s: : s:netrw_ftp_cmd<".s:netrw_ftp_cmd.">",'~'.expand("<slnum>")) " call Decho(" g:netrw_ftp_cmd<".g:netrw_ftp_cmd.">",'~'.expand("<slnum>")) - if g:netrw_ftp_cmd =~ '-[sS]:\S*MACHINE\>' + if g:netrw_ftp_cmd =~# '-[sS]:\S*MACHINE\>' let s:netrw_ftp_cmd= substitute(g:netrw_ftp_cmd,'\<MACHINE\>',g:netrw_machine,'') " call Decho("s:netrw_ftp_cmd<".s:netrw_ftp_cmd.">",'~'.expand("<slnum>")) endif @@ -3583,7 +3568,7 @@ fun! s:NetrwBrowse(islocal,dirname) " This is useful when one edits a local file, then :e ., then :Rex if a:islocal && !exists("w:netrw_rexfile") && bufname("#") != "" let w:netrw_rexfile= bufname("#") -" call Decho("setting w:netrw_rexfile<".w:netrw_rexfile."> win#".winnr()) +" call Decho("setting w:netrw_rexfile<".w:netrw_rexfile."> win#".winnr(),'~'.expand("<slnum>")) endif " s:NetrwBrowse : initialize history {{{3 @@ -3773,7 +3758,7 @@ fun! s:NetrwBrowse(islocal,dirname) " analyze dirname and g:netrw_list_cmd {{{3 " call Decho("b:netrw_curdir<".(exists("b:netrw_curdir")? b:netrw_curdir : "doesn't exist")."> dirname<".dirname.">",'~'.expand("<slnum>")) - if dirname =~ "^NetrwTreeListing\>" + if dirname =~# "^NetrwTreeListing\>" let dirname= b:netrw_curdir " call Decho("(dirname was <NetrwTreeListing>) dirname<".dirname.">",'~'.expand("<slnum>")) elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("b:netrw_curdir") @@ -3854,15 +3839,15 @@ endfun " directory is used. fun! s:NetrwFile(fname) " call Dfunc("s:NetrwFile(fname<".a:fname.">) win#".winnr()) -" call Decho("g:netrw_keepdir =".(exists("g:netrw_keepdir")? g:netrw_keepdir : 'n/a')) -" call Decho("g:netrw_cygwin =".(exists("g:netrw_cygwin")? g:netrw_cygwin : 'n/a')) -" call Decho("g:netrw_liststyle=".(exists("g:netrw_liststyle")? g:netrw_liststyle : 'n/a')) -" call Decho("w:netrw_liststyle=".(exists("w:netrw_liststyle")? w:netrw_liststyle : 'n/a')) +" call Decho("g:netrw_keepdir =".(exists("g:netrw_keepdir")? g:netrw_keepdir : 'n/a'),'~'.expand("<slnum>")) +" call Decho("g:netrw_cygwin =".(exists("g:netrw_cygwin")? g:netrw_cygwin : 'n/a'),'~'.expand("<slnum>")) +" call Decho("g:netrw_liststyle=".(exists("g:netrw_liststyle")? g:netrw_liststyle : 'n/a'),'~'.expand("<slnum>")) +" call Decho("w:netrw_liststyle=".(exists("w:netrw_liststyle")? w:netrw_liststyle : 'n/a'),'~'.expand("<slnum>")) " clean up any leading treedepthstring if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST let fname= substitute(a:fname,'^'.s:treedepthstring.'\+','','') -" call Decho("clean up any leading treedepthstring: fname<".fname.">") +" call Decho("clean up any leading treedepthstring: fname<".fname.">",'~'.expand("<slnum>")) else let fname= a:fname endif @@ -3897,6 +3882,8 @@ fun! s:NetrwFile(fname) " vim and netrw agree on the current directory let ret= fname " call Decho("vim and netrw agree on current directory (g:netrw_keepdir=".g:netrw_keepdir.")",'~'.expand("<slnum>")) +" call Decho("vim directory: ".getcwd(),'~'.expand("<slnum>")) +" call Decho("netrw directory: ".(exists("b:netrw_curdir")? b:netrw_curdir : 'n/a'),'~'.expand("<slnum>")) endif " call Dret("s:NetrwFile ".ret) @@ -3910,9 +3897,9 @@ fun! s:NetrwFileInfo(islocal,fname) let ykeep= @@ if a:islocal let lsopt= "-lsad" - if g:netrw_sizestyle =~ 'H' + if g:netrw_sizestyle =~# 'H' let lsopt= "-lsadh" - elseif g:netrw_sizestyle =~ 'h' + elseif g:netrw_sizestyle =~# 'h' let lsopt= "-lsadh --si" endif if (has("unix") || has("macunix")) && executable("/bin/ls") @@ -3944,7 +3931,7 @@ fun! s:NetrwFileInfo(islocal,fname) endif let t = getftime(s:NetrwFile(fname)) let sz = getfsize(s:NetrwFile(fname)) - if g:netrw_sizestyle =~ "[hH]" + if g:netrw_sizestyle =~# "[hH]" let sz= s:NetrwHumanReadable(sz) endif echo a:fname.": ".sz." ".strftime(g:netrw_timefmt,getftime(s:NetrwFile(fname))) @@ -3958,108 +3945,49 @@ fun! s:NetrwFileInfo(islocal,fname) endfun " --------------------------------------------------------------------- +" s:NetrwFullPath: returns the full path to a directory and/or file {{{2 +fun! s:NetrwFullPath(filename) +" " call Dfunc("s:NetrwFullPath(filename<".a:filename.">)") + let filename= a:filename + if filename !~ '^/' + let filename= resolve(getcwd().'/'.filename) + endif + if filename != "/" && filename =~ '/$' + let filename= substitute(filename,'/$','','') + endif +" " call Dret("s:NetrwFullPath <".filename.">") + return filename +endfun + +" --------------------------------------------------------------------- " s:NetrwGetBuffer: {{{2 " returns 0=cleared buffer " 1=re-used buffer (buffer not cleared) fun! s:NetrwGetBuffer(islocal,dirname) " call Dfunc("s:NetrwGetBuffer(islocal=".a:islocal." dirname<".a:dirname.">) liststyle=".g:netrw_liststyle) " 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,'~'.expand("<slnum>")) +" call Decho("netrwbuf dictionary=".string(s:netrwbuf),'~'.expand("<slnum>")) let dirname= a:dirname " re-use buffer if possible {{{3 " call Decho("--re-use a buffer if possible--",'~'.expand("<slnum>")) - if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST - " find NetrwTreeList buffer if there is one -" call Decho("case liststyle=treelist: find NetrwTreeList buffer if there is one",'~'.expand("<slnum>")) - if exists("w:netrw_treebufnr") && w:netrw_treebufnr > 0 -" call Decho(" re-using w:netrw_treebufnr=".w:netrw_treebufnr,'~'.expand("<slnum>")) - let eikeep= &ei - setl ei=all - exe "sil! keepj noswapfile keepalt b ".w:netrw_treebufnr - let &ei= eikeep - setl ma - sil! NetrwKeepj %d _ -" 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,'~'.expand("<slnum>")) -" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) -" call Dret("s:NetrwGetBuffer 0<buffer cleared> : bufnum#".w:netrw_treebufnr."<NetrwTreeListing>") - return 0 + if !exists("s:netrwbuf") + let s:netrwbuf= {} + endif + if has_key(s:netrwbuf,s:NetrwFullPath(dirname)) + let bufnum= s:netrwbuf[s:NetrwFullPath(dirname)] +" call Decho("lookup netrwbuf dictionary: s:netrwbuf[".s:NetrwFullPath(dirname)."]=".bufnum) + if !bufexists(bufnum) + call remove(s:netrwbuf,s:NetrwFullPath(dirname)) + let bufnum= -1 endif - let bufnum= -1 -" call Decho(" liststyle=TREE but w:netrw_treebufnr doesn't exist",'~'.expand("<slnum>")) - else - " find buffer number of buffer named precisely the same as dirname {{{3 -" call Decho("case listtyle not treelist: find buffer numnber of buffer named precisely the same as dirname--",'~'.expand("<slnum>")) -" call Dredir("(NetrwGetBuffer) ls!","ls!") - - " get dirname and associated buffer number - let bufnum = bufnr(escape(dirname,'\')) -" call Decho(" find buffer<".dirname.">'s number ",'~'.expand("<slnum>")) -" call Decho(" bufnr(dirname<".escape(dirname,'\').">)=".bufnum,'~'.expand("<slnum>")) - - if bufnum < 0 && dirname !~ '/$' - " try appending a trailing / -" call Decho(" try appending a trailing / to dirname<".dirname.">",'~'.expand("<slnum>")) - let bufnum= bufnr(escape(dirname.'/','\')) - if bufnum > 0 - let dirname= dirname.'/' - endif - endif - - if bufnum < 0 && dirname =~ '/$' - " try removing a trailing / -" call Decho(" try removing a trailing / from dirname<".dirname.">",'~'.expand("<slnum>")) - let bufnum= bufnr(escape(substitute(dirname,'/$','',''),'\')) - if bufnum > 0 - let dirname= substitute(dirname,'/$','','') - endif - endif - -" call Decho(" findbuf1: bufnum=bufnr('".dirname."')=".bufnum." bufname(".bufnum.")<".bufname(bufnum)."> (initial)",'~'.expand("<slnum>")) - " note: !~ was used just below, but that means using ../ to go back would match (ie. abc/def/ and abc/ matches) - if bufnum > 0 && bufname(bufnum) != dirname && bufname(bufnum) != '.' - " handle approximate matches -" call Decho(" handling approx match: bufnum#".bufnum.">0 AND bufname<".bufname(bufnum).">!=dirname<".dirname."> AND bufname(".bufnum.")!='.'",'~'.expand("<slnum>")) - let ibuf = 1 - let buflast = bufnr("$") -" call Decho(" findbuf2: buflast=bufnr($)=".buflast,'~'.expand("<slnum>")) - while ibuf <= buflast - let bname= substitute(bufname(ibuf),'\\','/','g') - let bname= substitute(bname,'.\zs/$','','') -" call Decho(" findbuf3: while [ibuf=",ibuf."]<=[buflast=".buflast."]: dirname<".dirname."> bname=bufname(".ibuf.")<".bname.">",'~'.expand("<slnum>")) - if bname != '' && dirname =~ '/'.bname.'/\=$' && dirname !~ '^/' - " bname is not empty - " dirname ends with bname, - " dirname doesn't start with /, so its not a absolute path -" call Decho(" findbuf3a: passes test 1 : dirname<".dirname.'> =~ /'.bname.'/\=$ && dirname !~ ^/','~'.expand("<slnum>")) - break - endif - if bname =~ '^'.dirname.'/\=$' - " bname begins with dirname -" call Decho(' findbuf3b: passes test 2 : bname<'.bname.'>=~^'.dirname.'/\=$','~'.expand("<slnum>")) - break - endif - if dirname =~ '^'.bname.'/$' -" call Decho(' findbuf3c: passes test 3 : dirname<'.dirname.'>=~^'.bname.'/$','~'.expand("<slnum>")) - break - endif - if bname != '' && dirname =~ '/'.bname.'$' && bname == bufname("%") && line("$") == 1 -" call Decho(' findbuf3d: passes test 4 : dirname<'.dirname.'>=~ /'.bname.'$','~'.expand("<slnum>")) - break - endif - let ibuf= ibuf + 1 - endwhile - if ibuf > buflast - let bufnum= -1 - else - let bufnum= ibuf - endif -" call Decho(" findbuf4: bufnum=".bufnum." (ibuf=".ibuf." buflast=".buflast.")",'~'.expand("<slnum>")) - endif +" call Decho("lookup netrwbuf dictionary: s:netrwbuf[".s:NetrwFullPath(dirname)."] not a key") + let bufnum= -1 endif " get enew buffer and name it -or- re-use buffer {{{3 - if bufnum < 0 || !bufexists(bufnum) " get enew buffer and name it + if bufnum < 0 " get enew buffer and name it " call Decho("--get enew buffer and name it (bufnum#".bufnum."<0 OR bufexists(".bufnum.")=".bufexists(bufnum)."==0)",'~'.expand("<slnum>")) call s:NetrwEnew(dirname) " call Decho(" got enew buffer#".bufnr("%")." (altbuf<".expand("#").">)",'~'.expand("<slnum>")) @@ -4093,6 +4021,10 @@ fun! s:NetrwGetBuffer(islocal,dirname) " let v:errmsg= "" " Decho exe 'sil! keepj keepalt file '.escdirname " call Decho(" errmsg<".v:errmsg."> bufnr(".escdirname.")=".bufnr(escdirname)."<".bufname(bufnr(escdirname)).">",'~'.expand("<slnum>")) + " enter the new buffer into the s:netrwbuf dictionary + let s:netrwbuf[s:NetrwFullPath(dirname)]= bufnr("%") +" call Decho("update netrwbuf dictionary: s:netrwbuf[".s:NetrwFullPath(dirname)."]=".bufnr("%"),'~'.expand("<slnum>")) +" call Decho("netrwbuf dictionary=".string(s:netrwbuf),'~'.expand("<slnum>")) endif " call Decho(" named enew buffer#".bufnr("%")."<".bufname("%").">",'~'.expand("<slnum>")) @@ -4100,7 +4032,7 @@ fun! s:NetrwGetBuffer(islocal,dirname) " call Decho("--re-use buffer#".bufnum." (bufnum#".bufnum.">=0 AND bufexists(".bufnum.")=".bufexists(bufnum)."!=0)",'~'.expand("<slnum>")) let eikeep= &ei setl ei=all - if getline(2) =~ '^" Netrw Directory Listing' + if getline(2) =~# '^" Netrw Directory Listing' " call Decho(" getline(2)<".getline(2).'> matches "Netrw Directory Listing" : using keepalt b '.bufnum,'~'.expand("<slnum>")) exe "sil! NetrwKeepj noswapfile keepalt b ".bufnum else @@ -4215,20 +4147,20 @@ fun! s:NetrwGetWord() let dirname= "./" let curline= getline('.') - if curline =~ '"\s*Sorted by\s' + if curline =~# '"\s*Sorted by\s' NetrwKeepj norm s let s:netrw_skipbrowse= 1 echo 'Pressing "s" also works' - elseif curline =~ '"\s*Sort sequence:' + elseif curline =~# '"\s*Sort sequence:' let s:netrw_skipbrowse= 1 echo 'Press "S" to edit sorting sequence' - elseif curline =~ '"\s*Quick Help:' + elseif curline =~# '"\s*Quick Help:' NetrwKeepj norm ? let s:netrw_skipbrowse= 1 - elseif curline =~ '"\s*\%(Hiding\|Showing\):' + elseif curline =~# '"\s*\%(Hiding\|Showing\):' NetrwKeepj norm a let s:netrw_skipbrowse= 1 echo 'Pressing "a" also works' @@ -4471,10 +4403,10 @@ fun! s:NetrwBookmark(del,...) let i = 1 while i <= a:0 if islocal - if v:version == 704 && has("patch656") - let mbfiles= glob(a:{i},0,1,1) + if v:version > 704 || (v:version == 704 && has("patch656")) + let mbfiles= glob(fnameescape(a:{i}),0,1,1) else - let mbfiles= glob(a:{i},0,1) + let mbfiles= glob(fnameescape(a:{i}),0,1) endif else let mbfiles= [a:{i}] @@ -4578,14 +4510,13 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...) " call Dret("s:NetrwBrowseChgDir") return endif +" call Decho("b:netrw_curdir<".b:netrw_curdir.">") " NetrwBrowseChgDir: save options and initialize {{{3 " call Decho("saving options",'~'.expand("<slnum>")) + call s:SavePosn(s:netrw_nbcd) NetrwKeepj call s:NetrwOptionSave("s:") NetrwKeepj call s:NetrwSafeOptions() - let nbcd_curpos = winsaveview() -" call Decho("saving posn to nbcd_curpos<".string(nbcd_curpos).">",'~'.expand("<slnum>")) - let s:nbcd_curpos_{bufnr('%')} = nbcd_curpos if (has("win32") || has("win95") || has("win64") || has("win16")) let dirname = substitute(b:netrw_curdir,'\\','/','ge') else @@ -4601,15 +4532,14 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...) if g:netrw_banner " call Decho("w:netrw_bannercnt=".(exists("w:netrw_bannercnt")? w:netrw_bannercnt : 'n/a')." line(.)#".line('.')." line($)#".line("#"),'~'.expand("<slnum>")) if exists("w:netrw_bannercnt") && line(".") < w:netrw_bannercnt && line("$") >= w:netrw_bannercnt - if getline(".") =~ 'Quick Help' + if getline(".") =~# 'Quick Help' " call Decho("#1: quickhelp=".g:netrw_quickhelp." ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) let g:netrw_quickhelp= (g:netrw_quickhelp + 1)%len(s:QuickHelp) " call Decho("#2: quickhelp=".g:netrw_quickhelp." ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) setl ma noro nowrap NetrwKeepj call setline(line('.'),'" Quick Help: <F1>:help '.s:QuickHelp[g:netrw_quickhelp]) setl noma nomod nowrap -" call Decho("restoring posn to nbcd_curpos<".string(nbcd_curpos).">",'~'.expand("<slnum>")) - NetrwKeepj call winrestview(nbcd_curpos) + call s:RestorePosn(s:netrw_nbcd) NetrwKeepj call s:NetrwOptionRestore("s:") " call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) endif @@ -4633,7 +4563,7 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...) " call Decho("adjusting dirname<".dirname.'> (put trailing "/" back)','~'.expand("<slnum>")) endif -" " call Decho("[newdir<".newdir."> ".((newdir =~ dirpat)? "=~" : "!~")." dirpat<".dirpat.">] && [islocal=".a:islocal."] && [newdir is ".(isdirectory(s:NetrwFile(newdir))? "" : "not ")."a directory]",'~'.expand("<slnum>")) +" call Decho("[newdir<".newdir."> ".((newdir =~ dirpat)? "=~" : "!~")." dirpat<".dirpat.">] && [islocal=".a:islocal."] && [newdir is ".(isdirectory(s:NetrwFile(newdir))? "" : "not ")."a directory]",'~'.expand("<slnum>")) if newdir !~ dirpat && !(a:islocal && isdirectory(s:NetrwFile(s:ComposePath(dirname,newdir)))) " ------------------------------ " NetrwBrowseChgDir: edit a file {{{3 @@ -4658,7 +4588,7 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...) " call Decho("edit-a-file: dirname<".dirname.">",'~'.expand("<slnum>")) " call Decho("edit-a-file: tree listing",'~'.expand("<slnum>")) elseif newdir =~ '^\(/\|\a:\)' -" call Decho("edit-a-file: handle an url or path starting with /: <".newdir.">") +" call Decho("edit-a-file: handle an url or path starting with /: <".newdir.">",'~'.expand("<slnum>")) let dirname= newdir else let dirname= s:ComposePath(dirname,newdir) @@ -4685,7 +4615,7 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...) if !&ea keepalt wincmd _ endif - call s:SetRexDir(a:islocal,b:netrw_curdir) + call s:SetRexDir(a:islocal,curdir) elseif g:netrw_browse_split == 2 " vertically splitting the window first " call Decho("edit-a-file: vertically splitting window prior to edit",'~'.expand("<slnum>")) @@ -4693,12 +4623,15 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...) if !&ea keepalt wincmd | endif - call s:SetRexDir(a:islocal,b:netrw_curdir) + call s:SetRexDir(a:islocal,curdir) elseif g:netrw_browse_split == 3 " open file in new tab " call Decho("edit-a-file: opening new tab prior to edit",'~'.expand("<slnum>")) keepalt tabnew - call s:SetRexDir(a:islocal,b:netrw_curdir) + if !exists("b:netrw_curdir") + let b:netrw_curdir= getcwd() + endif + call s:SetRexDir(a:islocal,curdir) elseif g:netrw_browse_split == 4 " act like "P" (ie. open previous window) " call Decho("edit-a-file: use previous window for edit",'~'.expand("<slnum>")) @@ -4707,7 +4640,7 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...) " call Dret("s:NetrwBrowseChgDir") return endif - call s:SetRexDir(a:islocal,b:netrw_curdir) + call s:SetRexDir(a:islocal,curdir) else " handling a file, didn't split, so remove menu " call Decho("edit-a-file: handling a file+didn't split, so remove menu",'~'.expand("<slnum>")) @@ -4860,7 +4793,7 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...) endif let treedir = s:NetrwTreeDir(a:islocal) " call Decho("tree-list: treedir<".treedir.">",'~'.expand("<slnum>")) - let s:treecurpos = nbcd_curpos + let s:treecurpos = winsaveview() let haskey = 0 " call Decho("tree-list: w:netrw_treedict<".string(w:netrw_treedict).">",'~'.expand("<slnum>")) @@ -4936,6 +4869,7 @@ fun! s:NetrwBrowseChgDir(islocal,newdir,...) " else " Decho " call Decho("skipping option restore (dorestore==0): hidden=".&hidden." bufhidden=".&bufhidden." mod=".&mod,'~'.expand("<slnum>")) endif + call s:RestorePosn(s:netrw_nbcd) if dolockout && dorestore " call Decho("restore: filewritable(dirname<".dirname.">)=".filewritable(dirname),'~'.expand("<slnum>")) if filewritable(dirname) @@ -4971,6 +4905,10 @@ fun! s:NetrwBrowseUpDir(islocal) return endif + if !exists("w:netrw_liststyle") || w:netrw_liststyle != s:TREELIST + call s:SavePosn(s:netrw_nbcd) + endif + norm! 0 if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict") " call Decho("case: treestyle",'~'.expand("<slnum>")) @@ -5010,7 +4948,9 @@ fun! s:NetrwBrowseUpDir(islocal) else call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,'../')) endif - if exists("w:netrw_bannercnt") + if has_key(s:netrw_nbcd,bufnr("%")) + call s:RestorePosn(s:netrw_nbcd) + elseif exists("w:netrw_bannercnt") " call Decho("moving to line#".w:netrw_bannercnt,'~'.expand("<slnum>")) exe w:netrw_bannercnt else @@ -5106,17 +5046,20 @@ fun! netrw#BrowseX(fname,remote) " call Decho("fname<".fname.">",'~'.expand("<slnum>")) " call Decho("exten<".exten."> "."netrwFileHandlers#NFH_".exten."():exists=".exists("*netrwFileHandlers#NFH_".exten),'~'.expand("<slnum>")) - " set up redirection - if &srr =~ "%s" - if (has("win32") || has("win95") || has("win64") || has("win16")) - let redir= substitute(&srr,"%s","nul","") + " set up redirection (avoids browser messages) + " by default, g:netrw_suppress_gx_mesg is true + if g:netrw_suppress_gx_mesg + if &srr =~ "%s" + if (has("win32") || has("win95") || has("win64") || has("win16")) + let redir= substitute(&srr,"%s","nul","") + else + let redir= substitute(&srr,"%s","/dev/null","") + endif + elseif (has("win32") || has("win95") || has("win64") || has("win16")) + let redir= &srr . "nul" else - let redir= substitute(&srr,"%s","/dev/null","") + let redir= &srr . "/dev/null" endif - elseif (has("win32") || has("win95") || has("win64") || has("win16")) - let redir= &srr . "nul" - else - let redir= &srr . "/dev/null" endif " call Decho("set up redirection: redir{".redir."} srr{".&srr."}",'~'.expand("<slnum>")) @@ -5377,8 +5320,12 @@ endfun " --------------------------------------------------------------------- " s:NetrwGlob: does glob() if local, remote listing otherwise {{{2 -fun! s:NetrwGlob(direntry,expr) -" call Dfunc("s:NetrwGlob(direntry<".a:direntry."> expr<".a:expr.">)") +" direntry: this is the name of the directory. Will be fnameescape'd to prevent wildcard handling by glob() +" expr : this is the expression to follow the directory. Will use s:ComposePath() +" pare =1: remove the current directory from the resulting glob() filelist +" =0: leave the current directory in the resulting glob() filelist +fun! s:NetrwGlob(direntry,expr,pare) +" call Dfunc("s:NetrwGlob(direntry<".a:direntry."> expr<".a:expr."> pare=".a:pare.")") if netrw#CheckIfRemote() keepalt 1sp keepalt enew @@ -5393,9 +5340,16 @@ fun! s:NetrwGlob(direntry,expr) let filelist= w:netrw_treedict[a:direntry] endif let w:netrw_liststyle= keep_liststyle + elseif v:version > 704 || (v:version == 704 && has("patch656")) + let filelist= glob(s:ComposePath(fnameescape(a:direntry),a:expr),0,1,1) + if a:pare + let filelist= map(filelist,'substitute(v:val, "^.*/", "", "")') + endif else - let filelist= glob(s:ComposePath(a:direntry,a:expr),0,1,1) - let filelist= map(filelist,'substitute(v:val, "^.*/", "", "")') + let filelist= glob(s:ComposePath(fnameescape(a:direntry),a:expr),0,1) + if a:pare + let filelist= map(filelist,'substitute(v:val, "^.*/", "", "")') + endif endif " call Dret("s:NetrwGlob ".string(filelist)) return filelist @@ -6087,7 +6041,7 @@ fun! s:NetrwMaps(islocal) nnoremap <buffer> <silent> <nowait> qf :<c-u>call <SID>NetrwFileInfo(1,<SID>NetrwGetWord())<cr> nnoremap <buffer> <silent> <nowait> qF :<c-u>call <SID>NetrwMarkFileQFEL(1,getqflist())<cr> nnoremap <buffer> <silent> <nowait> qL :<c-u>call <SID>NetrwMarkFileQFEL(1,getloclist(v:count))<cr> - nnoremap <buffer> <silent> <nowait> r :<c-u>let g:netrw_sort_direction= (g:netrw_sort_direction =~ 'n')? 'r' : 'n'<bar>exe "norm! 0"<bar>call <SID>NetrwRefresh(1,<SID>NetrwBrowseChgDir(1,'./'))<cr> + nnoremap <buffer> <silent> <nowait> r :<c-u>let g:netrw_sort_direction= (g:netrw_sort_direction =~# 'n')? 'r' : 'n'<bar>exe "norm! 0"<bar>call <SID>NetrwRefresh(1,<SID>NetrwBrowseChgDir(1,'./'))<cr> nnoremap <buffer> <silent> <nowait> s :call <SID>NetrwSortStyle(1)<cr> nnoremap <buffer> <silent> <nowait> S :<c-u>call <SID>NetSortSequence(1)<cr> nnoremap <buffer> <silent> <nowait> Tb :<c-u>call <SID>NetrwSetTgt(1,'b',v:count1)<cr> @@ -6140,7 +6094,7 @@ fun! s:NetrwMaps(islocal) " inoremap <buffer> <silent> <nowait> qf <c-o>:<c-u>call <SID>NetrwFileInfo(1,<SID>NetrwGetWord())<cr> " inoremap <buffer> <silent> <nowait> qF :<c-u>call <SID>NetrwMarkFileQFEL(1,getqflist())<cr> " inoremap <buffer> <silent> <nowait> qL :<c-u>call <SID>NetrwMarkFileQFEL(1,getloclist(v:count))<cr> -" inoremap <buffer> <silent> <nowait> r <c-o>:let g:netrw_sort_direction= (g:netrw_sort_direction =~ 'n')? 'r' : 'n'<bar>exe "norm! 0"<bar>call <SID>NetrwRefresh(1,<SID>NetrwBrowseChgDir(1,'./'))<cr> +" inoremap <buffer> <silent> <nowait> r <c-o>:let g:netrw_sort_direction= (g:netrw_sort_direction =~# 'n')? 'r' : 'n'<bar>exe "norm! 0"<bar>call <SID>NetrwRefresh(1,<SID>NetrwBrowseChgDir(1,'./'))<cr> " inoremap <buffer> <silent> <nowait> s <c-o>:call <SID>NetrwSortStyle(1)<cr> " inoremap <buffer> <silent> <nowait> S <c-o>:call <SID>NetSortSequence(1)<cr> " inoremap <buffer> <silent> <nowait> t <c-o>:call <SID>NetrwSplit(4)<cr> @@ -6261,7 +6215,7 @@ fun! s:NetrwMaps(islocal) nnoremap <buffer> <silent> <nowait> qf :<c-u>call <SID>NetrwFileInfo(0,<SID>NetrwGetWord())<cr> nnoremap <buffer> <silent> <nowait> qF :<c-u>call <SID>NetrwMarkFileQFEL(0,getqflist())<cr> nnoremap <buffer> <silent> <nowait> qL :<c-u>call <SID>NetrwMarkFileQFEL(0,getloclist(v:count))<cr> - nnoremap <buffer> <silent> <nowait> r :<c-u>let g:netrw_sort_direction= (g:netrw_sort_direction =~ 'n')? 'r' : 'n'<bar>exe "norm! 0"<bar>call <SID>NetrwBrowse(0,<SID>NetrwBrowseChgDir(0,'./'))<cr> + nnoremap <buffer> <silent> <nowait> r :<c-u>let g:netrw_sort_direction= (g:netrw_sort_direction =~# 'n')? 'r' : 'n'<bar>exe "norm! 0"<bar>call <SID>NetrwBrowse(0,<SID>NetrwBrowseChgDir(0,'./'))<cr> nnoremap <buffer> <silent> <nowait> s :call <SID>NetrwSortStyle(0)<cr> nnoremap <buffer> <silent> <nowait> S :<c-u>call <SID>NetSortSequence(0)<cr> nnoremap <buffer> <silent> <nowait> Tb :<c-u>call <SID>NetrwSetTgt(0,'b',v:count1)<cr> @@ -6311,7 +6265,7 @@ fun! s:NetrwMaps(islocal) " inoremap <buffer> <silent> <nowait> qf <c-o>:<c-u>call <SID>NetrwFileInfo(0,<SID>NetrwGetWord())<cr> " inoremap <buffer> <silent> <nowait> qF :<c-u>call <SID>NetrwMarkFileQFEL(0,getqflist())<cr> " inoremap <buffer> <silent> <nowait> qL :<c-u>call <SID>NetrwMarkFileQFEL(0,getloclist(v:count))<cr> -" inoremap <buffer> <silent> <nowait> r <c-o>:let g:netrw_sort_direction= (g:netrw_sort_direction =~ 'n')? 'r' : 'n'<bar>exe "norm! 0"<bar>call <SID>NetrwBrowse(0,<SID>NetrwBrowseChgDir(0,'./'))<cr> +" inoremap <buffer> <silent> <nowait> r <c-o>:let g:netrw_sort_direction= (g:netrw_sort_direction =~# 'n')? 'r' : 'n'<bar>exe "norm! 0"<bar>call <SID>NetrwBrowse(0,<SID>NetrwBrowseChgDir(0,'./'))<cr> " inoremap <buffer> <silent> <nowait> s <c-o>:call <SID>NetrwSortStyle(0)<cr> " inoremap <buffer> <silent> <nowait> S <c-o>:call <SID>NetSortSequence(0)<cr> " inoremap <buffer> <silent> <nowait> t <c-o>:call <SID>NetrwSplit(1)<cr> @@ -6415,10 +6369,10 @@ fun! s:NetrwMarkFiles(islocal,...) let i = 1 while i <= a:0 if a:islocal - if v:version == 704 && has("patch656") - let mffiles= glob(a:{i},0,1,1) + if v:version > 704 || (v:version == 704 && has("patch656")) + let mffiles= glob(fnameescape(a:{i}),0,1,1) else - let mffiles= glob(a:{i},0,1) + let mffiles= glob(fnameescape(a:{i}),0,1) endif else let mffiles= [a:{i}] @@ -6894,6 +6848,8 @@ fun! s:NetrwMarkFileCopy(islocal,...) " cleanup " ------- " call Decho("cleanup",'~'.expand("<slnum>")) + " remove markings from local buffer + call s:NetrwUnmarkList(curbufnr,curdir) " remove markings from local buffer " call Decho(" g:netrw_fastbrowse =".g:netrw_fastbrowse,'~'.expand("<slnum>")) " call Decho(" s:netrwmftgt =".s:netrwmftgt,'~'.expand("<slnum>")) " call Decho(" s:netrwmftgt_islocal=".s:netrwmftgt_islocal,'~'.expand("<slnum>")) @@ -7373,7 +7329,7 @@ fun! s:NetrwMarkFileGrep(islocal) if exists("nonisi") " original, user-supplied pattern did not begin with a character from isident " call Decho("looking for trailing nonisi<".nonisi."> followed by a j, gj, or jg",'~'.expand("<slnum>")) - if pat =~ nonisi.'j$\|'.nonisi.'gj$\|'.nonisi.'jg$' + if pat =~# nonisi.'j$\|'.nonisi.'gj$\|'.nonisi.'jg$' call s:NetrwMarkFileQFEL(a:islocal,getqflist()) endif endif @@ -7571,7 +7527,7 @@ fun! s:NetrwMarkFileRegexp(islocal) " get the matching list of files using local glob() " call Decho("handle local regexp",'~'.expand("<slnum>")) let dirname = escape(b:netrw_curdir,g:netrw_glob_escape) - if v:version == 704 && has("patch656") + if v:version > 704 || (v:version == 704 && has("patch656")) let files = glob(s:ComposePath(dirname,regexp),0,0,1) else let files = glob(s:ComposePath(dirname,regexp),0,0) @@ -7788,7 +7744,7 @@ fun! s:NetrwMarkFileTgt(islocal) " need to do refresh so that the banner will be updated " s:LocalBrowseRefresh handles all local-browsing buffers when not fast browsing if g:netrw_fastbrowse <= 1 -" call Decho("g:netrw_fastbrowse=".g:netrw_fastbrowse.", so refreshing all local netrw buffers") +" call Decho("g:netrw_fastbrowse=".g:netrw_fastbrowse.", so refreshing all local netrw buffers",'~'.expand("<slnum>")) call s:LocalBrowseRefresh() endif " call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./')) @@ -7821,10 +7777,10 @@ fun! s:NetrwGetCurdir(islocal) " call Decho("set b:netrw_curdir<".b:netrw_curdir."> (used getcwd)",'~'.expand("<slnum>")) endif -" call Decho("b:netrw_curdir<".b:netrw_curdir."> ".((b:netrw_curdir !~ '\<\a\{3,}://')? "does not match" : "matches")." url pattern") +" call Decho("b:netrw_curdir<".b:netrw_curdir."> ".((b:netrw_curdir !~ '\<\a\{3,}://')? "does not match" : "matches")." url pattern",'~'.expand("<slnum>")) if b:netrw_curdir !~ '\<\a\{3,}://' let curdir= b:netrw_curdir -" call Decho("g:netrw_keepdir=".g:netrw_keepdir) +" call Decho("g:netrw_keepdir=".g:netrw_keepdir,'~'.expand("<slnum>")) if g:netrw_keepdir == 0 call s:NetrwLcd(curdir) endif @@ -8125,7 +8081,7 @@ fun! s:NetrwMenu(domenu) elseif !a:domenu let s:netrwcnt = 0 let curwin = winnr() - windo if getline(2) =~ "Netrw" | let s:netrwcnt= s:netrwcnt + 1 | endif + windo if getline(2) =~# "Netrw" | let s:netrwcnt= s:netrwcnt + 1 | endif exe curwin."wincmd w" if s:netrwcnt <= 1 @@ -8708,7 +8664,7 @@ fun! s:NetrwSortStyle(islocal) let svpos= winsaveview() " call Decho("saving posn to svpos<".string(svpos).">",'~'.expand("<slnum>")) - let g:netrw_sort_by= (g:netrw_sort_by =~ '^n')? 'time' : (g:netrw_sort_by =~ '^t')? 'size' : (g:netrw_sort_by =~ '^siz')? 'exten' : 'name' + let g:netrw_sort_by= (g:netrw_sort_by =~# '^n')? 'time' : (g:netrw_sort_by =~# '^t')? 'size' : (g:netrw_sort_by =~# '^siz')? 'exten' : 'name' NetrwKeepj norm! 0 NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./')) " call Decho("restoring posn to svpos<".string(svpos).">",'~'.expand("<slnum>")) @@ -8811,7 +8767,6 @@ fun! s:NetrwSplit(mode) let s:didsplit= 1 NetrwKeepj call s:RestoreWinVars() NetrwKeepj call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,s:NetrwGetWord())) - "call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,s:NetrwGetWord())) unlet s:didsplit else @@ -8890,9 +8845,9 @@ endfun " (full path directory with trailing slash returned) fun! s:NetrwTreeDir(islocal) " call Dfunc("s:NetrwTreeDir(islocal=".a:islocal.") getline(".line(".").")"."<".getline('.')."> b:netrw_curdir<".b:netrw_curdir."> tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> ft=".&ft) -" call Decho("g:netrw_keepdir =".(exists("g:netrw_keepdir")? g:netrw_keepdir : 'n/a')) -" call Decho("w:netrw_liststyle=".(exists("w:netrw_liststyle")? w:netrw_liststyle : 'n/a')) -" call Decho("w:netrw_treetop =".(exists("w:netrw_treetop")? w:netrw_treetop : 'n/a')) +" call Decho("g:netrw_keepdir =".(exists("g:netrw_keepdir")? g:netrw_keepdir : 'n/a'),'~'.expand("<slnum>")) +" call Decho("w:netrw_liststyle=".(exists("w:netrw_liststyle")? w:netrw_liststyle : 'n/a'),'~'.expand("<slnum>")) +" call Decho("w:netrw_treetop =".(exists("w:netrw_treetop")? w:netrw_treetop : 'n/a'),'~'.expand("<slnum>")) if exists("s:treedir") " s:NetrwPrevWinOpen opens a "previous" window -- and thus needs to and does call s:NetrwTreeDir early @@ -8933,7 +8888,7 @@ fun! s:NetrwTreeDir(islocal) " detect user attempting to close treeroot " call Decho("check if user is attempting to close treeroot",'~'.expand("<slnum>")) " call Decho(".win#".winnr()." buf#".bufnr("%")."<".bufname("%").">",'~'.expand("<slnum>")) -" call Decho(".getline(".line(".").")<".getline('.').'> '.((getline('.') =~ '^'.s:treedepthstring)? '=~' : '!~').' ^'.s:treedepthstring,'~'.expand("<slnum>")) +" call Decho(".getline(".line(".").")<".getline('.').'> '.((getline('.') =~# '^'.s:treedepthstring)? '=~#' : '!~').' ^'.s:treedepthstring,'~'.expand("<slnum>")) if curline !~ '^'.s:treedepthstring && getline('.') != '..' " call Decho(".user may have attempted to close treeroot",'~'.expand("<slnum>")) " now force a refresh @@ -9040,24 +8995,24 @@ fun! s:NetrwRefreshTreeDict(dir) if entry =~ '/$' && has_key(w:netrw_treedict,direntry) " call Decho("<".direntry."> is a key in treedict - display subtree for it",'~'.expand("<slnum>")) NetrwKeepj call s:NetrwRefreshTreeDict(direntry) - let liststar = s:NetrwGlob(direntry,'*') - let listdotstar = s:NetrwGlob(direntry,'.*') + let liststar = s:NetrwGlob(direntry,'*',1) + let listdotstar = s:NetrwGlob(direntry,'.*',1) let w:netrw_treedict[direntry] = liststar + listdotstar " call Decho("updating w:netrw_treedict[".direntry.']='.string(w:netrw_treedict[direntry]),'~'.expand("<slnum>")) elseif entry =~ '/$' && has_key(w:netrw_treedict,direntry.'/') " call Decho("<".direntry."/> is a key in treedict - display subtree for it",'~'.expand("<slnum>")) NetrwKeepj call s:NetrwRefreshTreeDict(direntry.'/') - let liststar = s:NetrwGlob(direntry.'/','*') - let listdotstar= s:NetrwGlob(direntry.'/','.*') + let liststar = s:NetrwGlob(direntry.'/','*',1) + let listdotstar= s:NetrwGlob(direntry.'/','.*',1) let w:netrw_treedict[direntry]= liststar + listdotstar " call Decho("updating w:netrw_treedict[".direntry.']='.string(w:netrw_treedict[direntry]),'~'.expand("<slnum>")) elseif entry =~ '@$' && has_key(w:netrw_treedict,direntry.'@') " call Decho("<".direntry."/> is a key in treedict - display subtree for it",'~'.expand("<slnum>")) NetrwKeepj call s:NetrwRefreshTreeDict(direntry.'/') - let liststar = s:NetrwGlob(direntry.'/','*') - let listdotstar= s:NetrwGlob(direntry.'/','.*') + let liststar = s:NetrwGlob(direntry.'/','*',1) + let listdotstar= s:NetrwGlob(direntry.'/','.*',1) " call Decho("updating w:netrw_treedict[".direntry.']='.string(w:netrw_treedict[direntry]),'~'.expand("<slnum>")) else @@ -9315,14 +9270,14 @@ fun! s:PerformListing(islocal) " call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol()." line($)=".line("$"),'~'.expand("<slnum>")) let sortby= g:netrw_sort_by - if g:netrw_sort_direction =~ "^r" + if g:netrw_sort_direction =~# "^r" let sortby= sortby." reversed" endif " Sorted by... {{{3 if g:netrw_banner " call Decho("--handle specified sorting: g:netrw_sort_by<".g:netrw_sort_by.">",'~'.expand("<slnum>")) - if g:netrw_sort_by =~ "^n" + if g:netrw_sort_by =~# "^n" " call Decho("directories will be sorted by name",'~'.expand("<slnum>")) " sorted by name NetrwKeepj put ='\" Sorted by '.sortby @@ -9419,13 +9374,13 @@ fun! s:PerformListing(islocal) if !g:netrw_banner || line("$") >= w:netrw_bannercnt " call Decho("manipulate directory listing (sort) : g:netrw_sort_by<".g:netrw_sort_by.">",'~'.expand("<slnum>")) - if g:netrw_sort_by =~ "^n" + if g:netrw_sort_by =~# "^n" " sort by name NetrwKeepj call s:NetrwSetSort() if !g:netrw_banner || w:netrw_bannercnt < line("$") " call Decho("g:netrw_sort_direction=".g:netrw_sort_direction." (bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>")) - if g:netrw_sort_direction =~ 'n' + if g:netrw_sort_direction =~# 'n' " normal direction sorting exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$sort'.' '.g:netrw_sort_options else @@ -9438,7 +9393,7 @@ fun! s:PerformListing(islocal) exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\d\{3}'.g:netrw_sepchr.'//e' NetrwKeepj call histdel("/",-1) - elseif g:netrw_sort_by =~ "^ext" + elseif g:netrw_sort_by =~# "^ext" " sort by extension exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g+/+s/^/001'.g:netrw_sepchr.'/' NetrwKeepj call histdel("/",-1) @@ -9448,7 +9403,7 @@ fun! s:PerformListing(islocal) NetrwKeepj call histdel("/",-1) if !g:netrw_banner || w:netrw_bannercnt < line("$") " call Decho("g:netrw_sort_direction=".g:netrw_sort_direction." (bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>")) - if g:netrw_sort_direction =~ 'n' + if g:netrw_sort_direction =~# 'n' " normal direction sorting exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$sort'.' '.g:netrw_sort_options else @@ -9462,7 +9417,7 @@ fun! s:PerformListing(islocal) elseif a:islocal if !g:netrw_banner || w:netrw_bannercnt < line("$") " call Decho("g:netrw_sort_direction=".g:netrw_sort_direction,'~'.expand("<slnum>")) - if g:netrw_sort_direction =~ 'n' + if g:netrw_sort_direction =~# 'n' " call Decho('exe sil NetrwKeepj '.w:netrw_bannercnt.',$sort','~'.expand("<slnum>")) exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$sort'.' '.g:netrw_sort_options else @@ -9474,7 +9429,7 @@ fun! s:PerformListing(islocal) endif endif - elseif g:netrw_sort_direction =~ 'r' + elseif g:netrw_sort_direction =~# 'r' " call Decho('(s:PerformListing) reverse the sorted listing','~'.expand("<slnum>")) if !g:netrw_banner || w:netrw_bannercnt < line('$') exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$g/^/m '.w:netrw_bannercnt @@ -9790,9 +9745,9 @@ fun! s:NetrwRemoteListing() " call Decho("use ftp to get remote file listing",'~'.expand("<slnum>")) let s:method = "ftp" let listcmd = g:netrw_ftp_list_cmd - if g:netrw_sort_by =~ '^t' + if g:netrw_sort_by =~# '^t' let listcmd= g:netrw_ftp_timelist_cmd - elseif g:netrw_sort_by =~ '^s' + elseif g:netrw_sort_by =~# '^s' let listcmd= g:netrw_ftp_sizelist_cmd endif " call Decho("listcmd<".listcmd."> (using g:netrw_ftp_list_cmd)",'~'.expand("<slnum>")) @@ -9899,7 +9854,7 @@ fun! s:NetrwRemoteListing() if s:method == "ftp" " cleanup exe "sil! NetrwKeepj ".w:netrw_bannercnt - while getline('.') =~ g:netrw_ftp_browse_reject + while getline('.') =~# g:netrw_ftp_browse_reject sil! NetrwKeepj d endwhile " if there's no ../ listed, then put ../ in @@ -9952,9 +9907,9 @@ fun! s:NetrwRemoteRm(usrhost,path) range " call Decho("remove all marked files with bufnr#".bufnr("%"),'~'.expand("<slnum>")) for fname in s:netrwmarkfilelist_{bufnr("%")} let ok= s:NetrwRemoteRmFile(a:path,fname,all) - if ok =~ 'q\%[uit]' + if ok =~# 'q\%[uit]' break - elseif ok =~ 'a\%[ll]' + elseif ok =~# 'a\%[ll]' let all= 1 endif endfor @@ -9973,9 +9928,9 @@ fun! s:NetrwRemoteRm(usrhost,path) range while ctr <= a:lastline exe "NetrwKeepj ".ctr let ok= s:NetrwRemoteRmFile(a:path,s:NetrwGetWord(),all) - if ok =~ 'q\%[uit]' + if ok =~# 'q\%[uit]' break - elseif ok =~ 'a\%[ll]' + elseif ok =~# 'a\%[ll]' let all= 1 endif let ctr= ctr + 1 @@ -10014,12 +9969,12 @@ fun! s:NetrwRemoteRmFile(path,rmfile,all) let ok="no" endif let ok= substitute(ok,'\[{y(es)},n(o),a(ll),q(uit)]\s*','','e') - if ok =~ 'a\%[ll]' + if ok =~# 'a\%[ll]' let all= 1 endif endif - if all || ok =~ 'y\%[es]' || ok == "" + if all || ok =~# 'y\%[es]' || ok == "" " call Decho("case all=".all." or ok<".ok.">".(exists("w:netrw_method")? ': netrw_method='.w:netrw_method : ""),'~'.expand("<slnum>")) if exists("w:netrw_method") && (w:netrw_method == 2 || w:netrw_method == 3) " call Decho("case ftp:",'~'.expand("<slnum>")) @@ -10054,13 +10009,13 @@ fun! s:NetrwRemoteRmFile(path,rmfile,all) else call netrw#ErrorMsg(s:WARNING,"cmd<".netrw_rm_cmd."> failed",60) endif - else if ret != 0 + elseif ret != 0 call netrw#ErrorMsg(s:WARNING,"cmd<".netrw_rm_cmd."> failed",60) endif " call Decho("returned=".ret." errcode=".v:shell_error,'~'.expand("<slnum>")) endif endif - elseif ok =~ 'q\%[uit]' + elseif ok =~# 'q\%[uit]' " call Decho("ok==".ok,'~'.expand("<slnum>")) endif @@ -10075,12 +10030,12 @@ fun! s:NetrwRemoteRmFile(path,rmfile,all) let ok="no" endif let ok= substitute(ok,'\[{y(es)},n(o),a(ll),q(uit)]\s*','','e') - if ok =~ 'a\%[ll]' + if ok =~# 'a\%[ll]' let all= 1 endif endif - if all || ok =~ 'y\%[es]' || ok == "" + if all || ok =~# 'y\%[es]' || ok == "" if exists("w:netrw_method") && (w:netrw_method == 2 || w:netrw_method == 3) NetrwKeepj call s:NetrwRemoteFtpCmd(a:path,"rmdir ".a:rmfile) else @@ -10103,7 +10058,7 @@ fun! s:NetrwRemoteRmFile(path,rmfile,all) endif endif - elseif ok =~ 'q\%[uit]' + elseif ok =~# 'q\%[uit]' " call Decho("ok==".ok,'~'.expand("<slnum>")) endif endif @@ -10257,7 +10212,7 @@ fun! netrw#LocalBrowseCheck(dirname) " would hit when re-entering netrw windows, creating unexpected " refreshes (and would do so in the middle of NetrwSaveOptions(), too) " call Dfunc("netrw#LocalBrowseCheck(dirname<".a:dirname.">") -" call Decho("isdir<".a:dirname.">=".isdirectory(s:NetrwFile(a:dirname)).((exists("s:treeforceredraw")? " treeforceredraw" : "")).expand("<slnum>")) +" call Decho("isdir<".a:dirname."> =".isdirectory(s:NetrwFile(a:dirname)).((exists("s:treeforceredraw")? " treeforceredraw" : "")).'~'.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,'~'.expand("<slnum>")) " call Dredir("ls!","ls!") " call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) @@ -10281,7 +10236,6 @@ fun! netrw#LocalBrowseCheck(dirname) unlet s:treeforceredraw sil! NetrwKeepj keepalt call s:NetrwBrowse(1,a:dirname) endif - " call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) " call Dret("netrw#LocalBrowseCheck") return @@ -10361,7 +10315,7 @@ fun! s:LocalBrowseRefresh() " refresh any netrw buffer " call Decho("refresh buf#".ibuf.'-> win#'.bufwinnr(ibuf),'~'.expand("<slnum>")) exe bufwinnr(ibuf)."wincmd w" - if getline(".") =~ 'Quick Help' + if getline(".") =~# 'Quick Help' " decrement g:netrw_quickhelp to prevent refresh from changing g:netrw_quickhelp " (counteracts s:NetrwBrowseChgDir()'s incrementing) let g:netrw_quickhelp= g:netrw_quickhelp - 1 @@ -10467,15 +10421,8 @@ fun! s:LocalListing() " get the list of files contained in the current directory let dirname = b:netrw_curdir let dirnamelen = strlen(b:netrw_curdir) - if v:version == 704 && has("patch656") -" call Decho("using glob with patch656",'~'.expand("<slnum>")) - let filelist = glob(s:ComposePath(dirname,"*"),0,1,1) - let filelist = filelist + glob(s:ComposePath(dirname,".*"),0,1,1) - else -" call Decho("using glob without patch656",'~'.expand("<slnum>")) - let filelist = glob(s:ComposePath(dirname,"*"),0,1) - let filelist = filelist + glob(s:ComposePath(dirname,".*"),0,1) - endif + let filelist = s:NetrwGlob(dirname,"*",0) + let filelist = filelist + s:NetrwGlob(dirname,".*",0) " call Decho("filelist=".string(filelist),'~'.expand("<slnum>")) if g:netrw_cygwin == 0 && (has("win32") || has("win95") || has("win64") || has("win16")) @@ -10487,9 +10434,9 @@ fun! s:LocalListing() " call Decho("filelist=".string(filelist),'~'.expand("<slnum>")) endif -" call Decho("before while: dirname<".dirname.">",'~'.expand("<slnum>")) +" call Decho("before while: dirname <".dirname.">",'~'.expand("<slnum>")) " call Decho("before while: dirnamelen<".dirnamelen.">",'~'.expand("<slnum>")) -" call Decho("before while: filelist=".string(filelist),'~'.expand("<slnum>")) +" call Decho("before while: filelist =".string(filelist),'~'.expand("<slnum>")) if get(g:, 'netrw_dynamic_maxfilenamelen', 0) let filelistcopy = map(deepcopy(filelist),'fnamemodify(v:val, ":t")') @@ -10559,15 +10506,15 @@ fun! s:LocalListing() if w:netrw_liststyle == s:LONGLIST let sz = getfsize(filename) - if g:netrw_sizestyle =~ "[hH]" + if g:netrw_sizestyle =~# "[hH]" let sz= s:NetrwHumanReadable(sz) endif let fsz = strpart(" ",1,15-strlen(sz)).sz let pfile= pfile."\t".fsz." ".strftime(g:netrw_timefmt,getftime(filename)) -" call Decho("sz=".sz." fsz=".fsz,'~'.expand("<slnum>")) +" call Decho("longlist support: sz=".sz." fsz=".fsz,'~'.expand("<slnum>")) endif - if g:netrw_sort_by =~ "^t" + if g:netrw_sort_by =~# "^t" " sort by time (handles time up to 1 quintillion seconds, US) " call Decho("getftime(".filename.")=".getftime(filename),'~'.expand("<slnum>")) let t = getftime(filename) @@ -10580,7 +10527,7 @@ fun! s:LocalListing() " sort by size (handles file sizes up to 1 quintillion bytes, US) " call Decho("getfsize(".filename.")=".getfsize(filename),'~'.expand("<slnum>")) let sz = getfsize(filename) - if g:netrw_sizestyle =~ "[hH]" + if g:netrw_sizestyle =~# "[hH]" let sz= s:NetrwHumanReadable(sz) endif let fsz = strpart("000000000000000000",1,18-strlen(sz)).sz @@ -10733,9 +10680,9 @@ fun! s:NetrwLocalRm(path) range " call Decho("remove all marked files",'~'.expand("<slnum>")) for fname in s:netrwmarkfilelist_{bufnr("%")} let ok= s:NetrwLocalRmFile(a:path,fname,all) - if ok =~ 'q\%[uit]' || ok == "no" + if ok =~# 'q\%[uit]' || ok == "no" break - elseif ok =~ 'a\%[ll]' + elseif ok =~# 'a\%[ll]' let all= 1 endif endfor @@ -10762,9 +10709,9 @@ fun! s:NetrwLocalRm(path) range continue endif let ok= s:NetrwLocalRmFile(a:path,curword,all) - if ok =~ 'q\%[uit]' || ok == "no" + if ok =~# 'q\%[uit]' || ok == "no" break - elseif ok =~ 'a\%[ll]' + elseif ok =~# 'a\%[ll]' let all= 1 endif let ctr= ctr + 1 @@ -10811,12 +10758,12 @@ fun! s:NetrwLocalRmFile(path,fname,all) " call Decho("response: ok<".ok.">",'~'.expand("<slnum>")) let ok= substitute(ok,'\[{y(es)},n(o),a(ll),q(uit)]\s*','','e') " call Decho("response: ok<".ok."> (after sub)",'~'.expand("<slnum>")) - if ok =~ 'a\%[ll]' + if ok =~# 'a\%[ll]' let all= 1 endif endif - if all || ok =~ 'y\%[es]' || ok == "" + if all || ok =~# 'y\%[es]' || ok == "" let ret= s:NetrwDelete(rmfile) " call Decho("errcode=".v:shell_error." ret=".ret,'~'.expand("<slnum>")) endif @@ -10832,13 +10779,13 @@ fun! s:NetrwLocalRmFile(path,fname,all) if ok == "" let ok="no" endif - if ok =~ 'a\%[ll]' + if ok =~# 'a\%[ll]' let all= 1 endif endif let rmfile= substitute(rmfile,'[\/]$','','e') - if all || ok =~ 'y\%[es]' || ok == "" + if all || ok =~# 'y\%[es]' || ok == "" if v:version < 704 || !has("patch1109") " " call Decho("1st attempt: system(netrw#WinPath(".g:netrw_localrmdir.') '.s:ShellEscape(rmfile).')','~'.expand("<slnum>")) call system(netrw#WinPath(g:netrw_localrmdir).' '.s:ShellEscape(rmfile)) @@ -10879,6 +10826,17 @@ endfun " Support Functions: {{{1 " --------------------------------------------------------------------- +" s:WinNames: COMBAK {{{2 +fun! s:WinNames(id) + let curwin= winnr() + 1wincmd w +" call Decho("--- Windows By Name --- #".a:id) +" windo call Decho("win#".winnr()."<".expand("%").">") +" call Decho("--- --- --- --- --- ---") + exe curwin."wincmd w" +endfun + +" --------------------------------------------------------------------- " netrw#Access: intended to provide access to variable values for netrw's test suite {{{2 " 0: marked file list of current buffer " 1: marked file target @@ -10905,8 +10863,6 @@ fun! netrw#Call(funcname,...) " call Dret("netrw#Call") endfun -" ------------------------------------------------------------------------ - " --------------------------------------------------------------------- " netrw#Expose: allows UserMaps and pchk to look at otherwise script-local variables {{{2 " I expect this function to be used in @@ -10914,18 +10870,22 @@ endfun " for example. fun! netrw#Expose(varname) " call Dfunc("netrw#Expose(varname<".a:varname.">)") - exe "let retval= s:".a:varname - if exists("g:netrw_pchk") - if type(retval) == 3 - let retval = copy(retval) - let i = 0 - while i < len(retval) - let retval[i]= substitute(retval[i],expand("$HOME"),'~','') - let i = i + 1 - endwhile + if exists("s:".a:varname) + exe "let retval= s:".a:varname + if exists("g:netrw_pchk") + if type(retval) == 3 + let retval = copy(retval) + let i = 0 + while i < len(retval) + let retval[i]= substitute(retval[i],expand("$HOME"),'~','') + let i = i + 1 + endwhile + endif +" call Dret("netrw#Expose ".string(retval)) + return string(retval) endif -" call Dret("netrw#Expose ".string(retval)) - return string(retval) + else + let retval= "n/a" endif " call Dret("netrw#Expose ".string(retval)) @@ -11018,9 +10978,9 @@ fun! s:ComposePath(base,subdir) " call Decho("amiga",'~'.expand("<slnum>")) let ec = a:base[s:Strlen(a:base)-1] if ec != '/' && ec != ':' - let ret = a:base . "/" . a:subdir + let ret = a:base."/" . a:subdir else - let ret = a:base . a:subdir + let ret = a:base.a:subdir endif elseif a:subdir =~ '^\a:[/\\][^/\\]' && (has("win32") || has("win95") || has("win64") || has("win16")) @@ -11248,10 +11208,10 @@ fun! s:NetrwBMShow() redir END let bmshowlist = split(bmshowraw,'\n') if bmshowlist != [] - let bmshowfuncs= filter(bmshowlist,'v:val =~ "<SNR>\\d\\+_BMShow()"') + let bmshowfuncs= filter(bmshowlist,'v:val =~# "<SNR>\\d\\+_BMShow()"') if bmshowfuncs != [] let bmshowfunc = substitute(bmshowfuncs[0],'^.*:\(call.*BMShow()\).*$','\1','') - if bmshowfunc =~ '^call.*BMShow()' + if bmshowfunc =~# '^call.*BMShow()' exe "sil! NetrwKeepj ".bmshowfunc endif endif @@ -11395,11 +11355,12 @@ fun! s:NetrwEnew(...) " call Decho("generate a buffer with NetrwKeepj keepalt enew!",'~'.expand("<slnum>")) " when tree listing uses file TreeListing... a new buffer is made. " Want the old buffer to be unlisted. - setl nobl + " COMBAK: this causes a problem, see P43 +" setl nobl let netrw_keepdiff= &l:diff noswapfile NetrwKeepj keepalt enew! let &l:diff= netrw_keepdiff -" call Decho("bufnr($)=".bufnr("$")." winnr($)=".winnr("$"),'~'.expand("<slnum>")) +" call Decho("bufnr($)=".bufnr("$")."<".bufname(bufnr("$"))."> winnr($)=".winnr("$"),'~'.expand("<slnum>")) NetrwKeepj call s:NetrwOptionSave("w:") " copy function-local-variables to buffer variable equivalents @@ -11458,8 +11419,8 @@ endfun " --------------------------------------------------------------------- " s:NetrwInsureWinVars: insure that a netrw buffer has its w: variables in spite of a wincmd v or s {{{2 fun! s:NetrwInsureWinVars() -" call Dfunc("s:NetrwInsureWinVars() win#".winnr()) if !exists("w:netrw_liststyle") +" call Dfunc("s:NetrwInsureWinVars() win#".winnr()) let curbuf = bufnr("%") let curwin = winnr() let iwin = 1 @@ -11479,8 +11440,8 @@ fun! s:NetrwInsureWinVars() let w:{k}= winvars[k] endfor endif +" call Dret("s:NetrwInsureWinVars win#".winnr()) endif -" call Dret("s:NetrwInsureWinVars win#".winnr()) endfun " --------------------------------------------------------------------- @@ -11685,7 +11646,7 @@ endfun " s:SetRexDir() sets up <2-leftmouse> maps (if g:netrw_retmap " is true) and a command, :Rexplore, which call this function. " -" s:nbcd_curpos_{bufnr('%')} is set up by s:NetrwBrowseChgDir() +" s:netrw_nbcd is set up by s:NetrwBrowseChgDir() " " s:rexposn_BUFNR used to save/restore cursor position fun! s:NetrwRexplore(islocal,dirname) @@ -11766,6 +11727,29 @@ fun! s:SaveBufVars() endfun " --------------------------------------------------------------------- +" s:SavePosn: saves position associated with current buffer into a dictionary {{{2 +fun! s:SavePosn(posndict) +" call Dfunc("s:SavePosn(posndict) curbuf#".bufnr("%")."<".bufname("%").">") + + let a:posndict[bufnr("%")]= winsaveview() +" call Decho("saving posn: posndict[".bufnr("%")."]=".string(winsaveview()),'~'.expand("<slnum>")) + +" call Dret("s:SavePosn posndict") + return a:posndict +endfun + +" --------------------------------------------------------------------- +" s:RestorePosn: restores position associated with current buffer using dictionary {{{2 +fun! s:RestorePosn(posndict) +" call Dfunc("s:RestorePosn(posndict) curbuf#".bufnr("%")."<".bufname("%").">") + if has_key(a:posndict,bufnr("%")) + call winrestview(a:posndict[bufnr("%")]) +" call Decho("restoring posn: posndict[".bufnr("%")."]=".string(a:posndict[bufnr("%")]),'~'.expand("<slnum>")) + endif +" call Dret("s:RestorePosn") +endfun + +" --------------------------------------------------------------------- " s:SaveWinVars: (used by Explore() and NetrwSplit()) {{{2 fun! s:SaveWinVars() " call Dfunc("s:SaveWinVars() win#".winnr()) @@ -11819,10 +11803,10 @@ fun! s:SetRexDir(islocal,dirname) let w:netrw_rexdir = a:dirname let w:netrw_rexlocal = a:islocal let s:rexposn_{bufnr("%")} = winsaveview() -" call Decho("setting w:netrw_rexdir =".w:netrw_rexdir) -" call Decho("setting w:netrw_rexlocal=".w:netrw_rexlocal) +" call Decho("setting w:netrw_rexdir =".w:netrw_rexdir,'~'.expand("<slnum>")) +" call Decho("setting w:netrw_rexlocal=".w:netrw_rexlocal,'~'.expand("<slnum>")) " call Decho("saving posn to s:rexposn_".bufnr("%")."<".string(s:rexposn_{bufnr("%")}).">",'~'.expand("<slnum>")) -" call Decho("setting s:rexposn_".bufnr("%")."<".bufname("%")."> to SavePosn",'~'.expand("<slnum>")) +" call Decho("setting s:rexposn_".bufnr("%")."<".bufname("%")."> to ".string(winsaveview()),'~'.expand("<slnum>")) " call Dret("s:SetRexDir : win#".winnr()." ".(a:islocal? "local" : "remote")." dir: ".a:dirname) endfun @@ -11956,7 +11940,7 @@ fun! s:TreeListMove(dir) " call Decho("regfile srch back: ".nl,'~'.expand("<slnum>")) elseif a:dir == '[]' && nxtline != '' NetrwKeepj norm! 0 -" call Decho('srchpat<'.'^\%('.curindent.'\)\@!'.'>') +" call Decho('srchpat<'.'^\%('.curindent.'\)\@!'.'>','~'.expand("<slnum>")) let nl = search('^\%('.curindent.'\)\@!','We') " search forwards if nl != 0 NetrwKeepj norm! k diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim index b8baaa8c64..36232b99db 100644 --- a/runtime/autoload/provider/clipboard.vim +++ b/runtime/autoload/provider/clipboard.vim @@ -47,7 +47,7 @@ function! provider#clipboard#Error() abort endfunction function! provider#clipboard#Executable() abort - if executable('pbcopy') + if has('mac') && executable('pbcopy') let s:copy['+'] = 'pbcopy' let s:paste['+'] = 'pbpaste' let s:copy['*'] = s:copy['+'] diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index b6984c21eb..e99b7ab8b4 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -1,4 +1,4 @@ -*autocmd.txt* For Vim version 7.4. Last change: 2016 Mar 26 +*autocmd.txt* For Vim version 7.4. Last change: 2016 Jun 09 VIM REFERENCE MANUAL by Bram Moolenaar @@ -545,6 +545,9 @@ CursorHold When the user doesn't press a key for the time *CursorHoldI* CursorHoldI Just like CursorHold, but in Insert mode. + Not triggered when waiting for another key, + e.g. after CTRL-V, and not when in CTRL-X mode + |insert_expand|. *CursorMoved* CursorMoved After the cursor was moved in Normal or Visual @@ -1232,6 +1235,8 @@ option will not cause any commands to be executed. argument is present. You probably want to use <nomodeline> for events that are not used when loading a buffer, such as |User|. + Processing modelines is also skipped when no + matching autocommands were executed. *:doautoa* *:doautoall* :doautoa[ll] [<nomodeline>] [group] {event} [fname] diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index 2ccb9188a9..31a46f53bb 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -1,4 +1,4 @@ -*change.txt* For Vim version 7.4. Last change: 2016 Mar 08 +*change.txt* For Vim version 7.4. Last change: 2016 Apr 12 VIM REFERENCE MANUAL by Bram Moolenaar @@ -813,7 +813,7 @@ Examples: > :s/abcde/abc^Mde/ modifies "abcde" to "abc", "de" (two lines) :s/$/\^M/ modifies "abcde" to "abcde^M" :s/\w\+/\u\0/g modifies "bla bla" to "Bla Bla" - :s/\w\+/\L\u/g modifies "BLA bla" to "Bla Bla" + :s/\w\+/\L\u\0/g modifies "BLA bla" to "Bla Bla" Note: "\L\u" can be used to capitalize the first letter of a word. This is not compatible with Vi and older versions of Vim, where the "\u" would cancel @@ -1164,7 +1164,7 @@ which does not specify a register. Additionally you can access it with the name '"'. This means you have to type two double quotes. Writing to the "" register writes to register "0. -2. Numbered registers "0 to "9 *quote_number* *quote0* *quote1* +2. Numbered registers "0 to "9 *quote_number* *quote0* *quote1* *quote2* *quote3* *quote4* *quote9* Vim fills these registers with text from yank and delete commands. Numbered register 0 contains the text from the most recent yank command, diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt index 06dd21de06..5bfffac1f1 100644 --- a/runtime/doc/editing.txt +++ b/runtime/doc/editing.txt @@ -1152,7 +1152,7 @@ Examples: > If you want to always use ":confirm", set the 'confirm' option. - *:browse* *:bro* *E338* *E614* *E615* *E616* *E578* + *:browse* *:bro* *E338* *E614* *E615* *E616* :bro[wse] {command} Open a file selection dialog for an argument to {command}. At present this works for |:e|, |:w|, |:wall|, |:wq|, |:wqall|, |:x|, |:xall|, |:exit|, diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 0a5a51a0e1..69c8d0285a 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -783,7 +783,7 @@ A |Dictionary| can only be compared with a |Dictionary| and only "equal", "not equal" and "is" can be used. This compares the key/values of the |Dictionary| recursively. Ignoring case means case is ignored when comparing item values. - *E693* *E694* + *E694* A |Funcref| can only be compared with a |Funcref| and only "equal" and "not equal" can be used. Case is never ignored. @@ -1470,7 +1470,7 @@ v:exception The value of the exception most recently caught and not *v:false* *false-variable* v:false Special value used to put "false" in JSON and msgpack. See - |json_encode()|. This value is converted to "false" when used + |json_encode()|. This value is converted to "v:false" when used as a String (e.g. in |expr5| with string concatenation operator) and to zero when used as a Number (e.g. in |expr5| or |expr7| when used with numeric operators). @@ -1620,7 +1620,7 @@ v:msgpack_types Dictionary containing msgpack types used by |msgpackparse()| *v:null* *null-variable* v:null Special value used to put "null" in JSON and NIL in msgpack. - See |json_encode()|. This value is converted to "null" when + See |json_encode()|. This value is converted to "v:null" when used as a String (e.g. in |expr5| with string concatenation operator) and to zero when used as a Number (e.g. in |expr5| or |expr7| when used with numeric operators). @@ -1807,7 +1807,7 @@ v:throwpoint The point where the exception most recently caught and not *v:true* *true-variable* v:true Special value used to put "true" in JSON and msgpack. See - |json_encode()|. This value is converted to "true" when used + |json_encode()|. This value is converted to "v:true" when used as a String (e.g. in |expr5| with string concatenation operator) and to one when used as a Number (e.g. in |expr5| or |expr7| when used with numeric operators). @@ -2174,14 +2174,17 @@ sqrt({expr}) Float square root of {expr} str2float({expr}) Float convert String to Float str2nr({expr} [, {base}]) Number convert String to Number strchars({expr} [, {skipcc}]) Number character length of the String {expr} +strcharpart({str}, {start}[, {len}]) + String {len} characters of {str} at {start} strdisplaywidth({expr} [, {col}]) Number display length of the String {expr} strftime({format}[, {time}]) String time in specified format +strgetchar({str}, {index}) Number get char {index} from {str} stridx({haystack}, {needle}[, {start}]) Number index of {needle} in {haystack} string({expr}) String String representation of {expr} value strlen({expr}) Number length of the String {expr} -strpart({src}, {start}[, {len}]) - String {len} characters of {src} at {start} +strpart({str}, {start}[, {len}]) + String {len} characters of {str} at {start} strridx({haystack}, {needle} [, {start}]) Number last index of {needle} in {haystack} strtrans({expr}) String translate string to make it printable @@ -2591,7 +2594,9 @@ byteidx({expr}, {nr}) *byteidx()* same: > let s = strpart(str, byteidx(str, 3)) echo strpart(s, 0, byteidx(s, 1)) -< If there are less than {nr} characters -1 is returned. +< Also see |strgetchar()| and |strcharpart()|. + + If there are less than {nr} characters -1 is returned. If there are exactly {nr} characters the length of the string in bytes is returned. @@ -3065,7 +3070,7 @@ executable({expr}) *executable()* 0 does not exist -1 not implemented on this system -execute({command}) *execute()* +execute({command} [, {silent}]) *execute()* Execute {command} and capture its output. If {command} is a |String|, returns {command} output. If {command} is a |List|, returns concatenated outputs. @@ -3074,10 +3079,17 @@ execute({command}) *execute()* < foo > echo execute(['echon "foo"', 'echon "bar"']) < foobar + + The optional {silent} argument can have these values: + "" no `:silent` used + "silent" `:silent` used + "silent!" `:silent!` used + The default is 'silent'. Note that with "silent!", unlike + `:redir`, error messages are dropped. + This function is not available in the |sandbox|. - Note: {command} executes as if prepended with |:silent| - (output is collected but not displayed). If nested, an outer - execute() will not observe output of the inner calls. + Note: If nested, an outer execute() will not observe output of + the inner calls. Note: Text attributes (highlights) are not captured. exepath({expr}) *exepath()* @@ -3330,6 +3342,10 @@ feedkeys({string} [, {mode}]) *feedkeys()* will behave as if <Esc> is typed, to avoid getting stuck, waiting for a character to be typed before the script continues. + '!' When used with 'x' will not end Insert mode. Can be + used in a test when a timer is set to exit Insert mode + a little later. Useful for testing CursorHoldI. + Return value is always 0. filereadable({file}) *filereadable()* @@ -3525,7 +3541,7 @@ foreground() Move the Vim window to the foreground. Useful when sent from {only in the Win32 GUI and console version} - *function()* *E700* *E922* *E929* + *function()* *E700* *E922* *E923* function({name} [, {arglist}] [, {dict}]) Return a |Funcref| variable that refers to function {name}. {name} can be a user defined function or an internal function. @@ -4088,16 +4104,21 @@ getreg([{regname} [, 1 [, {list}]]]) *getreg()* The result is a String, which is the contents of register {regname}. Example: > :let cliptext = getreg('*') -< getreg('=') returns the last evaluated value of the expression +< When {regname} was not set the result is a empty string. + + getreg('=') returns the last evaluated value of the expression register. (For use in maps.) getreg('=', 1) returns the expression itself, so that it can be restored with |setreg()|. For other registers the extra argument is ignored, thus you can always give it. - If {list} is present and non-zero result type is changed to - |List|. Each list item is one text line. Use it if you care + + If {list} is present and non-zero, the result type is changed + to |List|. Each list item is one text line. Use it if you care about zero bytes possibly present inside register: without third argument both NLs and zero bytes are represented as NLs (see |NL-used-for-Nul|). + When the register was not set an empty list is returned. + If {regname} is not specified, |v:register| is used. @@ -4645,11 +4666,16 @@ jobstart({cmd}[, {opts}]) {Nvim} *jobstart()* Spawns {cmd} as a job. If {cmd} is a |List| it is run directly. If {cmd} is a |String| it is processed like this: > :call jobstart(split(&shell) + split(&shellcmdflag) + ['{cmd}']) -< NOTE: read |shell-unquoting| before constructing any lists - with 'shell' or 'shellcmdflag' options. The above call is - only written to show the idea, one needs to perform unquoting - and do split taking quotes into account. - +< NOTE: This only shows the idea; see |shell-unquoting| before + constructing lists with 'shell' or 'shellcmdflag'. + + NOTE: On Windows if {cmd} is a List, cmd[0] must be a valid + executable (.exe, .com). If the executable is in $PATH it can + be called by name, with or without an extension: > + :call jobstart(['ping', 'neovim.io']) +< If it is a path (not a name), it must include the extension: > + :call jobstart(['System32\ping.exe', 'neovim.io']) +< {opts} is a dictionary with these keys: on_stdout: stdout event handler (function name or |Funcref|) on_stderr: stderr event handler (function name or |Funcref|) @@ -5635,7 +5661,6 @@ pumvisible() *pumvisible()* This can be used to avoid some things that would remove the popup menu. - *E860* py3eval({expr}) *py3eval()* Evaluate Python expression {expr} and return its result converted to Vim data structures. @@ -6761,7 +6786,6 @@ strchars({expr} [, {skipcc}]) *strchars()* counted separately. When {skipcc} set to 1, Composing characters are ignored. Also see |strlen()|, |strdisplaywidth()| and |strwidth()|. - {skipcc} is only available after 7.4.755. For backward compatibility, you can define a wrapper function: > @@ -6779,6 +6803,13 @@ strchars({expr} [, {skipcc}]) *strchars()* endfunction endif < +strcharpart({src}, {start}[, {len}]) *strcharpart()* + Like |strpart()| but using character index and length instead + of byte index and length. + When a character index is used where a character does not + exist it is assumed to be one byte. For example: > + strcharpart('abc', -1, 2) +< results in 'a'. strdisplaywidth({expr}[, {col}]) *strdisplaywidth()* The result is a Number, which is the number of display cells @@ -6812,6 +6843,12 @@ strftime({format} [, {time}]) *strftime()* < Not available on all systems. To check use: > :if exists("*strftime") +strgetchar({str}, {index}) *strgetchar()* + Get character {index} from {str}. This uses a character + index, not a byte index. Composing characters are considered + separate characters here. + Also see |strcharpart()| and |strchars()|. + stridx({haystack}, {needle} [, {start}]) *stridx()* The result is a Number, which gives the byte index in {haystack} of the first occurrence of the String {needle}. @@ -6866,14 +6903,17 @@ strlen({expr}) The result is a Number, which is the length of the String strpart({src}, {start}[, {len}]) *strpart()* The result is a String, which is part of {src}, starting from byte {start}, with the byte length {len}. - When non-existing bytes are included, this doesn't result in - an error, the bytes are simply omitted. + To count characters instead of bytes use |strcharpart()|. + + When bytes are selected which do not exist, this doesn't + result in an error, the bytes are simply omitted. If {len} is missing, the copy continues from {start} till the end of the {src}. > strpart("abcdefg", 3, 2) == "de" strpart("abcdefg", -2, 4) == "ab" strpart("abcdefg", 5, 4) == "fg" strpart("abcdefg", 3) == "defg" + < Note: To get the first character, {start} must be 0. For example, to get three bytes under and after the cursor: > strpart(getline("."), col(".") - 1, 3) @@ -6979,9 +7019,9 @@ synID({lnum}, {col}, {trans}) *synID()* that's where the cursor can be in Insert mode, synID() returns zero. - When {trans} is non-zero, transparent items are reduced to the + When {trans} is |TRUE|, transparent items are reduced to the item that they reveal. This is useful when wanting to know - the effective color. When {trans} is zero, the transparent + the effective color. When {trans} is |FALSE|, the transparent item is returned. This is useful when wanting to know which syntax item is effective (e.g. inside parens). Warning: This function can be very slow. Best speed is @@ -7064,9 +7104,8 @@ synstack({lnum}, {col}) *synstack()* valid positions. system({cmd} [, {input}]) *system()* *E677* - Get the output of the shell command {cmd} as a |string|. {cmd} - will be run the same as in |jobstart()|. See |systemlist()| - to get the output as a |List|. + Get the output of {cmd} as a |string| (use |systemlist()| to + get a |List|). {cmd} is treated exactly as in |jobstart()|. Not to be used for interactive commands. If {input} is a string it is written to a pipe and passed as @@ -8499,14 +8538,6 @@ This does NOT work: > endfor < Note that reordering the list (e.g., with sort() or reverse()) may have unexpected effects. - Note that the type of each list item should be - identical to avoid errors for the type of {var} - changing. Unlet the variable at the end of the loop - to allow multiple item types: > - for item in ["foo", ["bar"]] - echo item - unlet item " E706 without this - endfor :for [{var1}, {var2}, ...] in {listlist} :endfo[r] diff --git a/runtime/doc/if_cscop.txt b/runtime/doc/if_cscop.txt index 99d1fe42e1..7482f5eebb 100644 --- a/runtime/doc/if_cscop.txt +++ b/runtime/doc/if_cscop.txt @@ -96,8 +96,8 @@ command does the same and also splits the window (short: "scs"). The available subcommands are: - *E563* *E564* *E566* *E568* *E569* *E622* *E623* - *E625* *E626* *E609* + *E563* *E564* *E566* *E568* *E622* *E623* *E625* + *E626* *E609* add : Add a new cscope database/connection. USAGE :cs add {file|dir} [pre-path] [flags] diff --git a/runtime/doc/if_pyth.txt b/runtime/doc/if_pyth.txt index 8946dd2e5a..b6fe234de4 100644 --- a/runtime/doc/if_pyth.txt +++ b/runtime/doc/if_pyth.txt @@ -699,7 +699,7 @@ if the `:py3` command is working: > :py3 print("Hello") < *:py3file* The `:py3file` command works similar to `:pyfile`. - *:py3do* *E863* + *:py3do* The `:py3do` command works similar to `:pydo`. *E880* diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index 1f4557fe30..7388652f16 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1,4 +1,4 @@ -*index.txt* For Vim version 7.4. Last change: 2016 Mar 12 +*index.txt* For Vim version 7.4. Last change: 2016 Jun 12 VIM REFERENCE MANUAL by Bram Moolenaar @@ -48,6 +48,7 @@ tag char action in Insert mode ~ |i_CTRL-G_k| CTRL-G k line up, to column where inserting started |i_CTRL-G_k| CTRL-G <Up> line up, to column where inserting started |i_CTRL-G_u| CTRL-G u start new undoable edit +|i_CTRL-G_U| CTRL-G U don't break undo with next cursor movement |i_<BS>| <BS> delete character before the cursor |i_digraph| {char1}<BS>{char2} enter digraph (only when 'digraph' option set) @@ -857,6 +858,7 @@ tag command note action in Visual mode ~ ------------------------------------------------------------------------------ |v_CTRL-\_CTRL-N| CTRL-\ CTRL-N stop Visual mode |v_CTRL-\_CTRL-G| CTRL-\ CTRL-G go to mode specified with 'insertmode' +|v_CTRL-A| CTRL-A 2 add N to number in highlighted text |v_CTRL-C| CTRL-C stop Visual mode |v_CTRL-G| CTRL-G toggle between Visual mode and Select mode |v_<BS>| <BS> 2 Select mode: delete highlighted area @@ -865,6 +867,7 @@ tag command note action in Visual mode ~ command |v_CTRL-V| CTRL-V make Visual mode blockwise or stop Visual mode +|v_CTRL-X| CTRL-X 2 subtract N from number in highlighted text |v_<Esc>| <Esc> stop Visual mode |v_CTRL-]| CTRL-] jump to highlighted tag |v_!| !{filter} 2 filter the highlighted lines through the @@ -921,6 +924,8 @@ tag command note action in Visual mode ~ |v_a}| a} same as aB |v_c| c 2 delete highlighted area and start insert |v_d| d 2 delete highlighted area +|v_g_CTRL-A| g CTRL-A 2 add N to number in highlighted text +|v_g_CTRL-X| g CTRL-X 2 subtract N from number in highlighted text |v_gJ| gJ 2 join the highlighted lines without inserting spaces |v_gq| gq 2 format the highlighted lines @@ -1147,8 +1152,9 @@ tag command action ~ |:chdir| :chd[ir] change directory |:checkpath| :che[ckpath] list included files |:checktime| :checkt[ime] check timestamp of loaded buffers -|:clist| :cl[ist] list all errors |:clast| :cla[st] go to the specified error, default last one +|:clearjumps| :cle[arjumps] clear the jump list +|:clist| :cl[ist] list all errors |:close| :clo[se] close current window |:cmap| :cm[ap] like ":map" but for Command-line mode |:cmapclear| :cmapc[lear] clear all mappings for Command-line mode diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index 4561020d22..991ecf8fb2 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -1,4 +1,4 @@ -*map.txt* For Vim version 7.4. Last change: 2016 Jan 10 +*map.txt* For Vim version 7.4. Last change: 2016 Jun 10 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1352,7 +1352,7 @@ Possible attributes are: Note that -range=N and -count=N are mutually exclusive - only one should be specified. - *E889* *:command-addr* + *:command-addr* It is possible that the special characters in the range like `.`, `$` or `%` which by default correspond to the current line, last line and the whole buffer, relate to arguments, (loaded) buffers, windows or tab pages. @@ -1408,11 +1408,11 @@ The valid escape sequences are expands to nothing. *<mods>* <mods> The command modifiers, if specified. Otherwise, expands to - nothing. Supported modifiers are |aboveleft|, |belowright|, - |botright|, |browse|, |confirm|, |hide|, |keepalt|, - |keepjumps|, |keepmarks|, |keeppatterns|, |lockmarks|, - |noswapfile|, |silent|, |tab|, |topleft|, |verbose|, and - |vertical|. + nothing. Supported modifiers are |:aboveleft|, |:belowright|, + |:botright|, |:browse|, |:confirm|, |:hide|, |:keepalt|, + |:keepjumps|, |:keepmarks|, |:keeppatterns|, |:lockmarks|, + |:noswapfile|, |:silent|, |:tab|, |:topleft|, |:verbose|, and + |:vertical|. Examples: > command! -nargs=+ -complete=file MyEdit \ for f in expand(<q-args>, 0, 1) | diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt index 4d6f0719e3..606fd53fee 100644 --- a/runtime/doc/motion.txt +++ b/runtime/doc/motion.txt @@ -1013,6 +1013,9 @@ CTRL-I Go to [count] newer cursor position in jump list *:ju* *:jumps* :ju[mps] Print the jump list (not a motion command). + *:cle* *:clearjumps* +:cle[arjumps] Clear the jump list of the current window. + *jumplist* Jumps are remembered in a jump list. With the CTRL-O and CTRL-I command you can go to cursor positions before older jumps, and back again. Thus you can diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index e15fb9dc84..d332e0053a 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -2682,6 +2682,40 @@ A jump table for the options with a short description can be found at |Q_op|. It is not allowed to change text or jump to another window while evaluating 'foldtext' |textlock|. + *'formatexpr'* *'fex'* +'formatexpr' 'fex' string (default "") + local to buffer + {not available when compiled without the |+eval| + feature} + Expression which is evaluated to format a range of lines for the |gq| + operator or automatic formatting (see 'formatoptions'). When this + option is empty 'formatprg' is used. + + The |v:lnum| variable holds the first line to be formatted. + The |v:count| variable holds the number of lines to be formatted. + The |v:char| variable holds the character that is going to be + inserted if the expression is being evaluated due to + automatic formatting. This can be empty. Don't insert + it yet! + + Example: > + :set formatexpr=mylang#Format() +< This will invoke the mylang#Format() function in the + autoload/mylang.vim file in 'runtimepath'. |autoload| + + The expression is also evaluated when 'textwidth' is set and adding + text beyond that limit. This happens under the same conditions as + when internal formatting is used. Make sure the cursor is kept in the + same spot relative to the text then! The |mode()| function will + return "i" or "R" in this situation. + + When the expression evaluates to non-zero Vim will fall back to using + the internal format mechanism. + + The expression will be evaluated in the |sandbox| when set from a + modeline, see |sandbox-option|. That stops the option from working, + since changing the buffer text is not allowed. + *'formatoptions'* *'fo'* 'formatoptions' 'fo' string (default: "tcqj", Vi default: "vt") local to buffer @@ -2720,40 +2754,6 @@ A jump table for the options with a short description can be found at |Q_op|. This option cannot be set from a |modeline| or in the |sandbox|, for security reasons. - *'formatexpr'* *'fex'* -'formatexpr' 'fex' string (default "") - local to buffer - {not available when compiled without the |+eval| - feature} - Expression which is evaluated to format a range of lines for the |gq| - operator or automatic formatting (see 'formatoptions'). When this - option is empty 'formatprg' is used. - - The |v:lnum| variable holds the first line to be formatted. - The |v:count| variable holds the number of lines to be formatted. - The |v:char| variable holds the character that is going to be - inserted if the expression is being evaluated due to - automatic formatting. This can be empty. Don't insert - it yet! - - Example: > - :set formatexpr=mylang#Format() -< This will invoke the mylang#Format() function in the - autoload/mylang.vim file in 'runtimepath'. |autoload| - - The expression is also evaluated when 'textwidth' is set and adding - text beyond that limit. This happens under the same conditions as - when internal formatting is used. Make sure the cursor is kept in the - same spot relative to the text then! The |mode()| function will - return "i" or "R" in this situation. - - When the expression evaluates to non-zero Vim will fall back to using - the internal format mechanism. - - The expression will be evaluated in the |sandbox| when set from a - modeline, see |sandbox-option|. That stops the option from working, - since changing the buffer text is not allowed. - *'fsync'* *'fs'* 'fsync' 'fs' boolean (default on) global diff --git a/runtime/doc/pattern.txt b/runtime/doc/pattern.txt index 8e50a67847..f3f5bcbd66 100644 --- a/runtime/doc/pattern.txt +++ b/runtime/doc/pattern.txt @@ -1,4 +1,4 @@ -*pattern.txt* For Vim version 7.4. Last change: 2016 Apr 26 +*pattern.txt* For Vim version 7.4. Last change: 2016 Jun 08 VIM REFERENCE MANUAL by Bram Moolenaar @@ -573,7 +573,7 @@ An atom can be followed by an indication of how many times the atom can be matched and in what way. This is called a multi. See |/multi| for an overview. - */star* */\star* *E56* + */star* */\star* * (use \* when 'magic' is not set) Matches 0 or more of the preceding atom, as many as possible. Example 'nomagic' matches ~ @@ -593,7 +593,7 @@ overview. the end of the file and then tries matching "END", backing up one character at a time. - */\+* *E57* + */\+* \+ Matches 1 or more of the preceding atom, as many as possible. Example matches ~ ^.\+$ any non-empty line @@ -608,7 +608,7 @@ overview. \? Just like \=. Cannot be used when searching backwards with the "?" command. - */\{* *E58* *E60* *E554* *E870* + */\{* *E60* *E554* *E870* \{n,m} Matches n to m of the preceding atom, as many as possible \{n} Matches n of the preceding atom \{n,} Matches at least n of the preceding atom, as many as possible @@ -947,14 +947,18 @@ $ At end of pattern or in front of "\|", "\)" or "\n" ('magic' on): < When 'hlsearch' is set and you move the cursor around and make changes this will clearly show when the match is updated or not. To match the text up to column 17: > - /.*\%17v -< Column 17 is included, because that's where the "\%17v" matches, - even though this is a |/zero-width| match. Adding a dot to match the - next character has the same result: > - /.*\%17v. + /^.*\%17v +< Column 17 is not included, because this is a |/zero-width| match. To + include the column use: > + /^.*\%17v. < This command does the same thing, but also matches when there is no character in column 17: > - /.*\%<18v. + /^.*\%<18v. +< Note that without the "^" to anchor the match in the first column, + this will also highlight column 17: > + /.*\%17v +< Column 17 is highlighted by 'hlsearch' because there is another match + where ".*" matches zero characters. < Character classes: diff --git a/runtime/doc/pi_netrw.txt b/runtime/doc/pi_netrw.txt index 2c240fe41f..f740143c61 100644 --- a/runtime/doc/pi_netrw.txt +++ b/runtime/doc/pi_netrw.txt @@ -1,4 +1,4 @@ -*pi_netrw.txt* For Vim version 7.4. Last change: 2016 Feb 16 +*pi_netrw.txt* For Vim version 7.4. Last change: 2016 Apr 20 ------------------------------------------------ NETRW REFERENCE MANUAL by Charles E. Campbell @@ -1523,6 +1523,7 @@ the |'isfname'| option (which is global, so netrw doesn't modify it). Associated setting variables: |g:netrw_gx| control how gx picks up the text under the cursor |g:netrw_nogx| prevent gx map while editing + |g:netrw_suppress_gx_mesg| controls gx's suppression of browser messages *netrw_filehandler* @@ -2929,6 +2930,13 @@ your browsing preferences. (see also: |netrw-settings|) such as listing, file removal, etc. default: ssh + *g:netrw_suppress_gx_mesg* =1 : browsers sometimes produce messages + which are normally unwanted intermixed + with the page. + However, when using links, for example, + those messages are what the browser produces. + By setting this option to 0, netrw will not + suppress browser messages. *g:netrw_tmpfile_escape* =' &;' escape() is applied to all temporary files @@ -3755,6 +3763,23 @@ netrw: ============================================================================== 12. History *netrw-history* {{{1 + v156: Feb 18, 2016 * Changed =~ to =~# where appropriate + Feb 23, 2016 * s:ComposePath(base,subdir) now uses + fnameescape() on the base portion + Mar 01, 2016 * (gt_macki) reported where :Explore would + make file unlisted. Fixed (tst943) + Apr 04, 2016 * (reported by John Little) netrw normally + suppresses browser messages, but sometimes + those "messages" are what is wanted. + See |g:netrw_suppress_gx_mesg| + Apr 06, 2016 * (reported by Carlos Pita) deleting a remote + file was giving an error message. Fixed. + Apr 08, 2016 * (Charles Cooper) had a problem with an + undefined b:netrw_curdir. He also provided + a fix. + Apr 20, 2016 * Changed s:NetrwGetBuffer(); now uses + dictionaries. Also fixed the "No Name" + buffer problem. v155: Oct 29, 2015 * (Timur Fayzrakhmanov) reported that netrw's mapping of ctrl-l was not allowing refresh of other windows when it was done in a netrw diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index cbb2a23a48..e94723f337 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -474,6 +474,9 @@ expression. The BufWinEnter event is also triggered, again using "quickfix" for the buffer name. +Note: When adding to an existing quickfix list the autocommand are not +triggered. + Note: Making changes in the quickfix window has no effect on the list of errors. 'modifiable' is off to avoid making changes. If you delete or insert lines anyway, the relation between the text and the error number is messed up. diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt index 6cd2ada513..ef98556260 100644 --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -529,7 +529,7 @@ whether Vim supports a feature or a dependency is missing. You can also load an optional plugin at startup, by putting this command in your |.vimrc|: > :packadd! foodebug -The extra "!" is so that the plugin isn't loaded with Vim was started with +The extra "!" is so that the plugin isn't loaded if Vim was started with |--noplugin|. It is perfectly normal for a package to only have files in the "opt" @@ -608,7 +608,7 @@ the command after changing the plugin help: > Dependencies between plugins ~ *packload-two-steps* -Suppose you have a two plugins that depend on the same functionality. You can +Suppose you have two plugins that depend on the same functionality. You can put the common functionality in an autoload directory, so that it will be found automatically. Your package would have these files: diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index bbc0260ffa..b58b3c7853 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -1,4 +1,4 @@ -*starting.txt* For Vim version 7.4. Last change: 2016 Apr 05 +*starting.txt* For Vim version 7.4. Last change: 2016 Jun 12 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1135,7 +1135,7 @@ files for different types of files (e.g., C code) and load them based on the file name, using the ":autocmd" command (see |:autocmd|). More information on ShaDa file format is contained in |shada-format| section. - *E136* *E138* *shada-error-handling* + *E136* *E929* *shada-error-handling* Some errors make Neovim leave temporary file named `{basename}.tmp.X` (X is any free letter from `a` to `z`) while normally it will create this file, write to it and then rename `{basename}.tmp.X` to `{basename}`. Such errors @@ -1155,7 +1155,7 @@ include: Do not forget to remove the temporary file or replace the target file with temporary one after getting one of the above errors or all attempts to create -a ShaDa file may fail with |E138|. If you got one of them when using +a ShaDa file may fail with |E929|. If you got one of them when using |:wshada| (and not when exiting Neovim: i.e. when you have Neovim session running) you have additional options: @@ -1187,7 +1187,7 @@ running) you have additional options: internal info is written (also disables safety checks described in |shada-error-handling|). If 'shada' is empty, marks for up to 100 files will be written. - When you get error "E138: All .tmp.X files exist, + When you get error "E929: All .tmp.X files exist, cannot write ShaDa file!" check that no old temp files were left behind (e.g. ~/.local/share/nvim/shada/main.shada.tmp*). diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 6194a636b3..e59f567826 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -4770,6 +4770,10 @@ font={font-name} *highlight-font* All fonts used, except for Menu and Tooltip, should be of the same character size as the default font! Otherwise redrawing problems will occur. + To use a font name with an embedded space or other special character, + put it in single quotes. The single quote cannot be used then. + Example: > + :hi comment font='Monospace 10' guifg={color-name} *highlight-guifg* guibg={color-name} *highlight-guibg* diff --git a/runtime/doc/term.txt b/runtime/doc/term.txt index 7d47368ba3..08ffee7a2f 100644 --- a/runtime/doc/term.txt +++ b/runtime/doc/term.txt @@ -473,7 +473,7 @@ correct values. One command can be used to set the screen size: - *:mod* *:mode* *E359* *E362* + *:mod* *:mode* :mod[e] Detects the screen size and redraws the screen. diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index bf8d31fef9..b0e5386224 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -602,7 +602,9 @@ String manipulation: *string-functions* strdisplaywidth() size of string when displayed, deals with tabs substitute() substitute a pattern match with a string submatch() get a specific match in ":s" and substitute() - strpart() get part of a string + strpart() get part of a string using byte index + strcharpart() get part of a string using char index + strgetchar() get character from a string using char index expand() expand special keywords iconv() convert text from one encoding to another byteidx() byte index of a character in a string @@ -732,11 +734,14 @@ Working with text in the current buffer: *text-functions* searchpair() find the other end of a start/skip/end searchpairpos() find the other end of a start/skip/end searchdecl() search for the declaration of a name + getcharsearch() return character search information + setcharsearch() set character search information *system-functions* *file-functions* System functions and manipulation of files: glob() expand wildcards globpath() expand wildcards in a number of directories + glob2regpat() convert a glob pattern into a search pattern findfile() find a file in a list of directories finddir() find a directory in a list of directories resolve() find out where a shortcut points to @@ -748,6 +753,7 @@ System functions and manipulation of files: filereadable() check if a file can be read filewritable() check if a file can be written to getfperm() get the permissions of a file + setfperm() set the permissions of a file getftype() get the kind of a file isdirectory() check if a directory exists getfsize() get the size of a file @@ -786,9 +792,15 @@ Buffers, windows and the argument list: tabpagenr() get the number of a tab page tabpagewinnr() like winnr() for a specified tab page winnr() get the window number for the current window + bufwinid() get the window ID of a specific buffer bufwinnr() get the window number of a specific buffer winbufnr() get the buffer number of a specific window getbufline() get a list of lines from the specified buffer + win_findbuf() find windows containing a buffer + win_getid() get window ID of a window + win_gotoid() go to window with ID + win_id2tabwin() get tab and window nr from window ID + win_id2win() get window nr from window ID getbufinfo() get a list with buffer information gettabinfo() get a list with tab page information getwininfo() get a list with window information @@ -905,6 +917,10 @@ Testing: *test-functions* assert_exception() assert that a command throws an exception assert_fails() assert that a function call fails +Timers: *timer-functions* + timer_start() create a timer + timer_stop() stop a timer + Various: *various-functions* mode() get current editing mode visualmode() last visual mode used @@ -1401,9 +1417,9 @@ Now we can instantiate a Dutch translation object: > And a German translator: > :let uk2de = copy(transdict) - :let uk2de.words = {'one': 'ein', 'two': 'zwei', 'three': 'drei'} + :let uk2de.words = {'one': 'eins', 'two': 'zwei', 'three': 'drei'} :echo uk2de.translate('three one') -< drei ein ~ +< drei eins ~ You see that the copy() function is used to make a copy of the "transdict" Dictionary and then the copy is changed to add the words. The original diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index d5c7db992e..5b94626e36 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -1,4 +1,4 @@ -*windows.txt* For Vim version 7.4. Last change: 2016 Feb 01 +*windows.txt* For Vim version 7.4. Last change: 2016 Jun 10 VIM REFERENCE MANUAL by Bram Moolenaar @@ -70,6 +70,16 @@ places where a Normal mode command can't be used or is inconvenient. The main Vim window can hold several split windows. There are also tab pages |tab-page|, each of which can hold multiple windows. +Each window has a unique identifier called the window ID. This identifier +will not change within a Vim session. The |win_getid()| and |win_id2tabwin()| +functions can be used to convert between the window/tab number and the +identifier. There is also the window number, which may change whenever +windows are opened or closed, see |winnr()|. + +Each buffer has a unique number and the number will not change within a Vim +session. The |bufnr()| and |bufname()| functions can be used to convert +between a buffer name and the buffer number. + ============================================================================== 2. Starting Vim *windows-starting* diff --git a/runtime/ftplugin/c.vim b/runtime/ftplugin/c.vim index d1b2a4941e..3717ea92a9 100644 --- a/runtime/ftplugin/c.vim +++ b/runtime/ftplugin/c.vim @@ -1,7 +1,7 @@ " Vim filetype plugin file " Language: C " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2012 Jul 10 +" Last Change: 2016 Jun 12 " Only do this when not done yet for this buffer if exists("b:did_ftplugin") @@ -32,7 +32,7 @@ setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:// " When the matchit plugin is loaded, this makes the % command skip parens and " braces in comments. let b:match_words = &matchpairs . ',^\s*#\s*if\(\|def\|ndef\)\>:^\s*#\s*elif\>:^\s*#\s*else\>:^\s*#\s*endif\>' -let b:match_skip = 's:comment\|string\|character' +let b:match_skip = 's:comment\|string\|character\|special' " Win32 can filter files in the browse dialog if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") diff --git a/runtime/ftplugin/python.vim b/runtime/ftplugin/python.vim index 75c7e87996..9e2c5a763e 100644 --- a/runtime/ftplugin/python.vim +++ b/runtime/ftplugin/python.vim @@ -1,8 +1,9 @@ " Vim filetype plugin file " Language: python -" Maintainer: Johannes Zellner <johannes@zellner.org> -" Last Change: 2014 Feb 09 -" Last Change By Johannes: Wed, 21 Apr 2004 13:13:08 CEST +" Maintainer: James Sully <sullyj3@gmail.com> +" Previous Maintainer: Johannes Zellner <johannes@zellner.org> +" Last Change: Fri, 10 June 2016 +" https://github.com/sullyj3/vim-ftplugin-python if exists("b:did_ftplugin") | finish | endif let b:did_ftplugin = 1 @@ -21,10 +22,10 @@ setlocal omnifunc=pythoncomplete#Complete set wildignore+=*.pyc -nnoremap <silent> <buffer> ]] :call <SID>Python_jump('/^\(class\\|def\)')<cr> -nnoremap <silent> <buffer> [[ :call <SID>Python_jump('?^\(class\\|def\)')<cr> -nnoremap <silent> <buffer> ]m :call <SID>Python_jump('/^\s*\(class\\|def\)')<cr> -nnoremap <silent> <buffer> [m :call <SID>Python_jump('?^\s*\(class\\|def\)')<cr> +nnoremap <silent> <buffer> ]] :call <SID>Python_jump('/^\(class\\|def\)\>')<cr> +nnoremap <silent> <buffer> [[ :call <SID>Python_jump('?^\(class\\|def\)\>')<cr> +nnoremap <silent> <buffer> ]m :call <SID>Python_jump('/^\s*\(class\\|def\)\>')<cr> +nnoremap <silent> <buffer> [m :call <SID>Python_jump('?^\s*\(class\\|def\)\>')<cr> if !exists('*<SID>Python_jump') fun! <SID>Python_jump(motion) range diff --git a/runtime/ftplugin/spec.vim b/runtime/ftplugin/spec.vim index 6d5bf4b806..2a961f8244 100644 --- a/runtime/ftplugin/spec.vim +++ b/runtime/ftplugin/spec.vim @@ -36,10 +36,11 @@ except ImportError: else: specfile = vim.current.buffer.name if specfile: + rpm.delMacro("dist") spec = rpm.spec(specfile) - headers = spec.packages[0].header - version = headers['Version'] - release = ".".join(headers['Release'].split(".")[:-1]) + headers = spec.sourceHeader + version = headers["Version"] + release = headers["Release"] vim.command("let ver = " + version) vim.command("let rel = " + release) PYEND @@ -113,7 +114,10 @@ if !exists("*s:SpecChangelog") endif endif if (chgline != -1) + let tmptime = v:lc_time + language time C let parsed_format = "* ".strftime(format)." - ".ver."-".rel + execute "language time" tmptime let release_info = "+ ".name."-".ver."-".rel let wrong_format = 0 let wrong_release = 0 @@ -179,12 +183,8 @@ if !exists("*s:ParseRpmVars") endif let varname = strpart(a:str, start+2, end-(start+2)) execute a:strline - let definestr = "^[ \t]*%define[ \t]\\+" . varname . "[ \t]\\+\\(.*\\)$" + let definestr = "^[ \t]*%(?:global|define)[ \t]\\+" . varname . "[ \t]\\+\\(.*\\)$" let linenum = search(definestr, "bW") - if (linenum == 0) - let definestr = substitute(definestr, "%define", "%global", "") - let linenum = search(definestr, "bW") - endif if (linenum != -1) let ret = ret . substitute(getline(linenum), definestr, "\\1", "") else @@ -201,7 +201,7 @@ endif let b:match_ignorecase = 0 let b:match_words = - \ '^Name:^%description:^%clean:^%setup:^%build:^%install:^%files:' . + \ '^Name:^%description:^%clean:^%(?:auto)?setup:^%build:^%install:^%files:' . \ '^%package:^%preun:^%postun:^%changelog' let &cpo = s:cpo_save diff --git a/runtime/indent/vhdl.vim b/runtime/indent/vhdl.vim index 3e847b9575..6982859670 100644 --- a/runtime/indent/vhdl.vim +++ b/runtime/indent/vhdl.vim @@ -1,8 +1,8 @@ " VHDL indent ('93 syntax) " Language: VHDL " Maintainer: Gerald Lai <laigera+vim?gmail.com> -" Version: 1.58 -" Last Change: 2011 Sep 27 +" Version: 1.60 +" Last Change: 2016 Feb 26 " URL: http://www.vim.org/scripts/script.php?script_id=1450 " only load this indent file when no other was loaded @@ -104,7 +104,7 @@ function GetVHDLindent() let pn = prevnonblank(pn - 1) let ps = getline(pn) endwhile - if (curs =~ '^\s*)' || curs =~? '^\s*\%(\<\%(procedure\|generic\|map\|port\)\>.*\)\@<!\w\+\s*\w*\s*\%(=>\s*\S\+\|:[^=]\@=\s*\%(\%(in\|out\|inout\|buffer\|linkage\)\>\|\w\+\s\+:=\)\)') && (prevs =~? s:NC.'\<\%(procedure\s\+\S\+\|generic\|map\|port\)\s*(\%(\s*\w\)\=' || (ps =~? s:NC.'\<\%(procedure\|generic\|map\|port\)'.s:ES && prevs =~ '^\s*(')) + if (curs =~ '^\s*)' || curs =~? '^\s*\%(\<\%(procedure\|generic\|map\|port\)\>.*\)\@<!\w\+\s*\w*\s*\((.*)\)*\s*\%(=>\s*\S\+\|:[^=]\@=\s*\%(\%(in\|out\|inout\|buffer\|linkage\)\>\|\s\+\)\)') && (prevs =~? s:NC.'\<\%(procedure\s\+\S\+\|generic\|map\|port\)\s*(\%(\s*\w\)\=' || (ps =~? s:NC.'\<\%(procedure\|generic\|map\|port\)'.s:ES && prevs =~ '^\s*(')) " align closing ")" with opening "(" if curs =~ '^\s*)' return ind2 + stridx(prevs_noi, '(') @@ -412,11 +412,22 @@ function GetVHDLindent() " **************************************************************************************** " indent: maintain indent of previous opening statement - " keywords: without "procedure", "generic", "map", "port" + ":" but not ":=" + "in", "out", "inout", "buffer", "linkage", variable & ":=" + " keywords: without "procedure", "generic", "map", "port" + ":" but not ":=" + eventually ;$ " where: start of current line - if curs =~? '^\s*\%(\<\%(procedure\|generic\|map\|port\)\>.*\)\@<!\w\+\s*\w*\s*:[^=]\@=\s*\%(\%(in\|out\|inout\|buffer\|linkage\)\>\|\w\+\s\+:=\)' + if curs =~? '^\s*\%(\<\%(procedure\|generic\|map\|port\)\>.*\)\@<!\w\+\s*\w*\s*:[^=].*;.*$' return ind2 endif + " **************************************************************************************** + " indent: maintain indent of previous opening statement, corner case which + " does not end in ;, but is part of a mapping + " keywords: without "procedure", "generic", "map", "port" + ":" but not ":=", never + ;$ and + " prevline without "procedure", "generic", "map", "port" + ":" but not ":=" + eventually ;$ + " where: start of current line + if curs =~? '^\s*\%(\<\%(procedure\|generic\|map\|port\)\>.*\)\@<!\w\+\s*\w*\s*:[^=].*[^;].*$' + if prevs =~? '^\s*\%(\<\%(procedure\|generic\|map\|port\)\>.*\)\@<!\w\+\s*\w*\s*:[^=].*;.*$' + return ind2 + endif + endif " return leftover filtered indent return ind diff --git a/runtime/indent/vim.vim b/runtime/indent/vim.vim index 31b76b8c0c..7ec7df849e 100644 --- a/runtime/indent/vim.vim +++ b/runtime/indent/vim.vim @@ -1,7 +1,7 @@ " Vim indent file " Language: Vim script " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2016 Jan 24 +" Last Change: 2016 Apr 19 " Only load this indent file when no other was loaded. if exists("b:did_indent") @@ -60,7 +60,7 @@ function GetVimIndentIntern() else let ind = ind + shiftwidth() * 3 endif - elseif prev_text =~ '^\s*aug\%[roup]' && prev_text !~ '^\s*aug\%[roup]\s*!\=\s\+END' + elseif prev_text =~ '^\s*aug\%[roup]' && prev_text !~ '^\s*aug\%[roup]\s*!\=\s\+[eE][nN][dD]' let ind = ind + shiftwidth() else " A line starting with :au does not increment/decrement indent. diff --git a/runtime/plugin/health.vim b/runtime/plugin/health.vim index 3c8e509acd..e3482cb0fe 100644 --- a/runtime/plugin/health.vim +++ b/runtime/plugin/health.vim @@ -1 +1,8 @@ -command! -nargs=* CheckHealth call health#check([<f-args>]) +function! s:complete(lead, _line, _pos) abort + return sort(filter(map(globpath(&runtimepath, 'autoload/health/*', 1, 1), + \ 'fnamemodify(v:val, ":t:r")'), + \ 'empty(a:lead) || v:val[:strlen(a:lead)-1] ==# a:lead')) +endfunction + +command! -nargs=* -complete=customlist,s:complete CheckHealth + \ call health#check([<f-args>]) diff --git a/runtime/plugin/netrwPlugin.vim b/runtime/plugin/netrwPlugin.vim index 69902b1f19..28e1c3ecf8 100644 --- a/runtime/plugin/netrwPlugin.vim +++ b/runtime/plugin/netrwPlugin.vim @@ -20,7 +20,7 @@ if &cp || exists("g:loaded_netrwPlugin") finish endif -let g:loaded_netrwPlugin = "v155" +let g:loaded_netrwPlugin = "v156" let s:keepcpo = &cpo set cpo&vim "DechoRemOn diff --git a/runtime/syntax/bib.vim b/runtime/syntax/bib.vim index f84d5ca95a..8bd0528e1e 100644 --- a/runtime/syntax/bib.vim +++ b/runtime/syntax/bib.vim @@ -2,7 +2,7 @@ " Language: BibTeX (bibliographic database format for (La)TeX) " Maintainer: Bernd Feige <Bernd.Feige@gmx.net> " Filenames: *.bib -" Last Change: 2014 Mar 26 +" Last Change: 2016 May 31 " Thanks to those who pointed out problems with this file or supplied fixes! @@ -35,8 +35,40 @@ syn keyword bibEntryKw contained crossref edition editor howpublished syn keyword bibEntryKw contained institution journal key month note syn keyword bibEntryKw contained number organization pages publisher syn keyword bibEntryKw contained school series title type volume year + +" biblatex keywords, cf. http://mirrors.ctan.org/macros/latex/contrib/biblatex/doc/biblatex.pdf +syn keyword bibType contained mvbook bookinbook suppbook collection mvcollection suppcollection +syn keyword bibType contained online patent periodical suppperiodical mvproceedings reference +syn keyword bibType contained mvreference inreference report set thesis xdata customa customb +syn keyword bibType contained customc customd custome customf electronic www artwork audio bibnote +syn keyword bibType contained commentary image jurisdiction legislation legal letter movie music +syn keyword bibType contained performance review software standard video + +syn keyword bibEntryKw contained abstract isbn issn keywords url +syn keyword bibEntryKw contained addendum afterwordannotation annotation annotator authortype +syn keyword bibEntryKw contained bookauthor bookpagination booksubtitle booktitleaddon +syn keyword bibEntryKw contained commentator date doi editora editorb editorc editortype +syn keyword bibEntryKw contained editoratype editorbtype editorctype eid entrysubtype +syn keyword bibEntryKw contained eprint eprintclass eprinttype eventdate eventtitle +syn keyword bibEntryKw contained eventtitleaddon file foreword holder indextitle +syn keyword bibEntryKw contained introduction isan ismn isrn issue issuesubtitle +syn keyword bibEntryKw contained issuetitle iswc journalsubtitle journaltitle label +syn keyword bibEntryKw contained language library location mainsubtitle maintitle +syn keyword bibEntryKw contained maintitleaddon nameaddon origdate origlanguage +syn keyword bibEntryKw contained origlocation origpublisher origtitle pagetotal +syn keyword bibEntryKw contained pagination part pubstate reprinttitle shortauthor +syn keyword bibEntryKw contained shorteditor shorthand shorthandintro shortjournal +syn keyword bibEntryKw contained shortseries shorttitle subtitle titleaddon translator +syn keyword bibEntryKw contained urldate venue version volumes entryset execute gender +syn keyword bibEntryKw contained langid langidopts ids indexsorttitle options presort +syn keyword bibEntryKw contained related relatedoptions relatedtype relatedstring +syn keyword bibEntryKw contained sortkey sortname sortshorthand sorttitle sortyear xdata +syn keyword bibEntryKw contained xref namea nameb namec nameatype namebtype namectype +syn keyword bibEntryKw contained lista listb listc listd liste listf usera userb userc +syn keyword bibEntryKw contained userd usere userf verba verbb verbc archiveprefix pdf +syn keyword bibEntryKw contained primaryclass + " Non-standard: -syn keyword bibNSEntryKw contained abstract isbn issn keywords url " AMS mref http://www.ams.org/mref syn keyword bibNSEntryKw contained mrclass mrnumber mrreviewer fjournal coden diff --git a/runtime/syntax/php.vim b/runtime/syntax/php.vim index 4e1a84651c..fc257418d0 100644 --- a/runtime/syntax/php.vim +++ b/runtime/syntax/php.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: php PHP 3/4/5 " Maintainer: Jason Woofenden <jason@jasonwoof.com> -" Last Change: Dec 26, 2015 +" Last Change: Apr 18, 2016 " URL: https://jasonwoof.com/gitweb/?p=vim-syntax.git;a=blob;f=php.vim;hb=HEAD " Former Maintainers: Peter Hodge <toomuchphp-vim@yahoo.com> " Debian VIM Maintainers <pkg-vim-maintainers@lists.alioth.debian.org> @@ -136,7 +136,7 @@ syn keyword phpConstant __LINE__ __FILE__ __FUNCTION__ __METHOD__ __CLASS__ __D " Function and Methods ripped from php_manual_de.tar.gz Jan 2003 syn keyword phpFunctions apache_child_terminate apache_get_modules apache_get_version apache_getenv apache_lookup_uri apache_note apache_request_headers apache_response_headers apache_setenv ascii2ebcdic ebcdic2ascii getallheaders virtual contained -syn keyword phpFunctions array_change_key_case array_chunk array_combine array_count_values array_diff_assoc array_diff_uassoc array_diff array_fill array_filter array_flip array_intersect_assoc array_intersect array_key_exists array_keys array_map array_merge_recursive array_merge array_multisort array_pad array_pop array_push array_rand array_reduce array_reverse array_search array_shift array_slice array_splice array_sum array_udiff_assoc array_udiff_uassoc array_udiff array_unique array_unshift array_values array_walk array arsort asort compact count current each end extract in_array key krsort ksort list natcasesort natsort next pos prev range reset rsort shuffle sizeof sort uasort uksort usort contained +syn keyword phpFunctions array_change_key_case array_chunk array_column array_combine array_count_values array_diff_assoc array_diff_key array_diff_uassoc array_diff_ukey array_diff array_fill_keys array_fill array_filter array_flip array_intersect_assoc array_intersect_key array_intersect_uassoc array_intersect_ukey array_intersect array_key_exists array_keys array_map array_merge_recursive array_merge array_multisort array_pad array_pop array_product array_push array_rand array_reduce array_replace_recursive array_replace array_reverse array_search array_shift array_slice array_splice array_sum array_udiff_assoc array_udiff_uassoc array_udiff array_uintersect_assoc array_uintersect_uassoc array_uintersect array_unique array_unshift array_values array_walk_recursive array_walk arsort asort count current each end in_array key_exists key krsort ksort natcasesort natsort next pos prev range reset rsort shuffle sizeof sort uasort uksort usort contained syn keyword phpFunctions aspell_check aspell_new aspell_suggest contained syn keyword phpFunctions bcadd bccomp bcdiv bcmod bcmul bcpow bcpowmod bcscale bcsqrt bcsub contained syn keyword phpFunctions bzclose bzcompress bzdecompress bzerrno bzerror bzerrstr bzflush bzopen bzread bzwrite contained diff --git a/runtime/syntax/spec.vim b/runtime/syntax/spec.vim index 5d96b57a8a..9952bd2548 100644 --- a/runtime/syntax/spec.vim +++ b/runtime/syntax/spec.vim @@ -3,7 +3,7 @@ " Language: SPEC: Build/install scripts for Linux RPM packages " Maintainer: Igor Gnatenko i.gnatenko.brain@gmail.com " Former Maintainer: Donovan Rebbechi elflord@panix.com (until March 2014) -" Last Change: Sun Mar 2 10:33 MSK 2014 Igor Gnatenko +" Last Change: Sat Apr 9 15:30 2016 Filip Szymański " For version 5.x: Clear all syntax items " For version 6.x: Quit when a syntax file was already loaded @@ -83,8 +83,8 @@ syn keyword specMacroNameLocal contained _arch _binary_payload _bindir _build _b "One line macros - valid in all ScriptAreas "tip: remember do include new items on specScriptArea's skip section -syn region specSectionMacroArea oneline matchgroup=specSectionMacro start='^%\(define\|global\|patch\d*\|setup\|configure\|GNUconfigure\|find_lang\|makeinstall\|make_install\|include\)\>' end='$' contains=specCommandOpts,specMacroIdentifier -syn region specSectionMacroBracketArea oneline matchgroup=specSectionMacro start='^%{\(configure\|GNUconfigure\|find_lang\|makeinstall\|make_install\)}' end='$' contains=specCommandOpts,specMacroIdentifier +syn region specSectionMacroArea oneline matchgroup=specSectionMacro start='^%\(define\|global\|patch\d*\|setup\|autosetup\|autopatch\|configure\|GNUconfigure\|find_lang\|make_build\|makeinstall\|make_install\|include\)\>' end='$' contains=specCommandOpts,specMacroIdentifier +syn region specSectionMacroBracketArea oneline matchgroup=specSectionMacro start='^%{\(configure\|GNUconfigure\|find_lang\|make_build\|makeinstall\|make_install\)}' end='$' contains=specCommandOpts,specMacroIdentifier "%% Files Section %% "TODO %config valid parameters: missingok\|noreplace @@ -105,7 +105,7 @@ syn case ignore "%% PreAmble Section %% "Copyright and Serial were deprecated by License and Epoch syn region specPreAmbleDeprecated oneline matchgroup=specError start='^\(Copyright\|Serial\)' end='$' contains=specEmail,specURL,specURLMacro,specLicense,specColon,specVariables,specSpecialChar,specMacroIdentifier -syn region specPreAmble oneline matchgroup=specCommand start='^\(Prereq\|Summary\|Name\|Version\|Packager\|Requires\|Icon\|URL\|Source\d*\|Patch\d*\|Prefix\|Packager\|Group\|License\|Release\|BuildRoot\|Distribution\|Vendor\|Provides\|ExclusiveArch\|ExcludeArch\|ExclusiveOS\|Obsoletes\|BuildArch\|BuildArchitectures\|BuildRequires\|BuildConflicts\|BuildPreReq\|Conflicts\|AutoRequires\|AutoReq\|AutoReqProv\|AutoProv\|Epoch\)' end='$' contains=specEmail,specURL,specURLMacro,specLicense,specColon,specVariables,specSpecialChar,specMacroIdentifier +syn region specPreAmble oneline matchgroup=specCommand start='^\(Prereq\|Summary\|Name\|Version\|Packager\|Requires\|Recommends\|Suggests\|Supplements\|Enhances\|Icon\|URL\|Source\d*\|Patch\d*\|Prefix\|Packager\|Group\|License\|Release\|BuildRoot\|Distribution\|Vendor\|Provides\|ExclusiveArch\|ExcludeArch\|ExclusiveOS\|Obsoletes\|BuildArch\|BuildArchitectures\|BuildRequires\|BuildConflicts\|BuildPreReq\|Conflicts\|AutoRequires\|AutoReq\|AutoReqProv\|AutoProv\|Epoch\)' end='$' contains=specEmail,specURL,specURLMacro,specLicense,specColon,specVariables,specSpecialChar,specMacroIdentifier "%% Description Section %% syn region specDescriptionArea matchgroup=specSection start='^%description' end='^%'me=e-1 contains=specDescriptionOpts,specEmail,specURL,specNumber,specMacroIdentifier,specComment @@ -114,7 +114,7 @@ syn region specDescriptionArea matchgroup=specSection start='^%description' end= syn region specPackageArea matchgroup=specSection start='^%package' end='^%'me=e-1 contains=specPackageOpts,specPreAmble,specComment "%% Scripts Section %% -syn region specScriptArea matchgroup=specSection start='^%\(prep\|build\|install\|clean\|pre\|postun\|preun\|post\|posttrans\)\>' skip='^%{\|^%\(define\|patch\d*\|configure\|GNUconfigure\|setup\|find_lang\|makeinstall\|make_install\)\>' end='^%'me=e-1 contains=specSpecialVariables,specVariables,@specCommands,specVariables,shDo,shFor,shCaseEsac,specNoNumberHilite,specCommandOpts,shComment,shIf,specSpecialChar,specMacroIdentifier,specSectionMacroArea,specSectionMacroBracketArea,shOperator,shQuote1,shQuote2 +syn region specScriptArea matchgroup=specSection start='^%\(prep\|build\|install\|clean\|pre\|postun\|preun\|post\|posttrans\)\>' skip='^%{\|^%\(define\|patch\d*\|configure\|GNUconfigure\|setup\|autosetup\|autopatch\|find_lang\|make_build\|makeinstall\|make_install\)\>' end='^%'me=e-1 contains=specSpecialVariables,specVariables,@specCommands,specVariables,shDo,shFor,shCaseEsac,specNoNumberHilite,specCommandOpts,shComment,shIf,specSpecialChar,specMacroIdentifier,specSectionMacroArea,specSectionMacroBracketArea,shOperator,shQuote1,shQuote2 "%% Changelog Section %% syn region specChangelogArea matchgroup=specSection start='^%changelog' end='^%'me=e-1 contains=specEmail,specURL,specWeekday,specMonth,specNumber,specComment,specLicense diff --git a/runtime/syntax/viminfo.vim b/runtime/syntax/viminfo.vim index 7af3b89ae0..667e1bab2a 100644 --- a/runtime/syntax/viminfo.vim +++ b/runtime/syntax/viminfo.vim @@ -1,7 +1,7 @@ " Vim syntax file " Language: Vim .viminfo file " Maintainer: Bram Moolenaar <Bram@vim.org> -" Last Change: 2012 Feb 03 +" Last Change: 2016 Jun 05 " Quit when a (custom) syntax file was already loaded if exists("b:current_syntax") @@ -30,11 +30,15 @@ syn match viminfoOptionName "\*\a*"ms=s+1 contained " Comments syn match viminfoComment "^#.*" +" New style lines. TODO: highlight numbers and strings. +syn match viminfoNew "^|.*" + " Define the default highlighting. " Only used when an item doesn't have highlighting yet hi def link viminfoComment Comment hi def link viminfoError Error hi def link viminfoStatement Statement +hi def link viminfoNew String let b:current_syntax = "viminfo" diff --git a/scripts/genoptions.lua b/scripts/genoptions.lua index 9f7d94969d..9d7f235a3b 100644 --- a/scripts/genoptions.lua +++ b/scripts/genoptions.lua @@ -31,6 +31,7 @@ local type_flags={ local redraw_flags={ statuslines='P_RSTAT', current_window='P_RWIN', + current_window_only='P_RWINONLY', current_buffer='P_RBUF', all_windows='P_RALL', everything='P_RCLR', diff --git a/src/clint.py b/src/clint.py index 07733d211e..0c9f55c71e 100755 --- a/src/clint.py +++ b/src/clint.py @@ -2516,6 +2516,10 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): cast_line = re.sub(r'^# *define +\w+\([^)]*\)', '', line) match = Search(r'(?<!\bkvec_t)' + r'(?<!\bkvec_withinit_t)' + r'(?<!\bklist_t)' + r'(?<!\bkliter_t)' + r'(?<!\bkhash_t)' r'\((?:const )?(?:struct )?[a-zA-Z_]\w*(?: *\*(?:const)?)*\)' r' +' r'-?(?:\*+|&)?(?:\w+|\+\+|--|\()', cast_line) @@ -2560,22 +2564,43 @@ def CheckBraces(filename, clean_lines, linenum, error): line = clean_lines.elided[linenum] # get rid of comments and strings - if not (filename.endswith('.c') or filename.endswith('.h')): - if Match(r'\s*{\s*$', line): - # We allow an open brace to start a line in the case where someone - # is using braces in a block to explicitly create a new scope, which - # is commonly used to control the lifetime of stack-allocated - # variables. Braces are also used for brace initializers inside - # function calls. We don't detect this perfectly: we just don't - # complain if the last non-whitespace character on the previous - # non-blank line is ',', ';', ':', '(', '{', or '}', or if the - # previous line starts a preprocessor block. - prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if (not Search(r'[,;:}{(]\s*$', prevline) and - not Match(r'\s*#', prevline)): - error(filename, linenum, 'whitespace/braces', 4, - '{ should almost always be at the end' - ' of the previous line') + if Match(r'\s+{\s*$', line): + # We allow an open brace to start a line in the case where someone + # is using braces in a block to explicitly create a new scope, which + # is commonly used to control the lifetime of stack-allocated + # variables. Braces are also used for brace initializers inside + # function calls. We don't detect this perfectly: we just don't + # complain if the last non-whitespace character on the previous + # non-blank line is ',', ';', ':', '(', '{', or '}', or if the + # previous line starts a preprocessor block. + prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] + if (not Search(r'[,;:}{(]\s*$', prevline) and + not Match(r'\s*#', prevline)): + error(filename, linenum, 'whitespace/braces', 4, + '{ should almost always be at the end' + ' of the previous line') + + # Brace must appear after function signature, but on the *next* line + if Match(r'^(?:\w+(?: ?\*+)? )+\w+\(', line): + pos = line.find('(') + (endline, end_linenum, endpos) = CloseExpression( + clean_lines, linenum, pos) + if endline.endswith('{'): + error(filename, end_linenum, 'readability/braces', 5, + 'Brace starting function body must be placed on its own line') + else: + func_start_linenum = end_linenum + 1 + while not clean_lines.lines[func_start_linenum] == '{': + if not Match(r'^(?:\s*\b(?:FUNC_ATTR|REAL_FATTR)_\w+\b(?:\(\d+(, \d+)*\))?)+$', + clean_lines.lines[func_start_linenum]): + if clean_lines.lines[func_start_linenum].endswith('{'): + error(filename, func_start_linenum, + 'readability/braces', 5, + 'Brace starting function body must be placed ' + 'after the function signature') + break + else: + func_start_linenum += 1 # An else clause should be on the same line as the preceding closing brace. # If there is no preceding closing brace, there should be one. @@ -3409,8 +3434,9 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]): # When reading from stdin, the extension is unknown, so no cpplint tests # should rely on the extension. if filename != '-' and file_extension not in _valid_extensions: - sys.stderr.write('Ignoring %s; not a valid file name ' - '(%s)\n' % (filename, ', '.join(_valid_extensions))) + sys.stderr.write('Ignoring {}; only linting {} files\n'.format( + filename, + ', '.join('.{}'.format(ext) for ext in _valid_extensions))) else: ProcessFileData(filename, file_extension, lines, Error, extra_check_functions) diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 701a1cbf2b..7daa4d7207 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -387,6 +387,8 @@ static inline void typval_encode_list_start(EncodedData *const edata, #define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \ typval_encode_list_start(edata, (size_t)(len)) +#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) + static inline void typval_encode_between_list_items(EncodedData *const edata) FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL { @@ -427,6 +429,8 @@ static inline void typval_encode_dict_start(EncodedData *const edata, #define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \ typval_encode_dict_start(edata, (size_t)(len)) +#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv) + #define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, kv_pair) static inline void typval_encode_after_key(EncodedData *const edata) @@ -499,11 +503,13 @@ static inline void typval_encode_dict_end(EncodedData *const edata) #undef TYPVAL_ENCODE_CONV_FUNC_END #undef TYPVAL_ENCODE_CONV_EMPTY_LIST #undef TYPVAL_ENCODE_CONV_LIST_START +#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START #undef TYPVAL_ENCODE_CONV_EMPTY_DICT #undef TYPVAL_ENCODE_CONV_NIL #undef TYPVAL_ENCODE_CONV_BOOL #undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER #undef TYPVAL_ENCODE_CONV_DICT_START +#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START #undef TYPVAL_ENCODE_CONV_DICT_END #undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY #undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index fa41f0f382..600cf575fc 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -189,14 +189,18 @@ open_buffer ( curwin->w_cursor.lnum = 1; curwin->w_cursor.col = 0; - /* Set or reset 'modified' before executing autocommands, so that - * it can be changed there. */ - if (!readonlymode && !bufempty()) + // Set or reset 'modified' before executing autocommands, so that + // it can be changed there. + if (!readonlymode && !bufempty()) { changed(); - else if (retval != FAIL) - unchanged(curbuf, FALSE); - apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, FALSE, - curbuf, &retval); + } else if (retval == OK) { + unchanged(curbuf, false); + } + + if (retval == OK) { + apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, false, + curbuf, &retval); + } } } @@ -206,22 +210,21 @@ open_buffer ( parse_cino(curbuf); } - /* - * Set/reset the Changed flag first, autocmds may change the buffer. - * Apply the automatic commands, before processing the modelines. - * So the modelines have priority over auto commands. - */ - /* When reading stdin, the buffer contents always needs writing, so set - * the changed flag. Unless in readonly mode: "ls | nvim -R -". - * When interrupted and 'cpoptions' contains 'i' set changed flag. */ + // Set/reset the Changed flag first, autocmds may change the buffer. + // Apply the automatic commands, before processing the modelines. + // So the modelines have priority over auto commands. + + // When reading stdin, the buffer contents always needs writing, so set + // the changed flag. Unless in readonly mode: "ls | nvim -R -". + // When interrupted and 'cpoptions' contains 'i' set changed flag. if ((got_int && vim_strchr(p_cpo, CPO_INTMOD) != NULL) - || modified_was_set /* ":set modified" used in autocmd */ - || (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL) - ) + || modified_was_set // ":set modified" used in autocmd + || (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL)) { changed(); - else if (retval != FAIL && !read_stdin) - unchanged(curbuf, FALSE); - save_file_ff(curbuf); /* keep this fileformat */ + } else if (retval == OK && !read_stdin) { + unchanged(curbuf, false); + } + save_file_ff(curbuf); // keep this fileformat /* require "!" to overwrite the file, because it wasn't read completely */ if (aborting()) @@ -3442,7 +3445,7 @@ int build_stl_str_hl( case STL_KEYMAP: fillable = false; - if (get_keymap_str(wp, tmp, TMPLEN)) + if (get_keymap_str(wp, (char_u *)"<%s>", tmp, TMPLEN)) str = tmp; break; case STL_PAGENUM: diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 4501c2e0a6..6b14d21da7 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6410,8 +6410,8 @@ static void dict_free_contents(dict_T *d) { while (!QUEUE_EMPTY(&d->watchers)) { QUEUE *w = QUEUE_HEAD(&d->watchers); DictWatcher *watcher = dictwatcher_node_data(w); - dictwatcher_free(watcher); QUEUE_REMOVE(w); + dictwatcher_free(watcher); } hash_clear(&d->dv_hashtab); @@ -6445,7 +6445,7 @@ void dict_free(dict_T *d) { */ dictitem_T *dictitem_alloc(char_u *key) FUNC_ATTR_NONNULL_RET { - dictitem_T *di = xmalloc(sizeof(dictitem_T) + STRLEN(key)); + dictitem_T *di = xmalloc(offsetof(dictitem_T, di_key) + STRLEN(key) + 1); #ifndef __clang_analyzer__ STRCPY(di->di_key, key); #endif @@ -6726,6 +6726,7 @@ static bool get_dict_callback(dict_T *d, char *key, Callback *result) /// Get a string item from a dictionary. /// /// @param save whether memory should be allocated for the return value +/// when false a shared buffer is used, can only be used once! /// /// @return the entry or NULL if the entry doesn't exist. char_u *get_dict_string(dict_T *d, char *key, bool save) @@ -8828,34 +8829,73 @@ static void f_executable(typval_T *argvars, typval_T *rettv, FunPtr fptr) || (gettail_dir(name) != name && os_can_exe(name, NULL, false)); } +static char_u * get_list_line(int c, void *cookie, int indent) +{ + listitem_T **p = (listitem_T **)cookie; + listitem_T *item = *p; + char_u buf[NUMBUFLEN]; + char_u *s; + + if (item == NULL) { + return NULL; + } + s = get_tv_string_buf_chk(&item->li_tv, buf); + *p = item->li_next; + return s == NULL ? NULL : vim_strsave(s); +} + // "execute(command)" function static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) { int save_msg_silent = msg_silent; + int save_emsg_silent = emsg_silent; + bool save_emsg_noredir = emsg_noredir; garray_T *save_capture_ga = capture_ga; if (check_secure()) { return; } + if (argvars[1].v_type != VAR_UNKNOWN) { + char_u buf[NUMBUFLEN]; + char_u *s = get_tv_string_buf_chk(&argvars[1], buf); + + if (s == NULL) { + return; + } + if (STRNCMP(s, "silent", 6) == 0) { + msg_silent++; + } + if (STRCMP(s, "silent!") == 0) { + emsg_silent = true; + emsg_noredir = true; + } + } else { + msg_silent++; + } + garray_T capture_local; + ga_init(&capture_local, (int)sizeof(char), 80); capture_ga = &capture_local; - ga_init(capture_ga, (int)sizeof(char), 80); - msg_silent++; if (argvars[0].v_type != VAR_LIST) { do_cmdline_cmd((char *)get_tv_string(&argvars[0])); } else if (argvars[0].vval.v_list != NULL) { - for (listitem_T *li = argvars[0].vval.v_list->lv_first; - li != NULL; li = li->li_next) { - do_cmdline_cmd((char *)get_tv_string(&li->li_tv)); - } + list_T *list = argvars[0].vval.v_list; + list->lv_refcount++; + listitem_T *item = list->lv_first; + do_cmdline(NULL, get_list_line, (void *)&item, + DOCMD_NOWAIT|DOCMD_VERBOSE|DOCMD_REPEAT|DOCMD_KEYTYPED); + list->lv_refcount--; } msg_silent = save_msg_silent; + emsg_silent = save_emsg_silent; + emsg_noredir = save_emsg_noredir; ga_append(capture_ga, NUL); rettv->v_type = VAR_STRING; - rettv->vval.v_string = capture_ga->ga_data; + rettv->vval.v_string = vim_strsave(capture_ga->ga_data); + ga_clear(capture_ga); capture_ga = save_capture_ga; } @@ -9534,24 +9574,29 @@ static void f_foldlevel(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T lnum; + linenr_T foldstart; + linenr_T foldend; + char_u *dashes; + linenr_T lnum; char_u *s; char_u *r; - int len; + int len; char *txt; + long count; rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; - if ((linenr_T)vimvars[VV_FOLDSTART].vv_nr > 0 - && (linenr_T)vimvars[VV_FOLDEND].vv_nr - <= curbuf->b_ml.ml_line_count - && vimvars[VV_FOLDDASHES].vv_str != NULL) { + + foldstart = (linenr_T)get_vim_var_nr(VV_FOLDSTART); + foldend = (linenr_T)get_vim_var_nr(VV_FOLDEND); + dashes = get_vim_var_str(VV_FOLDDASHES); + if (foldstart > 0 && foldend <= curbuf->b_ml.ml_line_count + && dashes != NULL) { /* Find first non-empty line in the fold. */ - lnum = (linenr_T)vimvars[VV_FOLDSTART].vv_nr; - while (lnum < (linenr_T)vimvars[VV_FOLDEND].vv_nr) { - if (!linewhite(lnum)) + for (lnum = foldstart; lnum < foldend; ++lnum) { + if (!linewhite(lnum)) { break; - ++lnum; + } } /* Find interesting text in this line. */ @@ -9559,21 +9604,19 @@ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr) /* skip C comment-start */ if (s[0] == '/' && (s[1] == '*' || s[1] == '/')) { s = skipwhite(s + 2); - if (*skipwhite(s) == NUL - && lnum + 1 < (linenr_T)vimvars[VV_FOLDEND].vv_nr) { + if (*skipwhite(s) == NUL && lnum + 1 < foldend) { s = skipwhite(ml_get(lnum + 1)); if (*s == '*') s = skipwhite(s + 1); } } + count = (long)(foldend - foldstart + 1); txt = _("+-%s%3ld lines: "); r = xmalloc(STRLEN(txt) - + STRLEN(vimvars[VV_FOLDDASHES].vv_str) // for %s - + 20 // for %3ld - + STRLEN(s)); // concatenated - sprintf((char *)r, txt, vimvars[VV_FOLDDASHES].vv_str, - (long)((linenr_T)vimvars[VV_FOLDEND].vv_nr - - (linenr_T)vimvars[VV_FOLDSTART].vv_nr + 1)); + + STRLEN(dashes) // for %s + + 20 // for %3ld + + STRLEN(s)); // concatenated + sprintf((char *)r, txt, dashes, count); len = (int)STRLEN(r); STRCAT(r, s); /* remove 'foldmarker' and 'commentstring' */ @@ -15406,12 +15449,11 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - char_u *group = get_dict_string(d, "group", false); + char_u *group = get_dict_string(d, "group", true); int priority = get_dict_number(d, "priority"); int id = get_dict_number(d, "id"); char_u *conceal = dict_find(d, (char_u *)"conceal", -1) != NULL - ? get_dict_string(d, "conceal", - false) + ? get_dict_string(d, "conceal", true) : NULL; if (i == 0) { match_add(curwin, group, @@ -15422,6 +15464,8 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) list_unref(s); s = NULL; } + xfree(group); + xfree(conceal); li = li->li_next; } rettv->vval.v_number = 0; @@ -16955,8 +16999,12 @@ static void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, } // get shell command to execute - char **argv = tv_to_argv(&argvars[0], NULL, NULL); + bool executable = true; + char **argv = tv_to_argv(&argvars[0], NULL, &executable); if (!argv) { + if (!executable) { + set_vim_var_nr(VV_SHELL_ERROR, (long)-1); + } xfree(input); return; // Already did emsg. } @@ -19147,23 +19195,25 @@ static inline void _nothing_conv_func_end(typval_T *const tv, const int copyID) } \ } while (0) -static inline int _nothing_conv_list_start(typval_T *const tv) +static inline int _nothing_conv_real_list_after_start( + typval_T *const tv, MPConvStackVal *const mpsv) FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT { - if (tv == NULL) { - return NOTDONE; - } + assert(tv != NULL); tv->v_lock = VAR_UNLOCKED; if (tv->vval.v_list->lv_refcount > 1) { tv->vval.v_list->lv_refcount--; tv->vval.v_list = NULL; + mpsv->data.l.li = NULL; return OK; } return NOTDONE; } -#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \ +#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) + +#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) \ do { \ - if (_nothing_conv_list_start(tv) != NOTDONE) { \ + if (_nothing_conv_real_list_after_start(tv, &mpsv) != NOTDONE) { \ goto typval_encode_stop_converting_one_item; \ } \ } while (0) @@ -19183,9 +19233,9 @@ static inline void _nothing_conv_list_end(typval_T *const tv) } #define TYPVAL_ENCODE_CONV_LIST_END(tv) _nothing_conv_list_end(tv) -static inline int _nothing_conv_dict_start(typval_T *const tv, - dict_T **const dictp, - const void *const nodictvar) +static inline int _nothing_conv_real_dict_after_start( + typval_T *const tv, dict_T **const dictp, const void *const nodictvar, + MPConvStackVal *const mpsv) FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT { if (tv != NULL) { @@ -19194,15 +19244,18 @@ static inline int _nothing_conv_dict_start(typval_T *const tv, if ((const void *)dictp != nodictvar && (*dictp)->dv_refcount > 1) { (*dictp)->dv_refcount--; *dictp = NULL; + mpsv->data.d.todo = 0; return OK; } return NOTDONE; } -#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \ +#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) + +#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv) \ do { \ - if (_nothing_conv_dict_start(tv, (dict_T **)&dict, \ - (void *)&TYPVAL_ENCODE_NODICT_VAR) \ - != NOTDONE) { \ + if (_nothing_conv_real_dict_after_start( \ + tv, (dict_T **)&dict, (void *)&TYPVAL_ENCODE_NODICT_VAR, \ + &mpsv) != NOTDONE) { \ goto typval_encode_stop_converting_one_item; \ } \ } while (0) @@ -19253,9 +19306,11 @@ static inline void _nothing_conv_dict_end(typval_T *const tv, #undef TYPVAL_ENCODE_CONV_EMPTY_LIST #undef TYPVAL_ENCODE_CONV_EMPTY_DICT #undef TYPVAL_ENCODE_CONV_LIST_START +#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START #undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS #undef TYPVAL_ENCODE_CONV_LIST_END #undef TYPVAL_ENCODE_CONV_DICT_START +#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START #undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK #undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY #undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS @@ -21658,9 +21713,12 @@ void func_unref(char_u *name) fp = find_func(name); if (fp == NULL) { #ifdef EXITFREE - if (!entered_free_all_mem) // NOLINT(readability/braces) -#endif + if (!entered_free_all_mem) { EMSG2(_(e_intern2), "func_unref()"); + } +#else + EMSG2(_(e_intern2), "func_unref()"); +#endif } else { user_func_unref(fp); } @@ -23130,11 +23188,10 @@ static void on_job_output(Stream *stream, TerminalJobData *data, RBuffer *buf, terminal_receive(data->term, ptr, count); } + rbuffer_consumed(buf, count); if (callback->type != kCallbackNone) { process_job_event(data, callback, type, ptr, count, 0); } - - rbuffer_consumed(buf, count); } static void eval_job_process_exit_cb(Process *proc, int status, void *d) diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 5fb99fecc6..e0b72feb19 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -79,7 +79,7 @@ return { eval={args=1}, eventhandler={}, executable={args=1}, - execute={args=1}, + execute={args={1, 2}}, exepath={args=1}, exists={args=1}, exp={args=1, func="float_op_wrapper", data="&exp"}, diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index 071fbc3923..ee66b7cf09 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -369,6 +369,8 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, #define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \ ga_append(gap, '[') +#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) + #define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ ga_concat(gap, "{}") @@ -383,6 +385,8 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, #define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \ ga_append(gap, '{') +#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv) + #define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \ ga_append(gap, '}') @@ -789,11 +793,13 @@ bool encode_check_json_key(const typval_T *const tv) #undef TYPVAL_ENCODE_CONV_FUNC_END #undef TYPVAL_ENCODE_CONV_EMPTY_LIST #undef TYPVAL_ENCODE_CONV_LIST_START +#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START #undef TYPVAL_ENCODE_CONV_EMPTY_DICT #undef TYPVAL_ENCODE_CONV_NIL #undef TYPVAL_ENCODE_CONV_BOOL #undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER #undef TYPVAL_ENCODE_CONV_DICT_START +#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START #undef TYPVAL_ENCODE_CONV_DICT_END #undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY #undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS @@ -933,6 +939,8 @@ char *encode_tv2json(typval_T *tv, size_t *len) #define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \ msgpack_pack_array(packer, (size_t)(len)) +#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) + #define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ msgpack_pack_map(packer, 0) @@ -954,6 +962,8 @@ char *encode_tv2json(typval_T *tv, size_t *len) #define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \ msgpack_pack_map(packer, (size_t)(len)) +#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv) + #define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) #define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) @@ -994,11 +1004,13 @@ char *encode_tv2json(typval_T *tv, size_t *len) #undef TYPVAL_ENCODE_CONV_FUNC_END #undef TYPVAL_ENCODE_CONV_EMPTY_LIST #undef TYPVAL_ENCODE_CONV_LIST_START +#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START #undef TYPVAL_ENCODE_CONV_EMPTY_DICT #undef TYPVAL_ENCODE_CONV_NIL #undef TYPVAL_ENCODE_CONV_BOOL #undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER #undef TYPVAL_ENCODE_CONV_DICT_START +#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START #undef TYPVAL_ENCODE_CONV_DICT_END #undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY #undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h index 3e1170b8fa..365eb2dd77 100644 --- a/src/nvim/eval/typval_encode.c.h +++ b/src/nvim/eval/typval_encode.c.h @@ -129,6 +129,16 @@ /// point to a special dictionary. /// @param len List length. Is an expression which evaluates to an integer. +/// @def TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START +/// @brief Macros used after pushing list onto the stack +/// +/// Only used for real list_T* lists, not for special dictionaries or partial +/// arguments. +/// +/// @param tv Pointer to typval where value is stored. May be NULL. May +/// point to a special dictionary. +/// @param mpsv Pushed MPConvStackVal value. + /// @def TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS /// @brief Macros used after finishing converting non-last list item /// @@ -142,6 +152,9 @@ /// @def TYPVAL_ENCODE_CONV_DICT_START /// @brief Macros used before starting to convert non-empty dictionary /// +/// Only used for real dict_T* dictionaries, not for special dictionaries. Also +/// used for partial self dictionary. +/// /// @param tv Pointer to typval where dictionary is stored. May be NULL. May /// point to a special dictionary. /// @param dict Converted dictionary, lvalue or #TYPVAL_ENCODE_NODICT_VAR @@ -149,6 +162,14 @@ /// @param len Dictionary length. Is an expression which evaluates to an /// integer. +/// @def TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START +/// @brief Macros used after pushing dictionary onto the stack +/// +/// @param tv Pointer to typval where dictionary is stored. May be NULL. +/// May not point to a special dictionary. +/// @param dict Converted dictionary, lvalue. +/// @param mpsv Pushed MPConvStackVal value. + /// @def TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK /// @brief Macros used to check special dictionary key /// @@ -354,6 +375,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE( }, }, })); + TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, _mp_last(*mpstack)); break; } case VAR_SPECIAL: { @@ -564,6 +586,8 @@ _convert_one_value_regular_dict: }, }, })); + TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, tv->vval.v_dict, + _mp_last(*mpstack)); break; } case VAR_UNKNOWN: { @@ -732,6 +756,8 @@ typval_encode_stop_converting_one_item: }, }, })); + TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(NULL, pt->pt_dict, + _mp_last(mpstack)); } else { TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, -1); } diff --git a/src/nvim/eval/typval_encode.h b/src/nvim/eval/typval_encode.h index 6517efa961..ba325b8f55 100644 --- a/src/nvim/eval/typval_encode.h +++ b/src/nvim/eval/typval_encode.h @@ -108,37 +108,38 @@ static inline size_t tv_strlen(const typval_T *const tv) } \ } while (0) -#define _TYPVAL_ENCODE_CHECK_SELF_REFERENCE_INNER_2(name) \ - _typval_encode_##name##_check_self_reference -#define _TYPVAL_ENCODE_CHECK_SELF_REFERENCE_INNER(name) \ - _TYPVAL_ENCODE_CHECK_SELF_REFERENCE_INNER_2(name) +#define _TYPVAL_ENCODE_FUNC_NAME_INNER_2(pref, name, suf) \ + pref##name##suf +#define _TYPVAL_ENCODE_FUNC_NAME_INNER(pref, name, suf) \ + _TYPVAL_ENCODE_FUNC_NAME_INNER_2(pref, name, suf) + +/// Construct function name, possibly using macros +/// +/// Is used to expand macros that may appear in arguments. +/// +/// @note Expands all arguments, even if only one is needed. +/// +/// @param[in] pref Prefix. +/// @param[in] suf Suffix. +/// +/// @return Concat: pref + #TYPVAL_ENCODE_NAME + suf. +#define _TYPVAL_ENCODE_FUNC_NAME(pref, suf) \ + _TYPVAL_ENCODE_FUNC_NAME_INNER(pref, TYPVAL_ENCODE_NAME, suf) /// Self reference checker function name #define _TYPVAL_ENCODE_CHECK_SELF_REFERENCE \ - _TYPVAL_ENCODE_CHECK_SELF_REFERENCE_INNER(TYPVAL_ENCODE_NAME) - -#define _TYPVAL_ENCODE_ENCODE_INNER_2(name) encode_vim_to_##name -#define _TYPVAL_ENCODE_ENCODE_INNER(name) _TYPVAL_ENCODE_ENCODE_INNER_2(name) + _TYPVAL_ENCODE_FUNC_NAME(_typval_encode_, _check_self_reference) /// Entry point function name -#define _TYPVAL_ENCODE_ENCODE _TYPVAL_ENCODE_ENCODE_INNER(TYPVAL_ENCODE_NAME) - -#define _TYPVAL_ENCODE_CONVERT_ONE_VALUE_INNER_2(name) \ - _typval_encode_##name##_convert_one_value -#define _TYPVAL_ENCODE_CONVERT_ONE_VALUE_INNER(name) \ - _TYPVAL_ENCODE_CONVERT_ONE_VALUE_INNER_2(name) +#define _TYPVAL_ENCODE_ENCODE \ + _TYPVAL_ENCODE_FUNC_NAME(encode_vim_to_, ) /// Name of the …convert_one_value function #define _TYPVAL_ENCODE_CONVERT_ONE_VALUE \ - _TYPVAL_ENCODE_CONVERT_ONE_VALUE_INNER(TYPVAL_ENCODE_NAME) - -#define _TYPVAL_ENCODE_NODICT_VAR_INNER_2(name) \ - _typval_encode_##name##_nodict_var -#define _TYPVAL_ENCODE_NODICT_VAR_INNER(name) \ - _TYPVAL_ENCODE_NODICT_VAR_INNER_2(name) + _TYPVAL_ENCODE_FUNC_NAME(_typval_encode_, _convert_one_value) /// Name of the dummy const dict_T *const variable #define TYPVAL_ENCODE_NODICT_VAR \ - _TYPVAL_ENCODE_NODICT_VAR_INNER(TYPVAL_ENCODE_NAME) + _TYPVAL_ENCODE_FUNC_NAME(_typval_encode_, _nodict_var) #endif // NVIM_EVAL_TYPVAL_ENCODE_H diff --git a/src/nvim/event/libuv_process.c b/src/nvim/event/libuv_process.c index a68badcc8f..907187aa17 100644 --- a/src/nvim/event/libuv_process.c +++ b/src/nvim/event/libuv_process.c @@ -19,8 +19,7 @@ bool libuv_process_spawn(LibuvProcess *uvproc) Process *proc = (Process *)uvproc; uvproc->uvopts.file = proc->argv[0]; uvproc->uvopts.args = proc->argv; - uvproc->uvopts.flags = UV_PROCESS_WINDOWS_HIDE - | UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS; + uvproc->uvopts.flags = UV_PROCESS_WINDOWS_HIDE; if (proc->detach) { uvproc->uvopts.flags |= UV_PROCESS_DETACHED; } diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c index 39dd5fd55a..dc7886469b 100644 --- a/src/nvim/event/process.c +++ b/src/nvim/event/process.c @@ -170,8 +170,9 @@ int process_wait(Process *proc, int ms, MultiQueue *events) int status = -1; bool interrupted = false; if (!proc->refcount) { + status = proc->status; LOOP_PROCESS_EVENTS(proc->loop, proc->events, 0); - return proc->status; + return status; } if (!events) { diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index b1a17e8c44..69eed33736 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1193,8 +1193,8 @@ static void do_filter( if (do_out) { if (otmp != NULL) { - if (readfile(otmp, NULL, line2, (linenr_T)0, (linenr_T)MAXLNUM, - eap, READ_FILTER) == FAIL) { + if (readfile(otmp, NULL, line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, + READ_FILTER) != OK) { if (!aborting()) { msg_putchar('\n'); EMSG2(_(e_notread), otmp); @@ -6093,7 +6093,6 @@ void ex_substitute(exarg_T *eap) int save_w_p_cul = curwin->w_p_cul; int save_w_p_cuc = curwin->w_p_cuc; - emsg_off++; // No error messages during command preview. curbuf->b_p_ul = LONG_MAX; // make sure we can undo all changes curwin->w_p_cul = false; // Disable 'cursorline' curwin->w_p_cuc = false; // Disable 'cursorcolumn' @@ -6120,6 +6119,5 @@ void ex_substitute(exarg_T *eap) restore_search_patterns(); win_size_restore(&save_view); ga_clear(&save_view); - emsg_off--; unblock_autocmds(); } diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index b998b81284..88095602ba 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -480,6 +480,12 @@ return { func='ex_close', }, { + command='clearjumps', + flags=bit.bor(TRLBAR, CMDWIN), + addr_type=ADDR_LINES, + func='ex_clearjumps', + }, + { command='cmap', flags=bit.bor(EXTRA, TRLBAR, NOTRLCOM, USECTRLV, CMDWIN), addr_type=ADDR_LINES, diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index eccece7ac7..5ff79bcfef 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -6910,9 +6910,10 @@ static void ex_read(exarg_T *eap) eap->line2, (linenr_T)0, (linenr_T)MAXLNUM, eap, 0); } - if (i == FAIL) { - if (!aborting()) + if (i != OK) { + if (!aborting()) { EMSG2(_(e_notopen), eap->arg); + } } else { if (empty && exmode_active) { /* Delete the empty line that remains. Historically ex does @@ -9674,9 +9675,20 @@ bool cmd_can_preview(char_u *cmd) if (*ea.cmd == '*') { ea.cmd = skipwhite(ea.cmd + 1); } - find_command(&ea, NULL); + char_u *end = find_command(&ea, NULL); + + switch (ea.cmdidx) { + case CMD_substitute: + case CMD_smagic: + case CMD_snomagic: + // Only preview once the pattern delimiter has been typed + if (*end && !ASCII_ISALNUM(*end)) { + return true; + } + break; + default: + break; + } - return ea.cmdidx == CMD_substitute - || ea.cmdidx == CMD_smagic - || ea.cmdidx == CMD_snomagic; + return false; } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 7c725179dd..dba7a73814 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -1607,7 +1607,9 @@ static int command_line_changed(CommandLineState *s) // - Update the screen while the effects are in place. // - Immediately undo the effects. State |= CMDPREVIEW; + emsg_silent++; // Block error reporting as the command may be incomplete do_cmdline(ccline.cmdbuff, NULL, NULL, DOCMD_KEEPLINE|DOCMD_NOWAIT); + emsg_silent--; // Unblock error reporting // Restore the window "view". curwin->w_cursor = s->old_cursor; diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index e10f7fd2a2..e734cde905 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -51,6 +51,7 @@ #include "nvim/window.h" #include "nvim/shada.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/os/time.h" #include "nvim/os/input.h" @@ -248,7 +249,7 @@ void filemess(buf_T *buf, char_u *name, char_u *s, int attr) * READ_DUMMY read into a dummy buffer (to check if file contents changed) * READ_KEEP_UNDO don't clear undo info or read it from a file * - * return FAIL for failure, OK otherwise + * return FAIL for failure, NOTDONE for directory (failure), or OK */ int readfile ( @@ -257,7 +258,7 @@ readfile ( linenr_T from, linenr_T lines_to_skip, linenr_T lines_to_read, - exarg_T *eap, /* can be NULL! */ + exarg_T *eap, // can be NULL! int flags ) { @@ -443,13 +444,14 @@ readfile ( // ... or a character special file named /dev/fd/<n> # endif ) { - if (S_ISDIR(perm)) + if (S_ISDIR(perm)) { filemess(curbuf, fname, (char_u *)_("is a directory"), 0); - else + } else { filemess(curbuf, fname, (char_u *)_("is not a file"), 0); + } msg_end(); msg_scroll = msg_save; - return FAIL; + return S_ISDIR(perm) ? NOTDONE : FAIL; } #endif } @@ -5103,13 +5105,13 @@ void buf_reload(buf_T *buf, int orig_mode) } if (saved == OK) { - curbuf->b_flags |= BF_CHECK_RO; /* check for RO again */ - keep_filetype = TRUE; /* don't detect 'filetype' */ - if (readfile(buf->b_ffname, buf->b_fname, (linenr_T)0, - (linenr_T)0, - (linenr_T)MAXLNUM, &ea, flags) == FAIL) { - if (!aborting()) + curbuf->b_flags |= BF_CHECK_RO; // check for RO again + keep_filetype = true; // don't detect 'filetype' + if (readfile(buf->b_ffname, buf->b_fname, (linenr_T)0, (linenr_T)0, + (linenr_T)MAXLNUM, &ea, flags) != OK) { + if (!aborting()) { EMSG2(_("E321: Could not reload \"%s\""), buf->b_fname); + } if (savebuf != NULL && buf_valid(savebuf) && buf == curbuf) { /* Put the text back from the save buffer. First * delete any lines that readfile() added. */ @@ -5224,6 +5226,10 @@ static void vim_maketempdir(void) // Try the entries in `TEMP_DIR_NAMES` to create the temp directory. char_u template[TEMP_FILE_PATH_MAXLEN]; char_u path[TEMP_FILE_PATH_MAXLEN]; + + // Make sure the umask doesn't remove the executable bit. + // "repl" has been reported to use "0177". + mode_t umask_save = umask(0077); for (size_t i = 0; i < ARRAY_SIZE(temp_dirs); i++) { // Expand environment variables, leave room for "/nvimXXXXXX/999999999" expand_env((char_u *)temp_dirs[i], template, TEMP_FILE_PATH_MAXLEN - 22); @@ -5247,6 +5253,7 @@ static void vim_maketempdir(void) os_rmdir((char *)path); } } + (void)umask(umask_save); } /// Delete "name" and everything in it, recursively. diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 872ff8d5b8..463f4fcd8d 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -409,10 +409,6 @@ EXTERN struct caller_scope { } provider_caller_scope; EXTERN int provider_call_nesting INIT(= 0); -/* Magic number used for hashitem "hi_key" value indicating a deleted item. - * Only the address is used. */ -EXTERN char_u hash_removed; - EXTERN int t_colors INIT(= 256); // int value of T_CCO @@ -636,10 +632,6 @@ EXTERN int exiting INIT(= FALSE); /* TRUE when planning to exit Vim. Might * still keep on running if there is a changed * buffer. */ -#if defined(EXITFREE) -// true when in or after free_all_mem() -EXTERN bool entered_free_all_mem INIT(= false); -#endif // volatile because it is used in signal handler deathtrap(). EXTERN volatile int full_screen INIT(= false); // TRUE when doing full-screen output @@ -877,9 +869,10 @@ EXTERN int mapped_ctrl_c INIT(= 0); // Modes where CTRL-C is mapped. EXTERN cmdmod_T cmdmod; /* Ex command modifiers */ -EXTERN int msg_silent INIT(= 0); /* don't print messages */ -EXTERN int emsg_silent INIT(= 0); /* don't print error messages */ -EXTERN int cmd_silent INIT(= FALSE); /* don't echo the command line */ +EXTERN int msg_silent INIT(= 0); // don't print messages +EXTERN int emsg_silent INIT(= 0); // don't print error messages +EXTERN bool emsg_noredir INIT(= false); // don't redirect error messages +EXTERN int cmd_silent INIT(= false); // don't echo the command line /* Values for swap_exists_action: what to do when swap file already exists */ #define SEA_NONE 0 /* don't use dialog */ diff --git a/src/nvim/hashtab.c b/src/nvim/hashtab.c index fa4077f22f..376f33e23e 100644 --- a/src/nvim/hashtab.c +++ b/src/nvim/hashtab.c @@ -36,6 +36,8 @@ # include "hashtab.c.generated.h" #endif +char hash_removed; + /// Initialize an empty hash table. void hash_init(hashtab_T *ht) { @@ -380,3 +382,13 @@ hash_T hash_hash(char_u *key) return hash; } + +/// Function to get HI_KEY_REMOVED value +/// +/// Used for testing because luajit ffi does not allow getting addresses of +/// globals. +const char_u *_hash_key_removed(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + return HI_KEY_REMOVED; +} diff --git a/src/nvim/hashtab.h b/src/nvim/hashtab.h index 7233d8c47c..0da2b13f2e 100644 --- a/src/nvim/hashtab.h +++ b/src/nvim/hashtab.h @@ -5,14 +5,19 @@ #include "nvim/types.h" +/// Magic number used for hashitem "hi_key" value indicating a deleted item +/// +/// Only the address is used. +extern char hash_removed; + /// Type for hash number (hash calculation result). typedef size_t hash_T; /// The address of "hash_removed" is used as a magic number /// for hi_key to indicate a removed item. -#define HI_KEY_REMOVED &hash_removed +#define HI_KEY_REMOVED ((char_u *)&hash_removed) #define HASHITEM_EMPTY(hi) ((hi)->hi_key == NULL \ - || (hi)->hi_key == &hash_removed) + || (hi)->hi_key == (char_u *)&hash_removed) /// A hastable item. /// diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c index 99e94fc60f..94bbaf4239 100644 --- a/src/nvim/keymap.c +++ b/src/nvim/keymap.c @@ -573,8 +573,10 @@ int find_special_key(const char_u **srcp, const size_t src_len, int *const modp, } else { l = 1; } - if (end - bp > l && bp[l + 1] == '>') { - bp += l; // anything accepted, like <C-?> + if (end - bp > l && bp[l] != '"' && bp[l + 1] == '>') { + // Anything accepted, like <C-?>, except <C-">, because the " + // ends the string. + bp += l; } } } diff --git a/src/nvim/mark.c b/src/nvim/mark.c index 6453c41415..4e05845eb5 100644 --- a/src/nvim/mark.c +++ b/src/nvim/mark.c @@ -130,17 +130,23 @@ int setmark_pos(int c, pos_T *pos, int fnum) return OK; } - if (c > 'z') /* some islower() and isupper() cannot handle - characters above 127 */ + buf_T *buf = buflist_findnr(fnum); + // Can't set a mark in a non-existant buffer. + if (buf == NULL) { return FAIL; - if (islower(c)) { + } + + if (ASCII_ISLOWER(c)) { i = c - 'a'; - RESET_FMARK(curbuf->b_namedm + i, *pos, curbuf->b_fnum); + RESET_FMARK(buf->b_namedm + i, *pos, fnum); return OK; } - if (isupper(c)) { - assert(c >= 'A' && c <= 'Z'); - i = c - 'A'; + if (ASCII_ISUPPER(c) || ascii_isdigit(c)) { + if (ascii_isdigit(c)) { + i = c - '0' + NMARKS; + } else { + i = c - 'A'; + } RESET_XFMARK(namedfm + i, *pos, fnum, NULL); return OK; } @@ -798,6 +804,13 @@ void ex_jumps(exarg_T *eap) MSG_PUTS("\n>"); } +void ex_clearjumps(exarg_T *eap) +{ + free_jumplist(curwin); + curwin->w_jumplistlen = 0; + curwin->w_jumplistidx = 0; +} + /* * print the changelist */ diff --git a/src/nvim/memline.c b/src/nvim/memline.c index a9a53ebca7..b8891f6560 100644 --- a/src/nvim/memline.c +++ b/src/nvim/memline.c @@ -1063,11 +1063,12 @@ void ml_recover(void) if (!cannot_open) { line_count = pp->pb_pointer[idx].pe_line_count; if (readfile(curbuf->b_ffname, NULL, lnum, - pp->pb_pointer[idx].pe_old_lnum - 1, - line_count, NULL, 0) == FAIL) - cannot_open = TRUE; - else + pp->pb_pointer[idx].pe_old_lnum - 1, line_count, + NULL, 0) != OK) { + cannot_open = true; + } else { lnum += line_count; + } } if (cannot_open) { ++error; diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 1884d55999..92ead873ae 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -17,16 +17,41 @@ // Force je_ prefix on jemalloc functions. # define JEMALLOC_NO_DEMANGLE # include <jemalloc/jemalloc.h> -# define malloc(size) je_malloc(size) -# define calloc(count, size) je_calloc(count, size) -# define realloc(ptr, size) je_realloc(ptr, size) -# define free(ptr) je_free(ptr) +#endif + +#ifdef UNIT_TESTING +# define malloc(size) mem_malloc(size) +# define calloc(count, size) mem_calloc(count, size) +# define realloc(ptr, size) mem_realloc(ptr, size) +# define free(ptr) mem_free(ptr) +# ifdef HAVE_JEMALLOC +MemMalloc mem_malloc = &je_malloc; +MemFree mem_free = &je_free; +MemCalloc mem_calloc = &je_calloc; +MemRealloc mem_realloc = &je_realloc; +# else +MemMalloc mem_malloc = &malloc; +MemFree mem_free = &free; +MemCalloc mem_calloc = &calloc; +MemRealloc mem_realloc = &realloc; +# endif +#else +# ifdef HAVE_JEMALLOC +# define malloc(size) je_malloc(size) +# define calloc(count, size) je_calloc(count, size) +# define realloc(ptr, size) je_realloc(ptr, size) +# define free(ptr) je_free(ptr) +# endif #endif #ifdef INCLUDE_GENERATED_DECLARATIONS # include "memory.c.generated.h" #endif +#ifdef EXITFREE +bool entered_free_all_mem = false; +#endif + /// Try to free memory. Used when trying to recover from out of memory errors. /// @see {xmalloc} void try_to_free_memory(void) @@ -353,15 +378,15 @@ char *xstpncpy(char *restrict dst, const char *restrict src, size_t maxlen) size_t xstrlcpy(char *restrict dst, const char *restrict src, size_t size) FUNC_ATTR_NONNULL_ALL { - size_t ret = strlen(src); + size_t ret = strlen(src); - if (size) { - size_t len = (ret >= size) ? size - 1 : ret; - memcpy(dst, src, len); - dst[len] = '\0'; - } + if (size) { + size_t len = (ret >= size) ? size - 1 : ret; + memcpy(dst, src, len); + dst[len] = '\0'; + } - return ret; + return ret; } /// strdup() wrapper @@ -371,6 +396,7 @@ size_t xstrlcpy(char *restrict dst, const char *restrict src, size_t size) /// @return pointer to a copy of the string char *xstrdup(const char *str) FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET + FUNC_ATTR_NONNULL_ALL { return xmemdupz(str, strlen(str)); } @@ -401,6 +427,7 @@ void *xmemrchr(const void *src, uint8_t c, size_t len) /// @return pointer to a copy of the string char *xstrndup(const char *str, size_t len) FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET + FUNC_ATTR_NONNULL_ALL { char *p = memchr(str, '\0', len); return xmemdupz(str, p ? (size_t)(p - str) : len); diff --git a/src/nvim/memory.h b/src/nvim/memory.h index 62cc78360c..250ac3e08f 100644 --- a/src/nvim/memory.h +++ b/src/nvim/memory.h @@ -1,9 +1,41 @@ #ifndef NVIM_MEMORY_H #define NVIM_MEMORY_H +#include <stdbool.h> // for bool #include <stdint.h> // for uint8_t #include <stddef.h> // for size_t -#include <time.h> // for time_t +#include <time.h> // for time_t + +/// `malloc()` function signature +typedef void *(*MemMalloc)(size_t); + +/// `free()` function signature +typedef void (*MemFree)(void *); + +/// `calloc()` function signature +typedef void *(*MemCalloc)(size_t, size_t); + +/// `realloc()` function signature +typedef void *(*MemRealloc)(void *, size_t); + +#ifdef UNIT_TESTING +/// When unit testing: pointer to the `malloc()` function, may be altered +extern MemMalloc mem_malloc; + +/// When unit testing: pointer to the `free()` function, may be altered +extern MemFree mem_free; + +/// When unit testing: pointer to the `calloc()` function, may be altered +extern MemCalloc mem_calloc; + +/// When unit testing: pointer to the `realloc()` function, may be altered +extern MemRealloc mem_realloc; +#endif + +#ifdef EXITFREE +/// Indicates that free_all_mem function was or is running +extern bool entered_free_all_mem; +#endif #ifdef INCLUDE_GENERATED_DECLARATIONS # include "memory.h.generated.h" diff --git a/src/nvim/message.c b/src/nvim/message.c index 637b89ccbe..2f8feda6ec 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -508,20 +508,22 @@ int emsg(char_u *s) * But do write it to the redirection file. */ if (emsg_silent != 0) { - msg_start(); - p = get_emsg_source(); - if (p != NULL) { - STRCAT(p, "\n"); - redir_write(p, STRLEN(p)); - xfree(p); - } - p = get_emsg_lnum(); - if (p != NULL) { - STRCAT(p, "\n"); - redir_write(p, STRLEN(p)); - xfree(p); + if (!emsg_noredir) { + msg_start(); + p = get_emsg_source(); + if (p != NULL) { + STRCAT(p, "\n"); + redir_write(p, STRLEN(p)); + xfree(p); + } + p = get_emsg_lnum(); + if (p != NULL) { + STRCAT(p, "\n"); + redir_write(p, STRLEN(p)); + xfree(p); + } + redir_write(s, STRLEN(s)); } - redir_write(s, STRLEN(s)); return true; } @@ -2508,8 +2510,7 @@ static void redir_write(char_u *str, int maxlen) int redirecting(void) { return redir_fd != NULL || *p_vfile != NUL - || redir_reg || redir_vname - ; + || redir_reg || redir_vname || capture_ga != NULL; } /* diff --git a/src/nvim/option.c b/src/nvim/option.c index 52a8b19ca4..a4e7da770e 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -213,12 +213,12 @@ typedef struct vimoption { #define P_VI_DEF 0x400U /* Use Vi default for Vim */ #define P_VIM 0x800U /* Vim option */ -/* when option changed, what to display: */ -#define P_RSTAT 0x1000U /* redraw status lines */ -#define P_RWIN 0x2000U /* redraw current window */ -#define P_RBUF 0x4000U /* redraw current buffer */ -#define P_RALL 0x6000U /* redraw all windows */ -#define P_RCLR 0x7000U /* clear and redraw all */ +// when option changed, what to display: +#define P_RSTAT 0x1000U ///< redraw status lines +#define P_RWIN 0x2000U ///< redraw current window and recompute text +#define P_RBUF 0x4000U ///< redraw current buffer and recompute text +#define P_RALL 0x6000U ///< redraw all windows +#define P_RCLR 0x7000U ///< clear and redraw all #define P_COMMA 0x8000U ///< comma separated list #define P_ONECOMMA 0x18000U ///< P_COMMA and cannot have two consecutive @@ -238,6 +238,8 @@ typedef struct vimoption { ///< when there is a redraw flag #define P_NO_DEF_EXP 0x8000000U ///< Do not expand default value. +#define P_RWINONLY 0x10000000U ///< only redraw current window + #define HIGHLIGHT_INIT \ "8:SpecialKey,~:EndOfBuffer,z:TermCursor,Z:TermCursorNC,@:NonText," \ "d:Directory,e:ErrorMsg,i:IncSearch,l:Search,m:MoreMsg,M:ModeMsg,n:LineNr," \ @@ -4357,17 +4359,24 @@ static void check_redraw(uint32_t flags) bool doclear = (flags & P_RCLR) == P_RCLR; bool all = ((flags & P_RALL) == P_RALL || doclear); - if ((flags & P_RSTAT) || all) /* mark all status lines dirty */ + if ((flags & P_RSTAT) || all) { // mark all status lines dirty status_redraw_all(); + } - if ((flags & P_RBUF) || (flags & P_RWIN) || all) + if ((flags & P_RBUF) || (flags & P_RWIN) || all) { changed_window_setting(); - if (flags & P_RBUF) + } + if (flags & P_RBUF) { redraw_curbuf_later(NOT_VALID); - if (doclear) + } + if (flags & P_RWINONLY) { + redraw_later(NOT_VALID); + } + if (doclear) { redraw_all_later(CLEAR); - else if (all) + } else if (all) { redraw_all_later(NOT_VALID); + } } /// Find index for named option diff --git a/src/nvim/options.lua b/src/nvim/options.lua index 70d1f73cf4..859658e40d 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -17,8 +17,8 @@ -- types: bool, number, string -- lists: (nil), comma, onecomma, flags, flagscomma -- scopes: global, buffer, window --- redraw options: statuslines, current_window, current_buffer, all_windows, --- everything, curswant +-- redraw options: statuslines, current_window, curent_window_only, +-- current_buffer, all_windows, everything, curswant -- default: {vi=…[, vim=…]} -- defaults: {condition=#if condition, if_true=default, if_false=default} -- #if condition: @@ -539,7 +539,7 @@ return { full_name='cursorline', abbreviation='cul', type='bool', scope={'window'}, vi_def=true, - redraw={'current_window'}, + redraw={'current_window_only'}, defaults={if_true={vi=false}} }, { diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index 83a0262545..47d541c6a7 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -560,8 +560,8 @@ const void *vim_colon_env_iter_rev(const char *const val, /// Vim's version of getenv(). /// Special handling of $HOME, $VIM and $VIMRUNTIME, allowing the user to /// override the vim runtime directory at runtime. Also does ACP to 'enc' -/// conversion for Win32. Results must be freed by the calling function. -/// @param name Name of environment variable to expand +/// conversion for Win32. Result must be freed by the caller. +/// @param name Environment variable to expand char *vim_getenv(const char *name) { const char *kos_env_path = os_getenv(name); @@ -598,6 +598,27 @@ char *vim_getenv(const char *name) if (p_hf != NULL && vim_strchr(p_hf, '$') == NULL) { vim_path = (char *)p_hf; } + +#ifdef WIN32 + // Find runtime path relative to the nvim binary i.e. ../share/runtime + if (vim_path == NULL) { + char exe_name[MAXPATHL]; + size_t exe_name_len = MAXPATHL; + if (os_exepath(exe_name, &exe_name_len) == 0) { + char *path_end = (char *)path_tail_with_sep((char_u *)exe_name); + *path_end = '\0'; // remove the trailing "nvim.exe" + path_end = (char *)path_tail((char_u *)exe_name); + *path_end = '\0'; // remove the trailing "bin/" + if (append_path( + exe_name, + "share" _PATHSEPSTR "nvim" _PATHSEPSTR "runtime" _PATHSEPSTR, + MAXPATHL) == OK) { + vim_path = exe_name; + } + } + } +#endif + if (vim_path != NULL) { // remove the file name char *vim_path_end = (char *)path_tail((char_u *)vim_path); diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 3c821936e9..4aa727733e 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -192,6 +192,18 @@ int os_nodetype(const char *name) return nodetype; } +/// Gets the absolute path of the currently running executable. +/// +/// @param[out] buffer Returns the path string. +/// @param[in] size Size of `buffer`. +/// +/// @return `0` on success, or libuv error code on failure. +int os_exepath(char *buffer, size_t *size) + FUNC_ATTR_NONNULL_ALL +{ + return uv_exepath(buffer, size); +} + /// Checks if the given path represents an executable file. /// /// @param[in] name Name of the executable. diff --git a/src/nvim/os/stdpaths.c b/src/nvim/os/stdpaths.c index 10b3f4c091..afb9bdec31 100644 --- a/src/nvim/os/stdpaths.c +++ b/src/nvim/os/stdpaths.c @@ -16,20 +16,29 @@ static const char *xdg_env_vars[] = { [kXDGDataDirs] = "XDG_DATA_DIRS", }; +#ifdef WIN32 +static const char *const xdg_defaults_env_vars[] = { + [kXDGConfigHome] = "LOCALAPPDATA", + [kXDGDataHome] = "LOCALAPPDATA", + [kXDGCacheHome] = "TEMP", + [kXDGRuntimeDir] = NULL, + [kXDGConfigDirs] = NULL, + [kXDGDataDirs] = NULL, +}; +#endif + /// Defaults for XDGVarType values /// /// Used in case environment variables contain nothing. Need to be expanded. static const char *const xdg_defaults[] = { #ifdef WIN32 - // Windows - [kXDGConfigHome] = "$LOCALAPPDATA", - [kXDGDataHome] = "$LOCALAPPDATA", - [kXDGCacheHome] = "$TEMP", + [kXDGConfigHome] = "~\\AppData\\Local", + [kXDGDataHome] = "~\\AppData\\Local", + [kXDGCacheHome] = "~\\AppData\\Local\\Temp", [kXDGRuntimeDir] = NULL, [kXDGConfigDirs] = NULL, [kXDGDataDirs] = NULL, #else - // Linux, BSD, CYGWIN, Apple [kXDGConfigHome] = "~/.config", [kXDGDataHome] = "~/.local/share", [kXDGCacheHome] = "~/.cache", @@ -50,7 +59,14 @@ char *stdpaths_get_xdg_var(const XDGVarType idx) const char *const env = xdg_env_vars[idx]; const char *const fallback = xdg_defaults[idx]; - const char *const env_val = os_getenv(env); + const char *env_val = os_getenv(env); + +#ifdef WIN32 + if (env_val == NULL) { + env_val = os_getenv(xdg_defaults_env_vars[idx]); + } +#endif + char *ret = NULL; if (env_val != NULL) { ret = xstrdup(env_val); diff --git a/src/nvim/po/uk.po b/src/nvim/po/uk.po index 3145931bfe..cff140508b 100644 --- a/src/nvim/po/uk.po +++ b/src/nvim/po/uk.po @@ -4424,8 +4424,8 @@ msgstr "" "перед записом: %s" #, c-format -msgid "E138: All %s.tmp.X files exist, cannot write ShaDa file!" -msgstr "E138: Усі файли %s.tmp.X зайнято, неможливо записати файл ShaDa!" +msgid "E929: All %s.tmp.X files exist, cannot write ShaDa file!" +msgstr "E929: Усі файли %s.tmp.X зайнято, неможливо записати файл ShaDa!" #, c-format msgid "System error while opening temporary ShaDa file %s for writing: %s" diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 41acc48f97..c0db076eff 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -4912,7 +4912,7 @@ void win_redr_status(win_T *wp) screen_fill(row, row + 1, len + wp->w_wincol, this_ru_col + wp->w_wincol, fillchar, fillchar, attr); - if (get_keymap_str(wp, NameBuff, MAXPATHL) + if (get_keymap_str(wp, (char_u *)"<%s>", NameBuff, MAXPATHL) && this_ru_col - len > (int)(STRLEN(NameBuff) + 1)) screen_puts(NameBuff, row, (int)(this_ru_col - STRLEN(NameBuff) - 1 + wp->w_wincol), attr); @@ -4993,8 +4993,9 @@ int stl_connected(win_T *wp) int get_keymap_str ( win_T *wp, - char_u *buf, /* buffer for the result */ - int len /* length of buffer */ + char_u *fmt, // format string containing one %s item + char_u *buf, // buffer for the result + int len // length of buffer ) { char_u *p; @@ -5021,10 +5022,9 @@ get_keymap_str ( else p = (char_u *)"lang"; } - if ((int)(STRLEN(p) + 3) < len) - sprintf((char *)buf, "<%s>", p); - else + if (vim_snprintf((char *)buf, len, (char *)fmt, p) > len - 1) { buf[0] = NUL; + } xfree(s); } return buf[0] != NUL; @@ -6752,10 +6752,12 @@ int showmode(void) if (p_fkmap) MSG_PUTS_ATTR(farsi_text_5, attr); if (State & LANGMAP) { - if (curwin->w_p_arab) + if (curwin->w_p_arab) { MSG_PUTS_ATTR(_(" Arabic"), attr); - else - MSG_PUTS_ATTR(_(" (lang)"), attr); + } else if (get_keymap_str(curwin, (char_u *)" (%s)", + NameBuff, MAXPATHL)) { + MSG_PUTS_ATTR(NameBuff, attr); + } } if ((State & INSERT) && p_paste) MSG_PUTS_ATTR(_(" (paste)"), attr); diff --git a/src/nvim/search.c b/src/nvim/search.c index 1029190db4..2a087276e7 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -3976,12 +3976,10 @@ current_search ( return OK; } -/* - * Check if the pattern is one character or zero-width. - * If move is true, check from the beginning of the buffer, - * else from the current cursor position. - * Returns TRUE, FALSE or -1 for failure. - */ +/// Check if the pattern is one character long or zero-width. +/// If move is true, check from the beginning of the buffer, +/// else from the current cursor position. +/// Returns TRUE, FALSE or -1 for failure. static int is_one_char(char_u *pattern, bool move) { regmmatch_T regmatch; @@ -3991,11 +3989,17 @@ static int is_one_char(char_u *pattern, bool move) int save_called_emsg = called_emsg; int flag = 0; + if (pattern == NULL) { + pattern = spats[last_idx].pat; + } + if (search_regcomp(pattern, RE_SEARCH, RE_SEARCH, SEARCH_KEEP, ®match) == FAIL) return -1; - /* move to match */ + // init startcol correctly + regmatch.startpos[0].col = -1; + // move to match if (move) { clearpos(&pos); } else { @@ -4003,21 +4007,29 @@ static int is_one_char(char_u *pattern, bool move) /* accept a match at the cursor position */ flag = SEARCH_START; } - if (searchit(curwin, curbuf, &pos, FORWARD, spats[last_idx].pat, 1, - SEARCH_KEEP + flag, RE_SEARCH, 0, NULL) != FAIL) { - /* Zero-width pattern should match somewhere, then we can check if - * start and end are in the same position. */ - called_emsg = FALSE; - nmatched = vim_regexec_multi(®match, curwin, curbuf, - pos.lnum, (colnr_T)0, NULL); - - if (!called_emsg) + if (searchit(curwin, curbuf, &pos, FORWARD, pattern, 1, + SEARCH_KEEP + flag, RE_SEARCH, 0, NULL) != FAIL) { + // Zero-width pattern should match somewhere, then we can check if + // start and end are in the same position. + called_emsg = false; + do { + regmatch.startpos[0].col++; + nmatched = vim_regexec_multi(®match, curwin, curbuf, + pos.lnum, regmatch.startpos[0].col, NULL); + if (!nmatched) { + break; + } + } while (regmatch.startpos[0].col < pos.col); + + if (!called_emsg) { result = (nmatched != 0 && regmatch.startpos[0].lnum == regmatch.endpos[0].lnum && regmatch.startpos[0].col == regmatch.endpos[0].col); - - if (!result && inc(&pos) >= 0 && pos.col == regmatch.endpos[0].col) - result = TRUE; + // one char width + if (!result && inc(&pos) >= 0 && pos.col == regmatch.endpos[0].col) { + result = true; + } + } } called_emsg |= save_called_emsg; diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 867c697a9a..8cf5976e8b 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -118,9 +118,10 @@ KHASH_SET_INIT_STR(strset) // E576: Missing '>' // E577: Illegal register name // E886: Can't rename viminfo file to %s! +// E929: Too many viminfo temp files, like %s! // Now only six of them are used: // E137: ShaDa file is not writeable (for pre-open checks) -// E138: All %s.tmp.X files exist, cannot write ShaDa file! +// E929: All %s.tmp.X files exist, cannot write ShaDa file! // RCERR (E576) for critical read errors. // RNERR (E136) for various errors when renaming. // RERR (E575) for various errors inside read ShaDa file. @@ -148,6 +149,9 @@ KHASH_SET_INIT_STR(strset) /// Common prefix for all ignorable “write” errors #define WERR "E574: " +/// Callback function for add_search_pattern +typedef void (*SearchPatternGetter)(SearchPattern *); + /// Flags for shada_read_file and children typedef enum { kShaDaWantInfo = 1, ///< Load non-mark information @@ -2323,8 +2327,9 @@ static inline ShaDaWriteResult shada_read_when_writing( /// @param[in] removable_bufs Buffers which are ignored /// /// @return ShadaEntry List of buffers to save, kSDItemBufferList entry. -static ShadaEntry shada_get_buflist(khash_t(bufset) *const removable_bufs) - FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +static inline ShadaEntry shada_get_buflist( + khash_t(bufset) *const removable_bufs) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE { int max_bufs = get_shada_parameter('%'); size_t buf_count = 0; @@ -2368,6 +2373,62 @@ static ShadaEntry shada_get_buflist(khash_t(bufset) *const removable_bufs) return buflist_entry; } +/// Save search pattern to PossiblyFreedShadaEntry +/// +/// @param[out] ret_pse Location where result will be saved. +/// @param[in] get_pattern Function used to get pattern. +/// @param[in] is_substitute_pattern True if pattern in question is substitute +/// pattern. Also controls whether some +/// fields should be initialized to default +/// or values from get_pattern. +/// @param[in] search_last_used Result of search_was_last_used(). +/// @param[in] search_highlighted True if search pattern was highlighted by +/// &hlsearch and this information should be +/// saved. +static inline void add_search_pattern(PossiblyFreedShadaEntry *const ret_pse, + const SearchPatternGetter get_pattern, + const bool is_substitute_pattern, + const bool search_last_used, + const bool search_highlighted) + FUNC_ATTR_ALWAYS_INLINE +{ + const ShadaEntry defaults = sd_default_values[kSDItemSearchPattern]; + SearchPattern pat; + get_pattern(&pat); + if (pat.pat != NULL) { + *ret_pse = (PossiblyFreedShadaEntry) { + .can_free_entry = false, + .data = { + .type = kSDItemSearchPattern, + .timestamp = pat.timestamp, + .data = { + .search_pattern = { + .magic = pat.magic, + .smartcase = !pat.no_scs, + .has_line_offset = (is_substitute_pattern + ? defaults.data.search_pattern.has_line_offset + : pat.off.line), + .place_cursor_at_end = ( + is_substitute_pattern + ? defaults.data.search_pattern.place_cursor_at_end + : pat.off.end), + .offset = (is_substitute_pattern + ? defaults.data.search_pattern.offset + : pat.off.off), + .is_last_used = (is_substitute_pattern ^ search_last_used), + .is_substitute_pattern = is_substitute_pattern, + .highlighted = ((is_substitute_pattern ^ search_last_used) + && search_highlighted), + .pat = (char *)pat.pat, + .additional_data = pat.additional_data, + .search_backward = (!is_substitute_pattern && pat.off.dir == '?'), + } + } + } + }; + } +} + /// Write ShaDa file /// /// @param[in] sd_writer Structure containing file writer definition. @@ -2529,45 +2590,14 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, const bool search_highlighted = !(no_hlsearch || find_shada_parameter('h') != NULL); const bool search_last_used = search_was_last_used(); -#define ADD_SEARCH_PAT(func, wms_attr, hlo, pcae, o, is_sub) \ - do { \ - SearchPattern pat; \ - func(&pat); \ - if (pat.pat != NULL) { \ - wms->wms_attr = (PossiblyFreedShadaEntry) { \ - .can_free_entry = false, \ - .data = { \ - .type = kSDItemSearchPattern, \ - .timestamp = pat.timestamp, \ - .data = { \ - .search_pattern = { \ - .magic = pat.magic, \ - .smartcase = !pat.no_scs, \ - .has_line_offset = hlo, \ - .place_cursor_at_end = pcae, \ - .offset = o, \ - .is_last_used = (is_sub ^ search_last_used), \ - .is_substitute_pattern = is_sub, \ - .highlighted = ((is_sub ^ search_last_used) \ - && search_highlighted), \ - .pat = (char *) pat.pat, \ - .additional_data = pat.additional_data, \ - .search_backward = (!is_sub && pat.off.dir == '?'), \ - } \ - } \ - } \ - }; \ - } \ - } while (0) // Initialize search pattern - ADD_SEARCH_PAT(get_search_pattern, search_pattern, pat.off.line, \ - pat.off.end, pat.off.off, false); + add_search_pattern(&wms->search_pattern, &get_search_pattern, false, + search_last_used, search_highlighted); // Initialize substitute search pattern - ADD_SEARCH_PAT(get_substitute_pattern, sub_search_pattern, false, false, 0, - true); -#undef ADD_SEARCH_PAT + add_search_pattern(&wms->sub_search_pattern, &get_substitute_pattern, true, + search_last_used, search_highlighted); // Initialize substitute replacement string { @@ -2590,10 +2620,12 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, // Initialize jump list const void *jump_iter = NULL; + setpcmark(); + cleanup_jumplist(); do { xfmark_T fm; - cleanup_jumplist(); jump_iter = mark_jumplist_iter(jump_iter, curwin, &fm); + const buf_T *const buf = (fm.fmark.fnum == 0 ? NULL : buflist_findnr(fm.fmark.fnum)); diff --git a/src/nvim/testdir/Makefile b/src/nvim/testdir/Makefile index 612071e2e2..814c0bc3cb 100644 --- a/src/nvim/testdir/Makefile +++ b/src/nvim/testdir/Makefile @@ -34,6 +34,7 @@ NEW_TESTS = \ test_cmdline.res \ test_cscope.res \ test_diffmode.res \ + test_gn.res \ test_hardcopy.res \ test_help_tagjump.res \ test_history.res \ diff --git a/src/nvim/testdir/test17.in b/src/nvim/testdir/test17.in index 83abe17770..1a4ac6b6d1 100644 --- a/src/nvim/testdir/test17.in +++ b/src/nvim/testdir/test17.in @@ -4,13 +4,7 @@ Tests for: STARTTEST :set isfname=@,48-57,/,.,-,_,+,,,$,:,~,{,} -:function! DeleteDirectory(dir) -: if has("win16") || has("win32") || has("win64") || has("dos16") || has("dos32") -: exec "silent !rmdir /Q /S " . a:dir -: else -: exec "silent !rm -rf " . a:dir -: endif -:endfun +:" :if has("unix") :let $CDIR = "." /CDIR @@ -36,7 +30,7 @@ STARTTEST :" check for 'include' without \zs or \ze :lang C :call delete("./Xbase.a") -:call DeleteDirectory("Xdir1") +:call delete("Xdir1", "rf") :!mkdir Xdir1 :!mkdir "Xdir1/dir2" :e! Xdir1/dir2/foo.a @@ -61,7 +55,7 @@ ENDTEST STARTTEST :" check for 'include' with \zs and \ze :call delete("./Xbase.b") -:call DeleteDirectory("Xdir1") +:call delete("Xdir1", "rf") :!mkdir Xdir1 :!mkdir "Xdir1/dir2" :let &include='^\s*%inc\s*/\zs[^/]\+\ze' @@ -91,7 +85,7 @@ ENDTEST STARTTEST :" check for 'include' with \zs and no \ze :call delete("./Xbase.c") -:call DeleteDirectory("Xdir1") +:call delete("Xdir1", "rf") :!mkdir Xdir1 :!mkdir "Xdir1/dir2" :let &include='^\s*%inc\s*\%([[:upper:]][^[:space:]]*\s\+\)\?\zs\S\+\ze' diff --git a/src/nvim/testdir/test53.in b/src/nvim/testdir/test53.in index f3778c5192..20b5d019af 100644 --- a/src/nvim/testdir/test53.in +++ b/src/nvim/testdir/test53.in @@ -4,8 +4,6 @@ Note that the end-of-line moves the cursor to the next test line. Also test match() and matchstr() -Also test the gn command and repeating it. - STARTTEST /^start:/ da" @@ -52,35 +50,6 @@ dit :put =match('abc', '\zs', 2, 1) " 2 :put =match('abc', '\zs', 3, 1) " 3 :put =match('abc', '\zs', 4, 1) " -1 -/^foobar -gncsearchmatch/one\_s*two\_s -:1 -gnd -/[a]bcdx -:1 -2gnd/join -/$ -0gnd -/\>\zs -0gnd/^ -gnd$h/\zs -gnd/[u]niquepattern/s -vlgnd -/mother -:set selection=exclusive -$cgNmongoose/i -cgnj -:" Make sure there is no other match y uppercase. -/x59 -gggnd -:" test repeating dgn -/^Johnny -ggdgn. -:" test repeating gUgn -/^Depp -gggUgn. -gg/a:0\@!\zs\d\+ -nygnop :/^start:/,/^end:/wq! test.out ENDTEST @@ -102,32 +71,4 @@ innertext object </b> </begin> SEARCH: -foobar -one -two -abcdx | abcdx | abcdx -join -lines -zero width pattern -delete first and last chars -uniquepattern uniquepattern -my very excellent mother just served us nachos -for (i=0; i<=10; i++) -a:10 - -a:1 - -a:20 -Y -text -Y ---1 -Johnny ---2 -Johnny ---3 -Depp ---4 -Depp ---5 end: diff --git a/src/nvim/testdir/test53.ok b/src/nvim/testdir/test53.ok index 05206972a4..d57d86bbb0 100644 --- a/src/nvim/testdir/test53.ok +++ b/src/nvim/testdir/test53.ok @@ -42,30 +42,4 @@ a 3 -1 SEARCH: -searchmatch -abcdx | | abcdx -join lines -zerowidth pattern -elete first and last char - uniquepattern -my very excellent mongoose just served us nachos -for (j=0; i<=10; i++) -a:10 - -a:1 -1 - -a:20 - -text -Y ---1 - ---2 - ---3 -DEPP ---4 -DEPP ---5 end: diff --git a/src/nvim/testdir/test73.in b/src/nvim/testdir/test73.in index 7d6c7287a5..9d50f7a789 100644 --- a/src/nvim/testdir/test73.in +++ b/src/nvim/testdir/test73.in @@ -8,16 +8,9 @@ STARTTEST :" This will cause a few errors, do it silently. :set visualbell :" -:function! DeleteDirectory(dir) -: if has("win16") || has("win32") || has("win64") || has("dos16") || has("dos32") -: exec "silent !rmdir /Q /S " . a:dir -: else -: exec "silent !rm -rf " . a:dir -: endif -:endfun :" On windows a stale "Xfind" directory may exist, remove it so that :" we start from a clean state. -:call DeleteDirectory("Xfind") +:call delete("Xfind", "rf") :new :let cwd=getcwd() :let test_out = cwd . '/test.out' @@ -169,7 +162,7 @@ SVoyager 2:w :exec "w >>" . test_out :q :exec "cd " . cwd -:call DeleteDirectory("Xfind") +:call delete("Xfind", "rf") :qa! ENDTEST diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index 818ff7cf54..60248bf430 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -4,6 +4,7 @@ source test_assign.vim source test_autocmd.vim source test_cursor_func.vim +source test_execute_func.vim source test_ex_undo.vim source test_expr.vim source test_expr_utf8.vim diff --git a/src/nvim/testdir/test_execute_func.vim b/src/nvim/testdir/test_execute_func.vim new file mode 100644 index 0000000000..6f61bede93 --- /dev/null +++ b/src/nvim/testdir/test_execute_func.vim @@ -0,0 +1,55 @@ +" test execute() + +func NestedEval() + let nested = execute('echo "nested\nlines"') + echo 'got: "' . nested . '"' +endfunc + +func NestedRedir() + redir => var + echo 'broken' + redir END +endfunc + +func Test_execute_string() + call assert_equal("\nnocompatible", execute('set compatible?')) + call assert_equal("\nsomething\nnice", execute('echo "something\nnice"')) + call assert_equal("noendofline", execute('echon "noendofline"')) + call assert_equal("", execute(123)) + + call assert_equal("\ngot: \"\nnested\nlines\"", execute('call NestedEval()')) + redir => redired + echo 'this' + let evaled = execute('echo "that"') + echo 'theend' + redir END +" Nvim supports execute('... :redir ...'), so this test is intentionally +" disabled. +" call assert_equal("\nthis\ntheend", redired) + call assert_equal("\nthat", evaled) + + call assert_fails('call execute("doesnotexist")', 'E492:') + call assert_fails('call execute(3.4)', 'E806:') +" Nvim supports execute('... :redir ...'), so this test is intentionally +" disabled. +" call assert_fails('call execute("call NestedRedir()")', 'E930:') + + call assert_equal("\nsomething", execute('echo "something"', '')) + call assert_equal("\nsomething", execute('echo "something"', 'silent')) + call assert_equal("\nsomething", execute('echo "something"', 'silent!')) + call assert_equal("", execute('burp', 'silent!')) + call assert_fails('call execute("echo \"x\"", 3.4)', 'E806:') + + call assert_equal("", execute("")) +endfunc + +func Test_execute_list() + call assert_equal("\nsomething\nnice", execute(['echo "something"', 'echo "nice"'])) + let l = ['for n in range(0, 3)', + \ 'echo n', + \ 'endfor'] + call assert_equal("\n0\n1\n2\n3", execute(l)) + + call assert_equal("", execute([])) + call assert_equal("", execute(v:_null_list)) +endfunc diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index 7483973fca..39dcacb55f 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -1,20 +1,5 @@ " Tests for expressions. -func Test_version() - call assert_true(has('patch-7.4.001')) - call assert_true(has('patch-7.4.01')) - call assert_true(has('patch-7.4.1')) - call assert_true(has('patch-6.9.999')) - call assert_true(has('patch-7.1.999')) - call assert_true(has('patch-7.4.123')) - - call assert_false(has('patch-7')) - call assert_false(has('patch-7.4')) - call assert_false(has('patch-7.4.')) - call assert_false(has('patch-9.1.0')) - call assert_false(has('patch-9.9.1')) -endfunc - func Test_equal() let base = {} func base.method() @@ -37,6 +22,35 @@ func Test_equal() call assert_fails('echo base.method > instance.method') endfunc +func Test_version() + call assert_true(has('patch-7.4.001')) + call assert_true(has('patch-7.4.01')) + call assert_true(has('patch-7.4.1')) + call assert_true(has('patch-6.9.999')) + call assert_true(has('patch-7.1.999')) + call assert_true(has('patch-7.4.123')) + + call assert_false(has('patch-7')) + call assert_false(has('patch-7.4')) + call assert_false(has('patch-7.4.')) + call assert_false(has('patch-9.1.0')) + call assert_false(has('patch-9.9.1')) +endfunc + +func Test_dict() + let d = {'': 'empty', 'a': 'a', 0: 'zero'} + call assert_equal('empty', d['']) + call assert_equal('a', d['a']) + call assert_equal('zero', d[0]) + call assert_true(has_key(d, '')) + call assert_true(has_key(d, 'a')) + + let d[''] = 'none' + let d['a'] = 'aaa' + call assert_equal('none', d['']) + call assert_equal('aaa', d['a']) +endfunc + func Test_strgetchar() call assert_equal(char2nr('a'), strgetchar('axb', 0)) call assert_equal(char2nr('x'), strgetchar('axb', 1)) @@ -61,20 +75,6 @@ func Test_strcharpart() call assert_equal('a', strcharpart('axb', -1, 2)) endfunc -func Test_dict() - let d = {'': 'empty', 'a': 'a', 0: 'zero'} - call assert_equal('empty', d['']) - call assert_equal('a', d['a']) - call assert_equal('zero', d[0]) - call assert_true(has_key(d, '')) - call assert_true(has_key(d, 'a')) - - let d[''] = 'none' - let d['a'] = 'aaa' - call assert_equal('none', d['']) - call assert_equal('aaa', d['a']) -endfunc - func Test_loop_over_null_list() let null_list = submatch(1, 1) for i in null_list @@ -92,3 +92,17 @@ endfunc func Test_set_reg_null_list() call setreg('x', v:_null_list) endfunc + +func Test_special_char() + " The failure is only visible using valgrind. + call assert_fails('echo "\<C-">') +endfunc + +func Test_setmatches() + hi def link 1 Comment + hi def link 2 PreProc + let set = [{"group": 1, "pattern": 2, "id": 3, "priority": 4, "conceal": 5}] + let exp = [{"group": '1', "pattern": '2', "id": 3, "priority": 4, "conceal": '5'}] + call setmatches(set) + call assert_equal(exp, getmatches()) +endfunc diff --git a/src/nvim/testdir/test_gn.vim b/src/nvim/testdir/test_gn.vim new file mode 100644 index 0000000000..3eca99bd99 --- /dev/null +++ b/src/nvim/testdir/test_gn.vim @@ -0,0 +1,93 @@ +" Test for gn command + +func Test_gn_command() + noa new + " replace a single char by itsself quoted: + call setline('.', 'abc x def x ghi x jkl') + let @/='x' + exe "norm! cgn'x'\<esc>.." + call assert_equal("abc 'x' def 'x' ghi 'x' jkl", getline('.')) + sil! %d_ + " simple search match + call setline('.', 'foobar') + let @/='foobar' + exe "norm! gncsearchmatch" + call assert_equal('searchmatch', getline('.')) + sil! %d _ + " replace a multi-line match + call setline('.', ['', 'one', 'two']) + let @/='one\_s*two\_s' + exe "norm! gnceins\<CR>zwei" + call assert_equal(['','eins','zwei'], getline(1,'$')) + sil! %d _ + " test count argument + call setline('.', ['', 'abcdx | abcdx | abcdx']) + let @/='[a]bcdx' + exe "norm! 2gnd" + call assert_equal(['','abcdx | | abcdx'], getline(1,'$')) + sil! %d _ + " join lines + call setline('.', ['join ', 'lines']) + let @/='$' + exe "norm! 0gnd" + call assert_equal(['join lines'], getline(1,'$')) + sil! %d _ + " zero-width match + call setline('.', ['', 'zero width pattern']) + let @/='\>\zs' + exe "norm! 0gnd" + call assert_equal(['', 'zerowidth pattern'], getline(1,'$')) + sil! %d _ + " delete first and last chars + call setline('.', ['delete first and last chars']) + let @/='^' + exe "norm! 0gnd$" + let @/='\zs' + exe "norm! gnd" + call assert_equal(['elete first and last char'], getline(1,'$')) + sil! %d _ + " using visual mode + call setline('.', ['', 'uniquepattern uniquepattern']) + exe "norm! /[u]niquepattern/s\<cr>vlgnd" + call assert_equal(['', ' uniquepattern'], getline(1,'$')) + sil! %d _ + " backwards search + call setline('.', ['my very excellent mother just served us nachos']) + let @/='mother' + exe "norm! $cgNmongoose" + call assert_equal(['my very excellent mongoose just served us nachos'], getline(1,'$')) + sil! %d _ + " search for single char + call setline('.', ['','for (i=0; i<=10; i++)']) + let @/='i' + exe "norm! cgnj" + call assert_equal(['','for (j=0; i<=10; i++)'], getline(1,'$')) + sil! %d _ + " search hex char + call setline('.', ['','Y']) + set noignorecase + let @/='\%x59' + exe "norm! gnd" + call assert_equal(['',''], getline(1,'$')) + sil! %d _ + " test repeating gdn + call setline('.', ['', '1', 'Johnny', '2', 'Johnny', '3']) + let @/='Johnny' + exe "norm! dgn." + call assert_equal(['','1', '', '2', '', '3'], getline(1,'$')) + sil! %d _ + " test repeating gUgn + call setline('.', ['', '1', 'Depp', '2', 'Depp', '3']) + let @/='Depp' + exe "norm! gUgn." + call assert_equal(['', '1', 'DEPP', '2', 'DEPP', '3'], getline(1,'$')) + sil! %d _ + " test using look-ahead assertions + call setline('.', ['a:10', '', 'a:1', '', 'a:20']) + let @/='a:0\@!\zs\d\+' + exe "norm! 2nygno\<esc>p" + call assert_equal(['a:10', '', 'a:1', '1', '', 'a:20'], getline(1,'$')) + sil! %d _ +endfu + +" vim: tabstop=2 shiftwidth=0 expandtab diff --git a/src/nvim/testdir/test_goto.vim b/src/nvim/testdir/test_goto.vim index 2afd96b296..b6ac5720c3 100644 --- a/src/nvim/testdir/test_goto.vim +++ b/src/nvim/testdir/test_goto.vim @@ -18,3 +18,18 @@ func Test_gee_dee() call assert_equal(14, col('.')) quit! endfunc + +" Check that setting 'cursorline' does not change curswant +func Test_cursorline_keep_col() + new + call setline(1, ['long long long line', 'short line']) + normal ggfi + let pos = getcurpos() + normal j + set cursorline + normal k + call assert_equal(pos, getcurpos()) + bwipe! + set nocursorline +endfunc + diff --git a/src/nvim/testdir/test_syn_attr.vim b/src/nvim/testdir/test_syn_attr.vim index 94d6b8735f..64aab3411d 100644 --- a/src/nvim/testdir/test_syn_attr.vim +++ b/src/nvim/testdir/test_syn_attr.vim @@ -20,7 +20,7 @@ func Test_missing_attr() if fontname == '' let fontname = 'something' endif - exe 'hi Mine guifg=blue guibg=red font=' . escape(fontname, ' \') + exe "hi Mine guifg=blue guibg=red font='" . fontname . "'" call assert_equal('blue', synIDattr(hlID("Mine"), "fg", 'gui')) call assert_equal('red', synIDattr(hlID("Mine"), "bg", 'gui')) call assert_equal(fontname, synIDattr(hlID("Mine"), "font", 'gui')) diff --git a/src/nvim/version.c b/src/nvim/version.c index f34473098c..acaad38ca2 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -280,7 +280,7 @@ static int included_patches[] = { 2163, 2162, // 2161, - // 2160, + 2160, // 2159, 2158, // 2157 NA @@ -340,8 +340,8 @@ static int included_patches[] = { 2103, // 2102 NA // 2101, - // 2100, - // 2099, + 2100, + 2099, // 2098, // 2097, // 2096, @@ -363,7 +363,7 @@ static int included_patches[] = { // 2080, // 2079 NA // 2078 NA - // 2077, + 2077, // 2076, 2075, // 2074, @@ -375,7 +375,7 @@ static int included_patches[] = { // 2068, // 2067, 2066, - // 2065, + 2065, // 2064, // 2063 NA // 2062, @@ -431,8 +431,8 @@ static int included_patches[] = { 2012, 2011, 2010, - // 2009, - // 2008, + 2009, + 2008, 2007, 2006, 2005, @@ -472,7 +472,7 @@ static int included_patches[] = { 1971, 1970, // 1969 NA - // 1968, + 1968, 1967, 1966, // 1965 NA @@ -515,7 +515,7 @@ static int included_patches[] = { 1928, // 1927 NA // 1926 NA - // 1925 NA + 1925, // 1924 NA 1923, // 1922 NA @@ -530,7 +530,7 @@ static int included_patches[] = { 1913, 1912, // 1911 NA - // 1910, + 1910, 1909, // 1908 NA // 1907 NA @@ -551,7 +551,7 @@ static int included_patches[] = { 1892, // 1891 NA // 1890 NA - // 1889, + 1889, // 1888 NA // 1887 NA // 1886 NA @@ -589,7 +589,7 @@ static int included_patches[] = { // 1854 NA // 1853 NA // 1852 NA - // 1851, + 1851, // 1850 NA // 1849 NA // 1848 NA diff --git a/test/functional/api/server_requests_spec.lua b/test/functional/api/server_requests_spec.lua index 3245e1b52d..aa91cd1396 100644 --- a/test/functional/api/server_requests_spec.lua +++ b/test/functional/api/server_requests_spec.lua @@ -8,8 +8,6 @@ local nvim_prog, command, funcs = helpers.nvim_prog, helpers.command, helpers.fu local source, next_message = helpers.source, helpers.next_message local meths = helpers.meths -if helpers.pending_win32(pending) then return end - describe('server -> client', function() local cid @@ -212,6 +210,8 @@ describe('server -> client', function() funcs.jobstop(jobid) end) + if helpers.pending_win32(pending) then return end + it('rpc and text stderr can be combined', function() eq("ok",funcs.rpcrequest(jobid, "poll")) funcs.rpcnotify(jobid, "ping") diff --git a/test/functional/autocmd/bufenter_spec.lua b/test/functional/autocmd/bufenter_spec.lua new file mode 100644 index 0000000000..ccbcdf5c5e --- /dev/null +++ b/test/functional/autocmd/bufenter_spec.lua @@ -0,0 +1,35 @@ +local helpers = require('test.functional.helpers')(after_each) + +local clear = helpers.clear +local command = helpers.command +local eq = helpers.eq +local eval = helpers.eval +local execute = helpers.execute +local request = helpers.request +local source = helpers.source + +describe('autocmd BufEnter', function() + before_each(clear) + + it("triggered by nvim_command('edit <dir>')", function() + command("autocmd BufEnter * if isdirectory(expand('<afile>')) | let g:dir_bufenter = 1 | endif") + request("nvim_command", "split .") + eq(1, eval("exists('g:dir_bufenter')")) -- Did BufEnter for the directory. + eq(2, eval("bufnr('%')")) -- Switched to the dir buffer. + end) + + it('triggered by "try|:split <dir>|endtry" in a function', function() + command("autocmd BufEnter * if isdirectory(expand('<afile>')) | let g:dir_bufenter = 1 | endif") + source([[ + function! Test() + try + exe 'split .' + catch + endtry + endfunction + ]]) + execute("call Test()") + eq(1, eval("exists('g:dir_bufenter')")) -- Did BufEnter for the directory. + eq(2, eval("bufnr('%')")) -- Switched to the dir buffer. + end) +end) diff --git a/test/functional/autocmd/tabnew_spec.lua b/test/functional/autocmd/tabnew_spec.lua index 2148b21832..ad40954f76 100644 --- a/test/functional/autocmd/tabnew_spec.lua +++ b/test/functional/autocmd/tabnew_spec.lua @@ -5,8 +5,6 @@ local command = helpers.command local eq = helpers.eq local eval = helpers.eval -if helpers.pending_win32(pending) then return end - describe('autocmd TabNew', function() before_each(clear) @@ -19,12 +17,11 @@ describe('autocmd TabNew', function() end) it('matches when opening a new tab for FILE', function() - local tmp_path = helpers.funcs.tempname() command('let g:test = "foo"') - command('autocmd! TabNew ' .. tmp_path .. ' let g:test = "bar"') - command('tabnew ' .. tmp_path ..'X') + command('autocmd! TabNew Xtest-tabnew let g:test = "bar"') + command('tabnew Xtest-tabnewX') eq('foo', eval('g:test')) - command('tabnew ' .. tmp_path) + command('tabnew Xtest-tabnew') eq('bar', eval('g:test')) end) end) diff --git a/test/functional/autocmd/tabnewentered_spec.lua b/test/functional/autocmd/tabnewentered_spec.lua index f033bd5fe4..bdbe677132 100644 --- a/test/functional/autocmd/tabnewentered_spec.lua +++ b/test/functional/autocmd/tabnewentered_spec.lua @@ -1,8 +1,6 @@ local helpers = require('test.functional.helpers')(after_each) local clear, nvim, eq = helpers.clear, helpers.nvim, helpers.eq -if helpers.pending_win32(pending) then return end - describe('TabNewEntered', function() describe('au TabNewEntered', function() describe('with * as <afile>', function() @@ -15,9 +13,9 @@ describe('TabNewEntered', function() end) describe('with FILE as <afile>', function() it('matches when opening a new tab for FILE', function() - local tmp_path = nvim('eval', 'tempname()') - nvim('command', 'au! TabNewEntered '..tmp_path..' echom "tabnewentered:match"') - eq("\n\""..tmp_path.."\" [New File]\ntabnewentered:4:4\ntabnewentered:match", nvim('command_output', 'tabnew '..tmp_path)) + nvim('command', 'au! TabNewEntered Xtest-tabnewentered echom "tabnewentered:match"') + eq('\n"Xtest-tabnewentered" [New File]\ntabnewentered:4:4\ntabnewentered:match', + nvim('command_output', 'tabnew Xtest-tabnewentered')) end) end) describe('with CTRL-W T', function() diff --git a/test/functional/core/job_partial_spec.lua b/test/functional/core/job_partial_spec.lua index b60f239db9..7643b283c4 100644 --- a/test/functional/core/job_partial_spec.lua +++ b/test/functional/core/job_partial_spec.lua @@ -2,13 +2,14 @@ local helpers = require('test.functional.helpers')(after_each) local clear, eq, next_msg, nvim, source = helpers.clear, helpers.eq, helpers.next_message, helpers.nvim, helpers.source -if helpers.pending_win32(pending) then return end - describe('jobs with partials', function() local channel before_each(function() clear() + if helpers.os_name() == 'windows' then + helpers.set_shell_powershell() + end channel = nvim('get_api_info')[1] nvim('set_var', 'channel', channel) end) @@ -16,12 +17,14 @@ describe('jobs with partials', function() it('works correctly', function() source([[ function PrintArgs(a1, a2, id, data, event) - call rpcnotify(g:channel, '1', a:a1, a:a2, a:data, a:event) + " Windows: Remove ^M char. + let normalized = map(a:data, 'substitute(v:val, "\r", "", "g")') + call rpcnotify(g:channel, '1', a:a1, a:a2, normalized, a:event) endfunction let Callback = function('PrintArgs', ["foo", "bar"]) let g:job_opts = {'on_stdout': Callback} - call jobstart(['echo'], g:job_opts) + call jobstart('echo "some text"', g:job_opts) ]]) - eq({'notification', '1', {'foo', 'bar', {'', ''}, 'stdout'}}, next_msg()) + eq({'notification', '1', {'foo', 'bar', {'some text', ''}, 'stdout'}}, next_msg()) end) end) diff --git a/test/functional/core/job_spec.lua b/test/functional/core/job_spec.lua index 75b50aad0a..48a4689545 100644 --- a/test/functional/core/job_spec.lua +++ b/test/functional/core/job_spec.lua @@ -312,6 +312,24 @@ describe('jobs', function() end) end) + it('does not repeat output with slow output handlers', function() + source([[ + let d = {'data': []} + function! d.on_stdout(job, data, event) dict + call add(self.data, a:data) + sleep 200m + endfunction + if has('win32') + let cmd = '1,2,3,4,5 | foreach-object -process {echo $_; sleep 0.1}' + else + let cmd = ['sh', '-c', 'for i in $(seq 1 5); do echo $i; sleep 0.1; done'] + endif + call jobwait([jobstart(cmd, d)]) + call rpcnotify(g:channel, 'data', d.data) + ]]) + eq({'notification', 'data', {{{'1', ''}, {'2', ''}, {'3', ''}, {'4', ''}, {'5', ''}}}}, next_msg()) + end) + describe('jobwait', function() it('returns a list of status codes', function() source([[ diff --git a/test/functional/eval/execute_spec.lua b/test/functional/eval/execute_spec.lua index fc13c0a72b..cc9b61b842 100644 --- a/test/functional/eval/execute_spec.lua +++ b/test/functional/eval/execute_spec.lua @@ -7,7 +7,7 @@ local redir_exec = helpers.redir_exec local exc_exec = helpers.exc_exec local funcs = helpers.funcs local Screen = require('test.functional.ui.screen') -local feed = helpers.feed +local command = helpers.command describe('execute()', function() before_each(clear) @@ -62,11 +62,11 @@ describe('execute()', function() ret = exc_exec('call execute(function("tr"))') eq('Vim(call):E729: using Funcref as a String', ret) ret = exc_exec('call execute(["echo 42", 0.0, "echo 44"])') - eq('Vim(call):E806: using Float as a String', ret) + eq('Vim:E806: using Float as a String', ret) ret = exc_exec('call execute(["echo 42", v:_null_dict, "echo 44"])') - eq('Vim(call):E731: using Dictionary as a String', ret) + eq('Vim:E731: using Dictionary as a String', ret) ret = exc_exec('call execute(["echo 42", function("tr"), "echo 44"])') - eq('Vim(call):E729: using Funcref as a String', ret) + eq('Vim:E729: using Funcref as a String', ret) end) -- This matches Vim behavior. @@ -74,18 +74,75 @@ describe('execute()', function() eq('\n:!echo "foo"\13\n', funcs.execute('!echo "foo"')) end) - it('silences command run inside', function() - local screen = Screen.new(40, 5) - screen:attach() - screen:set_default_attr_ids( {[0] = {bold=true, foreground=255}} ) - feed(':let g:mes = execute("echon 42")<CR>') - screen:expect([[ - ^ | - {0:~ }| - {0:~ }| - {0:~ }| - :let g:mes = execute("echon 42") | - ]]) - eq('42', eval('g:mes')) + describe('{silent} argument', function() + it('captures & displays output for ""', function() + local screen = Screen.new(40, 5) + screen:attach() + command('let g:mes = execute("echon 42", "")') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + 42 | + ]]) + eq('42', eval('g:mes')) + end) + + it('captures but does not display output for "silent"', function() + local screen = Screen.new(40, 5) + screen:attach() + command('let g:mes = execute("echon 42")') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + | + ]]) + eq('42', eval('g:mes')) + + command('let g:mes = execute("echon 13", "silent")') + screen:expect([[ + ^ | + ~ | + ~ | + ~ | + | + ]]) + eq('13', eval('g:mes')) + end) + + it('suppresses errors for "silent!"', function() + eq(0, exc_exec('let g:mes = execute(0.0, "silent!")')) + eq('', eval('g:mes')) + + eq(0, exc_exec('let g:mes = execute("echon add(1, 1)", "silent!")')) + eq('1', eval('g:mes')) + + eq(0, exc_exec('let g:mes = execute(["echon 42", "echon add(1, 1)"], "silent!")')) + eq('421', eval('g:mes')) + end) + + it('propagates errors for "" and "silent"', function() + local ret + ret = exc_exec('call execute(0.0, "")') + eq('Vim(call):E806: using Float as a String', ret) + + ret = exc_exec('call execute(v:_null_dict, "silent")') + eq('Vim(call):E731: using Dictionary as a String', ret) + + ret = exc_exec('call execute("echo add(1, 1)", "")') + eq('Vim(echo):E714: List required', ret) + + ret = exc_exec('call execute(["echon 42", "echo add(1, 1)"], "")') + eq('Vim(echo):E714: List required', ret) + + ret = exc_exec('call execute("echo add(1, 1)", "silent")') + eq('Vim(echo):E714: List required', ret) + + ret = exc_exec('call execute(["echon 42", "echo add(1, 1)"], "silent")') + eq('Vim(echo):E714: List required', ret) + end) end) end) diff --git a/test/functional/eval/server_spec.lua b/test/functional/eval/server_spec.lua index d2c985e894..420aea04aa 100644 --- a/test/functional/eval/server_spec.lua +++ b/test/functional/eval/server_spec.lua @@ -4,8 +4,6 @@ local nvim, eq, neq, eval = helpers.nvim, helpers.eq, helpers.neq, helpers.eval local clear, funcs, meths = helpers.clear, helpers.funcs, helpers.meths local os_name = helpers.os_name -if helpers.pending_win32(pending) then return end - describe('serverstart(), serverstop()', function() before_each(clear) @@ -42,8 +40,8 @@ describe('serverstart(), serverstop()', function() -- v:servername will take the next available server. local servername = (os_name() == 'windows' - and [[\\.\pipe\Xtest-functional-server-server-pipe]] - or 'Xtest-functional-server-server-socket') + and [[\\.\pipe\Xtest-functional-server-pipe]] + or 'Xtest-functional-server-socket') funcs.serverstart(servername) eq(servername, meths.get_vvar('servername')) end) @@ -63,9 +61,11 @@ describe('serverlist()', function() local n = eval('len(serverlist())') -- Add a few - local servs = {'should-not-exist', 'another-one-that-shouldnt'} + local servs = (os_name() == 'windows' + and { [[\\.\pipe\Xtest-pipe0934]], [[\\.\pipe\Xtest-pipe4324]] } + or { [[Xtest-pipe0934]], [[Xtest-pipe4324]] }) for _, s in ipairs(servs) do - eq(s, eval('serverstart("'..s..'")')) + eq(s, eval("serverstart('"..s.."')")) end local new_servs = eval('serverlist()') @@ -75,10 +75,9 @@ describe('serverlist()', function() -- The new servers should be at the end of the list. for i = 1, #servs do eq(servs[i], new_servs[i + n]) - nvim('command', 'call serverstop("'..servs[i]..'")') + nvim('command', "call serverstop('"..servs[i].."')") end - -- After calling serverstop() on the new servers, they should no longer be - -- in the list. + -- After serverstop() the servers should NOT be in the list. eq(n, eval('len(serverlist())')) end) end) diff --git a/test/functional/eval/setpos_spec.lua b/test/functional/eval/setpos_spec.lua new file mode 100644 index 0000000000..2e27cd8ac0 --- /dev/null +++ b/test/functional/eval/setpos_spec.lua @@ -0,0 +1,64 @@ +local helpers = require('test.functional.helpers')(after_each) +local setpos = helpers.funcs.setpos +local getpos = helpers.funcs.getpos +local insert = helpers.insert +local clear = helpers.clear +local execute = helpers.execute +local eval = helpers.eval +local eq = helpers.eq +local exc_exec = helpers.exc_exec + + +describe('setpos() function', function() + before_each(function() + clear() + insert([[ + First line of text + Second line of text + Third line of text]]) + execute('new') + insert([[ + Line of text 1 + Line of text 2 + Line of text 3]]) + end) + it('can set the current cursor position', function() + setpos(".", {0, 2, 1, 0}) + eq(getpos("."), {0, 2, 1, 0}) + setpos(".", {2, 1, 1, 0}) + eq(getpos("."), {0, 1, 1, 0}) + -- Ensure get an error attempting to set position to another buffer + local ret = exc_exec('call setpos(".", [1, 1, 1, 0])') + eq('Vim(call):E474: Invalid argument', ret) + end) + it('can set lowercase marks in the current buffer', function() + setpos("'d", {0, 2, 1, 0}) + eq(getpos("'d"), {0, 2, 1, 0}) + execute('undo', 'call setpos("\'d", [2, 3, 1, 0])') + eq(getpos("'d"), {0, 3, 1, 0}) + end) + it('can set lowercase marks in other buffers', function() + local retval = setpos("'d", {1, 2, 1, 0}) + eq(0, retval) + setpos("'d", {1, 2, 1, 0}) + eq(getpos("'d"), {0, 0, 0, 0}) + execute('wincmd w') + eq(eval('bufnr("%")'), 1) + eq(getpos("'d"), {0, 2, 1, 0}) + end) + it("fails when setting a mark in a buffer that doesn't exist", function() + local retval = setpos("'d", {3, 2, 1, 0}) + eq(-1, retval) + eq(getpos("'d"), {0, 0, 0, 0}) + retval = setpos("'D", {3, 2, 1, 0}) + eq(-1, retval) + eq(getpos("'D"), {0, 0, 0, 0}) + end) + it('can set uppercase marks', function() + setpos("'D", {2, 2, 3, 0}) + eq(getpos("'D"), {2, 2, 3, 0}) + -- Can set a mark in another buffer + setpos("'D", {1, 2, 2, 0}) + eq(getpos("'D"), {1, 2, 2, 0}) + end) +end) diff --git a/test/functional/eval/system_spec.lua b/test/functional/eval/system_spec.lua index 6393477260..d5845132dd 100644 --- a/test/functional/eval/system_spec.lua +++ b/test/functional/eval/system_spec.lua @@ -1,12 +1,10 @@ local helpers = require('test.functional.helpers')(after_each) -local eq, clear, eval, execute, feed, nvim = - helpers.eq, helpers.clear, helpers.eval, helpers.execute, helpers.feed, - helpers.nvim +local eq, call, clear, eval, execute, feed, nvim = + helpers.eq, helpers.call, helpers.clear, helpers.eval, helpers.execute, + helpers.feed, helpers.nvim local Screen = require('test.functional.ui.screen') -if helpers.pending_win32(pending) then return end - local function create_file_with_nuls(name) return function() feed('ipart1<C-V>000part2<C-V>000part3<ESC>:w '..name..'<CR>') @@ -31,7 +29,70 @@ end describe('system()', function() before_each(clear) - it('sets the v:shell_error variable', function() + describe('command passed as a List', function() + local printargs_path = helpers.nvim_dir..'/printargs-test' + .. (helpers.os_name() == 'windows' and '.exe' or '') + + it('sets v:shell_error if cmd[0] is not executable', function() + call('system', { 'this-should-not-exist' }) + eq(-1, eval('v:shell_error')) + end) + + it('parameter validation does NOT modify v:shell_error', function() + -- 1. Call system() with invalid parameters. + -- 2. Assert that v:shell_error was NOT set. + execute('call system({})') + eq('E475: Invalid argument: expected String or List', eval('v:errmsg')) + eq(0, eval('v:shell_error')) + execute('call system([])') + eq('E474: Invalid argument', eval('v:errmsg')) + eq(0, eval('v:shell_error')) + + -- Provoke a non-zero v:shell_error. + call('system', { 'this-should-not-exist' }) + local old_val = eval('v:shell_error') + eq(-1, old_val) + + -- 1. Call system() with invalid parameters. + -- 2. Assert that v:shell_error was NOT modified. + execute('call system({})') + eq(old_val, eval('v:shell_error')) + execute('call system([])') + eq(old_val, eval('v:shell_error')) + end) + + it('quotes arguments correctly #5280', function() + local out = call('system', + { printargs_path, [[1]], [[2 "3]], [[4 ' 5]], [[6 ' 7']] }) + + eq(0, eval('v:shell_error')) + eq([[arg1=1;arg2=2 "3;arg3=4 ' 5;arg4=6 ' 7';]], out) + + out = call('system', { printargs_path, [['1]], [[2 "3]] }) + eq(0, eval('v:shell_error')) + eq([[arg1='1;arg2=2 "3;]], out) + + out = call('system', { printargs_path, "A\nB" }) + eq(0, eval('v:shell_error')) + eq("arg1=A\nB;", out) + end) + + it('calls executable in $PATH', function() + if 0 == eval("executable('python')") then pending("missing `python`") end + eq("foo\n", eval([[system(['python', '-c', 'print("foo")'])]])) + eq(0, eval('v:shell_error')) + end) + + it('does NOT run in shell', function() + if helpers.os_name() ~= 'windows' then + eq("* $PATH %PATH%\n", eval("system(['echo', '*', '$PATH', '%PATH%'])")) + end + end) + end) + + if helpers.pending_win32(pending) then return end + + it('sets v:shell_error', function() eval([[system("sh -c 'exit'")]]) eq(0, eval('v:shell_error')) eval([[system("sh -c 'exit 1'")]]) @@ -158,7 +219,7 @@ describe('system()', function() end) end) - describe('passing number as input', function() + describe('input passed as Number', function() it('stringifies the input', function() eq('1', eval('system("cat", 1)')) end) @@ -175,8 +236,8 @@ describe('system()', function() end) end) - describe('passing list as input', function() - it('joins list items with linefeed characters', function() + describe('input passed as List', function() + it('joins List items with linefeed characters', function() eq('line1\nline2\nline3', eval("system('cat -', ['line1', 'line2', 'line3'])")) end) @@ -185,7 +246,7 @@ describe('system()', function() -- is inconsistent and is a good reason for the existence of the -- `systemlist()` function, where input and output map to the same -- characters(see the following tests with `systemlist()` below) - describe('with linefeed characters inside list items', function() + describe('with linefeed characters inside List items', function() it('converts linefeed characters to NULs', function() eq('l1\001p2\nline2\001a\001b\nl3', eval([[system('cat -', ["l1\np2", "line2\na\nb", 'l3'])]])) @@ -202,7 +263,7 @@ describe('system()', function() describe("with a program that doesn't close stdout", function() if not xclip then - pending('skipped (missing xclip)', function() end) + pending('missing `xclip`', function() end) else it('will exit properly after passing input', function() eq('', eval([[system('xclip -i -selection clipboard', 'clip-data')]])) @@ -210,18 +271,12 @@ describe('system()', function() end) end end) - - describe('command passed as a list', function() - it('does not execute &shell', function() - eq('* $NOTHING ~/file', - eval("system(['echo', '-n', '*', '$NOTHING', '~/file'])")) - end) - end) end) +if helpers.pending_win32(pending) then return end + describe('systemlist()', function() - -- behavior is similar to `system()` but it returns a list instead of a - -- string. + -- Similar to `system()`, but returns List instead of String. before_each(clear) it('sets the v:shell_error variable', function() @@ -334,14 +389,14 @@ describe('systemlist()', function() end) end) - describe('passing list as input', function() + describe('input passed as List', function() it('joins list items with linefeed characters', function() eq({'line1', 'line2', 'line3'}, eval("systemlist('cat -', ['line1', 'line2', 'line3'])")) end) -- Unlike `system()` which uses SOH to represent NULs, with `systemlist()` - -- input and ouput are the same + -- input and ouput are the same. describe('with linefeed characters inside list items', function() it('converts linefeed characters to NULs', function() eq({'l1\np2', 'line2\na\nb', 'l3'}, @@ -381,7 +436,7 @@ describe('systemlist()', function() describe("with a program that doesn't close stdout", function() if not xclip then - pending('skipped (missing xclip)', function() end) + pending('missing `xclip`', function() end) else it('will exit properly after passing input', function() eq({}, eval( diff --git a/test/functional/ex_cmds/dict_notifications_spec.lua b/test/functional/ex_cmds/dict_notifications_spec.lua index e6f7609016..5e89986c0f 100644 --- a/test/functional/ex_cmds/dict_notifications_spec.lua +++ b/test/functional/ex_cmds/dict_notifications_spec.lua @@ -3,6 +3,7 @@ local clear, nvim, source = helpers.clear, helpers.nvim, helpers.source local eq, next_msg = helpers.eq, helpers.next_message local exc_exec = helpers.exc_exec local command = helpers.command +local eval = helpers.eval describe('dictionary change notifications', function() @@ -255,5 +256,21 @@ describe('dictionary change notifications', function() eq({'notification', '2b', {'key', {old = 'v2', new = 'value'}}}, next_msg()) end) + + it('does not crash when freeing a watched dictionary', function() + source([[ + function! Watcher(dict, key, value) + echo a:key string(a:value) + endfunction + + function! MakeWatch() + let d = {'foo': 'bar'} + call dictwatcheradd(d, 'foo', function('Watcher')) + endfunction + ]]) + + command('call MakeWatch()') + eq(2, eval('1+1')) -- Still alive? + end) end) end) diff --git a/test/functional/fixtures/CMakeLists.txt b/test/functional/fixtures/CMakeLists.txt index 70aee6efa9..8537ea390f 100644 --- a/test/functional/fixtures/CMakeLists.txt +++ b/test/functional/fixtures/CMakeLists.txt @@ -2,3 +2,4 @@ add_executable(tty-test tty-test.c) target_link_libraries(tty-test ${LIBUV_LIBRARIES}) add_executable(shell-test shell-test.c) +add_executable(printargs-test printargs-test.c) diff --git a/test/functional/fixtures/printargs-test.c b/test/functional/fixtures/printargs-test.c new file mode 100644 index 0000000000..2c25cf8447 --- /dev/null +++ b/test/functional/fixtures/printargs-test.c @@ -0,0 +1,9 @@ +#include <stdio.h> + +int main(int argc, char **argv) +{ + for (int i=1; i<argc; i++) { + printf("arg%d=%s;", i, argv[i]); + } + return 0; +} diff --git a/test/functional/helpers.lua b/test/functional/helpers.lua index 5eec3afe65..2939184d2c 100644 --- a/test/functional/helpers.lua +++ b/test/functional/helpers.lua @@ -346,6 +346,14 @@ local function source(code) return fname end +local function set_shell_powershell() + source([[ + set shell=powershell shellquote=\" shellpipe=\| shellredir=> + set shellcmdflag=\ -ExecutionPolicy\ RemoteSigned\ -Command + let &shellxquote=' ' + ]]) +end + local function nvim(method, ...) return request('nvim_'..method, ...) end @@ -590,6 +598,7 @@ return function(after_each) curtabmeths = curtabmeths, pending_win32 = pending_win32, skip_fragile = skip_fragile, + set_shell_powershell = set_shell_powershell, tmpname = tmpname, NIL = mpack.NIL, } diff --git a/test/functional/options/defaults_spec.lua b/test/functional/options/defaults_spec.lua index 1ae855f26c..caeca5e4e2 100644 --- a/test/functional/options/defaults_spec.lua +++ b/test/functional/options/defaults_spec.lua @@ -9,8 +9,6 @@ local eval = helpers.eval local eq = helpers.eq local neq = helpers.neq -if helpers.pending_win32(pending) then return end - local function init_session(...) local args = { helpers.nvim_prog, '-i', 'NONE', '--embed', '--cmd', 'set shortmess+=I background=light noswapfile noautoindent', @@ -24,6 +22,8 @@ end describe('startup defaults', function() describe(':filetype', function() + if helpers.pending_win32(pending) then return end + local function expect_filetype(expected) local screen = Screen.new(48, 4) screen:attach() @@ -99,8 +99,37 @@ describe('startup defaults', function() end) describe('XDG-based defaults', function() - -- Need to be in separate describe() block to not run clear() twice. + -- Need separate describe() blocks to not run clear() twice. -- Do not put before_each() here for the same reasons. + + describe('with empty/broken environment', function() + it('sets correct defaults', function() + clear({env={ + XDG_CONFIG_HOME=nil, + XDG_DATA_HOME=nil, + XDG_CACHE_HOME=nil, + XDG_RUNTIME_DIR=nil, + XDG_CONFIG_DIRS=nil, + XDG_DATA_DIRS=nil, + LOCALAPPDATA=nil, + HOMEPATH=nil, + HOMEDRIVE=nil, + HOME=nil, + TEMP=nil, + VIMRUNTIME=nil, + USER=nil, + }}) + + eq('.', meths.get_option('backupdir')) + eq('.', meths.get_option('viewdir')) + eq('.', meths.get_option('directory')) + eq('.', meths.get_option('undodir')) + end) + end) + + -- TODO(jkeyes): tests below fail on win32 because of path separator. + if helpers.pending_win32(pending) then return end + describe('with too long XDG variables', function() before_each(function() clear({env={ diff --git a/test/functional/shada/buffers_spec.lua b/test/functional/shada/buffers_spec.lua index 7e6338897a..a4746c2205 100644 --- a/test/functional/shada/buffers_spec.lua +++ b/test/functional/shada/buffers_spec.lua @@ -8,8 +8,6 @@ local reset, set_additional_cmd, clear = shada_helpers.reset, shada_helpers.set_additional_cmd, shada_helpers.clear -if helpers.pending_win32(pending) then return end - describe('ShaDa support code', function() local testfilename = 'Xtestfile-functional-shada-buffers' local testfilename_2 = 'Xtestfile-functional-shada-buffers-2' diff --git a/test/functional/shada/marks_spec.lua b/test/functional/shada/marks_spec.lua index b7c0f61f57..fa760ceb5b 100644 --- a/test/functional/shada/marks_spec.lua +++ b/test/functional/shada/marks_spec.lua @@ -14,8 +14,6 @@ local nvim_current_line = function() return curwinmeths.get_cursor()[1] end -if helpers.pending_win32(pending) then return end - describe('ShaDa support code', function() local testfilename = 'Xtestfile-functional-shada-marks' local testfilename_2 = 'Xtestfile-functional-shada-marks-2' @@ -153,6 +151,19 @@ describe('ShaDa support code', function() eq(saved, redir_exec('jumps')) end) + it('when dumping jump list also dumps current position', function() + nvim_command('edit ' .. testfilename) + nvim_command('normal! G') + nvim_command('split ' .. testfilename_2) + nvim_command('normal! G') + nvim_command('wshada') + nvim_command('quit') + nvim_command('rshada') + nvim_command('normal! \15') -- <C-o> + eq(testfilename_2, funcs.bufname('%')) + eq({2, 0}, curwinmeths.get_cursor()) + end) + it('is able to dump and restore jump list with different times (slow!)', function() nvim_command('edit ' .. testfilename_2) diff --git a/test/functional/shada/shada_spec.lua b/test/functional/shada/shada_spec.lua index f845f6f93b..32598fc399 100644 --- a/test/functional/shada/shada_spec.lua +++ b/test/functional/shada/shada_spec.lua @@ -23,8 +23,6 @@ local wshada, _, shada_fname, clean = local dirname = 'Xtest-functional-shada-shada.d' local dirshada = dirname .. '/main.shada' -if helpers.pending_win32(pending) then return end - describe('ShaDa support code', function() before_each(reset) after_each(function() @@ -173,6 +171,7 @@ describe('ShaDa support code', function() end it('correctly uses shada-r option', function() + nvim_command('set shellslash') meths.set_var('__home', paths.test_source_path) nvim_command('let $HOME = __home') nvim_command('unlet __home') @@ -196,6 +195,7 @@ describe('ShaDa support code', function() end) it('correctly ignores case with shada-r option', function() + nvim_command('set shellslash') local pwd = funcs.getcwd() local relfname = 'абв/test' local fname = pwd .. '/' .. relfname @@ -240,6 +240,8 @@ describe('ShaDa support code', function() end) it('does not crash when ShaDa file directory is not writable', function() + if helpers.pending_win32(pending) then return end + funcs.mkdir(dirname, '', 0) eq(0, funcs.filewritable(dirname)) set_additional_cmd('set shada=') diff --git a/test/functional/ui/inccommand_spec.lua b/test/functional/ui/inccommand_spec.lua index 35aeb6e67c..41ebfd2334 100644 --- a/test/functional/ui/inccommand_spec.lua +++ b/test/functional/ui/inccommand_spec.lua @@ -42,6 +42,7 @@ local function common_setup(screen, inccommand, text) [14] = {foreground = Screen.colors.White, background = Screen.colors.Red}, [15] = {bold=true, foreground=Screen.colors.Blue}, [16] = {background=Screen.colors.Grey90}, -- cursorline + vis = {background=Screen.colors.LightGrey} }) end @@ -207,6 +208,42 @@ describe(":substitute, 'inccommand' preserves", function() end) end + for _, case in pairs{"", "split", "nosplit"} do + it("visual selection for non-previewable command (inccommand="..case..") #5888", function() + local screen = Screen.new(30,10) + common_setup(screen, case, default_text) + feed('1G2V') + + feed(':s') + screen:expect([[ + {vis:Inc substitution on} | + t{vis:wo lines} | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + :'<,'>s^ | + ]]) + + feed('o') + screen:expect([[ + {vis:Inc substitution on} | + t{vis:wo lines} | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + :'<,'>so^ | + ]]) + end) + end + end) describe(":substitute, 'inccommand' preserves undo", function() @@ -1201,6 +1238,40 @@ describe(":substitute, 'inccommand' with a failing expression", function() end end) + it('in the range does not error #5912', function() + for _, case in pairs(cases) do + refresh(case) + feed(':100s/') + + screen:expect([[ + Inc substitution on | + two lines | + | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + :100s/^ | + ]]) + + feed('<enter>') + screen:expect([[ + Inc substitution on | + two lines | + ^ | + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {15:~ }| + {14:E16: Invalid range} | + ]]) + end + end) + end) describe("'inccommand' and :cnoremap", function() diff --git a/test/unit/eval/helpers.lua b/test/unit/eval/helpers.lua index 712530a0aa..c3c27e4fed 100644 --- a/test/unit/eval/helpers.lua +++ b/test/unit/eval/helpers.lua @@ -5,7 +5,8 @@ local to_cstr = helpers.to_cstr local ffi = helpers.ffi local eq = helpers.eq -local eval = cimport('./src/nvim/eval.h', './src/nvim/eval_defs.h') +local eval = cimport('./src/nvim/eval.h', './src/nvim/eval_defs.h', + './src/nvim/hashtab.h') local null_string = {[true]='NULL string'} local null_list = {[true]='NULL list'} @@ -122,6 +123,32 @@ typvalt2lua = function(t, processed) end)(t, processed or {})) end +local function list_iter(l) + local init_s = { + idx=0, + li=l.lv_first, + } + local function f(s, _) + -- (listitem_T *) NULL is equal to nil, but yet it is not false. + if s.li == nil then + return nil + end + local ret_li = s.li + s.li = s.li.li_next + s.idx = s.idx + 1 + return s.idx, ret_li + end + return f, init_s, nil +end + +local function list_items(l) + local ret = {} + for i, li in list_iter(l) do + ret[i] = li + end + return ret +end + lst2tbl = function(l, processed) if l == nil then return null_list @@ -133,11 +160,8 @@ lst2tbl = function(l, processed) end local ret = {[type_key]=list_type} processed[p_key] = ret - local li = l.lv_first - -- (listitem_T *) NULL is equal to nil, but yet it is not false. - while li ~= nil do - ret[#ret + 1] = typvalt2lua(li.li_tv, processed) - li = li.li_next + for i, li in list_iter(l) do + ret[i] = typvalt2lua(li.li_tv, processed) end if ret[1] then ret[type_key] = nil @@ -145,7 +169,9 @@ lst2tbl = function(l, processed) return ret end -local function dict_iter(d) +local hi_key_removed = eval._hash_key_removed() + +local function dict_iter(d, return_hi) local init_s = { todo=d.dv_hashtab.ht_used, hi=d.dv_hashtab.ht_array, @@ -153,13 +179,18 @@ local function dict_iter(d) local function f(s, _) if s.todo == 0 then return nil end while s.todo > 0 do - if s.hi.hi_key ~= nil and s.hi ~= eval.hash_removed then + if s.hi.hi_key ~= nil and s.hi.hi_key ~= hi_key_removed then local key = ffi.string(s.hi.hi_key) - local di = ffi.cast('dictitem_T*', - s.hi.hi_key - ffi.offsetof('dictitem_T', 'di_key')) + local ret + if return_hi then + ret = s.hi + else + ret = ffi.cast('dictitem_T*', + s.hi.hi_key - ffi.offsetof('dictitem_T', 'di_key')) + end s.todo = s.todo - 1 s.hi = s.hi + 1 - return key, di + return key, ret end s.hi = s.hi + 1 end @@ -172,6 +203,16 @@ local function first_di(d) return select(2, f(init_s, v)) end +local function dict_items(d) + local ret = {[0]=0} + for k, hi in dict_iter(d) do + ret[k] = hi + ret[0] = ret[0] + 1 + ret[ret[0]] = hi + end + return ret +end + dct2tbl = function(d, processed) if d == nil then return null_dict @@ -324,6 +365,22 @@ lua2typvalt = function(l, processed) end end +local function void(ptr) + return ffi.cast('void*', ptr) +end + +local alloc_logging_helpers = { + list = function(l) return {func='calloc', args={1, ffi.sizeof('list_T')}, ret=void(l)} end, + li = function(li) return {func='malloc', args={ffi.sizeof('listitem_T')}, ret=void(li)} end, + dict = function(d) return {func='malloc', args={ffi.sizeof('dict_T')}, ret=void(d)} end, + di = function(di, size) + return {func='malloc', args={ffi.offsetof('dictitem_T', 'di_key') + size + 1}, ret=void(di)} + end, + str = function(s, size) return {func='malloc', args={size + 1}, ret=void(s)} end, + + freed = function(p) return {func='free', args={p and void(p)}} end, +} + return { null_string=null_string, null_list=null_list, @@ -350,5 +407,11 @@ return { li_alloc=li_alloc, dict_iter=dict_iter, + list_iter=list_iter, first_di=first_di, + + alloc_logging_helpers=alloc_logging_helpers, + + list_items=list_items, + dict_items=dict_items, } diff --git a/test/unit/eval/tv_clear_spec.lua b/test/unit/eval/tv_clear_spec.lua new file mode 100644 index 0000000000..96eccdbd71 --- /dev/null +++ b/test/unit/eval/tv_clear_spec.lua @@ -0,0 +1,127 @@ +local helpers = require('test.unit.helpers') +local eval_helpers = require('test.unit.eval.helpers') + +local alloc_log_new = helpers.alloc_log_new +local cimport = helpers.cimport +local ffi = helpers.ffi +local eq = helpers.eq + +local a = eval_helpers.alloc_logging_helpers +local type_key = eval_helpers.type_key +local list_type = eval_helpers.list_type +local list_items = eval_helpers.list_items +local dict_items = eval_helpers.dict_items +local lua2typvalt = eval_helpers.lua2typvalt + +local lib = cimport('./src/nvim/eval_defs.h', './src/nvim/eval.h') + +local alloc_log = alloc_log_new() + +before_each(function() + alloc_log:before_each() +end) + +after_each(function() + alloc_log:after_each() +end) + +describe('clear_tv()', function() + it('successfully frees all lists in [&l [1], *l, *l]', function() + local l_inner = {1} + local list = {l_inner, l_inner, l_inner} + local list_tv = ffi.gc(lua2typvalt(list), nil) + local list_p = list_tv.vval.v_list + local lis = list_items(list_p) + local list_inner_p = lis[1].li_tv.vval.v_list + local lis_inner = list_items(list_inner_p) + alloc_log:check({ + a.list(list_p), + a.list(list_inner_p), + a.li(lis_inner[1]), + a.li(lis[1]), + a.li(lis[2]), + a.li(lis[3]), + }) + eq(3, list_inner_p.lv_refcount) + lib.clear_tv(list_tv) + alloc_log:check({ + a.freed(lis_inner[1]), + a.freed(list_inner_p), + a.freed(lis[1]), + a.freed(lis[2]), + a.freed(lis[3]), + a.freed(list_p), + }) + end) + it('successfully frees all lists in [&l [], *l, *l]', function() + local l_inner = {[type_key]=list_type} + local list = {l_inner, l_inner, l_inner} + local list_tv = ffi.gc(lua2typvalt(list), nil) + local list_p = list_tv.vval.v_list + local lis = list_items(list_p) + local list_inner_p = lis[1].li_tv.vval.v_list + alloc_log:check({ + a.list(list_p), + a.list(list_inner_p), + a.li(lis[1]), + a.li(lis[2]), + a.li(lis[3]), + }) + eq(3, list_inner_p.lv_refcount) + lib.clear_tv(list_tv) + alloc_log:check({ + a.freed(list_inner_p), + a.freed(lis[1]), + a.freed(lis[2]), + a.freed(lis[3]), + a.freed(list_p), + }) + end) + it('successfully frees all dictionaries in [&d {}, *d]', function() + local d_inner = {} + local list = {d_inner, d_inner} + local list_tv = ffi.gc(lua2typvalt(list), nil) + local list_p = list_tv.vval.v_list + local lis = list_items(list_p) + local dict_inner_p = lis[1].li_tv.vval.v_dict + alloc_log:check({ + a.list(list_p), + a.dict(dict_inner_p), + a.li(lis[1]), + a.li(lis[2]), + }) + eq(2, dict_inner_p.dv_refcount) + lib.clear_tv(list_tv) + alloc_log:check({ + a.freed(dict_inner_p), + a.freed(lis[1]), + a.freed(lis[2]), + a.freed(list_p), + }) + end) + it('successfully frees all dictionaries in [&d {a: 1}, *d]', function() + local d_inner = {a=1} + local list = {d_inner, d_inner} + local list_tv = ffi.gc(lua2typvalt(list), nil) + local list_p = list_tv.vval.v_list + local lis = list_items(list_p) + local dict_inner_p = lis[1].li_tv.vval.v_dict + local dis = dict_items(dict_inner_p) + alloc_log:check({ + a.list(list_p), + a.dict(dict_inner_p), + a.di(dis.a, 1), + a.li(lis[1]), + a.li(lis[2]), + }) + eq(2, dict_inner_p.dv_refcount) + lib.clear_tv(list_tv) + alloc_log:check({ + a.freed(dis.a), + a.freed(dict_inner_p), + a.freed(lis[1]), + a.freed(lis[2]), + a.freed(list_p), + }) + end) +end) diff --git a/test/unit/helpers.lua b/test/unit/helpers.lua index 45bbaaeb10..1bfdd32739 100644 --- a/test/unit/helpers.lua +++ b/test/unit/helpers.lua @@ -9,6 +9,12 @@ local neq = global_helpers.neq local eq = global_helpers.eq local ok = global_helpers.ok +-- C constants. +local NULL = ffi.cast('void*', 0) + +local OK = 1 +local FAIL = 0 + -- add some standard header locations for _, p in ipairs(Paths.include_paths) do Preprocess.add_to_include_path(p) @@ -118,6 +124,67 @@ local function cppimport(path) return cimport(Paths.test_include_path .. '/' .. path) end +local function alloc_log_new() + local log = { + log={}, + lib=cimport('./src/nvim/memory.h'), + original_functions={}, + null={['\0:is_null']=true}, + } + local allocator_functions = {'malloc', 'free', 'calloc', 'realloc'} + function log:save_original_functions() + for _, funcname in ipairs(allocator_functions) do + self.original_functions[funcname] = self.lib['mem_' .. funcname] + end + end + function log:set_mocks() + for _, k in ipairs(allocator_functions) do + do + local kk = k + self.lib['mem_' .. k] = function(...) + local log_entry = {func=kk, args={...}} + self.log[#self.log + 1] = log_entry + if kk == 'free' then + self.original_functions[kk](...) + else + log_entry.ret = self.original_functions[kk](...) + end + for i, v in ipairs(log_entry.args) do + if v == nil then + -- XXX This thing thinks that {NULL} ~= {NULL}. + log_entry.args[i] = self.null + end + end + if self.hook then self:hook(log_entry) end + if log_entry.ret then + return log_entry.ret + end + end + end + end + end + function log:clear() + self.log = {} + end + function log:check(exp) + eq(exp, self.log) + self:clear() + end + function log:restore_original_functions() + for k, v in pairs(self.original_functions) do + self.lib['mem_' .. k] = v + end + end + function log:before_each() + log:save_original_functions() + log:set_mocks() + end + function log:after_each() + log:restore_original_functions() + end + return log +end + cimport('./src/nvim/types.h') -- take a pointer to a C-allocated string and return an interned @@ -142,12 +209,6 @@ do main.event_init() end --- C constants. -local NULL = ffi.cast('void*', 0) - -local OK = 1 -local FAIL = 0 - return { cimport = cimport, cppimport = cppimport, @@ -161,5 +222,6 @@ return { to_cstr = to_cstr, NULL = NULL, OK = OK, - FAIL = FAIL + FAIL = FAIL, + alloc_log_new = alloc_log_new, } diff --git a/test/unit/preprocess.lua b/test/unit/preprocess.lua index 8c2a5c73e5..1c9b290462 100644 --- a/test/unit/preprocess.lua +++ b/test/unit/preprocess.lua @@ -123,6 +123,7 @@ function Gcc:init_defines() self:define('INIT', {'...'}, '') self:define('_GNU_SOURCE') self:define('INCLUDE_GENERATED_DECLARATIONS') + self:define('UNIT_TESTING') -- Needed for FreeBSD self:define('_Thread_local', nil, '') -- Needed for macOS Sierra |