diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2025-02-05 23:09:29 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2025-02-05 23:09:29 +0000 |
commit | d5f194ce780c95821a855aca3c19426576d28ae0 (patch) | |
tree | d45f461b19f9118ad2bb1f440a7a08973ad18832 /runtime | |
parent | c5d770d311841ea5230426cc4c868e8db27300a8 (diff) | |
parent | 44740e561fc93afe3ebecfd3618bda2d2abeafb0 (diff) | |
download | rneovim-rahm.tar.gz rneovim-rahm.tar.bz2 rneovim-rahm.zip |
Diffstat (limited to 'runtime')
256 files changed, 25938 insertions, 18704 deletions
diff --git a/runtime/autoload/gzip.vim b/runtime/autoload/gzip.vim index 26b1cda034..a6fbe2c336 100644 --- a/runtime/autoload/gzip.vim +++ b/runtime/autoload/gzip.vim @@ -1,6 +1,6 @@ " Vim autoload file for editing compressed files. " Maintainer: The Vim Project <https://github.com/vim/vim> -" Last Change: 2023 Aug 10 +" Last Change: 2024 Nov 25 " Former Maintainer: Bram Moolenaar <Bram@vim.org> " These functions are used by the gzip plugin. @@ -148,6 +148,9 @@ fun gzip#read(cmd) else let fname = escape(expand("%:r"), " \t\n*?[{`$\\%#'\"|!<") endif + if filereadable(undofile(expand("%"))) + exe "sil rundo " . fnameescape(undofile(expand("%"))) + endif if &verbose >= 8 execute "doau BufReadPost " . fname else diff --git a/runtime/autoload/netrw.vim b/runtime/autoload/netrw.vim deleted file mode 100644 index 1df545278f..0000000000 --- a/runtime/autoload/netrw.vim +++ /dev/null @@ -1,11941 +0,0 @@ -" netrw.vim: Handles file transfer and remote directory listing across -" AUTOLOAD SECTION -" Maintainer: This runtime file is looking for a new maintainer. -" Date: May 03, 2023 -" Version: 173a -" Last Change: {{{1 -" 2023 Nov 21 by Vim Project: ignore wildignore when expanding $COMSPEC (v173a) -" 2023 Nov 22 by Vim Project: fix handling of very long filename on longlist style (v173a) -" 2024 Feb 19 by Vim Project: (announce adoption) -" 2024 Feb 29 by Vim Project: handle symlinks in tree mode correctly -" 2024 Apr 03 by Vim Project: detect filetypes for remote edited files -" 2024 May 08 by Vim Project: cleanup legacy Win9X checks -" 2024 May 09 by Vim Project: remove hard-coded private.ppk -" 2024 May 10 by Vim Project: recursively delete directories by default -" 2024 May 13 by Vim Project: prefer scp over pscp -" 2024 Jun 04 by Vim Project: set bufhidden if buffer changed, nohidden is set and buffer shall be switched (#14915) -" 2024 Jun 13 by Vim Project: glob() on Windows fails when a directory name contains [] (#14952) -" 2024 Jun 23 by Vim Project: save ad restore registers when liststyle = WIDELIST (#15077, #15114) -" 2024 Jul 22 by Vim Project: avoid endless recursion (#15318) -" 2024 Jul 23 by Vim Project: escape filename before trying to delete it (#15330) -" 2024 Jul 30 by Vim Project: handle mark-copy to same target directory (#12112) -" 2024 Aug 02 by Vim Project: honor g:netrw_alt{o,v} for :{S,H,V}explore (#15417) -" 2024 Aug 15 by Vim Project: style changes, prevent E121 (#15501) -" 2024 Aug 22 by Vim Project: fix mf-selection highlight (#15551) -" 2024 Aug 22 by Vim Project: adjust echo output of mx command (#15550) -" 2024 Sep 15 by Vim Project: more strict confirmation dialog (#15680) -" 2024 Sep 19 by Vim Project: mf-selection highlight uses wrong pattern (#15700) -" 2024 Sep 21 by Vim Project: remove extraneous closing bracket (#15718) -" 2024 Oct 21 by Vim Project: remove netrwFileHandlers (#15895) -" 2024 Oct 27 by Vim Project: clean up gx mapping (#15721) -" 2024 Oct 30 by Vim Project: fix filetype detection for remote files (#15961) -" 2024 Oct 30 by Vim Project: fix x mapping on cygwin (#13687) -" 2024 Oct 31 by Vim Project: add netrw#Launch() and netrw#Open() (#15962) -" 2024 Oct 31 by Vim Project: fix E874 when browsing remote dir (#15964) -" 2024 Nov 07 by Vim Project: use keeppatterns to prevent polluting the search history -" 2024 Nov 07 by Vim Project: fix a few issues with netrw tree listing (#15996) -" 2024 Nov 10 by Vim Project: directory symlink not resolved in tree view (#16020) -" 2024 Nov 14 by Vim Project: small fixes to netrw#BrowseX (#16056) -" }}} -" Former Maintainer: Charles E Campbell -" GetLatestVimScripts: 1075 1 :AutoInstall: netrw.vim -" Copyright: Copyright (C) 2016 Charles E. Campbell {{{1 -" Permission is hereby granted to use and distribute this code, -" with or without modifications, provided that this copyright -" notice is copied with it. Like anything else that's free, -" netrw.vim, netrwPlugin.vim, and netrwSettings.vim are provided -" *as is* and come with no warranty of any kind, either -" expressed or implied. By using this plugin, you agree that -" in no event will the copyright holder be liable for any damages -" resulting from the use of this software. -" -" Note: the code here was started in 1999 under a much earlier version of vim. The directory browsing -" code was written using vim v6, which did not have Lists (Lists were first offered with vim-v7). -" -"redraw!|call DechoSep()|call inputsave()|call input("Press <cr> to continue")|call inputrestore() -" -" But be doers of the Word, and not only hearers, deluding your own selves {{{1 -" (James 1:22 RSV) -" =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -" Load Once: {{{1 -if &cp || exists("g:loaded_netrw") - finish -endif - -" Check that vim has patches that netrw requires. -" Patches needed for v7.4: 1557, and 213. -" (netrw will benefit from vim's having patch#656, too) -let s:needspatches=[1557,213] -if exists("s:needspatches") - for ptch in s:needspatches - if v:version < 704 || (v:version == 704 && !has("patch".ptch)) - if !exists("s:needpatch{ptch}") - unsilent echomsg "***sorry*** this version of netrw requires vim v7.4 with patch#".ptch - endif - let s:needpatch{ptch}= 1 - finish - endif - endfor -endif - -let g:loaded_netrw = "v173" - -let s:keepcpo= &cpo -setl cpo&vim -"DechoFuncName 1 -"DechoRemOn -"call Decho("doing autoload/netrw.vim version ".g:loaded_netrw,'~'.expand("<slnum>")) - -" ====================== -" Netrw Variables: {{{1 -" ====================== - -" --------------------------------------------------------------------- -" netrw#ErrorMsg: {{{2 -" 0=note = s:NOTE -" 1=warning = s:WARNING -" 2=error = s:ERROR -" Usage: netrw#ErrorMsg(s:NOTE | s:WARNING | s:ERROR,"some message",error-number) -" netrw#ErrorMsg(s:NOTE | s:WARNING | s:ERROR,["message1","message2",...],error-number) -" (this function can optionally take a list of messages) -" Dec 2, 2019 : max errnum currently is 106 -fun! netrw#ErrorMsg(level,msg,errnum) -" call Dfunc("netrw#ErrorMsg(level=".a:level." msg<".a:msg."> errnum=".a:errnum.") g:netrw_use_errorwindow=".g:netrw_use_errorwindow) - - if a:level < g:netrw_errorlvl -" call Dret("netrw#ErrorMsg : suppressing level=".a:level." since g:netrw_errorlvl=".g:netrw_errorlvl) - return - endif - - if a:level == 1 - let level= "**warning** (netrw) " - elseif a:level == 2 - let level= "**error** (netrw) " - else - let level= "**note** (netrw) " - endif -" call Decho("level=".level,'~'.expand("<slnum>")) - - if g:netrw_use_errorwindow == 2 && exists("*popup_atcursor") - " use popup window - if type(a:msg) == 3 - let msg = [level]+a:msg - else - let msg= level.a:msg - endif - let s:popuperr_id = popup_atcursor(msg,{}) - let s:popuperr_text= "" - elseif g:netrw_use_errorwindow - " (default) netrw creates a one-line window to show error/warning - " messages (reliably displayed) - - " record current window number - let s:winBeforeErr= winnr() -" call Decho("s:winBeforeErr=".s:winBeforeErr,'~'.expand("<slnum>")) - - " getting messages out reliably is just plain difficult! - " This attempt splits the current window, creating a one line window. - if bufexists("NetrwMessage") && bufwinnr("NetrwMessage") > 0 -" call Decho("write to NetrwMessage buffer",'~'.expand("<slnum>")) - exe bufwinnr("NetrwMessage")."wincmd w" -" call Decho("setl ma noro",'~'.expand("<slnum>")) - setl ma noro - if type(a:msg) == 3 - for msg in a:msg - NetrwKeepj call setline(line("$")+1,level.msg) - endfor - else - NetrwKeepj call setline(line("$")+1,level.a:msg) - endif - NetrwKeepj $ - else -" call Decho("create a NetrwMessage buffer window",'~'.expand("<slnum>")) - bo 1split - sil! call s:NetrwEnew() - sil! NetrwKeepj call s:NetrwOptionsSafe(1) - setl bt=nofile - NetrwKeepj file NetrwMessage -" call Decho("setl ma noro",'~'.expand("<slnum>")) - setl ma noro - if type(a:msg) == 3 - for msg in a:msg - NetrwKeepj call setline(line("$")+1,level.msg) - endfor - else - NetrwKeepj call setline(line("$"),level.a:msg) - endif - NetrwKeepj $ - endif -" call Decho("wrote msg<".level.a:msg."> to NetrwMessage win#".winnr(),'~'.expand("<slnum>")) - if &fo !~ '[ta]' - syn clear - syn match netrwMesgNote "^\*\*note\*\*" - syn match netrwMesgWarning "^\*\*warning\*\*" - syn match netrwMesgError "^\*\*error\*\*" - hi link netrwMesgWarning WarningMsg - hi link netrwMesgError Error - endif -" call Decho("setl noma ro bh=wipe",'~'.expand("<slnum>")) - setl ro nomod noma bh=wipe - - else - " (optional) netrw will show messages using echomsg. Even if the - " message doesn't appear, at least it'll be recallable via :messages -" redraw! - if a:level == s:WARNING - echohl WarningMsg - elseif a:level == s:ERROR - echohl Error - endif - - if type(a:msg) == 3 - for msg in a:msg - unsilent echomsg level.msg - endfor - else - unsilent echomsg level.a:msg - endif - -" call Decho("echomsg ***netrw*** ".a:msg,'~'.expand("<slnum>")) - echohl None - endif - -" call Dret("netrw#ErrorMsg") -endfun - -" --------------------------------------------------------------------- -" s:NetrwInit: initializes variables if they haven't been defined {{{2 -" Loosely, varname = value. -fun s:NetrwInit(varname,value) -" call Decho("varname<".a:varname."> value=".a:value,'~'.expand("<slnum>")) - if !exists(a:varname) - if type(a:value) == 0 - exe "let ".a:varname."=".a:value - elseif type(a:value) == 1 && a:value =~ '^[{[]' - exe "let ".a:varname."=".a:value - elseif type(a:value) == 1 - exe "let ".a:varname."="."'".a:value."'" - else - exe "let ".a:varname."=".a:value - endif - endif -endfun - -" --------------------------------------------------------------------- -" Netrw Constants: {{{2 -call s:NetrwInit("g:netrw_dirhistcnt",0) -if !exists("s:LONGLIST") - call s:NetrwInit("s:THINLIST",0) - call s:NetrwInit("s:LONGLIST",1) - call s:NetrwInit("s:WIDELIST",2) - call s:NetrwInit("s:TREELIST",3) - call s:NetrwInit("s:MAXLIST" ,4) -endif - -let s:NOTE = 0 -let s:WARNING = 1 -let s:ERROR = 2 -call s:NetrwInit("g:netrw_errorlvl", s:NOTE) - -" --------------------------------------------------------------------- -" Default option values: {{{2 -let g:netrw_localcopycmdopt = "" -let g:netrw_localcopydircmdopt = "" -let g:netrw_localmkdiropt = "" -let g:netrw_localmovecmdopt = "" - -" --------------------------------------------------------------------- -" Default values for netrw's global protocol variables {{{2 -if !exists("g:netrw_use_errorwindow") - let g:netrw_use_errorwindow = 0 -endif - -if !exists("g:netrw_dav_cmd") - if executable("cadaver") - let g:netrw_dav_cmd = "cadaver" - elseif executable("curl") - let g:netrw_dav_cmd = "curl" - else - let g:netrw_dav_cmd = "" - endif -endif -if !exists("g:netrw_fetch_cmd") - if executable("fetch") - let g:netrw_fetch_cmd = "fetch -o" - else - let g:netrw_fetch_cmd = "" - endif -endif -if !exists("g:netrw_file_cmd") - if executable("elinks") - call s:NetrwInit("g:netrw_file_cmd","elinks") - elseif executable("links") - call s:NetrwInit("g:netrw_file_cmd","links") - endif -endif -if !exists("g:netrw_ftp_cmd") - let g:netrw_ftp_cmd = "ftp" -endif -let s:netrw_ftp_cmd= g:netrw_ftp_cmd -if !exists("g:netrw_ftp_options") - let g:netrw_ftp_options= "-i -n" -endif -if !exists("g:netrw_http_cmd") - if executable("wget") - let g:netrw_http_cmd = "wget" - call s:NetrwInit("g:netrw_http_xcmd","-q -O") - elseif executable("curl") - let g:netrw_http_cmd = "curl" - call s:NetrwInit("g:netrw_http_xcmd","-L -o") - elseif executable("elinks") - let g:netrw_http_cmd = "elinks" - call s:NetrwInit("g:netrw_http_xcmd","-source >") - elseif executable("fetch") - let g:netrw_http_cmd = "fetch" - call s:NetrwInit("g:netrw_http_xcmd","-o") - elseif executable("links") - let g:netrw_http_cmd = "links" - call s:NetrwInit("g:netrw_http_xcmd","-http.extra-header ".shellescape("Accept-Encoding: identity", 1)." -source >") - else - let g:netrw_http_cmd = "" - endif -endif -call s:NetrwInit("g:netrw_http_put_cmd","curl -T") -call s:NetrwInit("g:netrw_keepj","keepj") -call s:NetrwInit("g:netrw_rcp_cmd" , "rcp") -call s:NetrwInit("g:netrw_rsync_cmd", "rsync") -call s:NetrwInit("g:netrw_rsync_sep", "/") -if !exists("g:netrw_scp_cmd") - if executable("scp") - call s:NetrwInit("g:netrw_scp_cmd" , "scp -q") - elseif executable("pscp") - call s:NetrwInit("g:netrw_scp_cmd", 'pscp -q') - else - call s:NetrwInit("g:netrw_scp_cmd" , "scp -q") - endif -endif -call s:NetrwInit("g:netrw_sftp_cmd" , "sftp") -call s:NetrwInit("g:netrw_ssh_cmd" , "ssh") - -if has("win32") - \ && exists("g:netrw_use_nt_rcp") - \ && g:netrw_use_nt_rcp - \ && executable( $SystemRoot .'/system32/rcp.exe') - let s:netrw_has_nt_rcp = 1 - let s:netrw_rcpmode = '-b' -else - let s:netrw_has_nt_rcp = 0 - let s:netrw_rcpmode = '' -endif - -" --------------------------------------------------------------------- -" Default values for netrw's global variables {{{2 -" Cygwin Detection ------- {{{3 -if !exists("g:netrw_cygwin") - if has("win32unix") && &shell =~ '\%(\<bash\>\|\<zsh\>\)\%(\.exe\)\=$' - let g:netrw_cygwin= 1 - else - let g:netrw_cygwin= 0 - endif -endif -" Default values - a-c ---------- {{{3 -call s:NetrwInit("g:netrw_alto" , &sb) -call s:NetrwInit("g:netrw_altv" , &spr) -call s:NetrwInit("g:netrw_banner" , 1) -call s:NetrwInit("g:netrw_browse_split", 0) -call s:NetrwInit("g:netrw_bufsettings" , "noma nomod nonu nobl nowrap ro nornu") -call s:NetrwInit("g:netrw_chgwin" , -1) -call s:NetrwInit("g:netrw_clipboard" , 1) -call s:NetrwInit("g:netrw_compress" , "gzip") -call s:NetrwInit("g:netrw_ctags" , "ctags") -if exists("g:netrw_cursorline") && !exists("g:netrw_cursor") - call netrw#ErrorMsg(s:NOTE,'g:netrw_cursorline is deprecated; use g:netrw_cursor instead',77) - let g:netrw_cursor= g:netrw_cursorline -endif -call s:NetrwInit("g:netrw_cursor" , 2) -let s:netrw_usercul = &cursorline -let s:netrw_usercuc = &cursorcolumn -"call Decho("(netrw) COMBAK: cuc=".&l:cuc." cul=".&l:cul." initialization of s:netrw_cu[cl]") -call s:NetrwInit("g:netrw_cygdrive","/cygdrive") -" Default values - d-g ---------- {{{3 -call s:NetrwInit("s:didstarstar",0) -call s:NetrwInit("g:netrw_dirhistcnt" , 0) -call s:NetrwInit("g:netrw_decompress" , '{ ".gz" : "gunzip", ".bz2" : "bunzip2", ".zip" : "unzip", ".tar" : "tar -xf", ".xz" : "unxz" }') -call s:NetrwInit("g:netrw_dirhistmax" , 10) -call s:NetrwInit("g:netrw_fastbrowse" , 1) -call s:NetrwInit("g:netrw_ftp_browse_reject", '^total\s\+\d\+$\|^Trying\s\+\d\+.*$\|^KERBEROS_V\d rejected\|^Security extensions not\|No such file\|: connect to address [0-9a-fA-F:]*: No route to host$') -if !exists("g:netrw_ftp_list_cmd") - if has("unix") || (exists("g:netrw_cygwin") && g:netrw_cygwin) - let g:netrw_ftp_list_cmd = "ls -lF" - let g:netrw_ftp_timelist_cmd = "ls -tlF" - let g:netrw_ftp_sizelist_cmd = "ls -slF" - else - let g:netrw_ftp_list_cmd = "dir" - let g:netrw_ftp_timelist_cmd = "dir" - let g:netrw_ftp_sizelist_cmd = "dir" - endif -endif -call s:NetrwInit("g:netrw_ftpmode",'binary') -" Default values - h-lh ---------- {{{3 -call s:NetrwInit("g:netrw_hide",1) -if !exists("g:netrw_ignorenetrc") - if &shell =~ '\c\<\%(cmd\|4nt\)\.exe$' - let g:netrw_ignorenetrc= 1 - else - let g:netrw_ignorenetrc= 0 - endif -endif -call s:NetrwInit("g:netrw_keepdir",1) -if !exists("g:netrw_list_cmd") - if g:netrw_scp_cmd =~ '^pscp' && executable("pscp") - if exists("g:netrw_list_cmd_options") - let g:netrw_list_cmd= g:netrw_scp_cmd." -ls USEPORT HOSTNAME: ".g:netrw_list_cmd_options - else - let g:netrw_list_cmd= g:netrw_scp_cmd." -ls USEPORT HOSTNAME:" - endif - elseif executable(g:netrw_ssh_cmd) - " provide a scp-based default listing command - if exists("g:netrw_list_cmd_options") - let g:netrw_list_cmd= g:netrw_ssh_cmd." USEPORT HOSTNAME ls -FLa ".g:netrw_list_cmd_options - else - let g:netrw_list_cmd= g:netrw_ssh_cmd." USEPORT HOSTNAME ls -FLa" - endif - else -" call Decho(g:netrw_ssh_cmd." is not executable",'~'.expand("<slnum>")) - let g:netrw_list_cmd= "" - endif -endif -call s:NetrwInit("g:netrw_list_hide","") -" Default values - lh-lz ---------- {{{3 -if exists("g:netrw_local_copycmd") - let g:netrw_localcopycmd= g:netrw_local_copycmd - call netrw#ErrorMsg(s:NOTE,"g:netrw_local_copycmd is deprecated in favor of g:netrw_localcopycmd",84) -endif -if !exists("g:netrw_localcmdshell") - let g:netrw_localcmdshell= "" -endif -if !exists("g:netrw_localcopycmd") - if has("win32") - if g:netrw_cygwin - let g:netrw_localcopycmd= "cp" - else - let g:netrw_localcopycmd = expand("$COMSPEC", v:true) - let g:netrw_localcopycmdopt= " /c copy" - endif - elseif has("unix") || has("macunix") - let g:netrw_localcopycmd= "cp" - else - let g:netrw_localcopycmd= "" - endif -endif -if !exists("g:netrw_localcopydircmd") - if has("win32") - if g:netrw_cygwin - let g:netrw_localcopydircmd = "cp" - let g:netrw_localcopydircmdopt= " -R" - else - let g:netrw_localcopydircmd = expand("$COMSPEC", v:true) - let g:netrw_localcopydircmdopt= " /c xcopy /e /c /h /i /k" - endif - elseif has("unix") - let g:netrw_localcopydircmd = "cp" - let g:netrw_localcopydircmdopt= " -R" - elseif has("macunix") - let g:netrw_localcopydircmd = "cp" - let g:netrw_localcopydircmdopt= " -R" - else - let g:netrw_localcopydircmd= "" - endif -endif -if exists("g:netrw_local_mkdir") - let g:netrw_localmkdir= g:netrw_local_mkdir - call netrw#ErrorMsg(s:NOTE,"g:netrw_local_mkdir is deprecated in favor of g:netrw_localmkdir",87) -endif -if has("win32") - if g:netrw_cygwin - call s:NetrwInit("g:netrw_localmkdir","mkdir") - else - let g:netrw_localmkdir = expand("$COMSPEC", v:true) - let g:netrw_localmkdiropt= " /c mkdir" - endif -else - call s:NetrwInit("g:netrw_localmkdir","mkdir") -endif -call s:NetrwInit("g:netrw_remote_mkdir","mkdir") -if exists("g:netrw_local_movecmd") - let g:netrw_localmovecmd= g:netrw_local_movecmd - call netrw#ErrorMsg(s:NOTE,"g:netrw_local_movecmd is deprecated in favor of g:netrw_localmovecmd",88) -endif -if !exists("g:netrw_localmovecmd") - if has("win32") - if g:netrw_cygwin - let g:netrw_localmovecmd= "mv" - else - let g:netrw_localmovecmd = expand("$COMSPEC", v:true) - let g:netrw_localmovecmdopt= " /c move" - endif - elseif has("unix") || has("macunix") - let g:netrw_localmovecmd= "mv" - else - let g:netrw_localmovecmd= "" - endif -endif -" following serves as an example for how to insert a version&patch specific test -"if v:version < 704 || (v:version == 704 && !has("patch1107")) -"endif -call s:NetrwInit("g:netrw_liststyle" , s:THINLIST) -" sanity checks -if g:netrw_liststyle < 0 || g:netrw_liststyle >= s:MAXLIST - let g:netrw_liststyle= s:THINLIST -endif -if g:netrw_liststyle == s:LONGLIST && g:netrw_scp_cmd !~ '^pscp' - let g:netrw_list_cmd= g:netrw_list_cmd." -l" -endif -" Default values - m-r ---------- {{{3 -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_retmap" , 0) -if has("unix") || (exists("g:netrw_cygwin") && g:netrw_cygwin) - call s:NetrwInit("g:netrw_chgperm" , "chmod PERM FILENAME") -elseif has("win32") - call s:NetrwInit("g:netrw_chgperm" , "cacls FILENAME /e /p PERM") -else - call s:NetrwInit("g:netrw_chgperm" , "chmod PERM FILENAME") -endif -call s:NetrwInit("g:netrw_preview" , 0) -call s:NetrwInit("g:netrw_scpport" , "-P") -call s:NetrwInit("g:netrw_servername" , "NETRWSERVER") -call s:NetrwInit("g:netrw_sshport" , "-p") -call s:NetrwInit("g:netrw_rename_cmd" , g:netrw_ssh_cmd." USEPORT HOSTNAME mv") -call s:NetrwInit("g:netrw_rm_cmd" , g:netrw_ssh_cmd." USEPORT HOSTNAME rm") -call s:NetrwInit("g:netrw_rmdir_cmd" , g:netrw_ssh_cmd." USEPORT HOSTNAME rmdir") -call s:NetrwInit("g:netrw_rmf_cmd" , g:netrw_ssh_cmd." USEPORT HOSTNAME rm -f ") -" Default values - q-s ---------- {{{3 -call s:NetrwInit("g:netrw_quickhelp",0) -let s:QuickHelp= ["-:go up dir D:delete R:rename s:sort-by x:special", - \ "(create new) %:file d:directory", - \ "(windows split&open) o:horz v:vert p:preview", - \ "i:style qf:file info O:obtain r:reverse", - \ "(marks) mf:mark file mt:set target mm:move mc:copy", - \ "(bookmarks) mb:make mB:delete qb:list gb:go to", - \ "(history) qb:list u:go up U:go down", - \ "(targets) mt:target Tb:use bookmark Th:use history"] -" g:netrw_sepchr: picking a character that doesn't appear in filenames that can be used to separate priority from filename -call s:NetrwInit("g:netrw_sepchr" , (&enc == "euc-jp")? "\<Char-0x01>" : "\<Char-0xff>") -if !exists("g:netrw_keepj") || g:netrw_keepj == "keepj" - call s:NetrwInit("s:netrw_silentxfer" , (exists("g:netrw_silent") && g:netrw_silent != 0)? "sil keepj " : "keepj ") -else - call s:NetrwInit("s:netrw_silentxfer" , (exists("g:netrw_silent") && g:netrw_silent != 0)? "sil " : " ") -endif -call s:NetrwInit("g:netrw_sort_by" , "name") " alternatives: date , size -call s:NetrwInit("g:netrw_sort_options" , "") -call s:NetrwInit("g:netrw_sort_direction", "normal") " alternative: reverse (z y x ...) -if !exists("g:netrw_sort_sequence") - if has("unix") - let g:netrw_sort_sequence= '[\/]$,\<core\%(\.\d\+\)\=\>,\.h$,\.c$,\.cpp$,\~\=\*$,*,\.o$,\.obj$,\.info$,\.swp$,\.bak$,\~$' - else - let g:netrw_sort_sequence= '[\/]$,\.h$,\.c$,\.cpp$,*,\.o$,\.obj$,\.info$,\.swp$,\.bak$,\~$' - endif -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_use_noswf" , 1) -call s:NetrwInit("g:netrw_sizestyle" ,"b") -" Default values - t-w ---------- {{{3 -call s:NetrwInit("g:netrw_timefmt","%c") -if !exists("g:netrw_xstrlen") - if exists("g:Align_xstrlen") - let g:netrw_xstrlen= g:Align_xstrlen - elseif exists("g:drawit_xstrlen") - let g:netrw_xstrlen= g:drawit_xstrlen - elseif &enc == "latin1" || !has("multi_byte") - let g:netrw_xstrlen= 0 - else - let g:netrw_xstrlen= 1 - endif -endif -call s:NetrwInit("g:NetrwTopLvlMenu","Netrw.") -call s:NetrwInit("g:netrw_winsize",50) -call s:NetrwInit("g:netrw_wiw",1) -if g:netrw_winsize > 100|let g:netrw_winsize= 100|endif -" --------------------------------------------------------------------- -" Default values for netrw's script variables: {{{2 -call s:NetrwInit("g:netrw_fname_escape",' ?&;%') -if has("win32") - call s:NetrwInit("g:netrw_glob_escape",'*?`{[]$') -else - call s:NetrwInit("g:netrw_glob_escape",'*[]?`{~$\') -endif -call s:NetrwInit("g:netrw_menu_escape",'.&? \') -call s:NetrwInit("g:netrw_tmpfile_escape",' &;') -call s:NetrwInit("s:netrw_map_escape","<|\n\r\\\<C-V>\"") -if has("gui_running") && (&enc == 'utf-8' || &enc == 'utf-16' || &enc == 'ucs-4') - let s:treedepthstring= "│ " -else - let s:treedepthstring= "| " -endif -call s:NetrwInit("s:netrw_posn",'{}') - -" BufEnter event ignored by decho when following variable is true -" Has a side effect that doau BufReadPost doesn't work, so -" files read by network transfer aren't appropriately highlighted. -"let g:decho_bufenter = 1 "Decho - -" ====================== -" Netrw Initialization: {{{1 -" ====================== -if v:version >= 700 && has("balloon_eval") && !exists("s:initbeval") && !exists("g:netrw_nobeval") && has("syntax") && exists("g:syntax_on") -" call Decho("installed beval events",'~'.expand("<slnum>")) - let &l:bexpr = "netrw#BalloonHelp()" -" call Decho("&l:bexpr<".&l:bexpr."> buf#".bufnr()) - au FileType netrw setl beval - au WinLeave * if &ft == "netrw" && exists("s:initbeval")|let &beval= s:initbeval|endif - au VimEnter * let s:initbeval= &beval -"else " Decho -" if v:version < 700 | call Decho("did not install beval events: v:version=".v:version." < 700","~".expand("<slnum>")) | endif -" if !has("balloon_eval") | call Decho("did not install beval events: does not have balloon_eval","~".expand("<slnum>")) | endif -" if exists("s:initbeval") | call Decho("did not install beval events: s:initbeval exists","~".expand("<slnum>")) | endif -" if exists("g:netrw_nobeval") | call Decho("did not install beval events: g:netrw_nobeval exists","~".expand("<slnum>")) | endif -" if !has("syntax") | call Decho("did not install beval events: does not have syntax highlighting","~".expand("<slnum>")) | endif -" if exists("g:syntax_on") | call Decho("did not install beval events: g:syntax_on exists","~".expand("<slnum>")) | endif -endif -au WinEnter * if &ft == "netrw"|call s:NetrwInsureWinVars()|endif - -if g:netrw_keepj =~# "keepj" - com! -nargs=* NetrwKeepj keepj <args> -else - let g:netrw_keepj= "" - com! -nargs=* NetrwKeepj <args> -endif - -" ============================== -" Netrw Utility Functions: {{{1 -" ============================== - -" --------------------------------------------------------------------- -" netrw#BalloonHelp: {{{2 -if v:version >= 700 && has("balloon_eval") && has("syntax") && exists("g:syntax_on") && !exists("g:netrw_nobeval") -" call Decho("loading netrw#BalloonHelp()",'~'.expand("<slnum>")) - fun! netrw#BalloonHelp() - if &ft != "netrw" - return "" - endif - if exists("s:popuperr_id") && popup_getpos(s:popuperr_id) != {} - " popup error window is still showing - " s:pouperr_id and s:popuperr_text are set up in netrw#ErrorMsg() - if exists("s:popuperr_text") && s:popuperr_text != "" && v:beval_text != s:popuperr_text - " text under mouse hasn't changed; only close window when it changes - call popup_close(s:popuperr_id) - unlet s:popuperr_text - else - let s:popuperr_text= v:beval_text - endif - let mesg= "" - elseif !exists("w:netrw_bannercnt") || v:beval_lnum >= w:netrw_bannercnt || (exists("g:netrw_nobeval") && g:netrw_nobeval) - let mesg= "" - elseif v:beval_text == "Netrw" || v:beval_text == "Directory" || v:beval_text == "Listing" - let mesg = "i: thin-long-wide-tree gh: quick hide/unhide of dot-files qf: quick file info %:open new file" - elseif getline(v:beval_lnum) =~ '^"\s*/' - let mesg = "<cr>: edit/enter o: edit/enter in horiz window t: edit/enter in new tab v:edit/enter in vert window" - elseif v:beval_text == "Sorted" || v:beval_text == "by" - let mesg = 's: sort by name, time, file size, extension r: reverse sorting order mt: mark target' - elseif v:beval_text == "Sort" || v:beval_text == "sequence" - let mesg = "S: edit sorting sequence" - elseif v:beval_text == "Hiding" || v:beval_text == "Showing" - let mesg = "a: hiding-showing-all ctrl-h: editing hiding list mh: hide/show by suffix" - elseif v:beval_text == "Quick" || v:beval_text == "Help" - let mesg = "Help: press <F1>" - elseif v:beval_text == "Copy/Move" || v:beval_text == "Tgt" - let mesg = "mt: mark target mc: copy marked file to target mm: move marked file to target" - else - let mesg= "" - endif - return mesg - endfun -"else " Decho -" if v:version < 700 |call Decho("did not load netrw#BalloonHelp(): vim version ".v:version." < 700 -","~".expand("<slnum>"))|endif -" if !has("balloon_eval") |call Decho("did not load netrw#BalloonHelp(): does not have balloon eval","~".expand("<slnum>")) |endif -" if !has("syntax") |call Decho("did not load netrw#BalloonHelp(): syntax disabled","~".expand("<slnum>")) |endif -" if !exists("g:syntax_on") |call Decho("did not load netrw#BalloonHelp(): g:syntax_on n/a","~".expand("<slnum>")) |endif -" if exists("g:netrw_nobeval") |call Decho("did not load netrw#BalloonHelp(): g:netrw_nobeval exists","~".expand("<slnum>")) |endif -endif - -" ------------------------------------------------------------------------ -" netrw#Explore: launch the local browser in the directory of the current file {{{2 -" indx: == -1: Nexplore -" == -2: Pexplore -" == +: this is overloaded: -" * If Nexplore/Pexplore is in use, then this refers to the -" indx'th item in the w:netrw_explore_list[] of items which -" matched the */pattern **/pattern *//pattern **//pattern -" * If Hexplore or Vexplore, then this will override -" g:netrw_winsize to specify the qty of rows or columns the -" newly split window should have. -" dosplit==0: the window will be split iff the current file has been modified and hidden not set -" dosplit==1: the window will be split before running the local browser -" style == 0: Explore style == 1: Explore! -" == 2: Hexplore style == 3: Hexplore! -" == 4: Vexplore style == 5: Vexplore! -" == 6: Texplore -fun! netrw#Explore(indx,dosplit,style,...) - if !exists("b:netrw_curdir") - let b:netrw_curdir= getcwd() - endif - - " record current file for Rexplore's benefit - if &ft != "netrw" - let w:netrw_rexfile= expand("%:p") - endif - - " record current directory - let curdir = simplify(b:netrw_curdir) - let curfiledir = substitute(expand("%:p"),'^\(.*[/\\]\)[^/\\]*$','\1','e') - if !exists("g:netrw_cygwin") && has("win32") - let curdir= substitute(curdir,'\','/','g') - endif - - " using completion, directories with spaces in their names (thanks, Bill Gates, for a truly dumb idea) - " will end up with backslashes here. Solution: strip off backslashes that precede white space and - " try Explore again. - if a:0 > 0 - if a:1 =~ "\\\s" && !filereadable(s:NetrwFile(a:1)) && !isdirectory(s:NetrwFile(a:1)) - let a1 = substitute(a:1, '\\\(\s\)', '\1', 'g') - if a1 != a:1 - call netrw#Explore(a:indx, a:dosplit, a:style, a1) - return - endif - endif - endif - - " save registers - if !has('nvim') && has("clipboard") && g:netrw_clipboard -" call Decho("(netrw#Explore) save @* and @+",'~'.expand("<slnum>")) - sil! let keepregstar = @* - sil! let keepregplus = @+ - endif - sil! let keepregslash= @/ - - " if dosplit - " -or- file has been modified AND file not hidden when abandoned - " -or- Texplore used - if a:dosplit || (&modified && &hidden == 0 && &bufhidden != "hide") || a:style == 6 - call s:SaveWinVars() - let winsz= g:netrw_winsize - if a:indx > 0 - let winsz= a:indx - endif - - if a:style == 0 " Explore, Sexplore - let winsz= (winsz > 0)? (winsz*winheight(0))/100 : -winsz - if winsz == 0|let winsz= ""|endif - exe "noswapfile ".(g:netrw_alto ? "below " : "above ").winsz."wincmd s" - - elseif a:style == 1 " Explore!, Sexplore! - let winsz= (winsz > 0)? (winsz*winwidth(0))/100 : -winsz - if winsz == 0|let winsz= ""|endif - exe "keepalt noswapfile ".(g:netrw_altv ? "rightbelow " : "leftabove ").winsz."wincmd v" - - elseif a:style == 2 " Hexplore - let winsz= (winsz > 0)? (winsz*winheight(0))/100 : -winsz - if winsz == 0|let winsz= ""|endif - exe "keepalt noswapfile ".(g:netrw_alto ? "below " : "above ").winsz."wincmd s" - - elseif a:style == 3 " Hexplore! - let winsz= (winsz > 0)? (winsz*winheight(0))/100 : -winsz - if winsz == 0|let winsz= ""|endif - exe "keepalt noswapfile ".(!g:netrw_alto ? "below " : "above ").winsz."wincmd s" - - elseif a:style == 4 " Vexplore - let winsz= (winsz > 0)? (winsz*winwidth(0))/100 : -winsz - if winsz == 0|let winsz= ""|endif - exe "keepalt noswapfile ".(g:netrw_altv ? "rightbelow " : "leftabove ").winsz."wincmd v" - - elseif a:style == 5 " Vexplore! - let winsz= (winsz > 0)? (winsz*winwidth(0))/100 : -winsz - if winsz == 0|let winsz= ""|endif - exe "keepalt noswapfile ".(!g:netrw_altv ? "rightbelow " : "leftabove ").winsz."wincmd v" - - elseif a:style == 6 " Texplore - call s:SaveBufVars() - exe "keepalt tabnew ".fnameescape(curdir) - call s:RestoreBufVars() - endif - call s:RestoreWinVars() - endif - NetrwKeepj norm! 0 - - if a:0 > 0 - if a:1 =~ '^\~' && (has("unix") || (exists("g:netrw_cygwin") && g:netrw_cygwin)) - let dirname= simplify(substitute(a:1,'\~',expand("$HOME"),'')) - elseif a:1 == '.' - let dirname= simplify(exists("b:netrw_curdir")? b:netrw_curdir : getcwd()) - if dirname !~ '/$' - let dirname= dirname."/" - endif - elseif a:1 =~ '\$' - let dirname= simplify(expand(a:1)) - elseif a:1 !~ '^\*\{1,2}/' && a:1 !~ '^\a\{3,}://' - let dirname= simplify(a:1) - else - let dirname= a:1 - endif - else - " clear explore - call s:NetrwClearExplore() - return - endif - - if dirname =~ '\.\./\=$' - let dirname= simplify(fnamemodify(dirname,':p:h')) - elseif dirname =~ '\.\.' || dirname == '.' - let dirname= simplify(fnamemodify(dirname,':p')) - endif - - if dirname =~ '^\*//' - " starpat=1: Explore *//pattern (current directory only search for files containing pattern) - let pattern= substitute(dirname,'^\*//\(.*\)$','\1','') - let starpat= 1 - if &hls | let keepregslash= s:ExplorePatHls(pattern) | endif - - elseif dirname =~ '^\*\*//' - " starpat=2: Explore **//pattern (recursive descent search for files containing pattern) - let pattern= substitute(dirname,'^\*\*//','','') - let starpat= 2 - - elseif dirname =~ '/\*\*/' - " handle .../**/.../filepat - let prefixdir= substitute(dirname,'^\(.\{-}\)\*\*.*$','\1','') - if prefixdir =~ '^/' || (prefixdir =~ '^\a:/' && has("win32")) - let b:netrw_curdir = prefixdir - else - let b:netrw_curdir= getcwd().'/'.prefixdir - endif - let dirname= substitute(dirname,'^.\{-}\(\*\*/.*\)$','\1','') - let starpat= 4 - - elseif dirname =~ '^\*/' - " case starpat=3: Explore */filepat (search in current directory for filenames matching filepat) - let starpat= 3 - - elseif dirname=~ '^\*\*/' - " starpat=4: Explore **/filepat (recursive descent search for filenames matching filepat) - let starpat= 4 - - else - let starpat= 0 - endif - - if starpat == 0 && a:indx >= 0 - " [Explore Hexplore Vexplore Sexplore] [dirname] - if dirname == "" - let dirname= curfiledir - endif - if dirname =~# '^scp://' || dirname =~ '^ftp://' - call netrw#Nread(2,dirname) - else - if dirname == "" - let dirname= getcwd() - elseif has("win32") && !g:netrw_cygwin - " Windows : check for a drive specifier, or else for a remote share name ('\\Foo' or '//Foo', - " depending on whether backslashes have been converted to forward slashes by earlier code). - if dirname !~ '^[a-zA-Z]:' && dirname !~ '^\\\\\w\+' && dirname !~ '^//\w\+' - let dirname= b:netrw_curdir."/".dirname - endif - elseif dirname !~ '^/' - let dirname= b:netrw_curdir."/".dirname - endif - call netrw#LocalBrowseCheck(dirname) - endif - if exists("w:netrw_bannercnt") - " done to handle P08-Ingelrest. :Explore will _Always_ go to the line just after the banner. - " If one wants to return the same place in the netrw window, use :Rex instead. - exe w:netrw_bannercnt - endif - - - " starpat=1: Explore *//pattern (current directory only search for files containing pattern) - " starpat=2: Explore **//pattern (recursive descent search for files containing pattern) - " starpat=3: Explore */filepat (search in current directory for filenames matching filepat) - " starpat=4: Explore **/filepat (recursive descent search for filenames matching filepat) - elseif a:indx <= 0 - " Nexplore, Pexplore, Explore: handle starpat - if !mapcheck("<s-up>","n") && !mapcheck("<s-down>","n") && exists("b:netrw_curdir") - let s:didstarstar= 1 - nnoremap <buffer> <silent> <s-up> :Pexplore<cr> - nnoremap <buffer> <silent> <s-down> :Nexplore<cr> - endif - - if has("path_extra") - if !exists("w:netrw_explore_indx") - let w:netrw_explore_indx= 0 - endif - - let indx = a:indx - - if indx == -1 - " Nexplore - if !exists("w:netrw_explore_list") " sanity check - NetrwKeepj call netrw#ErrorMsg(s:WARNING,"using Nexplore or <s-down> improperly; see help for netrw-starstar",40) - if !has('nvim') && has("clipboard") && g:netrw_clipboard - if @* != keepregstar | sil! let @* = keepregstar | endif - if @+ != keepregplus | sil! let @+ = keepregplus | endif - endif - sil! let @/ = keepregslash - return - endif - let indx= w:netrw_explore_indx - if indx < 0 | let indx= 0 | endif - if indx >= w:netrw_explore_listlen | let indx= w:netrw_explore_listlen - 1 | endif - let curfile= w:netrw_explore_list[indx] - while indx < w:netrw_explore_listlen && curfile == w:netrw_explore_list[indx] - let indx= indx + 1 - endwhile - if indx >= w:netrw_explore_listlen | let indx= w:netrw_explore_listlen - 1 | endif - - elseif indx == -2 - " Pexplore - if !exists("w:netrw_explore_list") " sanity check - NetrwKeepj call netrw#ErrorMsg(s:WARNING,"using Pexplore or <s-up> improperly; see help for netrw-starstar",41) - if !has('nvim') && has("clipboard") && g:netrw_clipboard - if @* != keepregstar | sil! let @* = keepregstar | endif - if @+ != keepregplus | sil! let @+ = keepregplus | endif - endif - sil! let @/ = keepregslash - return - endif - let indx= w:netrw_explore_indx - if indx < 0 | let indx= 0 | endif - if indx >= w:netrw_explore_listlen | let indx= w:netrw_explore_listlen - 1 | endif - let curfile= w:netrw_explore_list[indx] - while indx >= 0 && curfile == w:netrw_explore_list[indx] - let indx= indx - 1 - endwhile - if indx < 0 | let indx= 0 | endif - - else - " Explore -- initialize - " build list of files to Explore with Nexplore/Pexplore - NetrwKeepj keepalt call s:NetrwClearExplore() - let w:netrw_explore_indx= 0 - if !exists("b:netrw_curdir") - let b:netrw_curdir= getcwd() - endif - - " switch on starpat to build the w:netrw_explore_list of files - if starpat == 1 - " starpat=1: Explore *//pattern (current directory only search for files containing pattern) - try - exe "NetrwKeepj noautocmd vimgrep /".pattern."/gj ".fnameescape(b:netrw_curdir)."/*" - catch /^Vim\%((\a\+)\)\=:E480/ - keepalt call netrw#ErrorMsg(s:WARNING,"no match with pattern<".pattern.">",76) - return - endtry - let w:netrw_explore_list = s:NetrwExploreListUniq(map(getqflist(),'bufname(v:val.bufnr)')) - if &hls | let keepregslash= s:ExplorePatHls(pattern) | endif - - elseif starpat == 2 - " starpat=2: Explore **//pattern (recursive descent search for files containing pattern) - try - exe "sil NetrwKeepj noautocmd keepalt vimgrep /".pattern."/gj "."**/*" - catch /^Vim\%((\a\+)\)\=:E480/ - keepalt call netrw#ErrorMsg(s:WARNING,'no files matched pattern<'.pattern.'>',45) - if &hls | let keepregslash= s:ExplorePatHls(pattern) | endif - if !has('nvim') && has("clipboard") && g:netrw_clipboard - if @* != keepregstar | sil! let @* = keepregstar | endif - if @+ != keepregplus | sil! let @+ = keepregplus | endif - endif - sil! let @/ = keepregslash - return - endtry - let s:netrw_curdir = b:netrw_curdir - let w:netrw_explore_list = getqflist() - let w:netrw_explore_list = s:NetrwExploreListUniq(map(w:netrw_explore_list,'s:netrw_curdir."/".bufname(v:val.bufnr)')) - if &hls | let keepregslash= s:ExplorePatHls(pattern) | endif - - elseif starpat == 3 - " starpat=3: Explore */filepat (search in current directory for filenames matching filepat) - let filepat= substitute(dirname,'^\*/','','') - let filepat= substitute(filepat,'^[%#<]','\\&','') - let w:netrw_explore_list= s:NetrwExploreListUniq(split(expand(b:netrw_curdir."/".filepat),'\n')) - if &hls | let keepregslash= s:ExplorePatHls(filepat) | endif - - elseif starpat == 4 - " starpat=4: Explore **/filepat (recursive descent search for filenames matching filepat) - let w:netrw_explore_list= s:NetrwExploreListUniq(split(expand(b:netrw_curdir."/".dirname),'\n')) - if &hls | let keepregslash= s:ExplorePatHls(dirname) | endif - endif " switch on starpat to build w:netrw_explore_list - - let w:netrw_explore_listlen = len(w:netrw_explore_list) - - if w:netrw_explore_listlen == 0 || (w:netrw_explore_listlen == 1 && w:netrw_explore_list[0] =~ '\*\*\/') - keepalt NetrwKeepj call netrw#ErrorMsg(s:WARNING,"no files matched",42) - if !has('nvim') && has("clipboard") && g:netrw_clipboard - if @* != keepregstar | sil! let @* = keepregstar | endif - if @+ != keepregplus | sil! let @+ = keepregplus | endif - endif - sil! let @/ = keepregslash - return - endif - endif " if indx ... endif - - " NetrwStatusLine support - for exploring support - let w:netrw_explore_indx= indx - - " wrap the indx around, but issue a note - if indx >= w:netrw_explore_listlen || indx < 0 - let indx = (indx < 0)? ( w:netrw_explore_listlen - 1 ) : 0 - let w:netrw_explore_indx= indx - keepalt NetrwKeepj call netrw#ErrorMsg(s:NOTE,"no more files match Explore pattern",43) - endif - - exe "let dirfile= w:netrw_explore_list[".indx."]" - let newdir= substitute(dirfile,'/[^/]*$','','e') - - call netrw#LocalBrowseCheck(newdir) - if !exists("w:netrw_liststyle") - let w:netrw_liststyle= g:netrw_liststyle - endif - if w:netrw_liststyle == s:THINLIST || w:netrw_liststyle == s:LONGLIST - keepalt NetrwKeepj call search('^'.substitute(dirfile,"^.*/","","").'\>',"W") - else - keepalt NetrwKeepj call search('\<'.substitute(dirfile,"^.*/","","").'\>',"w") - endif - let w:netrw_explore_mtchcnt = indx + 1 - let w:netrw_explore_bufnr = bufnr("%") - let w:netrw_explore_line = line(".") - keepalt NetrwKeepj call s:SetupNetrwStatusLine('%f %h%m%r%=%9*%{NetrwStatusLine()}') - - else - if !exists("g:netrw_quiet") - keepalt NetrwKeepj call netrw#ErrorMsg(s:WARNING,"your vim needs the +path_extra feature for Exploring with **!",44) - endif - if !has('nvim') && has("clipboard") && g:netrw_clipboard - if @* != keepregstar | sil! let @* = keepregstar | endif - if @+ != keepregplus | sil! let @+ = keepregplus | endif - endif - sil! let @/ = keepregslash - return - endif - - else - if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && dirname =~ '/' - sil! unlet w:netrw_treedict - sil! unlet w:netrw_treetop - endif - let newdir= dirname - if !exists("b:netrw_curdir") - NetrwKeepj call netrw#LocalBrowseCheck(getcwd()) - else - NetrwKeepj call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,newdir,0)) - endif - endif - - " visual display of **/ **// */ Exploration files - if exists("w:netrw_explore_indx") && exists("b:netrw_curdir") - if !exists("s:explore_prvdir") || s:explore_prvdir != b:netrw_curdir - " only update match list when current directory isn't the same as before - let s:explore_prvdir = b:netrw_curdir - let s:explore_match = "" - let dirlen = strlen(b:netrw_curdir) - if b:netrw_curdir !~ '/$' - let dirlen= dirlen + 1 - endif - let prvfname= "" - for fname in w:netrw_explore_list - if fname =~ '^'.b:netrw_curdir - if s:explore_match == "" - let s:explore_match= '\<'.escape(strpart(fname,dirlen),g:netrw_markfileesc).'\>' - else - let s:explore_match= s:explore_match.'\|\<'.escape(strpart(fname,dirlen),g:netrw_markfileesc).'\>' - endif - elseif fname !~ '^/' && fname != prvfname - if s:explore_match == "" - let s:explore_match= '\<'.escape(fname,g:netrw_markfileesc).'\>' - else - let s:explore_match= s:explore_match.'\|\<'.escape(fname,g:netrw_markfileesc).'\>' - endif - endif - let prvfname= fname - endfor - if has("syntax") && exists("g:syntax_on") && g:syntax_on - exe "2match netrwMarkFile /".s:explore_match."/" - endif - endif - echo "<s-up>==Pexplore <s-down>==Nexplore" - else - 2match none - if exists("s:explore_match") | unlet s:explore_match | endif - if exists("s:explore_prvdir") | unlet s:explore_prvdir | endif - endif - - " since Explore may be used to initialize netrw's browser, - " there's no danger of a late FocusGained event on initialization. - " Consequently, set s:netrw_events to 2. - let s:netrw_events= 2 - if !has('nvim') && has("clipboard") && g:netrw_clipboard - if @* != keepregstar | sil! let @* = keepregstar | endif - if @+ != keepregplus | sil! let @+ = keepregplus | endif - endif - sil! let @/ = keepregslash -endfun - -" --------------------------------------------------------------------- -" netrw#Lexplore: toggle Explorer window, keeping it on the left of the current tab {{{2 -" Uses g:netrw_chgwin : specifies the window where Lexplore files are to be opened -" t:netrw_lexposn : winsaveview() output (used on Lexplore window) -" t:netrw_lexbufnr: the buffer number of the Lexplore buffer (internal to this function) -" s:lexplore_win : window number of Lexplore window (serves to indicate which window is a Lexplore window) -" w:lexplore_buf : buffer number of Lexplore window (serves to indicate which window is a Lexplore window) -fun! netrw#Lexplore(count,rightside,...) -" call Dfunc("netrw#Lexplore(count=".a:count." rightside=".a:rightside.",...) a:0=".a:0." ft=".&ft) - let curwin= winnr() - - if a:0 > 0 && a:1 != "" - " if a netrw window is already on the left-side of the tab - " and a directory has been specified, explore with that - " directory. - let a1 = expand(a:1) - exe "1wincmd w" - if &ft == "netrw" - exe "Explore ".fnameescape(a1) - exe curwin."wincmd w" - let s:lexplore_win= curwin - let w:lexplore_buf= bufnr("%") - if exists("t:netrw_lexposn") - unlet t:netrw_lexposn - endif - return - endif - exe curwin."wincmd w" - else - let a1= "" - endif - - if exists("t:netrw_lexbufnr") - " check if t:netrw_lexbufnr refers to a netrw window - let lexwinnr = bufwinnr(t:netrw_lexbufnr) - else - let lexwinnr= 0 - endif - - if lexwinnr > 0 - " close down netrw explorer window - exe lexwinnr."wincmd w" - let g:netrw_winsize = -winwidth(0) - let t:netrw_lexposn = winsaveview() - close - if lexwinnr < curwin - let curwin= curwin - 1 - endif - if lexwinnr != curwin - exe curwin."wincmd w" - endif - unlet t:netrw_lexbufnr - - else - " open netrw explorer window - exe "1wincmd w" - let keep_altv = g:netrw_altv - let g:netrw_altv = 0 - if a:count != 0 - let netrw_winsize = g:netrw_winsize - let g:netrw_winsize = a:count - endif - let curfile= expand("%") - exe (a:rightside? "botright" : "topleft")." vertical ".((g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize) . " new" - if a:0 > 0 && a1 != "" - call netrw#Explore(0,0,0,a1) - exe "Explore ".fnameescape(a1) - elseif curfile =~ '^\a\{3,}://' - call netrw#Explore(0,0,0,substitute(curfile,'[^/\\]*$','','')) - else - call netrw#Explore(0,0,0,".") - endif - if a:count != 0 - let g:netrw_winsize = netrw_winsize - endif - setlocal winfixwidth - let g:netrw_altv = keep_altv - let t:netrw_lexbufnr = bufnr("%") - " done to prevent build-up of hidden buffers due to quitting and re-invocation of :Lexplore. - " Since the intended use of :Lexplore is to have an always-present explorer window, the extra - " effort to prevent mis-use of :Lex is warranted. - set bh=wipe - if exists("t:netrw_lexposn") - call winrestview(t:netrw_lexposn) - unlet t:netrw_lexposn - endif - endif - - " set up default window for editing via <cr> - if exists("g:netrw_chgwin") && g:netrw_chgwin == -1 - if a:rightside - let g:netrw_chgwin= 1 - else - let g:netrw_chgwin= 2 - endif - endif - -endfun - -" --------------------------------------------------------------------- -" netrw#Clean: remove netrw {{{2 -" supports :NetrwClean -- remove netrw from first directory on runtimepath -" :NetrwClean! -- remove netrw from all directories on runtimepath -fun! netrw#Clean(sys) -" call Dfunc("netrw#Clean(sys=".a:sys.")") - - if a:sys - let choice= confirm("Remove personal and system copies of netrw?","&Yes\n&No") - else - let choice= confirm("Remove personal copy of netrw?","&Yes\n&No") - endif -" call Decho("choice=".choice,'~'.expand("<slnum>")) - let diddel= 0 - let diddir= "" - - if choice == 1 - for dir in split(&rtp,',') - if filereadable(dir."/plugin/netrwPlugin.vim") -" call Decho("removing netrw-related files from ".dir,'~'.expand("<slnum>")) - if s:NetrwDelete(dir."/plugin/netrwPlugin.vim") |call netrw#ErrorMsg(1,"unable to remove ".dir."/plugin/netrwPlugin.vim",55) |endif - if s:NetrwDelete(dir."/autoload/netrwFileHandlers.vim")|call netrw#ErrorMsg(1,"unable to remove ".dir."/autoload/netrwFileHandlers.vim",55)|endif - if s:NetrwDelete(dir."/autoload/netrwSettings.vim") |call netrw#ErrorMsg(1,"unable to remove ".dir."/autoload/netrwSettings.vim",55) |endif - if s:NetrwDelete(dir."/autoload/netrw.vim") |call netrw#ErrorMsg(1,"unable to remove ".dir."/autoload/netrw.vim",55) |endif - if s:NetrwDelete(dir."/syntax/netrw.vim") |call netrw#ErrorMsg(1,"unable to remove ".dir."/syntax/netrw.vim",55) |endif - if s:NetrwDelete(dir."/syntax/netrwlist.vim") |call netrw#ErrorMsg(1,"unable to remove ".dir."/syntax/netrwlist.vim",55) |endif - let diddir= dir - let diddel= diddel + 1 - if !a:sys|break|endif - endif - endfor - endif - - echohl WarningMsg - if diddel == 0 - echomsg "netrw is either not installed or not removable" - elseif diddel == 1 - echomsg "removed one copy of netrw from <".diddir.">" - else - echomsg "removed ".diddel." copies of netrw" - endif - echohl None - -" call Dret("netrw#Clean") -endfun - -" --------------------------------------------------------------------- -" netrw#MakeTgt: make a target out of the directory name provided {{{2 -fun! netrw#MakeTgt(dname) -" call Dfunc("netrw#MakeTgt(dname<".a:dname.">)") - " simplify the target (eg. /abc/def/../ghi -> /abc/ghi) - let svpos = winsaveview() -" call Decho("saving posn to svpos<".string(svpos).">",'~'.expand("<slnum>")) - let s:netrwmftgt_islocal= (a:dname !~ '^\a\{3,}://') -" call Decho("s:netrwmftgt_islocal=".s:netrwmftgt_islocal,'~'.expand("<slnum>")) - if s:netrwmftgt_islocal - let netrwmftgt= simplify(a:dname) - else - let netrwmftgt= a:dname - endif - if exists("s:netrwmftgt") && netrwmftgt == s:netrwmftgt - " re-selected target, so just clear it - unlet s:netrwmftgt s:netrwmftgt_islocal - else - let s:netrwmftgt= netrwmftgt - endif - if g:netrw_fastbrowse <= 1 - call s:NetrwRefresh((b:netrw_curdir !~ '\a\{3,}://'),b:netrw_curdir) - endif -" call Decho("restoring posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))" - call winrestview(svpos) -" call Dret("netrw#MakeTgt") -endfun - -" --------------------------------------------------------------------- -" netrw#Obtain: {{{2 -" netrw#Obtain(islocal,fname[,tgtdirectory]) -" islocal=0 obtain from remote source -" =1 obtain from local source -" fname : a filename or a list of filenames -" tgtdir : optional place where files are to go (not present, uses getcwd()) -fun! netrw#Obtain(islocal,fname,...) -" call Dfunc("netrw#Obtain(islocal=".a:islocal." fname<".((type(a:fname) == 1)? a:fname : string(a:fname)).">) a:0=".a:0) - " NetrwStatusLine support - for obtaining support - - if type(a:fname) == 1 - let fnamelist= [ a:fname ] - elseif type(a:fname) == 3 - let fnamelist= a:fname - else - call netrw#ErrorMsg(s:ERROR,"attempting to use NetrwObtain on something not a filename or a list",62) -" call Dret("netrw#Obtain") - return - endif -" call Decho("fnamelist<".string(fnamelist).">",'~'.expand("<slnum>")) - if a:0 > 0 - let tgtdir= a:1 - else - let tgtdir= getcwd() - endif -" call Decho("tgtdir<".tgtdir.">",'~'.expand("<slnum>")) - - if exists("b:netrw_islocal") && b:netrw_islocal - " obtain a file from local b:netrw_curdir to (local) tgtdir -" call Decho("obtain a file from local ".b:netrw_curdir." to ".tgtdir,'~'.expand("<slnum>")) - if exists("b:netrw_curdir") && getcwd() != b:netrw_curdir - let topath= s:ComposePath(tgtdir,"") - if has("win32") - " transfer files one at time -" call Decho("transfer files one at a time",'~'.expand("<slnum>")) - for fname in fnamelist -" call Decho("system(".g:netrw_localcopycmd." ".s:ShellEscape(fname)." ".s:ShellEscape(topath).")",'~'.expand("<slnum>")) - call system(g:netrw_localcopycmd.g:netrw_localcopycmdopt." ".s:ShellEscape(fname)." ".s:ShellEscape(topath)) - if v:shell_error != 0 - call netrw#ErrorMsg(s:WARNING,"consider setting g:netrw_localcopycmd<".g:netrw_localcopycmd."> to something that works",80) -" call Dret("s:NetrwObtain 0 : failed: ".g:netrw_localcopycmd." ".s:ShellEscape(fname)." ".s:ShellEscape(topath)) - return - endif - endfor - else - " transfer files with one command -" call Decho("transfer files with one command",'~'.expand("<slnum>")) - let filelist= join(map(deepcopy(fnamelist),"s:ShellEscape(v:val)")) -" call Decho("system(".g:netrw_localcopycmd." ".filelist." ".s:ShellEscape(topath).")",'~'.expand("<slnum>")) - call system(g:netrw_localcopycmd.g:netrw_localcopycmdopt." ".filelist." ".s:ShellEscape(topath)) - if v:shell_error != 0 - call netrw#ErrorMsg(s:WARNING,"consider setting g:netrw_localcopycmd<".g:netrw_localcopycmd."> to something that works",80) -" call Dret("s:NetrwObtain 0 : failed: ".g:netrw_localcopycmd." ".filelist." ".s:ShellEscape(topath)) - return - endif - endif - elseif !exists("b:netrw_curdir") - call netrw#ErrorMsg(s:ERROR,"local browsing directory doesn't exist!",36) - else - call netrw#ErrorMsg(s:WARNING,"local browsing directory and current directory are identical",37) - endif - - else - " obtain files from remote b:netrw_curdir to local tgtdir -" call Decho("obtain a file from remote ".b:netrw_curdir." to ".tgtdir,'~'.expand("<slnum>")) - if type(a:fname) == 1 - call s:SetupNetrwStatusLine('%f %h%m%r%=%9*Obtaining '.a:fname) - endif - call s:NetrwMethod(b:netrw_curdir) - - if b:netrw_method == 4 - " obtain file using scp -" call Decho("obtain via scp (method#4)",'~'.expand("<slnum>")) - if exists("g:netrw_port") && g:netrw_port != "" - let useport= " ".g:netrw_scpport." ".g:netrw_port - else - let useport= "" - endif - if b:netrw_fname =~ '/' - let path= substitute(b:netrw_fname,'^\(.*/\).\{-}$','\1','') - else - let path= "" - endif - let filelist= join(map(deepcopy(fnamelist),'escape(s:ShellEscape(g:netrw_machine.":".path.v:val,1)," ")')) - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_scp_cmd.s:ShellEscape(useport,1)." ".filelist." ".s:ShellEscape(tgtdir,1)) - - elseif b:netrw_method == 2 - " obtain file using ftp + .netrc -" call Decho("obtain via ftp+.netrc (method #2)",'~'.expand("<slnum>")) - call s:SaveBufVars()|sil NetrwKeepj new|call s:RestoreBufVars() - let tmpbufnr= bufnr("%") - setl ff=unix - if exists("g:netrw_ftpmode") && g:netrw_ftpmode != "" - NetrwKeepj put =g:netrw_ftpmode -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - endif - - if exists("b:netrw_fname") && b:netrw_fname != "" - call setline(line("$")+1,'cd "'.b:netrw_fname.'"') -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - endif - - if exists("g:netrw_ftpextracmd") - NetrwKeepj put =g:netrw_ftpextracmd -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - endif - for fname in fnamelist - call setline(line("$")+1,'get "'.fname.'"') -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - endfor - if exists("g:netrw_port") && g:netrw_port != "" - call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1)) - else - call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)) - endif - " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) - if getline(1) !~ "^$" && !exists("g:netrw_quiet") && getline(1) !~ '^Trying ' - let debugkeep= &debug - setl debug=msg - call netrw#ErrorMsg(s:ERROR,getline(1),4) - let &debug= debugkeep - endif - - elseif b:netrw_method == 3 - " obtain with ftp + machine, id, passwd, and fname (ie. no .netrc) -" call Decho("obtain via ftp+mipf (method #3)",'~'.expand("<slnum>")) - call s:SaveBufVars()|sil NetrwKeepj new|call s:RestoreBufVars() - let tmpbufnr= bufnr("%") - setl ff=unix - - if exists("g:netrw_port") && g:netrw_port != "" - NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - else - NetrwKeepj put ='open '.g:netrw_machine -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - endif - - if exists("g:netrw_uid") && g:netrw_uid != "" - if exists("g:netrw_ftp") && g:netrw_ftp == 1 - NetrwKeepj put =g:netrw_uid -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - if exists("s:netrw_passwd") && s:netrw_passwd != "" - NetrwKeepj put ='\"'.s:netrw_passwd.'\"' - endif -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - elseif exists("s:netrw_passwd") - NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"' -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - endif - endif - - if exists("g:netrw_ftpmode") && g:netrw_ftpmode != "" - NetrwKeepj put =g:netrw_ftpmode -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - endif - - if exists("b:netrw_fname") && b:netrw_fname != "" - NetrwKeepj call setline(line("$")+1,'cd "'.b:netrw_fname.'"') -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - endif - - if exists("g:netrw_ftpextracmd") - NetrwKeepj put =g:netrw_ftpextracmd -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - endif - - if exists("g:netrw_ftpextracmd") - NetrwKeepj put =g:netrw_ftpextracmd -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - endif - for fname in fnamelist - NetrwKeepj call setline(line("$")+1,'get "'.fname.'"') - endfor -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - - " perform ftp: - " -i : turns off interactive prompting from ftp - " -n unix : DON'T use <.netrc>, even though it exists - " -n win32: quit being obnoxious about password - " Note: using "_dd to delete to the black hole register; avoids messing up @@ - NetrwKeepj norm! 1G"_dd - call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." ".g:netrw_ftp_options) - " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) - if getline(1) !~ "^$" -" call Decho("error<".getline(1).">",'~'.expand("<slnum>")) - if !exists("g:netrw_quiet") - NetrwKeepj call netrw#ErrorMsg(s:ERROR,getline(1),5) - endif - endif - - elseif b:netrw_method == 9 - " obtain file using sftp -" call Decho("obtain via sftp (method #9)",'~'.expand("<slnum>")) - if a:fname =~ '/' - let localfile= substitute(a:fname,'^.*/','','') - else - let localfile= a:fname - endif - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_sftp_cmd." ".s:ShellEscape(g:netrw_machine.":".b:netrw_fname,1).s:ShellEscape(localfile)." ".s:ShellEscape(tgtdir)) - - elseif !exists("b:netrw_method") || b:netrw_method < 0 - " probably a badly formed url; protocol not recognized -" call Dret("netrw#Obtain : unsupported method") - return - - else - " protocol recognized but not supported for Obtain (yet?) - if !exists("g:netrw_quiet") - NetrwKeepj call netrw#ErrorMsg(s:ERROR,"current protocol not supported for obtaining file",97) - endif -" call Dret("netrw#Obtain : current protocol not supported for obtaining file") - return - endif - - " restore status line - if type(a:fname) == 1 && exists("s:netrw_users_stl") - NetrwKeepj call s:SetupNetrwStatusLine(s:netrw_users_stl) - endif - - endif - - " cleanup - if exists("tmpbufnr") - if bufnr("%") != tmpbufnr - exe tmpbufnr."bw!" - else - q! - endif - endif - -" call Dret("netrw#Obtain") -endfun - -" --------------------------------------------------------------------- -" netrw#Nread: save position, call netrw#NetRead(), and restore position {{{2 -fun! netrw#Nread(mode,fname) -" call Dfunc("netrw#Nread(mode=".a:mode." fname<".a:fname.">)") - let svpos= winsaveview() -" call Decho("saving posn to svpos<".string(svpos).">",'~'.expand("<slnum>")) - call netrw#NetRead(a:mode,a:fname) -" call Decho("restoring posn to svpos<".string(svpos).">",'~'.expand("<slnum>")) - call winrestview(svpos) - - if exists("w:netrw_liststyle") && w:netrw_liststyle != s:TREELIST - if exists("w:netrw_bannercnt") - " start with cursor just after the banner - exe w:netrw_bannercnt - endif - endif -" call Dret("netrw#Nread") -endfun - -" ------------------------------------------------------------------------ -" s:NetrwOptionsSave: save options prior to setting to "netrw-buffer-standard" form {{{2 -" Options get restored by s:NetrwOptionsRestore() -" -" Option handling: -" * save user's options (s:NetrwOptionsSave) -" * set netrw-safe options (s:NetrwOptionsSafe) -" - change an option only when user option != safe option (s:netrwSetSafeSetting) -" * restore user's options (s:netrwOPtionsRestore) -" - restore a user option when != safe option (s:NetrwRestoreSetting) -" vt: (variable type) normally its either "w:" or "s:" -fun! s:NetrwOptionsSave(vt) -" call Dfunc("s:NetrwOptionsSave(vt<".a:vt.">) win#".winnr()." buf#".bufnr("%")."<".bufname(bufnr("%")).">"." winnr($)=".winnr("$")." mod=".&mod." ma=".&ma) -" call Decho(a:vt."netrw_optionsave".(exists("{a:vt}netrw_optionsave")? ("=".{a:vt}netrw_optionsave) : " doesn't exist"),'~'.expand("<slnum>")) -" call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt." hid=".&hid,'~'.expand("<slnum>")) -" call Decho("(s:NetrwOptionsSave) lines=".&lines) - - if !exists("{a:vt}netrw_optionsave") - let {a:vt}netrw_optionsave= 1 - else -" call Dret("s:NetrwOptionsSave : options already saved") - return - endif -" call Decho("prior to save: fo=".&fo.(exists("+acd")? " acd=".&acd : " acd doesn't exist")." diff=".&l:diff,'~'.expand("<slnum>")) - - " Save current settings and current directory -" call Decho("saving current settings and current directory",'~'.expand("<slnum>")) - let s:yykeep = @@ - if exists("&l:acd")|let {a:vt}netrw_acdkeep = &l:acd|endif - let {a:vt}netrw_aikeep = &l:ai - let {a:vt}netrw_awkeep = &l:aw - let {a:vt}netrw_bhkeep = &l:bh - let {a:vt}netrw_blkeep = &l:bl - let {a:vt}netrw_btkeep = &l:bt - let {a:vt}netrw_bombkeep = &l:bomb - let {a:vt}netrw_cedit = &cedit - let {a:vt}netrw_cikeep = &l:ci - let {a:vt}netrw_cinkeep = &l:cin - let {a:vt}netrw_cinokeep = &l:cino - let {a:vt}netrw_comkeep = &l:com - let {a:vt}netrw_cpokeep = &l:cpo - let {a:vt}netrw_cuckeep = &l:cuc - let {a:vt}netrw_culkeep = &l:cul -" call Decho("(s:NetrwOptionsSave) COMBAK: cuc=".&l:cuc." cul=".&l:cul) - let {a:vt}netrw_diffkeep = &l:diff - let {a:vt}netrw_fenkeep = &l:fen - if !exists("g:netrw_ffkeep") || g:netrw_ffkeep - let {a:vt}netrw_ffkeep = &l:ff - endif - let {a:vt}netrw_fokeep = &l:fo " formatoptions - let {a:vt}netrw_gdkeep = &l:gd " gdefault - let {a:vt}netrw_gokeep = &go " guioptions - let {a:vt}netrw_hidkeep = &l:hidden - let {a:vt}netrw_imkeep = &l:im - let {a:vt}netrw_iskkeep = &l:isk - let {a:vt}netrw_lines = &lines - let {a:vt}netrw_lskeep = &l:ls - let {a:vt}netrw_makeep = &l:ma - let {a:vt}netrw_magickeep = &l:magic - let {a:vt}netrw_modkeep = &l:mod - let {a:vt}netrw_nukeep = &l:nu - let {a:vt}netrw_rnukeep = &l:rnu - let {a:vt}netrw_repkeep = &l:report - let {a:vt}netrw_rokeep = &l:ro - let {a:vt}netrw_selkeep = &l:sel - let {a:vt}netrw_spellkeep = &l:spell - if !g:netrw_use_noswf - let {a:vt}netrw_swfkeep = &l:swf - endif - let {a:vt}netrw_tskeep = &l:ts - let {a:vt}netrw_twkeep = &l:tw " textwidth - let {a:vt}netrw_wigkeep = &l:wig " wildignore - let {a:vt}netrw_wrapkeep = &l:wrap - let {a:vt}netrw_writekeep = &l:write - - " save a few selected netrw-related variables -" call Decho("saving a few selected netrw-related variables",'~'.expand("<slnum>")) - if g:netrw_keepdir - let {a:vt}netrw_dirkeep = getcwd() -" call Decho("saving to ".a:vt."netrw_dirkeep<".{a:vt}netrw_dirkeep.">",'~'.expand("<slnum>")) - endif - if !has('nvim') && has("clipboard") && g:netrw_clipboard - sil! let {a:vt}netrw_starkeep = @* - sil! let {a:vt}netrw_pluskeep = @+ - endif - sil! let {a:vt}netrw_slashkeep= @/ - -" call Decho("(s:NetrwOptionsSave) lines=".&lines) -" 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 Dret("s:NetrwOptionsSave : tab#".tabpagenr()." win#".winnr()) -endfun - -" --------------------------------------------------------------------- -" s:NetrwOptionsSafe: sets options to help netrw do its job {{{2 -" Use s:NetrwSaveOptions() to save user settings -" Use s:NetrwOptionsRestore() to restore user settings -fun! s:NetrwOptionsSafe(islocal) -" call Dfunc("s:NetrwOptionsSafe(islocal=".a:islocal.") win#".winnr()." buf#".bufnr("%")."<".bufname(bufnr("%"))."> winnr($)=".winnr("$")) -" call Decho("win#".winnr()."'s ft=".&ft,'~'.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>")) - if exists("+acd") | call s:NetrwSetSafeSetting("&l:acd",0)|endif - call s:NetrwSetSafeSetting("&l:ai",0) - call s:NetrwSetSafeSetting("&l:aw",0) - call s:NetrwSetSafeSetting("&l:bl",0) - call s:NetrwSetSafeSetting("&l:bomb",0) - if a:islocal - call s:NetrwSetSafeSetting("&l:bt","nofile") - else - call s:NetrwSetSafeSetting("&l:bt","acwrite") - endif - call s:NetrwSetSafeSetting("&l:ci",0) - call s:NetrwSetSafeSetting("&l:cin",0) - if g:netrw_fastbrowse > a:islocal - call s:NetrwSetSafeSetting("&l:bh","hide") - else - call s:NetrwSetSafeSetting("&l:bh","delete") - endif - call s:NetrwSetSafeSetting("&l:cino","") - call s:NetrwSetSafeSetting("&l:com","") - if &cpo =~ 'a' | call s:NetrwSetSafeSetting("&cpo",substitute(&cpo,'a','','g')) | endif - if &cpo =~ 'A' | call s:NetrwSetSafeSetting("&cpo",substitute(&cpo,'A','','g')) | endif - setl fo=nroql2 - if &go =~ 'a' | set go-=a | endif - if &go =~ 'A' | set go-=A | endif - if &go =~ 'P' | set go-=P | endif - call s:NetrwSetSafeSetting("&l:hid",0) - call s:NetrwSetSafeSetting("&l:im",0) - setl isk+=@ isk+=* isk+=/ - call s:NetrwSetSafeSetting("&l:magic",1) - if g:netrw_use_noswf - call s:NetrwSetSafeSetting("swf",0) - endif - call s:NetrwSetSafeSetting("&l:report",10000) - call s:NetrwSetSafeSetting("&l:sel","inclusive") - call s:NetrwSetSafeSetting("&l:spell",0) - call s:NetrwSetSafeSetting("&l:tw",0) - call s:NetrwSetSafeSetting("&l:wig","") - setl cedit& - - " set up cuc and cul based on g:netrw_cursor and listing style - " COMBAK -- cuc cul related - call s:NetrwCursor(0) - - " allow the user to override safe options -" call Decho("ft<".&ft."> ei=".&ei,'~'.expand("<slnum>")) - if &ft == "netrw" -" call Decho("do any netrw FileType autocmds (doau FileType netrw)",'~'.expand("<slnum>")) - keepalt NetrwKeepj doau FileType netrw - endif - -" call Decho("fo=".&fo.(exists("+acd")? " acd=".&acd : " acd doesn't exist")." bh=".&l:bh." bt<".&bt.">",'~'.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 Dret("s:NetrwOptionsSafe") -endfun - -" --------------------------------------------------------------------- -" s:NetrwOptionsRestore: restore options (based on prior s:NetrwOptionsSave) {{{2 -fun! s:NetrwOptionsRestore(vt) - if !exists("{a:vt}netrw_optionsave") - " filereadable() returns zero for remote files (e.g. scp://user@localhost//etc/fstab) - " Note: @ may not be in 'isfname', so '^\w\+://\f\+/' may not match - if filereadable(expand("%")) || expand("%") =~# '^\w\+://\f\+' - filetype detect - else - setl ft=netrw - endif - return - endif - unlet {a:vt}netrw_optionsave - - if exists("+acd") - if exists("{a:vt}netrw_acdkeep") - let curdir = getcwd() - let &l:acd = {a:vt}netrw_acdkeep - unlet {a:vt}netrw_acdkeep - if &l:acd - call s:NetrwLcd(curdir) - endif - endif - endif - call s:NetrwRestoreSetting(a:vt."netrw_aikeep","&l:ai") - call s:NetrwRestoreSetting(a:vt."netrw_awkeep","&l:aw") - call s:NetrwRestoreSetting(a:vt."netrw_blkeep","&l:bl") - call s:NetrwRestoreSetting(a:vt."netrw_btkeep","&l:bt") - call s:NetrwRestoreSetting(a:vt."netrw_bombkeep","&l:bomb") - call s:NetrwRestoreSetting(a:vt."netrw_cedit","&cedit") - call s:NetrwRestoreSetting(a:vt."netrw_cikeep","&l:ci") - call s:NetrwRestoreSetting(a:vt."netrw_cinkeep","&l:cin") - call s:NetrwRestoreSetting(a:vt."netrw_cinokeep","&l:cino") - call s:NetrwRestoreSetting(a:vt."netrw_comkeep","&l:com") - call s:NetrwRestoreSetting(a:vt."netrw_cpokeep","&l:cpo") - call s:NetrwRestoreSetting(a:vt."netrw_diffkeep","&l:diff") - call s:NetrwRestoreSetting(a:vt."netrw_fenkeep","&l:fen") - if exists("g:netrw_ffkeep") && g:netrw_ffkeep - call s:NetrwRestoreSetting(a:vt."netrw_ffkeep")","&l:ff") - endif - call s:NetrwRestoreSetting(a:vt."netrw_fokeep" ,"&l:fo") - call s:NetrwRestoreSetting(a:vt."netrw_gdkeep" ,"&l:gd") - call s:NetrwRestoreSetting(a:vt."netrw_gokeep" ,"&go") - call s:NetrwRestoreSetting(a:vt."netrw_hidkeep" ,"&l:hidden") - call s:NetrwRestoreSetting(a:vt."netrw_imkeep" ,"&l:im") - call s:NetrwRestoreSetting(a:vt."netrw_iskkeep" ,"&l:isk") - call s:NetrwRestoreSetting(a:vt."netrw_lines" ,"&lines") - call s:NetrwRestoreSetting(a:vt."netrw_lskeep" ,"&l:ls") - call s:NetrwRestoreSetting(a:vt."netrw_makeep" ,"&l:ma") - call s:NetrwRestoreSetting(a:vt."netrw_magickeep","&l:magic") - call s:NetrwRestoreSetting(a:vt."netrw_modkeep" ,"&l:mod") - call s:NetrwRestoreSetting(a:vt."netrw_nukeep" ,"&l:nu") - call s:NetrwRestoreSetting(a:vt."netrw_rnukeep" ,"&l:rnu") - call s:NetrwRestoreSetting(a:vt."netrw_repkeep" ,"&l:report") - call s:NetrwRestoreSetting(a:vt."netrw_rokeep" ,"&l:ro") - call s:NetrwRestoreSetting(a:vt."netrw_selkeep" ,"&l:sel") - call s:NetrwRestoreSetting(a:vt."netrw_spellkeep","&l:spell") - call s:NetrwRestoreSetting(a:vt."netrw_twkeep" ,"&l:tw") - call s:NetrwRestoreSetting(a:vt."netrw_wigkeep" ,"&l:wig") - call s:NetrwRestoreSetting(a:vt."netrw_wrapkeep" ,"&l:wrap") - call s:NetrwRestoreSetting(a:vt."netrw_writekeep","&l:write") - call s:NetrwRestoreSetting("s:yykeep","@@") - " former problem: start with liststyle=0; press <i> : result, following line resets l:ts. - " Fixed; in s:PerformListing, when w:netrw_liststyle is s:LONGLIST, will use a printf to pad filename with spaces - " rather than by appending a tab which previously was using "&ts" to set the desired spacing. (Sep 28, 2018) - call s:NetrwRestoreSetting(a:vt."netrw_tskeep","&l:ts") - - if exists("{a:vt}netrw_swfkeep") - if &directory == "" - " user hasn't specified a swapfile directory; - " netrw will temporarily set the swapfile directory - " to the current directory as returned by getcwd(). - let &l:directory= getcwd() - sil! let &l:swf = {a:vt}netrw_swfkeep - setl directory= - unlet {a:vt}netrw_swfkeep - elseif &l:swf != {a:vt}netrw_swfkeep - if !g:netrw_use_noswf - " following line causes a Press ENTER in windows -- can't seem to work around it!!! - sil! let &l:swf= {a:vt}netrw_swfkeep - endif - unlet {a:vt}netrw_swfkeep - endif - endif - if exists("{a:vt}netrw_dirkeep") && isdirectory(s:NetrwFile({a:vt}netrw_dirkeep)) && g:netrw_keepdir - let dirkeep = substitute({a:vt}netrw_dirkeep,'\\','/','g') - if exists("{a:vt}netrw_dirkeep") - call s:NetrwLcd(dirkeep) - unlet {a:vt}netrw_dirkeep - endif - endif - if !has('nvim') && has("clipboard") && g:netrw_clipboard - call s:NetrwRestoreSetting(a:vt."netrw_starkeep","@*") - call s:NetrwRestoreSetting(a:vt."netrw_pluskeep","@+") - endif - call s:NetrwRestoreSetting(a:vt."netrw_slashkeep","@/") - - " Moved the filetype detect here from NetrwGetFile() because remote files - " were having their filetype detect-generated settings overwritten by - " NetrwOptionRestore. - if &ft != "netrw" - filetype detect - endif -endfun - -" --------------------------------------------------------------------- -" s:NetrwSetSafeSetting: sets an option to a safe setting {{{2 -" but only when the options' value and the safe setting differ -" Doing this means that netrw will not come up as having changed a -" setting last when it really didn't actually change it. -" -" Called from s:NetrwOptionsSafe -" ex. call s:NetrwSetSafeSetting("&l:sel","inclusive") -fun! s:NetrwSetSafeSetting(setting,safesetting) -" call Dfunc("s:NetrwSetSafeSetting(setting<".a:setting."> safesetting<".a:safesetting.">)") - - if a:setting =~ '^&' -" call Decho("fyi: a:setting starts with &") - exe "let settingval= ".a:setting -" call Decho("fyi: settingval<".settingval.">") - - if settingval != a:safesetting -" call Decho("set setting<".a:setting."> to option value<".a:safesetting.">") - if type(a:safesetting) == 0 - exe "let ".a:setting."=".a:safesetting - elseif type(a:safesetting) == 1 - exe "let ".a:setting."= '".a:safesetting."'" - else - call netrw#ErrorMsg(s:ERROR,"(s:NetrwRestoreSetting) doesn't know how to restore ".a:setting." with a safesetting of type#".type(a:safesetting),105) - endif - endif - endif - -" call Dret("s:NetrwSetSafeSetting") -endfun - -" ------------------------------------------------------------------------ -" s:NetrwRestoreSetting: restores specified setting using associated keepvar, {{{2 -" but only if the setting value differs from the associated keepvar. -" Doing this means that netrw will not come up as having changed a -" setting last when it really didn't actually change it. -" -" Used by s:NetrwOptionsRestore() to restore each netrw-sensitive setting -" keepvars are set up by s:NetrwOptionsSave -fun! s:NetrwRestoreSetting(keepvar,setting) -""" call Dfunc("s:NetrwRestoreSetting(a:keepvar<".a:keepvar."> a:setting<".a:setting.">)") - - " typically called from s:NetrwOptionsRestore - " call s:NetrwRestoreSettings(keep-option-variable-name,'associated-option') - " ex. call s:NetrwRestoreSetting(a:vt."netrw_selkeep","&l:sel") - " Restores option (but only if different) from a:keepvar - if exists(a:keepvar) - exe "let keepvarval= ".a:keepvar - exe "let setting= ".a:setting - -"" call Decho("fyi: a:keepvar<".a:keepvar."> exists") -"" call Decho("fyi: keepvarval=".keepvarval) -"" call Decho("fyi: a:setting<".a:setting."> setting<".setting.">") - - if setting != keepvarval -"" call Decho("restore setting<".a:setting."> (currently=".setting.") to keepvarval<".keepvarval.">") - if type(a:setting) == 0 - exe "let ".a:setting."= ".keepvarval - elseif type(a:setting) == 1 - exe "let ".a:setting."= '".substitute(keepvarval,"'","''","g")."'" - else - call netrw#ErrorMsg(s:ERROR,"(s:NetrwRestoreSetting) doesn't know how to restore ".a:keepvar." with a setting of type#".type(a:setting),105) - endif - endif - - exe "unlet ".a:keepvar - endif - -"" call Dret("s:NetrwRestoreSetting") -endfun - -" --------------------------------------------------------------------- -" NetrwStatusLine: {{{2 -fun! NetrwStatusLine() - -" vvv NetrwStatusLine() debugging vvv -" let g:stlmsg="" -" if !exists("w:netrw_explore_bufnr") -" let g:stlmsg="!X<explore_bufnr>" -" elseif w:netrw_explore_bufnr != bufnr("%") -" let g:stlmsg="explore_bufnr!=".bufnr("%") -" endif -" if !exists("w:netrw_explore_line") -" let g:stlmsg=" !X<explore_line>" -" elseif w:netrw_explore_line != line(".") -" let g:stlmsg=" explore_line!={line(.)<".line(".").">" -" endif -" if !exists("w:netrw_explore_list") -" let g:stlmsg=" !X<explore_list>" -" endif -" ^^^ NetrwStatusLine() debugging ^^^ - - if !exists("w:netrw_explore_bufnr") || w:netrw_explore_bufnr != bufnr("%") || !exists("w:netrw_explore_line") || w:netrw_explore_line != line(".") || !exists("w:netrw_explore_list") - " restore user's status line - let &l:stl = s:netrw_users_stl - let &laststatus = s:netrw_users_ls - if exists("w:netrw_explore_bufnr")|unlet w:netrw_explore_bufnr|endif - if exists("w:netrw_explore_line") |unlet w:netrw_explore_line |endif - return "" - else - return "Match ".w:netrw_explore_mtchcnt." of ".w:netrw_explore_listlen - endif -endfun - -" =============================== -" Netrw Transfer Functions: {{{1 -" =============================== - -" ------------------------------------------------------------------------ -" netrw#NetRead: responsible for reading a file over the net {{{2 -" mode: =0 read remote file and insert before current line -" =1 read remote file and insert after current line -" =2 replace with remote file -" =3 obtain file, but leave in temporary format -fun! netrw#NetRead(mode,...) -" call Dfunc("netrw#NetRead(mode=".a:mode.",...) a:0=".a:0." ".g:loaded_netrw.((a:0 > 0)? " a:1<".a:1.">" : "")) - - " NetRead: save options {{{3 - call s:NetrwOptionsSave("w:") - call s:NetrwOptionsSafe(0) - call s:RestoreCursorline() - " NetrwSafeOptions sets a buffer up for a netrw listing, which includes buflisting off. - " However, this setting is not wanted for a remote editing session. The buffer should be "nofile", still. - setl bl -" call Decho("buf#".bufnr("%")."<".bufname("%")."> bl=".&bl." bt=".&bt." bh=".&bh,'~'.expand("<slnum>")) - - " NetRead: interpret mode into a readcmd {{{3 - if a:mode == 0 " read remote file before current line - let readcmd = "0r" - elseif a:mode == 1 " read file after current line - let readcmd = "r" - elseif a:mode == 2 " replace with remote file - let readcmd = "%r" - elseif a:mode == 3 " skip read of file (leave as temporary) - let readcmd = "t" - else - exe a:mode - let readcmd = "r" - endif - let ichoice = (a:0 == 0)? 0 : 1 -" call Decho("readcmd<".readcmd."> ichoice=".ichoice,'~'.expand("<slnum>")) - - " NetRead: get temporary filename {{{3 - let tmpfile= s:GetTempfile("") - if tmpfile == "" -" call Dret("netrw#NetRead : unable to get a tempfile!") - return - endif - - while ichoice <= a:0 - - " attempt to repeat with previous host-file-etc - if exists("b:netrw_lastfile") && a:0 == 0 -" call Decho("using b:netrw_lastfile<" . b:netrw_lastfile . ">",'~'.expand("<slnum>")) - let choice = b:netrw_lastfile - let ichoice= ichoice + 1 - - else - exe "let choice= a:" . ichoice -" call Decho("no lastfile: choice<" . choice . ">",'~'.expand("<slnum>")) - - if match(choice,"?") == 0 - " give help - echomsg 'NetRead Usage:' - echomsg ':Nread machine:path uses rcp' - echomsg ':Nread "machine path" uses ftp with <.netrc>' - echomsg ':Nread "machine id password path" uses ftp' - echomsg ':Nread dav://machine[:port]/path uses cadaver' - echomsg ':Nread fetch://machine/path uses fetch' - echomsg ':Nread ftp://[user@]machine[:port]/path uses ftp autodetects <.netrc>' - echomsg ':Nread http://[user@]machine/path uses http wget' - echomsg ':Nread file:///path uses elinks' - echomsg ':Nread https://[user@]machine/path uses http wget' - echomsg ':Nread rcp://[user@]machine/path uses rcp' - echomsg ':Nread rsync://machine[:port]/path uses rsync' - echomsg ':Nread scp://[user@]machine[[:#]port]/path uses scp' - echomsg ':Nread sftp://[user@]machine[[:#]port]/path uses sftp' - sleep 4 - break - - elseif match(choice,'^"') != -1 - " Reconstruct Choice if choice starts with '"' -" call Decho("reconstructing choice",'~'.expand("<slnum>")) - if match(choice,'"$') != -1 - " case "..." - let choice= strpart(choice,1,strlen(choice)-2) - else - " case "... ... ..." - let choice = strpart(choice,1,strlen(choice)-1) - let wholechoice = "" - - while match(choice,'"$') == -1 - let wholechoice = wholechoice . " " . choice - let ichoice = ichoice + 1 - if ichoice > a:0 - if !exists("g:netrw_quiet") - call netrw#ErrorMsg(s:ERROR,"Unbalanced string in filename '". wholechoice ."'",3) - endif -" call Dret("netrw#NetRead :2 getcwd<".getcwd().">") - return - endif - let choice= a:{ichoice} - endwhile - let choice= strpart(wholechoice,1,strlen(wholechoice)-1) . " " . strpart(choice,0,strlen(choice)-1) - endif - endif - endif - -" call Decho("choice<" . choice . ">",'~'.expand("<slnum>")) - let ichoice= ichoice + 1 - - " NetRead: Determine method of read (ftp, rcp, etc) {{{3 - call s:NetrwMethod(choice) - if !exists("b:netrw_method") || b:netrw_method < 0 -" call Dret("netrw#NetRead : unsupported method") - return - endif - let tmpfile= s:GetTempfile(b:netrw_fname) " apply correct suffix - - " Check whether or not NetrwBrowse() should be handling this request -" call Decho("checking if NetrwBrowse() should handle choice<".choice."> with netrw_list_cmd<".g:netrw_list_cmd.">",'~'.expand("<slnum>")) - if choice =~ "^.*[\/]$" && b:netrw_method != 5 && choice !~ '^https\=://' -" call Decho("yes, choice matches '^.*[\/]$'",'~'.expand("<slnum>")) - NetrwKeepj call s:NetrwBrowse(0,choice) -" call Dret("netrw#NetRead :3 getcwd<".getcwd().">") - return - endif - - " ============ - " NetRead: Perform Protocol-Based Read {{{3 - " =========================== - if exists("g:netrw_silent") && g:netrw_silent == 0 && &ch >= 1 - echo "(netrw) Processing your read request..." - endif - - "......................................... - " NetRead: (rcp) NetRead Method #1 {{{3 - if b:netrw_method == 1 " read with rcp -" call Decho("read via rcp (method #1)",'~'.expand("<slnum>")) - " ER: nothing done with g:netrw_uid yet? - " ER: on Win2K" rcp machine[.user]:file tmpfile - " ER: when machine contains '.' adding .user is required (use $USERNAME) - " ER: the tmpfile is full path: rcp sees C:\... as host C - if s:netrw_has_nt_rcp == 1 - if exists("g:netrw_uid") && ( g:netrw_uid != "" ) - let uid_machine = g:netrw_machine .'.'. g:netrw_uid - else - " Any way needed it machine contains a '.' - let uid_machine = g:netrw_machine .'.'. $USERNAME - endif - else - if exists("g:netrw_uid") && ( g:netrw_uid != "" ) - let uid_machine = g:netrw_uid .'@'. g:netrw_machine - else - let uid_machine = g:netrw_machine - endif - endif - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_rcp_cmd." ".s:netrw_rcpmode." ".s:ShellEscape(uid_machine.":".b:netrw_fname,1)." ".s:ShellEscape(tmpfile,1)) - let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) - let b:netrw_lastfile = choice - - "......................................... - " NetRead: (ftp + <.netrc>) NetRead Method #2 {{{3 - elseif b:netrw_method == 2 " read with ftp + <.netrc> -" call Decho("read via ftp+.netrc (method #2)",'~'.expand("<slnum>")) - let netrw_fname= b:netrw_fname - NetrwKeepj call s:SaveBufVars()|new|NetrwKeepj call s:RestoreBufVars() - let filtbuf= bufnr("%") - setl ff=unix - NetrwKeepj put =g:netrw_ftpmode -" call Decho("filter input: ".getline(line("$")),'~'.expand("<slnum>")) - if exists("g:netrw_ftpextracmd") - NetrwKeepj put =g:netrw_ftpextracmd -" call Decho("filter input: ".getline(line("$")),'~'.expand("<slnum>")) - endif - call setline(line("$")+1,'get "'.netrw_fname.'" '.tmpfile) -" call Decho("filter input: ".getline(line("$")),'~'.expand("<slnum>")) - if exists("g:netrw_port") && g:netrw_port != "" - call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1)) - else - call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)) - endif - " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) - if getline(1) !~ "^$" && !exists("g:netrw_quiet") && getline(1) !~ '^Trying ' - let debugkeep = &debug - setl debug=msg - NetrwKeepj call netrw#ErrorMsg(s:ERROR,getline(1),4) - let &debug = debugkeep - endif - call s:SaveBufVars() - keepj bd! - if bufname("%") == "" && getline("$") == "" && line('$') == 1 - " needed when one sources a file in a nolbl setting window via ftp - q! - endif - call s:RestoreBufVars() - let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) - let b:netrw_lastfile = choice - - "......................................... - " NetRead: (ftp + machine,id,passwd,filename) NetRead Method #3 {{{3 - elseif b:netrw_method == 3 " read with ftp + machine, id, passwd, and fname - " Construct execution string (four lines) which will be passed through filter -" call Decho("read via ftp+mipf (method #3)",'~'.expand("<slnum>")) - let netrw_fname= escape(b:netrw_fname,g:netrw_fname_escape) - NetrwKeepj call s:SaveBufVars()|new|NetrwKeepj call s:RestoreBufVars() - let filtbuf= bufnr("%") - setl ff=unix - if exists("g:netrw_port") && g:netrw_port != "" - NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port -" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) - else - NetrwKeepj put ='open '.g:netrw_machine -" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) - endif - - if exists("g:netrw_uid") && g:netrw_uid != "" - if exists("g:netrw_ftp") && g:netrw_ftp == 1 - NetrwKeepj put =g:netrw_uid -" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) - if exists("s:netrw_passwd") - NetrwKeepj put ='\"'.s:netrw_passwd.'\"' - endif -" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) - elseif exists("s:netrw_passwd") - NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"' -" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) - endif - endif - - if exists("g:netrw_ftpmode") && g:netrw_ftpmode != "" - NetrwKeepj put =g:netrw_ftpmode -" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) - endif - if exists("g:netrw_ftpextracmd") - NetrwKeepj put =g:netrw_ftpextracmd -" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) - endif - NetrwKeepj put ='get \"'.netrw_fname.'\" '.tmpfile -" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) - - " perform ftp: - " -i : turns off interactive prompting from ftp - " -n unix : DON'T use <.netrc>, even though it exists - " -n win32: quit being obnoxious about password - NetrwKeepj norm! 1G"_dd - call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." ".g:netrw_ftp_options) - " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) - if getline(1) !~ "^$" -" call Decho("error<".getline(1).">",'~'.expand("<slnum>")) - if !exists("g:netrw_quiet") - call netrw#ErrorMsg(s:ERROR,getline(1),5) - endif - endif - call s:SaveBufVars()|keepj bd!|call s:RestoreBufVars() - let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) - let b:netrw_lastfile = choice - - "......................................... - " NetRead: (scp) NetRead Method #4 {{{3 - elseif b:netrw_method == 4 " read with scp -" call Decho("read via scp (method #4)",'~'.expand("<slnum>")) - if exists("g:netrw_port") && g:netrw_port != "" - let useport= " ".g:netrw_scpport." ".g:netrw_port - else - let useport= "" - endif - " 'C' in 'C:\path\to\file' is handled as hostname on windows. - " This is workaround to avoid mis-handle windows local-path: - if g:netrw_scp_cmd =~ '^scp' && has("win32") - let tmpfile_get = substitute(tr(tmpfile, '\', '/'), '^\(\a\):[/\\]\(.*\)$', '/\1/\2', '') - else - let tmpfile_get = tmpfile - endif - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_scp_cmd.useport." ".escape(s:ShellEscape(g:netrw_machine.":".b:netrw_fname,1),' ')." ".s:ShellEscape(tmpfile_get,1)) - let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) - let b:netrw_lastfile = choice - - "......................................... - " NetRead: (http) NetRead Method #5 (wget) {{{3 - elseif b:netrw_method == 5 -" call Decho("read via http (method #5)",'~'.expand("<slnum>")) - if g:netrw_http_cmd == "" - if !exists("g:netrw_quiet") - call netrw#ErrorMsg(s:ERROR,"neither the wget nor the fetch command is available",6) - endif -" call Dret("netrw#NetRead :4 getcwd<".getcwd().">") - return - endif - - if match(b:netrw_fname,"#") == -1 || exists("g:netrw_http_xcmd") - " using g:netrw_http_cmd (usually elinks, links, curl, wget, or fetch) -" call Decho('using '.g:netrw_http_cmd.' (# not in b:netrw_fname<'.b:netrw_fname.">)",'~'.expand("<slnum>")) - if exists("g:netrw_http_xcmd") - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_cmd." ".s:ShellEscape(b:netrw_http."://".g:netrw_machine.b:netrw_fname,1)." ".g:netrw_http_xcmd." ".s:ShellEscape(tmpfile,1)) - else - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(b:netrw_http."://".g:netrw_machine.b:netrw_fname,1)) - endif - let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) - - else - " wget/curl/fetch plus a jump to an in-page marker (ie. http://abc/def.html#aMarker) -" call Decho("wget/curl plus jump (# in b:netrw_fname<".b:netrw_fname.">)",'~'.expand("<slnum>")) - let netrw_html= substitute(b:netrw_fname,"#.*$","","") - let netrw_tag = substitute(b:netrw_fname,"^.*#","","") -" call Decho("netrw_html<".netrw_html.">",'~'.expand("<slnum>")) -" call Decho("netrw_tag <".netrw_tag.">",'~'.expand("<slnum>")) - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(b:netrw_http."://".g:netrw_machine.netrw_html,1)) - let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) -" call Decho('<\s*a\s*name=\s*"'.netrw_tag.'"/','~'.expand("<slnum>")) - exe 'NetrwKeepj norm! 1G/<\s*a\s*name=\s*"'.netrw_tag.'"/'."\<CR>" - endif - let b:netrw_lastfile = choice -" call Decho("setl ro",'~'.expand("<slnum>")) - setl ro nomod - - "......................................... - " NetRead: (dav) NetRead Method #6 {{{3 - elseif b:netrw_method == 6 -" call Decho("read via cadaver (method #6)",'~'.expand("<slnum>")) - - if !executable(g:netrw_dav_cmd) - call netrw#ErrorMsg(s:ERROR,g:netrw_dav_cmd." is not executable",73) -" call Dret("netrw#NetRead : ".g:netrw_dav_cmd." not executable") - return - endif - if g:netrw_dav_cmd =~ "curl" - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_dav_cmd." ".s:ShellEscape("dav://".g:netrw_machine.b:netrw_fname,1)." ".s:ShellEscape(tmpfile,1)) - else - " Construct execution string (four lines) which will be passed through filter - let netrw_fname= escape(b:netrw_fname,g:netrw_fname_escape) - new - setl ff=unix - if exists("g:netrw_port") && g:netrw_port != "" - NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port - else - NetrwKeepj put ='open '.g:netrw_machine - endif - if exists("g:netrw_uid") && exists("s:netrw_passwd") && g:netrw_uid != "" - NetrwKeepj put ='user '.g:netrw_uid.' '.s:netrw_passwd - endif - NetrwKeepj put ='get '.netrw_fname.' '.tmpfile - NetrwKeepj put ='quit' - - " perform cadaver operation: - NetrwKeepj norm! 1G"_dd - call s:NetrwExe(s:netrw_silentxfer."%!".g:netrw_dav_cmd) - keepj bd! - endif - let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) - let b:netrw_lastfile = choice - - "......................................... - " NetRead: (rsync) NetRead Method #7 {{{3 - elseif b:netrw_method == 7 -" call Decho("read via rsync (method #7)",'~'.expand("<slnum>")) - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_rsync_cmd." ".s:ShellEscape(g:netrw_machine.g:netrw_rsync_sep.b:netrw_fname,1)." ".s:ShellEscape(tmpfile,1)) - let result = s:NetrwGetFile(readcmd,tmpfile, b:netrw_method) - let b:netrw_lastfile = choice - - "......................................... - " NetRead: (fetch) NetRead Method #8 {{{3 - " fetch://[user@]host[:http]/path - elseif b:netrw_method == 8 -" call Decho("read via fetch (method #8)",'~'.expand("<slnum>")) - if g:netrw_fetch_cmd == "" - if !exists("g:netrw_quiet") - NetrwKeepj call netrw#ErrorMsg(s:ERROR,"fetch command not available",7) - endif -" call Dret("NetRead") - return - endif - if exists("g:netrw_option") && g:netrw_option =~ ":https\=" - let netrw_option= "http" - else - let netrw_option= "ftp" - endif -" call Decho("read via fetch for ".netrw_option,'~'.expand("<slnum>")) - - if exists("g:netrw_uid") && g:netrw_uid != "" && exists("s:netrw_passwd") && s:netrw_passwd != "" - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_fetch_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(netrw_option."://".g:netrw_uid.':'.s:netrw_passwd.'@'.g:netrw_machine."/".b:netrw_fname,1)) - else - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_fetch_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(netrw_option."://".g:netrw_machine."/".b:netrw_fname,1)) - endif - - let result = s:NetrwGetFile(readcmd,tmpfile, b:netrw_method) - let b:netrw_lastfile = choice -" call Decho("setl ro",'~'.expand("<slnum>")) - setl ro nomod - - "......................................... - " NetRead: (sftp) NetRead Method #9 {{{3 - elseif b:netrw_method == 9 -" call Decho("read via sftp (method #9)",'~'.expand("<slnum>")) - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_sftp_cmd." ".s:ShellEscape(g:netrw_machine.":".b:netrw_fname,1)." ".tmpfile) - let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) - let b:netrw_lastfile = choice - - "......................................... - " NetRead: (file) NetRead Method #10 {{{3 - elseif b:netrw_method == 10 && exists("g:netrw_file_cmd") -" " call Decho("read via ".b:netrw_file_cmd." (method #10)",'~'.expand("<slnum>")) - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_file_cmd." ".s:ShellEscape(b:netrw_fname,1)." ".tmpfile) - let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) - let b:netrw_lastfile = choice - - "......................................... - " NetRead: Complain {{{3 - else - call netrw#ErrorMsg(s:WARNING,"unable to comply with your request<" . choice . ">",8) - endif - endwhile - - " NetRead: cleanup {{{3 - if exists("b:netrw_method") -" call Decho("cleanup b:netrw_method and b:netrw_fname",'~'.expand("<slnum>")) - unlet b:netrw_method - unlet b:netrw_fname - endif - if s:FileReadable(tmpfile) && tmpfile !~ '.tar.bz2$' && tmpfile !~ '.tar.gz$' && tmpfile !~ '.zip' && tmpfile !~ '.tar' && readcmd != 't' && tmpfile !~ '.tar.xz$' && tmpfile !~ '.txz' -" call Decho("cleanup by deleting tmpfile<".tmpfile.">",'~'.expand("<slnum>")) - NetrwKeepj call s:NetrwDelete(tmpfile) - endif - NetrwKeepj call s:NetrwOptionsRestore("w:") - -" call Dret("netrw#NetRead :5 getcwd<".getcwd().">") -endfun - -" ------------------------------------------------------------------------ -" netrw#NetWrite: responsible for writing a file over the net {{{2 -fun! netrw#NetWrite(...) range -" call Dfunc("netrw#NetWrite(a:0=".a:0.") ".g:loaded_netrw) - - " NetWrite: option handling {{{3 - let mod= 0 - call s:NetrwOptionsSave("w:") - call s:NetrwOptionsSafe(0) - - " NetWrite: Get Temporary Filename {{{3 - let tmpfile= s:GetTempfile("") - if tmpfile == "" -" call Dret("netrw#NetWrite : unable to get a tempfile!") - return - endif - - if a:0 == 0 - let ichoice = 0 - else - let ichoice = 1 - endif - - let curbufname= expand("%") -" call Decho("curbufname<".curbufname.">",'~'.expand("<slnum>")) - if &binary - " For binary writes, always write entire file. - " (line numbers don't really make sense for that). - " Also supports the writing of tar and zip files. -" call Decho("(write entire file) sil exe w! ".fnameescape(v:cmdarg)." ".fnameescape(tmpfile),'~'.expand("<slnum>")) - exe "sil NetrwKeepj w! ".fnameescape(v:cmdarg)." ".fnameescape(tmpfile) - elseif g:netrw_cygwin - " write (selected portion of) file to temporary - let cygtmpfile= substitute(tmpfile,g:netrw_cygdrive.'/\(.\)','\1:','') -" call Decho("(write selected portion) sil exe ".a:firstline."," . a:lastline . "w! ".fnameescape(v:cmdarg)." ".fnameescape(cygtmpfile),'~'.expand("<slnum>")) - exe "sil NetrwKeepj ".a:firstline."," . a:lastline . "w! ".fnameescape(v:cmdarg)." ".fnameescape(cygtmpfile) - else - " write (selected portion of) file to temporary -" call Decho("(write selected portion) sil exe ".a:firstline."," . a:lastline . "w! ".fnameescape(v:cmdarg)." ".fnameescape(tmpfile),'~'.expand("<slnum>")) - exe "sil NetrwKeepj ".a:firstline."," . a:lastline . "w! ".fnameescape(v:cmdarg)." ".fnameescape(tmpfile) - endif - - if curbufname == "" - " when the file is [No Name], and one attempts to Nwrite it, the buffer takes - " on the temporary file's name. Deletion of the temporary file during - " cleanup then causes an error message. - 0file! - endif - - " NetWrite: while choice loop: {{{3 - while ichoice <= a:0 - - " Process arguments: {{{4 - " attempt to repeat with previous host-file-etc - if exists("b:netrw_lastfile") && a:0 == 0 -" call Decho("using b:netrw_lastfile<" . b:netrw_lastfile . ">",'~'.expand("<slnum>")) - let choice = b:netrw_lastfile - let ichoice= ichoice + 1 - else - exe "let choice= a:" . ichoice - - " Reconstruct Choice when choice starts with '"' - if match(choice,"?") == 0 - echomsg 'NetWrite Usage:"' - echomsg ':Nwrite machine:path uses rcp' - echomsg ':Nwrite "machine path" uses ftp with <.netrc>' - echomsg ':Nwrite "machine id password path" uses ftp' - echomsg ':Nwrite dav://[user@]machine/path uses cadaver' - echomsg ':Nwrite fetch://[user@]machine/path uses fetch' - echomsg ':Nwrite ftp://machine[#port]/path uses ftp (autodetects <.netrc>)' - echomsg ':Nwrite rcp://machine/path uses rcp' - echomsg ':Nwrite rsync://[user@]machine/path uses rsync' - echomsg ':Nwrite scp://[user@]machine[[:#]port]/path uses scp' - echomsg ':Nwrite sftp://[user@]machine/path uses sftp' - sleep 4 - break - - elseif match(choice,"^\"") != -1 - if match(choice,"\"$") != -1 - " case "..." - let choice=strpart(choice,1,strlen(choice)-2) - else - " case "... ... ..." - let choice = strpart(choice,1,strlen(choice)-1) - let wholechoice = "" - - while match(choice,"\"$") == -1 - let wholechoice= wholechoice . " " . choice - let ichoice = ichoice + 1 - if choice > a:0 - if !exists("g:netrw_quiet") - call netrw#ErrorMsg(s:ERROR,"Unbalanced string in filename '". wholechoice ."'",13) - endif -" call Dret("netrw#NetWrite") - return - endif - let choice= a:{ichoice} - endwhile - let choice= strpart(wholechoice,1,strlen(wholechoice)-1) . " " . strpart(choice,0,strlen(choice)-1) - endif - endif - endif - let ichoice= ichoice + 1 -" call Decho("choice<" . choice . "> ichoice=".ichoice,'~'.expand("<slnum>")) - - " Determine method of write (ftp, rcp, etc) {{{4 - NetrwKeepj call s:NetrwMethod(choice) - if !exists("b:netrw_method") || b:netrw_method < 0 -" call Dfunc("netrw#NetWrite : unsupported method") - return - endif - - " ============= - " NetWrite: Perform Protocol-Based Write {{{3 - " ============================ - if exists("g:netrw_silent") && g:netrw_silent == 0 && &ch >= 1 - echo "(netrw) Processing your write request..." -" call Decho("Processing your write request...",'~'.expand("<slnum>")) - endif - - "......................................... - " NetWrite: (rcp) NetWrite Method #1 {{{3 - if b:netrw_method == 1 -" call Decho("write via rcp (method #1)",'~'.expand("<slnum>")) - if s:netrw_has_nt_rcp == 1 - if exists("g:netrw_uid") && ( g:netrw_uid != "" ) - let uid_machine = g:netrw_machine .'.'. g:netrw_uid - else - let uid_machine = g:netrw_machine .'.'. $USERNAME - endif - else - if exists("g:netrw_uid") && ( g:netrw_uid != "" ) - let uid_machine = g:netrw_uid .'@'. g:netrw_machine - else - let uid_machine = g:netrw_machine - endif - endif - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_rcp_cmd." ".s:netrw_rcpmode." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(uid_machine.":".b:netrw_fname,1)) - let b:netrw_lastfile = choice - - "......................................... - " NetWrite: (ftp + <.netrc>) NetWrite Method #2 {{{3 - elseif b:netrw_method == 2 -" call Decho("write via ftp+.netrc (method #2)",'~'.expand("<slnum>")) - let netrw_fname = b:netrw_fname - - " formerly just a "new...bd!", that changed the window sizes when equalalways. Using enew workaround instead - let bhkeep = &l:bh - let curbuf = bufnr("%") - setl bh=hide - keepj keepalt enew - -" call Decho("filter input window#".winnr(),'~'.expand("<slnum>")) - setl ff=unix - NetrwKeepj put =g:netrw_ftpmode -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - if exists("g:netrw_ftpextracmd") - NetrwKeepj put =g:netrw_ftpextracmd -" call Decho("filter input: ".getline("$"),'~'.expand("<slnum>")) - endif - NetrwKeepj call setline(line("$")+1,'put "'.tmpfile.'" "'.netrw_fname.'"') -" call Decho("filter input: ".getline("$"),'~'.expand("<slnum>")) - if exists("g:netrw_port") && g:netrw_port != "" - call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1)) - else -" call Decho("filter input window#".winnr(),'~'.expand("<slnum>")) - call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)) - endif - " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) - if getline(1) !~ "^$" - if !exists("g:netrw_quiet") - NetrwKeepj call netrw#ErrorMsg(s:ERROR,getline(1),14) - endif - let mod=1 - endif - - " remove enew buffer (quietly) - let filtbuf= bufnr("%") - exe curbuf."b!" - let &l:bh = bhkeep - exe filtbuf."bw!" - - let b:netrw_lastfile = choice - - "......................................... - " NetWrite: (ftp + machine, id, passwd, filename) NetWrite Method #3 {{{3 - elseif b:netrw_method == 3 - " Construct execution string (three or more lines) which will be passed through filter -" call Decho("read via ftp+mipf (method #3)",'~'.expand("<slnum>")) - let netrw_fname = b:netrw_fname - let bhkeep = &l:bh - - " formerly just a "new...bd!", that changed the window sizes when equalalways. Using enew workaround instead - let curbuf = bufnr("%") - setl bh=hide - keepj keepalt enew - setl ff=unix - - if exists("g:netrw_port") && g:netrw_port != "" - NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port -" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) - else - NetrwKeepj put ='open '.g:netrw_machine -" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) - endif - if exists("g:netrw_uid") && g:netrw_uid != "" - if exists("g:netrw_ftp") && g:netrw_ftp == 1 - NetrwKeepj put =g:netrw_uid -" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) - if exists("s:netrw_passwd") && s:netrw_passwd != "" - NetrwKeepj put ='\"'.s:netrw_passwd.'\"' - endif -" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) - elseif exists("s:netrw_passwd") && s:netrw_passwd != "" - NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"' -" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) - endif - endif - NetrwKeepj put =g:netrw_ftpmode -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - if exists("g:netrw_ftpextracmd") - NetrwKeepj put =g:netrw_ftpextracmd -" call Decho("filter input: ".getline("$"),'~'.expand("<slnum>")) - endif - NetrwKeepj put ='put \"'.tmpfile.'\" \"'.netrw_fname.'\"' -" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) - " save choice/id/password for future use - let b:netrw_lastfile = choice - - " perform ftp: - " -i : turns off interactive prompting from ftp - " -n unix : DON'T use <.netrc>, even though it exists - " -n win32: quit being obnoxious about password - NetrwKeepj norm! 1G"_dd - call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." ".g:netrw_ftp_options) - " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) - if getline(1) !~ "^$" - if !exists("g:netrw_quiet") - call netrw#ErrorMsg(s:ERROR,getline(1),15) - endif - let mod=1 - endif - - " remove enew buffer (quietly) - let filtbuf= bufnr("%") - exe curbuf."b!" - let &l:bh= bhkeep - exe filtbuf."bw!" - - "......................................... - " NetWrite: (scp) NetWrite Method #4 {{{3 - elseif b:netrw_method == 4 -" call Decho("write via scp (method #4)",'~'.expand("<slnum>")) - if exists("g:netrw_port") && g:netrw_port != "" - let useport= " ".g:netrw_scpport." ".fnameescape(g:netrw_port) - else - let useport= "" - endif - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_scp_cmd.useport." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(g:netrw_machine.":".b:netrw_fname,1)) - let b:netrw_lastfile = choice - - "......................................... - " NetWrite: (http) NetWrite Method #5 {{{3 - elseif b:netrw_method == 5 -" call Decho("write via http (method #5)",'~'.expand("<slnum>")) - let curl= substitute(g:netrw_http_put_cmd,'\s\+.*$',"","") - if executable(curl) - let url= g:netrw_choice - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_put_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(url,1) ) - elseif !exists("g:netrw_quiet") - call netrw#ErrorMsg(s:ERROR,"can't write to http using <".g:netrw_http_put_cmd.">",16) - endif - - "......................................... - " NetWrite: (dav) NetWrite Method #6 (cadaver) {{{3 - elseif b:netrw_method == 6 -" call Decho("write via cadaver (method #6)",'~'.expand("<slnum>")) - - " Construct execution string (four lines) which will be passed through filter - let netrw_fname = escape(b:netrw_fname,g:netrw_fname_escape) - let bhkeep = &l:bh - - " formerly just a "new...bd!", that changed the window sizes when equalalways. Using enew workaround instead - let curbuf = bufnr("%") - setl bh=hide - keepj keepalt enew - - setl ff=unix - if exists("g:netrw_port") && g:netrw_port != "" - NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port - else - NetrwKeepj put ='open '.g:netrw_machine - endif - if exists("g:netrw_uid") && exists("s:netrw_passwd") && g:netrw_uid != "" - NetrwKeepj put ='user '.g:netrw_uid.' '.s:netrw_passwd - endif - NetrwKeepj put ='put '.tmpfile.' '.netrw_fname - - " perform cadaver operation: - NetrwKeepj norm! 1G"_dd - call s:NetrwExe(s:netrw_silentxfer."%!".g:netrw_dav_cmd) - - " remove enew buffer (quietly) - let filtbuf= bufnr("%") - exe curbuf."b!" - let &l:bh = bhkeep - exe filtbuf."bw!" - - let b:netrw_lastfile = choice - - "......................................... - " NetWrite: (rsync) NetWrite Method #7 {{{3 - elseif b:netrw_method == 7 -" call Decho("write via rsync (method #7)",'~'.expand("<slnum>")) - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_rsync_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(g:netrw_machine.g:netrw_rsync_sep.b:netrw_fname,1)) - let b:netrw_lastfile = choice - - "......................................... - " NetWrite: (sftp) NetWrite Method #9 {{{3 - elseif b:netrw_method == 9 -" call Decho("write via sftp (method #9)",'~'.expand("<slnum>")) - let netrw_fname= escape(b:netrw_fname,g:netrw_fname_escape) - if exists("g:netrw_uid") && ( g:netrw_uid != "" ) - let uid_machine = g:netrw_uid .'@'. g:netrw_machine - else - let uid_machine = g:netrw_machine - endif - - " formerly just a "new...bd!", that changed the window sizes when equalalways. Using enew workaround instead - let bhkeep = &l:bh - let curbuf = bufnr("%") - setl bh=hide - keepj keepalt enew - - setl ff=unix - call setline(1,'put "'.escape(tmpfile,'\').'" '.netrw_fname) -" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) - let sftpcmd= substitute(g:netrw_sftp_cmd,"%TEMPFILE%",escape(tmpfile,'\'),"g") - call s:NetrwExe(s:netrw_silentxfer."%!".sftpcmd.' '.s:ShellEscape(uid_machine,1)) - let filtbuf= bufnr("%") - exe curbuf."b!" - let &l:bh = bhkeep - exe filtbuf."bw!" - let b:netrw_lastfile = choice - - "......................................... - " NetWrite: Complain {{{3 - else - call netrw#ErrorMsg(s:WARNING,"unable to comply with your request<" . choice . ">",17) - let leavemod= 1 - endif - endwhile - - " NetWrite: Cleanup: {{{3 -" call Decho("cleanup",'~'.expand("<slnum>")) - if s:FileReadable(tmpfile) -" call Decho("tmpfile<".tmpfile."> readable, will now delete it",'~'.expand("<slnum>")) - call s:NetrwDelete(tmpfile) - endif - call s:NetrwOptionsRestore("w:") - - if a:firstline == 1 && a:lastline == line("$") - " restore modifiability; usually equivalent to set nomod - let &l:mod= mod -" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) - elseif !exists("leavemod") - " indicate that the buffer has not been modified since last written -" call Decho("set nomod",'~'.expand("<slnum>")) - setl nomod -" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) - endif - -" call Dret("netrw#NetWrite") -endfun - -" --------------------------------------------------------------------- -" netrw#NetSource: source a remotely hosted vim script {{{2 -" uses NetRead to get a copy of the file into a temporarily file, -" then sources that file, -" then removes that file. -fun! netrw#NetSource(...) -" call Dfunc("netrw#NetSource() a:0=".a:0) - if a:0 > 0 && a:1 == '?' - " give help - echomsg 'NetSource Usage:' - echomsg ':Nsource dav://machine[:port]/path uses cadaver' - echomsg ':Nsource fetch://machine/path uses fetch' - echomsg ':Nsource ftp://[user@]machine[:port]/path uses ftp autodetects <.netrc>' - echomsg ':Nsource http[s]://[user@]machine/path uses http wget' - echomsg ':Nsource rcp://[user@]machine/path uses rcp' - echomsg ':Nsource rsync://machine[:port]/path uses rsync' - echomsg ':Nsource scp://[user@]machine[[:#]port]/path uses scp' - echomsg ':Nsource sftp://[user@]machine[[:#]port]/path uses sftp' - sleep 4 - else - let i= 1 - while i <= a:0 - call netrw#NetRead(3,a:{i}) -" call Decho("s:netread_tmpfile<".s:netrw_tmpfile.">",'~'.expand("<slnum>")) - if s:FileReadable(s:netrw_tmpfile) -" call Decho("exe so ".fnameescape(s:netrw_tmpfile),'~'.expand("<slnum>")) - exe "so ".fnameescape(s:netrw_tmpfile) -" call Decho("delete(".s:netrw_tmpfile.")",'~'.expand("<slnum>")) - if delete(s:netrw_tmpfile) - call netrw#ErrorMsg(s:ERROR,"unable to delete directory <".s:netrw_tmpfile.">!",103) - endif - unlet s:netrw_tmpfile - else - call netrw#ErrorMsg(s:ERROR,"unable to source <".a:{i}.">!",48) - endif - let i= i + 1 - endwhile - endif -" call Dret("netrw#NetSource") -endfun - -" --------------------------------------------------------------------- -" netrw#SetTreetop: resets the tree top to the current directory/specified directory {{{2 -" (implements the :Ntree command) -fun! netrw#SetTreetop(iscmd,...) - - " iscmd==0: netrw#SetTreetop called using gn mapping - " iscmd==1: netrw#SetTreetop called using :Ntree from the command line - " clear out the current tree - if exists("w:netrw_treetop") - let inittreetop= w:netrw_treetop - unlet w:netrw_treetop - endif - if exists("w:netrw_treedict") - unlet w:netrw_treedict - endif - - if (a:iscmd == 0 || a:1 == "") && exists("inittreetop") - let treedir = s:NetrwTreePath(inittreetop) - else - if isdirectory(s:NetrwFile(a:1)) - let treedir = a:1 - let s:netrw_treetop = treedir - elseif exists("b:netrw_curdir") && (isdirectory(s:NetrwFile(b:netrw_curdir."/".a:1)) || a:1 =~ '^\a\{3,}://') - let treedir = b:netrw_curdir."/".a:1 - let s:netrw_treetop = treedir - else - " normally the cursor is left in the message window. - " However, here this results in the directory being listed in the message window, which is not wanted. - let netrwbuf= bufnr("%") - call netrw#ErrorMsg(s:ERROR,"sorry, ".a:1." doesn't seem to be a directory!",95) - exe bufwinnr(netrwbuf)."wincmd w" - let treedir = "." - let s:netrw_treetop = getcwd() - endif - endif - - " determine if treedir is remote or local - let islocal= expand("%") !~ '^\a\{3,}://' - - " browse the resulting directory - if islocal - call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(islocal,treedir,0)) - else - call s:NetrwBrowse(islocal,s:NetrwBrowseChgDir(islocal,treedir,0)) - endif - -endfun - -" =========================================== -" s:NetrwGetFile: Function to read temporary file "tfile" with command "readcmd". {{{2 -" readcmd == %r : replace buffer with newly read file -" == 0r : read file at top of buffer -" == r : read file after current line -" == t : leave file in temporary form (ie. don't read into buffer) -fun! s:NetrwGetFile(readcmd, tfile, method) -" call Dfunc("NetrwGetFile(readcmd<".a:readcmd.">,tfile<".a:tfile."> method<".a:method.">)") - - " readcmd=='t': simply do nothing - if a:readcmd == 't' -" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) -" call Dret("NetrwGetFile : skip read of tfile<".a:tfile.">") - return - endif - - " get name of remote filename (ie. url and all) - let rfile= bufname("%") -" call Decho("rfile<".rfile.">",'~'.expand("<slnum>")) - - if exists("*NetReadFixup") - " for the use of NetReadFixup (not otherwise used internally) - let line2= line("$") - endif - - if a:readcmd[0] == '%' - " get file into buffer -" call Decho("get file into buffer",'~'.expand("<slnum>")) - - " rename the current buffer to the temp file (ie. tfile) - if g:netrw_cygwin - let tfile= substitute(a:tfile,g:netrw_cygdrive.'/\(.\)','\1:','') - else - let tfile= a:tfile - endif - call s:NetrwBufRename(tfile) - - " edit temporary file (ie. read the temporary file in) - if rfile =~ '\.zip$' -" call Decho("handling remote zip file with zip#Browse(tfile<".tfile.">)",'~'.expand("<slnum>")) - call zip#Browse(tfile) - elseif rfile =~ '\.tar$' -" call Decho("handling remote tar file with tar#Browse(tfile<".tfile.">)",'~'.expand("<slnum>")) - call tar#Browse(tfile) - elseif rfile =~ '\.tar\.gz$' -" call Decho("handling remote gzip-compressed tar file",'~'.expand("<slnum>")) - call tar#Browse(tfile) - elseif rfile =~ '\.tar\.bz2$' -" call Decho("handling remote bz2-compressed tar file",'~'.expand("<slnum>")) - call tar#Browse(tfile) - elseif rfile =~ '\.tar\.xz$' -" call Decho("handling remote xz-compressed tar file",'~'.expand("<slnum>")) - call tar#Browse(tfile) - elseif rfile =~ '\.txz$' -" call Decho("handling remote xz-compressed tar file (.txz)",'~'.expand("<slnum>")) - call tar#Browse(tfile) - else -" call Decho("edit temporary file",'~'.expand("<slnum>")) - NetrwKeepj e! - endif - - " rename buffer back to remote filename - call s:NetrwBufRename(rfile) - - " Jan 19, 2022: COMBAK -- bram problem with https://github.com/vim/vim/pull/9554.diff filetype - " Detect filetype of local version of remote file. - " Note that isk must not include a "/" for scripts.vim - " to process this detection correctly. -" call Decho("detect filetype of local version of remote file<".rfile.">",'~'.expand("<slnum>")) -" call Decho("..did_filetype()=".did_filetype()) -" setl ft= -" call Decho("..initial filetype<".&ft."> for buf#".bufnr()."<".bufname().">") - let iskkeep= &isk - setl isk-=/ - filetype detect -" call Decho("..local filetype<".&ft."> for buf#".bufnr()."<".bufname().">") - let &l:isk= iskkeep -" call Dredir("ls!","NetrwGetFile (renamed buffer back to remote filename<".rfile."> : expand(%)<".expand("%").">)") - let line1 = 1 - let line2 = line("$") - - elseif !&ma - " attempting to read a file after the current line in the file, but the buffer is not modifiable - NetrwKeepj call netrw#ErrorMsg(s:WARNING,"attempt to read<".a:tfile."> into a non-modifiable buffer!",94) -" call Dret("NetrwGetFile : attempt to read<".a:tfile."> into a non-modifiable buffer!") - return - - elseif s:FileReadable(a:tfile) - " read file after current line -" call Decho("read file<".a:tfile."> after current line",'~'.expand("<slnum>")) - let curline = line(".") - let lastline= line("$") -" call Decho("exe<".a:readcmd." ".fnameescape(v:cmdarg)." ".fnameescape(a:tfile)."> line#".curline,'~'.expand("<slnum>")) - exe "NetrwKeepj ".a:readcmd." ".fnameescape(v:cmdarg)." ".fnameescape(a:tfile) - let line1= curline + 1 - let line2= line("$") - lastline + 1 - - else - " not readable -" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) -" call Decho("tfile<".a:tfile."> not readable",'~'.expand("<slnum>")) - NetrwKeepj call netrw#ErrorMsg(s:WARNING,"file <".a:tfile."> not readable",9) -" call Dret("NetrwGetFile : tfile<".a:tfile."> not readable") - return - endif - - " User-provided (ie. optional) fix-it-up command - if exists("*NetReadFixup") -" call Decho("calling NetReadFixup(method<".a:method."> line1=".line1." line2=".line2.")",'~'.expand("<slnum>")) - NetrwKeepj call NetReadFixup(a:method, line1, line2) -" else " Decho -" call Decho("NetReadFixup() not called, doesn't exist (line1=".line1." line2=".line2.")",'~'.expand("<slnum>")) - endif - - if has("gui") && has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu - " update the Buffers menu - NetrwKeepj call s:UpdateBuffersMenu() - endif - -" call Decho("readcmd<".a:readcmd."> cmdarg<".v:cmdarg."> tfile<".a:tfile."> readable=".s:FileReadable(a:tfile),'~'.expand("<slnum>")) - - " make sure file is being displayed -" redraw! - -" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) -" call Dret("NetrwGetFile") -endfun - -" ------------------------------------------------------------------------ -" s:NetrwMethod: determine method of transfer {{{2 -" Input: -" choice = url [protocol:]//[userid@]hostname[:port]/[path-to-file] -" Output: -" b:netrw_method= 1: rcp -" 2: ftp + <.netrc> -" 3: ftp + machine, id, password, and [path]filename -" 4: scp -" 5: http[s] (wget) -" 6: dav -" 7: rsync -" 8: fetch -" 9: sftp -" 10: file -" g:netrw_machine= hostname -" b:netrw_fname = filename -" g:netrw_port = optional port number (for ftp) -" g:netrw_choice = copy of input url (choice) -fun! s:NetrwMethod(choice) -" call Dfunc("s:NetrwMethod(a:choice<".a:choice.">)") - - " sanity check: choice should have at least three slashes in it - if strlen(substitute(a:choice,'[^/]','','g')) < 3 - call netrw#ErrorMsg(s:ERROR,"not a netrw-style url; netrw uses protocol://[user@]hostname[:port]/[path])",78) - let b:netrw_method = -1 -" call Dret("s:NetrwMethod : incorrect url format<".a:choice.">") - return - endif - - " record current g:netrw_machine, if any - " curmachine used if protocol == ftp and no .netrc - if exists("g:netrw_machine") - let curmachine= g:netrw_machine -" call Decho("curmachine<".curmachine.">",'~'.expand("<slnum>")) - else - let curmachine= "N O T A HOST" - endif - if exists("g:netrw_port") - let netrw_port= g:netrw_port - endif - - " insure that netrw_ftp_cmd starts off every method determination - " with the current g:netrw_ftp_cmd - let s:netrw_ftp_cmd= g:netrw_ftp_cmd - - " initialization - let b:netrw_method = 0 - let g:netrw_machine = "" - let b:netrw_fname = "" - let g:netrw_port = "" - let g:netrw_choice = a:choice - - " Patterns: - " mipf : a:machine a:id password filename Use ftp - " mf : a:machine filename Use ftp + <.netrc> or g:netrw_uid s:netrw_passwd - " ftpurm : ftp://[user@]host[[#:]port]/filename Use ftp + <.netrc> or g:netrw_uid s:netrw_passwd - " rcpurm : rcp://[user@]host/filename Use rcp - " rcphf : [user@]host:filename Use rcp - " scpurm : scp://[user@]host[[#:]port]/filename Use scp - " httpurm : http[s]://[user@]host/filename Use wget - " davurm : dav[s]://host[:port]/path Use cadaver/curl - " rsyncurm : rsync://host[:port]/path Use rsync - " fetchurm : fetch://[user@]host[:http]/filename Use fetch (defaults to ftp, override for http) - " sftpurm : sftp://[user@]host/filename Use scp - " fileurm : file://[user@]host/filename Use elinks or links - let mipf = '^\(\S\+\)\s\+\(\S\+\)\s\+\(\S\+\)\s\+\(\S\+\)$' - let mf = '^\(\S\+\)\s\+\(\S\+\)$' - let ftpurm = '^ftp://\(\([^/]*\)@\)\=\([^/#:]\{-}\)\([#:]\d\+\)\=/\(.*\)$' - let rcpurm = '^rcp://\%(\([^/]*\)@\)\=\([^/]\{-}\)/\(.*\)$' - let rcphf = '^\(\(\h\w*\)@\)\=\(\h\w*\):\([^@]\+\)$' - let scpurm = '^scp://\([^/#:]\+\)\%([#:]\(\d\+\)\)\=/\(.*\)$' - let httpurm = '^https\=://\([^/]\{-}\)\(/.*\)\=$' - let davurm = '^davs\=://\([^/]\+\)/\(.*/\)\([-_.~[:alnum:]]\+\)$' - let rsyncurm = '^rsync://\([^/]\{-}\)/\(.*\)\=$' - let fetchurm = '^fetch://\(\([^/]*\)@\)\=\([^/#:]\{-}\)\(:http\)\=/\(.*\)$' - let sftpurm = '^sftp://\([^/]\{-}\)/\(.*\)\=$' - let fileurm = '^file\=://\(.*\)$' - -" call Decho("determine method:",'~'.expand("<slnum>")) - " Determine Method - " Method#1: rcp://user@hostname/...path-to-file {{{3 - if match(a:choice,rcpurm) == 0 -" call Decho("rcp://...",'~'.expand("<slnum>")) - let b:netrw_method = 1 - let userid = substitute(a:choice,rcpurm,'\1',"") - let g:netrw_machine = substitute(a:choice,rcpurm,'\2',"") - let b:netrw_fname = substitute(a:choice,rcpurm,'\3',"") - if userid != "" - let g:netrw_uid= userid - endif - - " Method#4: scp://user@hostname/...path-to-file {{{3 - elseif match(a:choice,scpurm) == 0 -" call Decho("scp://...",'~'.expand("<slnum>")) - let b:netrw_method = 4 - let g:netrw_machine = substitute(a:choice,scpurm,'\1',"") - let g:netrw_port = substitute(a:choice,scpurm,'\2',"") - let b:netrw_fname = substitute(a:choice,scpurm,'\3',"") - - " Method#5: http[s]://user@hostname/...path-to-file {{{3 - elseif match(a:choice,httpurm) == 0 -" call Decho("http[s]://...",'~'.expand("<slnum>")) - let b:netrw_method = 5 - let g:netrw_machine= substitute(a:choice,httpurm,'\1',"") - let b:netrw_fname = substitute(a:choice,httpurm,'\2',"") - let b:netrw_http = (a:choice =~ '^https:')? "https" : "http" - - " Method#6: dav://hostname[:port]/..path-to-file.. {{{3 - elseif match(a:choice,davurm) == 0 -" call Decho("dav://...",'~'.expand("<slnum>")) - let b:netrw_method= 6 - if a:choice =~ 'davs:' - let g:netrw_machine= 'https://'.substitute(a:choice,davurm,'\1/\2',"") - else - let g:netrw_machine= 'http://'.substitute(a:choice,davurm,'\1/\2',"") - endif - let b:netrw_fname = substitute(a:choice,davurm,'\3',"") - - " Method#7: rsync://user@hostname/...path-to-file {{{3 - elseif match(a:choice,rsyncurm) == 0 -" call Decho("rsync://...",'~'.expand("<slnum>")) - let b:netrw_method = 7 - let g:netrw_machine= substitute(a:choice,rsyncurm,'\1',"") - let b:netrw_fname = substitute(a:choice,rsyncurm,'\2',"") - - " Methods 2,3: ftp://[user@]hostname[[:#]port]/...path-to-file {{{3 - elseif match(a:choice,ftpurm) == 0 -" call Decho("ftp://...",'~'.expand("<slnum>")) - let userid = substitute(a:choice,ftpurm,'\2',"") - let g:netrw_machine= substitute(a:choice,ftpurm,'\3',"") - let g:netrw_port = substitute(a:choice,ftpurm,'\4',"") - let b:netrw_fname = substitute(a:choice,ftpurm,'\5',"") -" call Decho("g:netrw_machine<".g:netrw_machine.">",'~'.expand("<slnum>")) - if userid != "" - let g:netrw_uid= userid - endif - - if curmachine != g:netrw_machine - if exists("s:netrw_hup[".g:netrw_machine."]") - call NetUserPass("ftp:".g:netrw_machine) - elseif exists("s:netrw_passwd") - " if there's a change in hostname, require password re-entry - unlet s:netrw_passwd - endif - if exists("netrw_port") - unlet netrw_port - endif - endif - - if exists("g:netrw_uid") && exists("s:netrw_passwd") - let b:netrw_method = 3 - else - let host= substitute(g:netrw_machine,'\..*$','','') - if exists("s:netrw_hup[host]") - call NetUserPass("ftp:".host) - - elseif has("win32") && 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\>' - 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 - let b:netrw_method= 2 - elseif s:FileReadable(expand("$HOME/.netrc")) && !g:netrw_ignorenetrc -" call Decho("using <".expand("$HOME/.netrc")."> (readable)",'~'.expand("<slnum>")) - let b:netrw_method= 2 - else - if !exists("g:netrw_uid") || g:netrw_uid == "" - call NetUserPass() - elseif !exists("s:netrw_passwd") || s:netrw_passwd == "" - call NetUserPass(g:netrw_uid) - " else just use current g:netrw_uid and s:netrw_passwd - endif - let b:netrw_method= 3 - endif - endif - - " Method#8: fetch {{{3 - elseif match(a:choice,fetchurm) == 0 -" call Decho("fetch://...",'~'.expand("<slnum>")) - let b:netrw_method = 8 - let g:netrw_userid = substitute(a:choice,fetchurm,'\2',"") - let g:netrw_machine= substitute(a:choice,fetchurm,'\3',"") - let b:netrw_option = substitute(a:choice,fetchurm,'\4',"") - let b:netrw_fname = substitute(a:choice,fetchurm,'\5',"") - - " Method#3: Issue an ftp : "machine id password [path/]filename" {{{3 - elseif match(a:choice,mipf) == 0 -" call Decho("(ftp) host id pass file",'~'.expand("<slnum>")) - let b:netrw_method = 3 - let g:netrw_machine = substitute(a:choice,mipf,'\1',"") - let g:netrw_uid = substitute(a:choice,mipf,'\2',"") - let s:netrw_passwd = substitute(a:choice,mipf,'\3',"") - let b:netrw_fname = substitute(a:choice,mipf,'\4',"") - call NetUserPass(g:netrw_machine,g:netrw_uid,s:netrw_passwd) - - " Method#3: Issue an ftp: "hostname [path/]filename" {{{3 - elseif match(a:choice,mf) == 0 -" call Decho("(ftp) host file",'~'.expand("<slnum>")) - if exists("g:netrw_uid") && exists("s:netrw_passwd") - let b:netrw_method = 3 - let g:netrw_machine = substitute(a:choice,mf,'\1',"") - let b:netrw_fname = substitute(a:choice,mf,'\2',"") - - elseif s:FileReadable(expand("$HOME/.netrc")) - let b:netrw_method = 2 - let g:netrw_machine = substitute(a:choice,mf,'\1',"") - let b:netrw_fname = substitute(a:choice,mf,'\2',"") - endif - - " Method#9: sftp://user@hostname/...path-to-file {{{3 - elseif match(a:choice,sftpurm) == 0 -" call Decho("sftp://...",'~'.expand("<slnum>")) - let b:netrw_method = 9 - let g:netrw_machine= substitute(a:choice,sftpurm,'\1',"") - let b:netrw_fname = substitute(a:choice,sftpurm,'\2',"") - - " Method#1: Issue an rcp: hostname:filename" (this one should be last) {{{3 - elseif match(a:choice,rcphf) == 0 -" call Decho("(rcp) [user@]host:file) rcphf<".rcphf.">",'~'.expand("<slnum>")) - let b:netrw_method = 1 - let userid = substitute(a:choice,rcphf,'\2',"") - let g:netrw_machine = substitute(a:choice,rcphf,'\3',"") - let b:netrw_fname = substitute(a:choice,rcphf,'\4',"") -" call Decho('\1<'.substitute(a:choice,rcphf,'\1',"").">",'~'.expand("<slnum>")) -" call Decho('\2<'.substitute(a:choice,rcphf,'\2',"").">",'~'.expand("<slnum>")) -" call Decho('\3<'.substitute(a:choice,rcphf,'\3',"").">",'~'.expand("<slnum>")) -" call Decho('\4<'.substitute(a:choice,rcphf,'\4',"").">",'~'.expand("<slnum>")) - if userid != "" - let g:netrw_uid= userid - endif - - " Method#10: file://user@hostname/...path-to-file {{{3 - elseif match(a:choice,fileurm) == 0 && exists("g:netrw_file_cmd") -" call Decho("http[s]://...",'~'.expand("<slnum>")) - let b:netrw_method = 10 - let b:netrw_fname = substitute(a:choice,fileurm,'\1',"") -" call Decho('\1<'.substitute(a:choice,fileurm,'\1',"").">",'~'.expand("<slnum>")) - - " Cannot Determine Method {{{3 - else - if !exists("g:netrw_quiet") - call netrw#ErrorMsg(s:WARNING,"cannot determine method (format: protocol://[user@]hostname[:port]/[path])",45) - endif - let b:netrw_method = -1 - endif - "}}}3 - - if g:netrw_port != "" - " remove any leading [:#] from port number - let g:netrw_port = substitute(g:netrw_port,'[#:]\+','','') - elseif exists("netrw_port") - " retain port number as implicit for subsequent ftp operations - let g:netrw_port= netrw_port - endif - -" call Decho("a:choice <".a:choice.">",'~'.expand("<slnum>")) -" call Decho("b:netrw_method <".b:netrw_method.">",'~'.expand("<slnum>")) -" call Decho("g:netrw_machine<".g:netrw_machine.">",'~'.expand("<slnum>")) -" call Decho("g:netrw_port <".g:netrw_port.">",'~'.expand("<slnum>")) -" if exists("g:netrw_uid") "Decho -" call Decho("g:netrw_uid <".g:netrw_uid.">",'~'.expand("<slnum>")) -" endif "Decho -" if exists("s:netrw_passwd") "Decho -" call Decho("s:netrw_passwd <".s:netrw_passwd.">",'~'.expand("<slnum>")) -" endif "Decho -" call Decho("b:netrw_fname <".b:netrw_fname.">",'~'.expand("<slnum>")) -" call Dret("s:NetrwMethod : b:netrw_method=".b:netrw_method." g:netrw_port=".g:netrw_port) -endfun - -" --------------------------------------------------------------------- -" NetUserPass: set username and password for subsequent ftp transfer {{{2 -" Usage: :call NetUserPass() -- will prompt for userid and password -" :call NetUserPass("uid") -- will prompt for password -" :call NetUserPass("uid","password") -- sets global userid and password -" :call NetUserPass("ftp:host") -- looks up userid and password using hup dictionary -" :call NetUserPass("host","uid","password") -- sets hup dictionary with host, userid, password -fun! NetUserPass(...) - -" call Dfunc("NetUserPass() a:0=".a:0) - - if !exists('s:netrw_hup') - let s:netrw_hup= {} - endif - - if a:0 == 0 - " case: no input arguments - - " change host and username if not previously entered; get new password - if !exists("g:netrw_machine") - let g:netrw_machine= input('Enter hostname: ') - endif - if !exists("g:netrw_uid") || g:netrw_uid == "" - " get username (user-id) via prompt - let g:netrw_uid= input('Enter username: ') - endif - " get password via prompting - let s:netrw_passwd= inputsecret("Enter Password: ") - - " set up hup database - let host = substitute(g:netrw_machine,'\..*$','','') - if !exists('s:netrw_hup[host]') - let s:netrw_hup[host]= {} - endif - let s:netrw_hup[host].uid = g:netrw_uid - let s:netrw_hup[host].passwd = s:netrw_passwd - - elseif a:0 == 1 - " case: one input argument - - if a:1 =~ '^ftp:' - " get host from ftp:... url - " access userid and password from hup (host-user-passwd) dictionary -" call Decho("case a:0=1: a:1<".a:1."> (get host from ftp:... url)",'~'.expand("<slnum>")) - let host = substitute(a:1,'^ftp:','','') - let host = substitute(host,'\..*','','') - if exists("s:netrw_hup[host]") - let g:netrw_uid = s:netrw_hup[host].uid - let s:netrw_passwd = s:netrw_hup[host].passwd -" call Decho("get s:netrw_hup[".host."].uid <".s:netrw_hup[host].uid.">",'~'.expand("<slnum>")) -" call Decho("get s:netrw_hup[".host."].passwd<".s:netrw_hup[host].passwd.">",'~'.expand("<slnum>")) - else - let g:netrw_uid = input("Enter UserId: ") - let s:netrw_passwd = inputsecret("Enter Password: ") - endif - - else - " case: one input argument, not an url. Using it as a new user-id. -" call Decho("case a:0=1: a:1<".a:1."> (get host from input argument, not an url)",'~'.expand("<slnum>")) - if exists("g:netrw_machine") - if g:netrw_machine =~ '[0-9.]\+' - let host= g:netrw_machine - else - let host= substitute(g:netrw_machine,'\..*$','','') - endif - else - let g:netrw_machine= input('Enter hostname: ') - endif - let g:netrw_uid = a:1 -" call Decho("set g:netrw_uid= <".g:netrw_uid.">",'~'.expand("<slnum>")) - if exists("g:netrw_passwd") - " ask for password if one not previously entered - let s:netrw_passwd= g:netrw_passwd - else - let s:netrw_passwd = inputsecret("Enter Password: ") - endif - endif - -" call Decho("host<".host.">",'~'.expand("<slnum>")) - if exists("host") - if !exists('s:netrw_hup[host]') - let s:netrw_hup[host]= {} - endif - let s:netrw_hup[host].uid = g:netrw_uid - let s:netrw_hup[host].passwd = s:netrw_passwd - endif - - elseif a:0 == 2 - let g:netrw_uid = a:1 - let s:netrw_passwd = a:2 - - elseif a:0 == 3 - " enter hostname, user-id, and password into the hup dictionary - let host = substitute(a:1,'^\a\+:','','') - let host = substitute(host,'\..*$','','') - if !exists('s:netrw_hup[host]') - let s:netrw_hup[host]= {} - endif - let s:netrw_hup[host].uid = a:2 - let s:netrw_hup[host].passwd = a:3 - let g:netrw_uid = s:netrw_hup[host].uid - let s:netrw_passwd = s:netrw_hup[host].passwd -" call Decho("set s:netrw_hup[".host."].uid <".s:netrw_hup[host].uid.">",'~'.expand("<slnum>")) -" call Decho("set s:netrw_hup[".host."].passwd<".s:netrw_hup[host].passwd.">",'~'.expand("<slnum>")) - endif - -" call Dret("NetUserPass : uid<".g:netrw_uid."> passwd<".s:netrw_passwd.">") -endfun - -" ================================= -" Shared Browsing Support: {{{1 -" ================================= - -" --------------------------------------------------------------------- -" s:ExplorePatHls: converts an Explore pattern into a regular expression search pattern {{{2 -fun! s:ExplorePatHls(pattern) -" call Dfunc("s:ExplorePatHls(pattern<".a:pattern.">)") - let repat= substitute(a:pattern,'^**/\{1,2}','','') -" call Decho("repat<".repat.">",'~'.expand("<slnum>")) - let repat= escape(repat,'][.\') -" call Decho("repat<".repat.">",'~'.expand("<slnum>")) - let repat= '\<'.substitute(repat,'\*','\\(\\S\\+ \\)*\\S\\+','g').'\>' -" call Dret("s:ExplorePatHls repat<".repat.">") - return repat -endfun - -" --------------------------------------------------------------------- -" s:NetrwBookHistHandler: {{{2 -" 0: (user: <mb>) bookmark current directory -" 1: (user: <gb>) change to the bookmarked directory -" 2: (user: <qb>) list bookmarks -" 3: (browsing) records current directory history -" 4: (user: <u>) go up (previous) directory, using history -" 5: (user: <U>) go down (next) directory, using history -" 6: (user: <mB>) delete bookmark -fun! s:NetrwBookHistHandler(chg,curdir) -" call Dfunc("s:NetrwBookHistHandler(chg=".a:chg." curdir<".a:curdir.">) cnt=".v:count." histcnt=".g:netrw_dirhistcnt." histmax=".g:netrw_dirhistmax) - if !exists("g:netrw_dirhistmax") || g:netrw_dirhistmax <= 0 -" " call Dret("s:NetrwBookHistHandler - suppressed due to g:netrw_dirhistmax") - return - endif - - let ykeep = @@ - let curbufnr = bufnr("%") - - if a:chg == 0 - " bookmark the current directory -" call Decho("(user: <b>) bookmark the current directory",'~'.expand("<slnum>")) - if exists("s:netrwmarkfilelist_{curbufnr}") - call s:NetrwBookmark(0) - echo "bookmarked marked files" - else - call s:MakeBookmark(a:curdir) - echo "bookmarked the current directory" - endif - - try - call s:NetrwBookHistSave() - catch - endtry - - elseif a:chg == 1 - " change to the bookmarked directory -" call Decho("(user: <".v:count."gb>) change to the bookmarked directory",'~'.expand("<slnum>")) - if exists("g:netrw_bookmarklist[v:count-1]") -" call Decho("(user: <".v:count."gb>) bookmarklist=".string(g:netrw_bookmarklist),'~'.expand("<slnum>")) - exe "NetrwKeepj e ".fnameescape(g:netrw_bookmarklist[v:count-1]) - else - echomsg "Sorry, bookmark#".v:count." doesn't exist!" - endif - - elseif a:chg == 2 -" redraw! - let didwork= 0 - " list user's bookmarks -" call Decho("(user: <q>) list user's bookmarks",'~'.expand("<slnum>")) - if exists("g:netrw_bookmarklist") -" call Decho('list '.len(g:netrw_bookmarklist).' bookmarks','~'.expand("<slnum>")) - let cnt= 1 - for bmd in g:netrw_bookmarklist -" call Decho("Netrw Bookmark#".cnt.": ".g:netrw_bookmarklist[cnt-1],'~'.expand("<slnum>")) - echo printf("Netrw Bookmark#%-2d: %s",cnt,g:netrw_bookmarklist[cnt-1]) - let didwork = 1 - let cnt = cnt + 1 - endfor - endif - - " list directory history - " Note: history is saved only when PerformListing is done; - " ie. when netrw can re-use a netrw buffer, the current directory is not saved in the history. - let cnt = g:netrw_dirhistcnt - let first = 1 - let histcnt = 0 - if g:netrw_dirhistmax > 0 - while ( first || cnt != g:netrw_dirhistcnt ) -" call Decho("first=".first." cnt=".cnt." dirhistcnt=".g:netrw_dirhistcnt,'~'.expand("<slnum>")) - if exists("g:netrw_dirhist_{cnt}") -" call Decho("Netrw History#".histcnt.": ".g:netrw_dirhist_{cnt},'~'.expand("<slnum>")) - echo printf("Netrw History#%-2d: %s",histcnt,g:netrw_dirhist_{cnt}) - let didwork= 1 - endif - let histcnt = histcnt + 1 - let first = 0 - let cnt = ( cnt - 1 ) % g:netrw_dirhistmax - if cnt < 0 - let cnt= cnt + g:netrw_dirhistmax - endif - endwhile - else - let g:netrw_dirhistcnt= 0 - endif - if didwork - call inputsave()|call input("Press <cr> to continue")|call inputrestore() - endif - - elseif a:chg == 3 - " saves most recently visited directories (when they differ) -" call Decho("(browsing) record curdir history",'~'.expand("<slnum>")) - if !exists("g:netrw_dirhistcnt") || !exists("g:netrw_dirhist_{g:netrw_dirhistcnt}") || g:netrw_dirhist_{g:netrw_dirhistcnt} != a:curdir - if g:netrw_dirhistmax > 0 - let g:netrw_dirhistcnt = ( g:netrw_dirhistcnt + 1 ) % g:netrw_dirhistmax - let g:netrw_dirhist_{g:netrw_dirhistcnt} = a:curdir - endif -" call Decho("save dirhist#".g:netrw_dirhistcnt."<".g:netrw_dirhist_{g:netrw_dirhistcnt}.">",'~'.expand("<slnum>")) - endif - - elseif a:chg == 4 - " u: change to the previous directory stored on the history list -" call Decho("(user: <u>) chg to prev dir from history",'~'.expand("<slnum>")) - if g:netrw_dirhistmax > 0 - let g:netrw_dirhistcnt= ( g:netrw_dirhistcnt - v:count1 ) % g:netrw_dirhistmax - if g:netrw_dirhistcnt < 0 - let g:netrw_dirhistcnt= g:netrw_dirhistcnt + g:netrw_dirhistmax - endif - else - let g:netrw_dirhistcnt= 0 - endif - if exists("g:netrw_dirhist_{g:netrw_dirhistcnt}") -" call Decho("changedir u#".g:netrw_dirhistcnt."<".g:netrw_dirhist_{g:netrw_dirhistcnt}.">",'~'.expand("<slnum>")) - if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("b:netrw_curdir") - setl ma noro -" call Decho("setl ma noro",'~'.expand("<slnum>")) - sil! NetrwKeepj %d _ - setl nomod -" call Decho("setl nomod",'~'.expand("<slnum>")) -" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) - endif -" call Decho("exe e! ".fnameescape(g:netrw_dirhist_{g:netrw_dirhistcnt}),'~'.expand("<slnum>")) - exe "NetrwKeepj e! ".fnameescape(g:netrw_dirhist_{g:netrw_dirhistcnt}) - else - if g:netrw_dirhistmax > 0 - let g:netrw_dirhistcnt= ( g:netrw_dirhistcnt + v:count1 ) % g:netrw_dirhistmax - else - let g:netrw_dirhistcnt= 0 - endif - echo "Sorry, no predecessor directory exists yet" - endif - - elseif a:chg == 5 - " U: change to the subsequent directory stored on the history list -" call Decho("(user: <U>) chg to next dir from history",'~'.expand("<slnum>")) - if g:netrw_dirhistmax > 0 - let g:netrw_dirhistcnt= ( g:netrw_dirhistcnt + 1 ) % g:netrw_dirhistmax - if exists("g:netrw_dirhist_{g:netrw_dirhistcnt}") -" call Decho("changedir U#".g:netrw_dirhistcnt."<".g:netrw_dirhist_{g:netrw_dirhistcnt}.">",'~'.expand("<slnum>")) - if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("b:netrw_curdir") -" call Decho("setl ma noro",'~'.expand("<slnum>")) - setl ma noro - sil! NetrwKeepj %d _ -" call Decho("removed all lines from buffer (%d)",'~'.expand("<slnum>")) -" call Decho("setl nomod",'~'.expand("<slnum>")) - setl nomod -" call Decho("(set nomod) ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) - endif -" call Decho("exe e! ".fnameescape(g:netrw_dirhist_{g:netrw_dirhistcnt}),'~'.expand("<slnum>")) - exe "NetrwKeepj e! ".fnameescape(g:netrw_dirhist_{g:netrw_dirhistcnt}) - else - let g:netrw_dirhistcnt= ( g:netrw_dirhistcnt - 1 ) % g:netrw_dirhistmax - if g:netrw_dirhistcnt < 0 - let g:netrw_dirhistcnt= g:netrw_dirhistcnt + g:netrw_dirhistmax - endif - echo "Sorry, no successor directory exists yet" - endif - else - let g:netrw_dirhistcnt= 0 - echo "Sorry, no successor directory exists yet (g:netrw_dirhistmax is ".g:netrw_dirhistmax.")" - endif - - elseif a:chg == 6 -" call Decho("(user: <mB>) delete bookmark'd directory",'~'.expand("<slnum>")) - if exists("s:netrwmarkfilelist_{curbufnr}") - call s:NetrwBookmark(1) - echo "removed marked files from bookmarks" - else - " delete the v:count'th bookmark - let iremove = v:count - let dremove = g:netrw_bookmarklist[iremove - 1] -" call Decho("delete bookmark#".iremove."<".g:netrw_bookmarklist[iremove - 1].">",'~'.expand("<slnum>")) - call s:MergeBookmarks() -" call Decho("remove g:netrw_bookmarklist[".(iremove-1)."]<".g:netrw_bookmarklist[(iremove-1)].">",'~'.expand("<slnum>")) - NetrwKeepj call remove(g:netrw_bookmarklist,iremove-1) - echo "removed ".dremove." from g:netrw_bookmarklist" -" call Decho("g:netrw_bookmarklist=".string(g:netrw_bookmarklist),'~'.expand("<slnum>")) - endif -" call Decho("resulting g:netrw_bookmarklist=".string(g:netrw_bookmarklist),'~'.expand("<slnum>")) - - try - call s:NetrwBookHistSave() - catch - endtry - endif - call s:NetrwBookmarkMenu() - call s:NetrwTgtMenu() - let @@= ykeep -" call Dret("s:NetrwBookHistHandler") -endfun - -" --------------------------------------------------------------------- -" s:NetrwBookHistRead: this function reads bookmarks and history {{{2 -" Will source the history file (.netrwhist) only if the g:netrw_disthistmax is > 0. -" Sister function: s:NetrwBookHistSave() -fun! s:NetrwBookHistRead() -" call Dfunc("s:NetrwBookHistRead()") - if !exists("g:netrw_dirhistmax") || g:netrw_dirhistmax <= 0 -" call Dret("s:NetrwBookHistRead - nothing read (suppressed due to dirhistmax=".(exists("g:netrw_dirhistmax")? g:netrw_dirhistmax : "n/a").")") - return - endif - let ykeep= @@ - - " read bookmarks - if !exists("s:netrw_initbookhist") - let home = s:NetrwHome() - let savefile= home."/.netrwbook" - if filereadable(s:NetrwFile(savefile)) -" call Decho("sourcing .netrwbook",'~'.expand("<slnum>")) - exe "keepalt NetrwKeepj so ".savefile - endif - - " read history - if g:netrw_dirhistmax > 0 - let savefile= home."/.netrwhist" - if filereadable(s:NetrwFile(savefile)) -" call Decho("sourcing .netrwhist",'~'.expand("<slnum>")) - exe "keepalt NetrwKeepj so ".savefile - endif - let s:netrw_initbookhist= 1 - au VimLeave * call s:NetrwBookHistSave() - endif - endif - - let @@= ykeep -" call Decho("dirhistmax=".(exists("g:netrw_dirhistmax")? g:netrw_dirhistmax : "n/a"),'~'.expand("<slnum>")) -" call Decho("dirhistcnt=".(exists("g:netrw_dirhistcnt")? g:netrw_dirhistcnt : "n/a"),'~'.expand("<slnum>")) -" call Dret("s:NetrwBookHistRead") -endfun - -" --------------------------------------------------------------------- -" s:NetrwBookHistSave: this function saves bookmarks and history to files {{{2 -" Sister function: s:NetrwBookHistRead() -" I used to do this via viminfo but that appears to -" be unreliable for long-term storage -" If g:netrw_dirhistmax is <= 0, no history or bookmarks -" will be saved. -" (s:NetrwBookHistHandler(3,...) used to record history) -fun! s:NetrwBookHistSave() -" call Dfunc("s:NetrwBookHistSave() dirhistmax=".g:netrw_dirhistmax." dirhistcnt=".g:netrw_dirhistcnt) - if !exists("g:netrw_dirhistmax") || g:netrw_dirhistmax <= 0 -" call Dret("s:NetrwBookHistSave : nothing saved (dirhistmax=".g:netrw_dirhistmax.")") - return - endif - - let savefile= s:NetrwHome()."/.netrwhist" -" call Decho("savefile<".savefile.">",'~'.expand("<slnum>")) - 1split - - " setting up a new buffer which will become .netrwhist - call s:NetrwEnew() -" call Decho("case g:netrw_use_noswf=".g:netrw_use_noswf.(exists("+acd")? " +acd" : " -acd"),'~'.expand("<slnum>")) - if g:netrw_use_noswf - setl cino= com= cpo-=a cpo-=A fo=nroql2 tw=0 report=10000 noswf - else - setl cino= com= cpo-=a cpo-=A fo=nroql2 tw=0 report=10000 - endif - setl nocin noai noci magic nospell nohid wig= noaw - setl ma noro write - if exists("+acd") | setl noacd | endif - sil! NetrwKeepj keepalt %d _ - - " rename enew'd file: .netrwhist -- no attempt to merge - " record dirhistmax and current dirhistcnt - " save history -" call Decho("saving history: dirhistmax=".g:netrw_dirhistmax." dirhistcnt=".g:netrw_dirhistcnt." lastline=".line("$"),'~'.expand("<slnum>")) - sil! keepalt file .netrwhist - call setline(1,"let g:netrw_dirhistmax =".g:netrw_dirhistmax) - call setline(2,"let g:netrw_dirhistcnt =".g:netrw_dirhistcnt) - if g:netrw_dirhistmax > 0 - let lastline = line("$") - let cnt = g:netrw_dirhistcnt - let first = 1 - while ( first || cnt != g:netrw_dirhistcnt ) - let lastline= lastline + 1 - if exists("g:netrw_dirhist_{cnt}") - call setline(lastline,'let g:netrw_dirhist_'.cnt."='".g:netrw_dirhist_{cnt}."'") -" call Decho("..".lastline.'let g:netrw_dirhist_'.cnt."='".g:netrw_dirhist_{cnt}."'",'~'.expand("<slnum>")) - endif - let first = 0 - let cnt = ( cnt - 1 ) % g:netrw_dirhistmax - if cnt < 0 - let cnt= cnt + g:netrw_dirhistmax - endif - endwhile - exe "sil! w! ".savefile -" call Decho("exe sil! w! ".savefile,'~'.expand("<slnum>")) - endif - - " save bookmarks - sil NetrwKeepj %d _ - if exists("g:netrw_bookmarklist") && g:netrw_bookmarklist != [] -" call Decho("saving bookmarks",'~'.expand("<slnum>")) - " merge and write .netrwbook - let savefile= s:NetrwHome()."/.netrwbook" - - if filereadable(s:NetrwFile(savefile)) - let booklist= deepcopy(g:netrw_bookmarklist) - exe "sil NetrwKeepj keepalt so ".savefile - for bdm in booklist - if index(g:netrw_bookmarklist,bdm) == -1 - call add(g:netrw_bookmarklist,bdm) - endif - endfor - call sort(g:netrw_bookmarklist) - endif - - " construct and save .netrwbook - call setline(1,"let g:netrw_bookmarklist= ".string(g:netrw_bookmarklist)) - exe "sil! w! ".savefile -" call Decho("exe sil! w! ".savefile,'~'.expand("<slnum>")) - endif - - " cleanup -- remove buffer used to construct history - let bgone= bufnr("%") - q! - exe "keepalt ".bgone."bwipe!" - -" call Dret("s:NetrwBookHistSave") -endfun - -" --------------------------------------------------------------------- -" s:NetrwBrowse: This function uses the command in g:netrw_list_cmd to provide a {{{2 -" list of the contents of a local or remote directory. It is assumed that the -" g:netrw_list_cmd has a string, USEPORT HOSTNAME, that needs to be substituted -" with the requested remote hostname first. -" Often called via: Explore/e dirname/etc -> netrw#LocalBrowseCheck() -> s:NetrwBrowse() -fun! s:NetrwBrowse(islocal,dirname) - if !exists("w:netrw_liststyle")|let w:netrw_liststyle= g:netrw_liststyle|endif - - " save alternate-file's filename if w:netrw_rexlocal doesn't exist - " 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("#") - endif - - " s:NetrwBrowse : initialize history {{{3 - if !exists("s:netrw_initbookhist") - NetrwKeepj call s:NetrwBookHistRead() - endif - - " s:NetrwBrowse : simplify the dirname (especially for ".."s in dirnames) {{{3 - if a:dirname !~ '^\a\{3,}://' - let dirname= simplify(a:dirname) - else - let dirname= a:dirname - endif - - " repoint t:netrw_lexbufnr if appropriate - if exists("t:netrw_lexbufnr") && bufnr("%") == t:netrw_lexbufnr - let repointlexbufnr= 1 - endif - - " s:NetrwBrowse : sanity checks: {{{3 - if exists("s:netrw_skipbrowse") - unlet s:netrw_skipbrowse - return - endif - if !exists("*shellescape") - NetrwKeepj call netrw#ErrorMsg(s:ERROR,"netrw can't run -- your vim is missing shellescape()",69) - return - endif - if !exists("*fnameescape") - NetrwKeepj call netrw#ErrorMsg(s:ERROR,"netrw can't run -- your vim is missing fnameescape()",70) - return - endif - - " s:NetrwBrowse : save options: {{{3 - call s:NetrwOptionsSave("w:") - - " s:NetrwBrowse : re-instate any marked files {{{3 - if has("syntax") && exists("g:syntax_on") && g:syntax_on - if exists("s:netrwmarkfilelist_{bufnr('%')}") - exe "2match netrwMarkFile /".s:netrwmarkfilemtch_{bufnr("%")}."/" - endif - endif - - if a:islocal && exists("w:netrw_acdkeep") && w:netrw_acdkeep - " s:NetrwBrowse : set up "safe" options for local directory/file {{{3 - if s:NetrwLcd(dirname) - return - endif - - elseif !a:islocal && dirname !~ '[\/]$' && dirname !~ '^"' - " s:NetrwBrowse : remote regular file handler {{{3 - if bufname(dirname) != "" - exe "NetrwKeepj b ".bufname(dirname) - else - " attempt transfer of remote regular file - - " remove any filetype indicator from end of dirname, except for the - " "this is a directory" indicator (/). - " There shouldn't be one of those here, anyway. - let path= substitute(dirname,'[*=@|]\r\=$','','e') - call s:RemotePathAnalysis(dirname) - - " s:NetrwBrowse : remote-read the requested file into current buffer {{{3 - call s:NetrwEnew(dirname) - call s:NetrwOptionsSafe(a:islocal) - setl ma noro - let b:netrw_curdir = dirname - let url = s:method."://".((s:user == "")? "" : s:user."@").s:machine.(s:port ? ":".s:port : "")."/".s:path - call s:NetrwBufRename(url) - exe "sil! NetrwKeepj keepalt doau BufReadPre ".fnameescape(s:fname) - sil call netrw#NetRead(2,url) - " netrw.vim and tar.vim have already handled decompression of the tarball; avoiding gzip.vim error - if s:path =~ '.bz2' - exe "sil NetrwKeepj keepalt doau BufReadPost ".fnameescape(substitute(s:fname,'\.bz2$','','')) - elseif s:path =~ '.gz' - exe "sil NetrwKeepj keepalt doau BufReadPost ".fnameescape(substitute(s:fname,'\.gz$','','')) - elseif s:path =~ '.gz' - exe "sil NetrwKeepj keepalt doau BufReadPost ".fnameescape(substitute(s:fname,'\.txz$','','')) - else - exe "sil NetrwKeepj keepalt doau BufReadPost ".fnameescape(s:fname) - endif - endif - - " s:NetrwBrowse : save certain window-oriented variables into buffer-oriented variables {{{3 - call s:SetBufWinVars() - call s:NetrwOptionsRestore("w:") - setl ma nomod noro - return - endif - - " use buffer-oriented WinVars if buffer variables exist but associated window variables don't {{{3 - call s:UseBufWinVars() - - " set up some variables {{{3 - let b:netrw_browser_active = 1 - let dirname = dirname - let s:last_sort_by = g:netrw_sort_by - - " set up menu {{{3 - NetrwKeepj call s:NetrwMenu(1) - - " get/set-up buffer {{{3 - let svpos = winsaveview() - - " NetrwGetBuffer might change buffers but s:rexposn_X was set for the - " previous buffer - let prevbufnr = bufnr('%') - let reusing= s:NetrwGetBuffer(a:islocal,dirname) - if exists("s:rexposn_".prevbufnr) - let s:rexposn_{bufnr('%')} = s:rexposn_{prevbufnr} - endif - - " maintain markfile highlighting - if has("syntax") && exists("g:syntax_on") && g:syntax_on - if exists("s:netrwmarkfilemtch_{bufnr('%')}") && s:netrwmarkfilemtch_{bufnr("%")} != "" - exe "2match netrwMarkFile /".s:netrwmarkfilemtch_{bufnr("%")}."/" - else - 2match none - endif - endif - if reusing && line("$") > 1 - call s:NetrwOptionsRestore("w:") - setl noma nomod nowrap - return - endif - - " set b:netrw_curdir to the new directory name {{{3 - let b:netrw_curdir= dirname - if b:netrw_curdir =~ '[/\\]$' - let b:netrw_curdir= substitute(b:netrw_curdir,'[/\\]$','','e') - endif - if b:netrw_curdir =~ '\a:$' && has("win32") - let b:netrw_curdir= b:netrw_curdir."/" - endif - if b:netrw_curdir == '' - if has("amiga") - " On the Amiga, the empty string connotes the current directory - let b:netrw_curdir= getcwd() - else - " under unix, when the root directory is encountered, the result - " from the preceding substitute is an empty string. - let b:netrw_curdir= '/' - endif - endif - if !a:islocal && b:netrw_curdir !~ '/$' - let b:netrw_curdir= b:netrw_curdir.'/' - endif - - " ------------ - " (local only) {{{3 - " ------------ - if a:islocal - " Set up ShellCmdPost handling. Append current buffer to browselist - call s:LocalFastBrowser() - - " handle g:netrw_keepdir: set vim's current directory to netrw's notion of the current directory {{{3 - if !g:netrw_keepdir - if !exists("&l:acd") || !&l:acd - if s:NetrwLcd(b:netrw_curdir) - return - endif - endif - endif - - " -------------------------------- - " remote handling: {{{3 - " -------------------------------- - else - - " analyze dirname and g:netrw_list_cmd {{{3 - if dirname =~# "^NetrwTreeListing\>" - let dirname= b:netrw_curdir - elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("b:netrw_curdir") - let dirname= substitute(b:netrw_curdir,'\\','/','g') - if dirname !~ '/$' - let dirname= dirname.'/' - endif - let b:netrw_curdir = dirname - else - let dirname = substitute(dirname,'\\','/','g') - endif - - let dirpat = '^\(\w\{-}\)://\(\w\+@\)\=\([^/]\+\)/\(.*\)$' - if dirname !~ dirpat - if !exists("g:netrw_quiet") - NetrwKeepj call netrw#ErrorMsg(s:ERROR,"netrw doesn't understand your dirname<".dirname.">",20) - endif - NetrwKeepj call s:NetrwOptionsRestore("w:") - setl noma nomod nowrap - return - endif - let b:netrw_curdir= dirname - endif " (additional remote handling) - - " ------------------------------- - " Perform Directory Listing: {{{3 - " ------------------------------- - NetrwKeepj call s:NetrwMaps(a:islocal) - NetrwKeepj call s:NetrwCommands(a:islocal) - NetrwKeepj call s:PerformListing(a:islocal) - - " restore option(s) - call s:NetrwOptionsRestore("w:") - - " If there is a rexposn: restore position with rexposn - " Otherwise : set rexposn - if exists("s:rexposn_".bufnr("%")) - NetrwKeepj call winrestview(s:rexposn_{bufnr('%')}) - if exists("w:netrw_bannercnt") && line(".") < w:netrw_bannercnt - NetrwKeepj exe w:netrw_bannercnt - endif - else - NetrwKeepj call s:SetRexDir(a:islocal,b:netrw_curdir) - endif - if v:version >= 700 && has("balloon_eval") && &beval == 0 && &l:bexpr == "" && !exists("g:netrw_nobeval") - let &l:bexpr= "netrw#BalloonHelp()" - setl beval - endif - - " repoint t:netrw_lexbufnr if appropriate - if exists("repointlexbufnr") - let t:netrw_lexbufnr= bufnr("%") - endif - - " restore position - if reusing - call winrestview(svpos) - endif - - " The s:LocalBrowseRefresh() function is called by an autocmd - " installed by s:LocalFastBrowser() when g:netrw_fastbrowse <= 1 (ie. slow or medium speed). - " However, s:NetrwBrowse() causes the FocusGained event to fire the first time. - return -endfun - -" --------------------------------------------------------------------- -" s:NetrwFile: because of g:netrw_keepdir, isdirectory(), type(), etc may or {{{2 -" may not apply correctly; ie. netrw's idea of the current directory may -" differ from vim's. This function insures that netrw's idea of the current -" directory is used. -" Returns a path to the file specified by a:fname -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'),'~'.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.">",'~'.expand("<slnum>")) - else - let fname= a:fname - endif - - if g:netrw_keepdir - " vim's idea of the current directory possibly may differ from netrw's - if !exists("b:netrw_curdir") - let b:netrw_curdir= getcwd() - endif - - if !exists("g:netrw_cygwin") && has("win32") - if fname =~ '^\' || fname =~ '^\a:\' - " windows, but full path given - let ret= fname -" "" call Decho("windows+full path: isdirectory(".fname.")",'~'.expand("<slnum>")) - else - " windows, relative path given - let ret= s:ComposePath(b:netrw_curdir,fname) -" "" call Decho("windows+rltv path: isdirectory(".fname.")",'~'.expand("<slnum>")) - endif - - elseif fname =~ '^/' - " not windows, full path given - let ret= fname -" "" call Decho("unix+full path: isdirectory(".fname.")",'~'.expand("<slnum>")) - else - " not windows, relative path given - let ret= s:ComposePath(b:netrw_curdir,fname) -" "" call Decho("unix+rltv path: isdirectory(".fname.")",'~'.expand("<slnum>")) - endif - else - " 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) - return ret -endfun - -" --------------------------------------------------------------------- -" s:NetrwFileInfo: supports qf (query for file information) {{{2 -fun! s:NetrwFileInfo(islocal,fname) -" call Dfunc("s:NetrwFileInfo(islocal=".a:islocal." fname<".a:fname.">) b:netrw_curdir<".b:netrw_curdir.">") - let ykeep= @@ - if a:islocal - let lsopt= "-lsad" - if g:netrw_sizestyle =~# 'H' - let lsopt= "-lsadh" - elseif g:netrw_sizestyle =~# 'h' - let lsopt= "-lsadh --si" - endif -" call Decho("(s:NetrwFileInfo) lsopt<".lsopt.">") - if (has("unix") || has("macunix")) && executable("/bin/ls") - - if getline(".") == "../" - echo system("/bin/ls ".lsopt." ".s:ShellEscape("..")) -" call Decho("#1: echo system(/bin/ls -lsad ".s:ShellEscape(..).")",'~'.expand("<slnum>")) - - elseif w:netrw_liststyle == s:TREELIST && getline(".") !~ '^'.s:treedepthstring - echo system("/bin/ls ".lsopt." ".s:ShellEscape(b:netrw_curdir)) -" call Decho("#2: echo system(/bin/ls -lsad ".s:ShellEscape(b:netrw_curdir).")",'~'.expand("<slnum>")) - - elseif exists("b:netrw_curdir") - echo system("/bin/ls ".lsopt." ".s:ShellEscape(s:ComposePath(b:netrw_curdir,a:fname))) -" call Decho("#3: echo system(/bin/ls -lsad ".s:ShellEscape(b:netrw_curdir.a:fname).")",'~'.expand("<slnum>")) - - else -" call Decho('using ls '.a:fname." using cwd<".getcwd().">",'~'.expand("<slnum>")) - echo system("/bin/ls ".lsopt." ".s:ShellEscape(s:NetrwFile(a:fname))) -" call Decho("#5: echo system(/bin/ls -lsad ".s:ShellEscape(a:fname).")",'~'.expand("<slnum>")) - endif - else - " use vim functions to return information about file below cursor -" call Decho("using vim functions to query for file info",'~'.expand("<slnum>")) - if !isdirectory(s:NetrwFile(a:fname)) && !filereadable(s:NetrwFile(a:fname)) && a:fname =~ '[*@/]' - let fname= substitute(a:fname,".$","","") - else - let fname= a:fname - endif - let t = getftime(s:NetrwFile(fname)) - let sz = getfsize(s:NetrwFile(fname)) - if g:netrw_sizestyle =~# "[hH]" - let sz= s:NetrwHumanReadable(sz) - endif - echo a:fname.": ".sz." ".strftime(g:netrw_timefmt,getftime(s:NetrwFile(fname))) -" call Decho("fname.": ".sz." ".strftime(g:netrw_timefmt,getftime(fname)),'~'.expand("<slnum>")) - endif - else - echo "sorry, \"qf\" not supported yet for remote files" - endif - let @@= ykeep -" call Dret("s:NetrwFileInfo") -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: [get a new|find an old netrw] buffer for a netrw listing {{{2 -" returns 0=cleared buffer -" 1=re-used buffer (buffer not cleared) -" Nov 09, 2020: tst952 shows that when user does :set hidden that NetrwGetBuffer will come up with a [No Name] buffer (hid fix) -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." hid=".&hid,'~'.expand("<slnum>")) -" call Decho("netrwbuf dictionary=".(exists("s:netrwbuf")? string(s:netrwbuf) : 'n/a'),'~'.expand("<slnum>")) -" call Dredir("ls!","s:NetrwGetBuffer") - let dirname= a:dirname - - " re-use buffer if possible {{{3 -" call Decho("--re-use a buffer if possible--",'~'.expand("<slnum>")) - if !exists("s:netrwbuf") -" call Decho(" s:netrwbuf initialized to {}",'~'.expand("<slnum>")) - let s:netrwbuf= {} - endif -" call Decho(" s:netrwbuf =".string(s:netrwbuf),'~'.expand("<slnum>")) -" call Decho(" w:netrw_liststyle =".(exists("w:netrw_liststyle")? w:netrw_liststyle : "n/a"),'~'.expand("<slnum>")) - - if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST - let bufnum = -1 - - if !empty(s:netrwbuf) && has_key(s:netrwbuf,s:NetrwFullPath(dirname)) - if has_key(s:netrwbuf,"NetrwTreeListing") - let bufnum= s:netrwbuf["NetrwTreeListing"] - else - let bufnum= s:netrwbuf[s:NetrwFullPath(dirname)] - endif -" call Decho(" NetrwTreeListing: bufnum#".bufnum,'~'.expand("<slnum>")) - if !bufexists(bufnum) - call remove(s:netrwbuf,"NetrwTreeListing") - let bufnum= -1 - endif - elseif bufnr("NetrwTreeListing") != -1 - let bufnum= bufnr("NetrwTreeListing") -" call Decho(" NetrwTreeListing".": bufnum#".bufnum,'~'.expand("<slnum>")) - else -" call Decho(" did not find a NetrwTreeListing buffer",'~'.expand("<slnum>")) - let bufnum= -1 - endif - - elseif 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,'~'.expand("<slnum>")) - if !bufexists(bufnum) - call remove(s:netrwbuf,s:NetrwFullPath(dirname)) - let bufnum= -1 - endif - - else -" call Decho(" lookup netrwbuf dictionary: s:netrwbuf[".s:NetrwFullPath(dirname)."] not a key",'~'.expand("<slnum>")) - let bufnum= -1 - endif -" call Decho(" bufnum#".bufnum,'~'.expand("<slnum>")) - - " highjack the current buffer - " IF the buffer already has the desired name - " AND it is empty - let curbuf = bufname("%") - if curbuf == '.' - let curbuf = getcwd() - endif -" call Dredir("ls!","NetrwGetFile (renamed buffer back to remote filename<".rfile."> : expand(%)<".expand("%").">)") -" call Decho("deciding if netrw may highjack the current buffer#".bufnr("%")."<".curbuf.">",'~'.expand("<slnum>")) -" call Decho("..dirname<".dirname."> IF dirname == bufname",'~'.expand("<slnum>")) -" call Decho("..curbuf<".curbuf.">",'~'.expand("<slnum>")) -" call Decho("..line($)=".line("$")." AND this is 1",'~'.expand("<slnum>")) -" call Decho("..getline(%)<".getline("%")."> AND this line is empty",'~'.expand("<slnum>")) - if dirname == curbuf && line("$") == 1 && getline("%") == "" -" call Dret("s:NetrwGetBuffer 0<cleared buffer> : highjacking buffer#".bufnr("%")) - return 0 - else " DEBUG -" call Decho("..did NOT highjack buffer",'~'.expand("<slnum>")) - endif - " Aug 14, 2021: was thinking about looking for a [No Name] buffer here and using it, but that might cause problems - - " get enew buffer and name it -or- re-use buffer {{{3 - 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>")) - " name the buffer - if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST - " Got enew buffer; transform into a NetrwTreeListing -" call Decho("--transform enew buffer#".bufnr("%")." into a NetrwTreeListing --",'~'.expand("<slnum>")) - let w:netrw_treebufnr = bufnr("%") - call s:NetrwBufRename("NetrwTreeListing") - if g:netrw_use_noswf - setl nobl bt=nofile noswf - else - setl nobl bt=nofile - endif - nnoremap <silent> <buffer> [[ :sil call <SID>TreeListMove('[[')<cr> - nnoremap <silent> <buffer> ]] :sil call <SID>TreeListMove(']]')<cr> - nnoremap <silent> <buffer> [] :sil call <SID>TreeListMove('[]')<cr> - nnoremap <silent> <buffer> ][ :sil call <SID>TreeListMove('][')<cr> -" call Decho(" tree listing bufnr=".w:netrw_treebufnr,'~'.expand("<slnum>")) - else - call s:NetrwBufRename(dirname) - " 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>")) - - else " Re-use the buffer -" call Decho("--re-use buffer#".bufnum." (bufnum#".bufnum.">=0 AND bufexists(".bufnum.")=".bufexists(bufnum)."!=0)",'~'.expand("<slnum>")) - " ignore all events - let eikeep= &ei - setl ei=all - - if &ft == "netrw" -" call Decho("buffer type is netrw; not using keepalt with b ".bufnum) - exe "sil! NetrwKeepj noswapfile b ".bufnum -" call Dredir("ls!","one") - else -" call Decho("buffer type is not netrw; using keepalt with b ".bufnum) - call s:NetrwEditBuf(bufnum) -" call Dredir("ls!","two") - endif -" call Decho(" line($)=".line("$"),'~'.expand("<slnum>")) - if bufname("%") == '.' - call s:NetrwBufRename(getcwd()) - endif - - " restore ei - let &ei= eikeep - - if line("$") <= 1 && getline(1) == "" - " empty buffer - NetrwKeepj call s:NetrwListSettings(a:islocal) -" 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 empty> : re-using buffer#".bufnr("%").", but its empty, so refresh it") - return 0 - - elseif g:netrw_fastbrowse == 0 || (a:islocal && g:netrw_fastbrowse == 1) -" call Decho("g:netrw_fastbrowse=".g:netrw_fastbrowse." a:islocal=".a:islocal.": clear buffer",'~'.expand("<slnum>")) - NetrwKeepj call s:NetrwListSettings(a:islocal) - 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<cleared buffer> : re-using buffer#".bufnr("%").", but refreshing due to g:netrw_fastbrowse=".g:netrw_fastbrowse) - return 0 - - elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST -" call Decho("--re-use tree listing--",'~'.expand("<slnum>")) -" call Decho(" clear buffer<".expand("%")."> with :%d",'~'.expand("<slnum>")) - setl ma - sil NetrwKeepj %d _ - NetrwKeepj call s:NetrwListSettings(a:islocal) -" 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<cleared buffer> : re-using buffer#".bufnr("%").", but treelist mode always needs a refresh") - return 0 - - else -" 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 1<buffer not cleared>") - return 1 - endif - endif - - " do netrw settings: make this buffer not-a-file, modifiable, not line-numbered, etc {{{3 - " fastbrowse Local Remote Hiding a buffer implies it may be re-used (fast) - " slow 0 D D Deleting a buffer implies it will not be re-used (slow) - " med 1 D H - " fast 2 H H -" call Decho("--do netrw settings: make this buffer#".bufnr("%")." not-a-file, modifiable, not line-numbered, etc--",'~'.expand("<slnum>")) - let fname= expand("%") - NetrwKeepj call s:NetrwListSettings(a:islocal) - call s:NetrwBufRename(fname) - - " delete all lines from buffer {{{3 -" call Decho("--delete all lines from buffer--",'~'.expand("<slnum>")) -" call Decho(" clear buffer<".expand("%")."> with :%d",'~'.expand("<slnum>")) - sil! keepalt 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<cleared buffer>") - return 0 -endfun - -" --------------------------------------------------------------------- -" s:NetrwGetcwd: get the current directory. {{{2 -" Change backslashes to forward slashes, if any. -" If doesc is true, escape certain troublesome characters -fun! s:NetrwGetcwd(doesc) -" call Dfunc("NetrwGetcwd(doesc=".a:doesc.")") - let curdir= substitute(getcwd(),'\\','/','ge') - if curdir !~ '[\/]$' - let curdir= curdir.'/' - endif - if a:doesc - let curdir= fnameescape(curdir) - endif -" call Dret("NetrwGetcwd <".curdir.">") - return curdir -endfun - -" --------------------------------------------------------------------- -" s:NetrwGetWord: it gets the directory/file named under the cursor {{{2 -fun! s:NetrwGetWord() -" call Dfunc("s:NetrwGetWord() liststyle=".s:ShowStyle()." virtcol=".virtcol(".")) -" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) - let keepsol= &l:sol - setl nosol - - call s:UseBufWinVars() - - " insure that w:netrw_liststyle is set up - if !exists("w:netrw_liststyle") - if exists("g:netrw_liststyle") - let w:netrw_liststyle= g:netrw_liststyle - else - let w:netrw_liststyle= s:THINLIST - endif -" call Decho("w:netrw_liststyle=".w:netrw_liststyle,'~'.expand("<slnum>")) - endif - - if exists("w:netrw_bannercnt") && line(".") < w:netrw_bannercnt - " Active Banner support -" call Decho("active banner handling",'~'.expand("<slnum>")) - NetrwKeepj norm! 0 - let dirname= "./" - let curline= getline('.') - - if curline =~# '"\s*Sorted by\s' - NetrwKeepj norm! "_s - let s:netrw_skipbrowse= 1 - echo 'Pressing "s" also works' - - elseif curline =~# '"\s*Sort sequence:' - let s:netrw_skipbrowse= 1 - echo 'Press "S" to edit sorting sequence' - - elseif curline =~# '"\s*Quick Help:' - NetrwKeepj norm! ? - let s:netrw_skipbrowse= 1 - - elseif curline =~# '"\s*\%(Hiding\|Showing\):' - NetrwKeepj norm! a - let s:netrw_skipbrowse= 1 - echo 'Pressing "a" also works' - - elseif line("$") > w:netrw_bannercnt - exe 'sil NetrwKeepj '.w:netrw_bannercnt - endif - - elseif w:netrw_liststyle == s:THINLIST -" call Decho("thin column handling",'~'.expand("<slnum>")) - NetrwKeepj norm! 0 - let dirname= substitute(getline('.'),'\t -->.*$','','') - - elseif w:netrw_liststyle == s:LONGLIST -" call Decho("long column handling",'~'.expand("<slnum>")) - NetrwKeepj norm! 0 - let dirname= substitute(getline('.'),'^\(\%(\S\+ \)*\S\+\).\{-}$','\1','e') - - elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST -" call Decho("treelist handling",'~'.expand("<slnum>")) - let dirname= substitute(getline('.'),'^\('.s:treedepthstring.'\)*','','e') - let dirname= substitute(dirname,'\t -->.*$','','') - - else -" call Decho("obtain word from wide listing",'~'.expand("<slnum>")) - let dirname= getline('.') - - if !exists("b:netrw_cpf") - let b:netrw_cpf= 0 - exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g/^./if virtcol("$") > b:netrw_cpf|let b:netrw_cpf= virtcol("$")|endif' - call histdel("/",-1) -" "call Decho("computed cpf=".b:netrw_cpf,'~'.expand("<slnum>")) - endif - -" call Decho("buf#".bufnr("%")."<".bufname("%").">",'~'.expand("<slnum>")) - let filestart = (virtcol(".")/b:netrw_cpf)*b:netrw_cpf -" call Decho("filestart= ([virtcol=".virtcol(".")."]/[b:netrw_cpf=".b:netrw_cpf."])*b:netrw_cpf=".filestart." bannercnt=".w:netrw_bannercnt,'~'.expand("<slnum>")) -" call Decho("1: dirname<".dirname.">",'~'.expand("<slnum>")) - if filestart == 0 - NetrwKeepj norm! 0ma - else - call cursor(line("."),filestart+1) - NetrwKeepj norm! ma - endif - - let dict={} - " save the unnamed register and register 0-9 and a - let dict.a=[getreg('a'), getregtype('a')] - for i in range(0, 9) - let dict[i] = [getreg(i), getregtype(i)] - endfor - let dict.unnamed = [getreg(''), getregtype('')] - - let eofname= filestart + b:netrw_cpf + 1 - if eofname <= col("$") - call cursor(line("."),filestart+b:netrw_cpf+1) - NetrwKeepj norm! "ay`a - else - NetrwKeepj norm! "ay$ - endif - - let dirname = @a - call s:RestoreRegister(dict) - -" call Decho("2: dirname<".dirname.">",'~'.expand("<slnum>")) - let dirname= substitute(dirname,'\s\+$','','e') -" call Decho("3: dirname<".dirname.">",'~'.expand("<slnum>")) - endif - - " symlinks are indicated by a trailing "@". Remove it before further processing. - let dirname= substitute(dirname,"@$","","") - - " executables are indicated by a trailing "*". Remove it before further processing. - let dirname= substitute(dirname,"\*$","","") - - let &l:sol= keepsol - -" call Dret("s:NetrwGetWord <".dirname.">") - return dirname -endfun - -" --------------------------------------------------------------------- -" s:NetrwListSettings: make standard settings for making a netrw listing {{{2 -" g:netrw_bufsettings will be used after the listing is produced. -" Called by s:NetrwGetBuffer() -fun! s:NetrwListSettings(islocal) -" call Dfunc("s:NetrwListSettings(islocal=".a:islocal.")") -" 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>")) - let fname= bufname("%") -" " call Decho("setl bt=nofile nobl ma nonu nowrap noro nornu",'~'.expand("<slnum>")) - " nobl noma nomod nonu noma nowrap ro nornu (std g:netrw_bufsettings) - setl bt=nofile nobl ma nonu nowrap noro nornu - call s:NetrwBufRename(fname) - if g:netrw_use_noswf - setl noswf - endif -" call Dredir("ls!","s:NetrwListSettings") -" call Decho("exe setl ts=".(g:netrw_maxfilenamelen+1),'~'.expand("<slnum>")) - exe "setl ts=".(g:netrw_maxfilenamelen+1) - setl isk+=.,~,- - if g:netrw_fastbrowse > a:islocal - setl bh=hide - else - setl bh=delete - endif -" 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 Dret("s:NetrwListSettings") -endfun - -" --------------------------------------------------------------------- -" s:NetrwListStyle: change list style (thin - long - wide - tree) {{{2 -" islocal=0: remote browsing -" =1: local browsing -fun! s:NetrwListStyle(islocal) - let ykeep = @@ - let fname = s:NetrwGetWord() - if !exists("w:netrw_liststyle")|let w:netrw_liststyle= g:netrw_liststyle|endif - let svpos = winsaveview() - let w:netrw_liststyle = (w:netrw_liststyle + 1) % s:MAXLIST - - " repoint t:netrw_lexbufnr if appropriate - if exists("t:netrw_lexbufnr") && bufnr("%") == t:netrw_lexbufnr - let repointlexbufnr= 1 - endif - - if w:netrw_liststyle == s:THINLIST - " use one column listing - let g:netrw_list_cmd = substitute(g:netrw_list_cmd,' -l','','ge') - - elseif w:netrw_liststyle == s:LONGLIST - " use long list - let g:netrw_list_cmd = g:netrw_list_cmd." -l" - - elseif w:netrw_liststyle == s:WIDELIST - " give wide list - let g:netrw_list_cmd = substitute(g:netrw_list_cmd,' -l','','ge') - - elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST - let g:netrw_list_cmd = substitute(g:netrw_list_cmd,' -l','','ge') - - else - NetrwKeepj call netrw#ErrorMsg(s:WARNING,"bad value for g:netrw_liststyle (=".w:netrw_liststyle.")",46) - let g:netrw_liststyle = s:THINLIST - let w:netrw_liststyle = g:netrw_liststyle - let g:netrw_list_cmd = substitute(g:netrw_list_cmd,' -l','','ge') - endif - setl ma noro - - " clear buffer - this will cause NetrwBrowse/LocalBrowseCheck to do a refresh - sil! NetrwKeepj %d _ - " following prevents tree listing buffer from being marked "modified" - setl nomod - - " refresh the listing - NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) - NetrwKeepj call s:NetrwCursor(0) - - " repoint t:netrw_lexbufnr if appropriate - if exists("repointlexbufnr") - let t:netrw_lexbufnr= bufnr("%") - endif - - " restore position; keep cursor on the filename -" call Decho("restoring posn to svpos<".string(svpos).">",'~'.expand("<slnum>")) - NetrwKeepj call winrestview(svpos) - let @@= ykeep - -endfun - -" --------------------------------------------------------------------- -" s:NetrwBannerCtrl: toggles the display of the banner {{{2 -fun! s:NetrwBannerCtrl(islocal) - let ykeep= @@ - " toggle the banner (enable/suppress) - let g:netrw_banner= !g:netrw_banner - - " refresh the listing - let svpos= winsaveview() - call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) - - " keep cursor on the filename - if g:netrw_banner && exists("w:netrw_bannercnt") && line(".") >= w:netrw_bannercnt - let fname= s:NetrwGetWord() - sil NetrwKeepj $ - let result= search('\%(^\%(|\+\s\)\=\|\s\{2,}\)\zs'.escape(fname,'.\[]*$^').'\%(\s\{2,}\|$\)','bc') -" " call Decho("search result=".result." w:netrw_bannercnt=".(exists("w:netrw_bannercnt")? w:netrw_bannercnt : 'N/A'),'~'.expand("<slnum>")) - if result <= 0 && exists("w:netrw_bannercnt") - exe "NetrwKeepj ".w:netrw_bannercnt - endif - endif - let @@= ykeep -" call Dret("s:NetrwBannerCtrl : g:netrw_banner=".g:netrw_banner) -endfun - -" --------------------------------------------------------------------- -" s:NetrwBookmark: supports :NetrwMB[!] [file]s {{{2 -" -" No bang: enters files/directories into Netrw's bookmark system -" No argument and in netrw buffer: -" if there are marked files: bookmark marked files -" otherwise : bookmark file/directory under cursor -" No argument and not in netrw buffer: bookmarks current open file -" Has arguments: globs them individually and bookmarks them -" -" With bang: deletes files/directories from Netrw's bookmark system -fun! s:NetrwBookmark(del,...) - if a:0 == 0 - if &ft == "netrw" - let curbufnr = bufnr("%") - - if exists("s:netrwmarkfilelist_{curbufnr}") - " for every filename in the marked list - let svpos = winsaveview() - let islocal= expand("%") !~ '^\a\{3,}://' - for fname in s:netrwmarkfilelist_{curbufnr} - if a:del|call s:DeleteBookmark(fname)|else|call s:MakeBookmark(fname)|endif - endfor - let curdir = exists("b:netrw_curdir")? b:netrw_curdir : getcwd() - call s:NetrwUnmarkList(curbufnr,curdir) - NetrwKeepj call s:NetrwRefresh(islocal,s:NetrwBrowseChgDir(islocal,'./',0)) - NetrwKeepj call winrestview(svpos) - else - let fname= s:NetrwGetWord() - if a:del|call s:DeleteBookmark(fname)|else|call s:MakeBookmark(fname)|endif - endif - - else - " bookmark currently open file - let fname= expand("%") - if a:del|call s:DeleteBookmark(fname)|else|call s:MakeBookmark(fname)|endif - endif - - else - " bookmark specified files - " attempts to infer if working remote or local - " by deciding if the current file begins with an url - " Globbing cannot be done remotely. - let islocal= expand("%") !~ '^\a\{3,}://' - let i = 1 - while i <= a:0 - if islocal - if v:version > 704 || (v:version == 704 && has("patch656")) - let mbfiles= glob(fnameescape(a:{i}),0,1,1) - else - let mbfiles= glob(fnameescape(a:{i}),0,1) - endif - else - let mbfiles= [a:{i}] - endif - for mbfile in mbfiles - if a:del|call s:DeleteBookmark(mbfile)|else|call s:MakeBookmark(mbfile)|endif - endfor - let i= i + 1 - endwhile - endif - - " update the menu - call s:NetrwBookmarkMenu() -endfun - -" --------------------------------------------------------------------- -" s:NetrwBookmarkMenu: Uses menu priorities {{{2 -" .2.[cnt] for bookmarks, and -" .3.[cnt] for history -" (see s:NetrwMenu()) -fun! s:NetrwBookmarkMenu() - if !exists("s:netrw_menucnt") - return - endif -" call Dfunc("NetrwBookmarkMenu() histcnt=".g:netrw_dirhistcnt." menucnt=".s:netrw_menucnt) - - " the following test assures that gvim is running, has menus available, and has menus enabled. - if has("gui") && has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu - if exists("g:NetrwTopLvlMenu") -" call Decho("removing ".g:NetrwTopLvlMenu."Bookmarks menu item(s)",'~'.expand("<slnum>")) - exe 'sil! unmenu '.g:NetrwTopLvlMenu.'Bookmarks' - exe 'sil! unmenu '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History.Bookmark\ Delete' - endif - if !exists("s:netrw_initbookhist") - call s:NetrwBookHistRead() - endif - - " show bookmarked places - if exists("g:netrw_bookmarklist") && g:netrw_bookmarklist != [] && g:netrw_dirhistmax > 0 - let cnt= 1 - for bmd in g:netrw_bookmarklist -" call Decho('sil! menu '.g:NetrwMenuPriority.".2.".cnt." ".g:NetrwTopLvlMenu.'Bookmark.'.bmd.' :e '.bmd,'~'.expand("<slnum>")) - let bmd= escape(bmd,g:netrw_menu_escape) - - " show bookmarks for goto menu - exe 'sil! menu '.g:NetrwMenuPriority.".2.".cnt." ".g:NetrwTopLvlMenu.'Bookmarks.'.bmd.' :e '.bmd."\<cr>" - - " show bookmarks for deletion menu - exe 'sil! menu '.g:NetrwMenuPriority.".8.2.".cnt." ".g:NetrwTopLvlMenu.'Bookmarks\ and\ History.Bookmark\ Delete.'.bmd.' '.cnt."mB" - let cnt= cnt + 1 - endfor - - endif - - " show directory browsing history - if g:netrw_dirhistmax > 0 - let cnt = g:netrw_dirhistcnt - let first = 1 - let histcnt = 0 - while ( first || cnt != g:netrw_dirhistcnt ) - let histcnt = histcnt + 1 - let priority = g:netrw_dirhistcnt + histcnt - if exists("g:netrw_dirhist_{cnt}") - let histdir= escape(g:netrw_dirhist_{cnt},g:netrw_menu_escape) -" call Decho('sil! menu '.g:NetrwMenuPriority.".3.".priority." ".g:NetrwTopLvlMenu.'History.'.histdir.' :e '.histdir,'~'.expand("<slnum>")) - exe 'sil! menu '.g:NetrwMenuPriority.".3.".priority." ".g:NetrwTopLvlMenu.'History.'.histdir.' :e '.histdir."\<cr>" - endif - let first = 0 - let cnt = ( cnt - 1 ) % g:netrw_dirhistmax - if cnt < 0 - let cnt= cnt + g:netrw_dirhistmax - endif - endwhile - endif - - endif -" call Dret("NetrwBookmarkMenu") -endfun - -" --------------------------------------------------------------------- -" s:NetrwBrowseChgDir: constructs a new directory based on the current {{{2 -" directory and a new directory name. Also, if the -" "new directory name" is actually a file, -" NetrwBrowseChgDir() edits the file. -" cursor=0: newdir is relative to b:netrw_curdir -" =1: newdir is relative to the path to the word under the cursor in -" tree view -fun! s:NetrwBrowseChgDir(islocal,newdir,cursor,...) - let ykeep= @@ - if !exists("b:netrw_curdir") - " Don't try to change-directory: this can happen, for example, when netrw#ErrorMsg has been called - " and the current window is the NetrwMessage window. - let @@= ykeep - return - endif - - " NetrwBrowseChgDir; save options and initialize {{{3 - call s:SavePosn(s:netrw_posn) - NetrwKeepj call s:NetrwOptionsSave("s:") - NetrwKeepj call s:NetrwOptionsSafe(a:islocal) - - let newdir = a:newdir - if a:cursor && exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treetop") - " dirname is the path to the word under the cursor - let dirname = s:NetrwTreePath(w:netrw_treetop) - " newdir resolves to a directory and points to a directory in dirname - " /tmp/test/folder_symlink/ -> /tmp/test/original_folder/ - if a:islocal && fnamemodify(dirname, ':t') == newdir && isdirectory(resolve(dirname)) && resolve(dirname) == resolve(newdir) - let dirname = fnamemodify(resolve(dirname), ':p:h:h') - let newdir = fnamemodify(resolve(newdir), ':t') - endif - " Remove trailing "/" - let dirname = substitute(dirname, "/$", "", "") - - " If the word under the cursor is a directory (except for ../), NetrwTreePath - " returns the full path, including the word under the cursor, remove it - if newdir =~ "/$" && newdir != "../" - let dirname = fnamemodify(dirname, ":h") - endif - else - let dirname = b:netrw_curdir - endif - if has("win32") - let dirname = substitute(dirname,'\\','/','ge') - endif - let dolockout = 0 - let dorestore = 1 - - " ignore <cr>s when done in the banner - if g:netrw_banner - if exists("w:netrw_bannercnt") && line(".") < w:netrw_bannercnt && line("$") >= w:netrw_bannercnt - if getline(".") =~# 'Quick Help' - let g:netrw_quickhelp= (g:netrw_quickhelp + 1)%len(s:QuickHelp) - setl ma noro nowrap - NetrwKeepj call setline(line('.'),'" Quick Help: <F1>:help '.s:QuickHelp[g:netrw_quickhelp]) - setl noma nomod nowrap - NetrwKeepj call s:NetrwOptionsRestore("s:") - endif - endif - endif - - " set up o/s-dependent directory recognition pattern - if has("amiga") - let dirpat= '[\/:]$' - else - let dirpat= '[\/]$' - endif - - if dirname !~ dirpat - " apparently vim is "recognizing" that it is in a directory and - " is removing the trailing "/". Bad idea, so let's put it back. - let dirname= dirname.'/' - endif - - if newdir !~ dirpat && !(a:islocal && isdirectory(s:NetrwFile(s:ComposePath(dirname,newdir)))) - " ------------------------------ - " NetrwBrowseChgDir: edit a file {{{3 - " ------------------------------ - - " save position for benefit of Rexplore - let s:rexposn_{bufnr("%")}= winsaveview() - - if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict") && newdir !~ '^\(/\|\a:\)' - if dirname =~ '/$' - let dirname= dirname.newdir - else - let dirname= dirname."/".newdir - endif - elseif newdir =~ '^\(/\|\a:\)' - let dirname= newdir - else - let dirname= s:ComposePath(dirname,newdir) - endif - " this lets netrw#BrowseX avoid the edit - if a:0 < 1 - NetrwKeepj call s:NetrwOptionsRestore("s:") - let curdir= b:netrw_curdir - if !exists("s:didsplit") - if type(g:netrw_browse_split) == 3 - " open file in server - " Note that g:netrw_browse_split is a List: [servername,tabnr,winnr] - call s:NetrwServerEdit(a:islocal,dirname) - return - - elseif g:netrw_browse_split == 1 - " horizontally splitting the window first - let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winheight(0))/100 : -g:netrw_winsize - exe "keepalt ".(g:netrw_alto? "bel " : "abo ").winsz."wincmd s" - if !&ea - keepalt wincmd _ - endif - call s:SetRexDir(a:islocal,curdir) - - elseif g:netrw_browse_split == 2 - " vertically splitting the window first - let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize - exe "keepalt ".(g:netrw_alto? "top " : "bot ")."vert ".winsz."wincmd s" - if !&ea - keepalt wincmd | - endif - call s:SetRexDir(a:islocal,curdir) - - elseif g:netrw_browse_split == 3 - " open file in new tab - keepalt tabnew - 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) - if s:NetrwPrevWinOpen(2) == 3 - let @@= ykeep - return - endif - call s:SetRexDir(a:islocal,curdir) - - else - " handling a file, didn't split, so remove menu - call s:NetrwMenu(0) - " optional change to window - if g:netrw_chgwin >= 1 - if winnr("$")+1 == g:netrw_chgwin - " if g:netrw_chgwin is set to one more than the last window, then - " vertically split the last window to make that window available. - let curwin= winnr() - exe "NetrwKeepj keepalt ".winnr("$")."wincmd w" - vs - exe "NetrwKeepj keepalt ".g:netrw_chgwin."wincmd ".curwin - endif - exe "NetrwKeepj keepalt ".g:netrw_chgwin."wincmd w" - endif - call s:SetRexDir(a:islocal,curdir) - endif - - endif - - " the point where netrw actually edits the (local) file - " if its local only: LocalBrowseCheck() doesn't edit a file, but NetrwBrowse() will - " use keepalt to support :e # to return to a directory listing - if !&mod - " if e the new file would fail due to &mod, then don't change any of the flags - let dolockout= 1 - endif - if a:islocal - " some like c-^ to return to the last edited file - " others like c-^ to return to the netrw buffer - " Apr 30, 2020: used to have e! here. That can cause loss of a modified file, - " so emit error E37 instead. - call s:NetrwEditFile("e","",dirname) - call s:NetrwCursor(1) - if &hidden || &bufhidden == "hide" - " file came from vim's hidden storage. Don't "restore" options with it. - let dorestore= 0 - endif - else - endif - - " handle g:Netrw_funcref -- call external-to-netrw functions - " This code will handle g:Netrw_funcref as an individual function reference - " or as a list of function references. It will ignore anything that's not - " a function reference. See :help Funcref for information about function references. - if exists("g:Netrw_funcref") - if type(g:Netrw_funcref) == 2 - NetrwKeepj call g:Netrw_funcref() - elseif type(g:Netrw_funcref) == 3 - for Fncref in g:Netrw_funcref - if type(Fncref) == 2 - NetrwKeepj call Fncref() - endif - endfor - endif - endif - endif - - elseif newdir =~ '^/' - " ---------------------------------------------------- - " NetrwBrowseChgDir: just go to the new directory spec {{{3 - " ---------------------------------------------------- - let dirname = newdir - NetrwKeepj call s:SetRexDir(a:islocal,dirname) - NetrwKeepj call s:NetrwOptionsRestore("s:") - norm! m` - - elseif newdir == './' - " --------------------------------------------- - " NetrwBrowseChgDir: refresh the directory list {{{3 - " --------------------------------------------- - NetrwKeepj call s:SetRexDir(a:islocal,dirname) - norm! m` - - elseif newdir == '../' - " -------------------------------------- - " NetrwBrowseChgDir: go up one directory {{{3 - " -------------------------------------- - - if w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict") - " force a refresh - setl noro ma - NetrwKeepj %d _ - endif - - if has("amiga") - " amiga - if a:islocal - let dirname= substitute(dirname,'^\(.*[/:]\)\([^/]\+$\)','\1','') - let dirname= substitute(dirname,'/$','','') - else - let dirname= substitute(dirname,'^\(.*[/:]\)\([^/]\+/$\)','\1','') - endif - - elseif !g:netrw_cygwin && has("win32") - " windows - if a:islocal - let dirname= substitute(dirname,'^\(.*\)/\([^/]\+\)/$','\1','') - if dirname == "" - let dirname= '/' - endif - else - let dirname= substitute(dirname,'^\(\a\{3,}://.\{-}/\{1,2}\)\(.\{-}\)\([^/]\+\)/$','\1\2','') - endif - if dirname =~ '^\a:$' - let dirname= dirname.'/' - endif - - else - " unix or cygwin - if a:islocal - let dirname= substitute(dirname,'^\(.*\)/\([^/]\+\)/$','\1','') - if dirname == "" - let dirname= '/' - endif - else - let dirname= substitute(dirname,'^\(\a\{3,}://.\{-}/\{1,2}\)\(.\{-}\)\([^/]\+\)/$','\1\2','') - endif - endif - NetrwKeepj call s:SetRexDir(a:islocal,dirname) - norm! m` - - elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict") - " -------------------------------------- - " NetrwBrowseChgDir: Handle Tree Listing {{{3 - " -------------------------------------- - " force a refresh (for TREELIST, NetrwTreeDir() will force the refresh) - setl noro ma - if !(exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("b:netrw_curdir")) - NetrwKeepj %d _ - endif - let treedir = s:NetrwTreeDir(a:islocal) - let s:treecurpos = winsaveview() - let haskey = 0 - - " search treedict for tree dir as-is - if has_key(w:netrw_treedict,treedir) - let haskey= 1 - else - endif - - " search treedict for treedir with a [/@] appended - if !haskey && treedir !~ '[/@]$' - if has_key(w:netrw_treedict,treedir."/") - let treedir= treedir."/" - let haskey = 1 - else - endif - endif - - " search treedict for treedir with any trailing / elided - if !haskey && treedir =~ '/$' - let treedir= substitute(treedir,'/$','','') - if has_key(w:netrw_treedict,treedir) - let haskey = 1 - else - endif - endif - - if haskey - " close tree listing for selected subdirectory - call remove(w:netrw_treedict,treedir) - let dirname= w:netrw_treetop - else - " go down one directory - let dirname= substitute(treedir,'/*$','/','') - endif - NetrwKeepj call s:SetRexDir(a:islocal,dirname) - let s:treeforceredraw = 1 - - else - " ---------------------------------------- - " NetrwBrowseChgDir: Go down one directory {{{3 - " ---------------------------------------- - let dirname = s:ComposePath(dirname,newdir) - NetrwKeepj call s:SetRexDir(a:islocal,dirname) - norm! m` - endif - - " -------------------------------------- - " NetrwBrowseChgDir: Restore and Cleanup {{{3 - " -------------------------------------- - if dorestore - " dorestore is zero'd when a local file was hidden or bufhidden; - " in such a case, we want to keep whatever settings it may have. - NetrwKeepj call s:NetrwOptionsRestore("s:") - endif - if dolockout && dorestore - if filewritable(dirname) - setl ma noro nomod - else - setl ma ro nomod - endif - endif - call s:RestorePosn(s:netrw_posn) - let @@= ykeep - - return dirname -endfun - -" --------------------------------------------------------------------- -" s:NetrwBrowseUpDir: implements the "-" mappings {{{2 -" for thin, long, and wide: cursor placed just after banner -" for tree, keeps cursor on current filename -fun! s:NetrwBrowseUpDir(islocal) - if exists("w:netrw_bannercnt") && line(".") < w:netrw_bannercnt-1 - " this test needed because occasionally this function seems to be incorrectly called - " when multiple leftmouse clicks are taken when atop the one line help in the banner. - " I'm allowing the very bottom line to permit a "-" exit so that one may escape empty - " directories. - return - endif - - norm! 0 - if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict") - let curline= getline(".") - let swwline= winline() - 1 - if exists("w:netrw_treetop") - let b:netrw_curdir= w:netrw_treetop - elseif exists("b:netrw_curdir") - let w:netrw_treetop= b:netrw_curdir - else - let w:netrw_treetop= getcwd() - let b:netrw_curdir = w:netrw_treetop - endif - let curfile = getline(".") - let curpath = s:NetrwTreePath(w:netrw_treetop) - if a:islocal - call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,'../',0)) - else - call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,'../',0)) - endif - if w:netrw_treetop == '/' - keepj call search('^\M'.curfile,"w") - elseif curfile == '../' - keepj call search('^\M'.curfile,"wb") - else - while 1 - keepj call search('^\M'.s:treedepthstring.curfile,"wb") - let treepath= s:NetrwTreePath(w:netrw_treetop) - if treepath == curpath - break - endif - endwhile - endif - - else - call s:SavePosn(s:netrw_posn) - if exists("b:netrw_curdir") - let curdir= b:netrw_curdir - else - let curdir= expand(getcwd()) - endif - if a:islocal - call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,'../',0)) - else - call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,'../',0)) - endif - call s:RestorePosn(s:netrw_posn) - let curdir= substitute(curdir,'^.*[\/]','','') - let curdir= '\<'. escape(curdir, '~'). '/' - call search(curdir,'wc') - endif -endfun - -func s:redir() - " set up redirection (avoids browser messages) - " by default if not set, g:netrw_suppress_gx_mesg is true - if get(g:, 'netrw_suppress_gx_mesg', 1) - if &srr =~# "%s" - return printf(&srr, has("win32") ? "nul" : "/dev/null") - else - return &srr .. (has("win32") ? "nul" : "/dev/null") - endif - endif - return '' -endfunc - -if has('unix') - if has('win32unix') - " Cygwin provides cygstart - if executable('cygstart') - fun! netrw#Launch(args) - exe 'silent ! cygstart --hide' a:args s:redir() | redraw! - endfun - elseif !empty($MSYSTEM) && executable('start') - " MSYS2/Git Bash comes by default without cygstart; see - " https://www.msys2.org/wiki/How-does-MSYS2-differ-from-Cygwin - " Instead it provides /usr/bin/start script running `cmd.exe //c start` - " Adding "" //b` sets void title, hides cmd window and blocks path conversion - " of /b to \b\ " by MSYS2; see https://www.msys2.org/docs/filesystem-paths/ - fun! netrw#Launch(args) - exe 'silent !start "" //b' a:args s:redir() | redraw! - endfun - else - " imitate /usr/bin/start script for other environments and hope for the best - fun! netrw#Launch(args) - exe 'silent !cmd //c start "" //b' a:args s:redir() | redraw! - endfun - endif - elseif exists('$WSL_DISTRO_NAME') " use cmd.exe to start GUI apps in WSL - fun! netrw#Launch(args) - let args = a:args - exe 'silent !' .. - \ ((args =~? '\v<\f+\.(exe|com|bat|cmd)>') ? - \ 'cmd.exe /c start /b ' .. args : - \ 'nohup ' .. args .. ' ' .. s:redir() .. ' &') - \ | redraw! - endfun - else - fun! netrw#Launch(args) - exe ':silent ! nohup' a:args s:redir() '&' | redraw! - endfun - endif -elseif has('win32') - fun! netrw#Launch(args) - exe 'silent !' .. (&shell =~? '\<cmd\.exe\>' ? '' : 'cmd.exe /c') - \ 'start "" /b' a:args s:redir() | redraw! - endfun -else - fun! netrw#Launch(dummy) - echom 'No common launcher found' - endfun -endif - -" Git Bash -if has('win32unix') - " (cyg)start suffices - let s:os_viewer = '' -" Windows / WSL -elseif executable('explorer.exe') - let s:os_viewer = 'explorer.exe' -" Linux / BSD -elseif executable('xdg-open') - let s:os_viewer = 'xdg-open' -" MacOS -elseif executable('open') - let s:os_viewer = 'open' -endif - -fun! s:viewer() - if exists('g:netrw_browsex_viewer') && executable(g:netrw_browsex_viewer) - " extract any viewing options. Assumes that they're set apart by spaces. - " call Decho("extract any viewing options from g:netrw_browsex_viewer<".g:netrw_browsex_viewer.">",'~'.expand("<slnum>")) - if g:netrw_browsex_viewer =~ '\s' - let viewer = substitute(g:netrw_browsex_viewer,'\s.*$','','') - let viewopt = substitute(g:netrw_browsex_viewer,'^\S\+\s*','','')." " - let oviewer = '' - let cnt = 1 - while !executable(viewer) && viewer != oviewer - let viewer = substitute(g:netrw_browsex_viewer,'^\(\(^\S\+\s\+\)\{'.cnt.'}\S\+\)\(.*\)$','\1','') - let viewopt = substitute(g:netrw_browsex_viewer,'^\(\(^\S\+\s\+\)\{'.cnt.'}\S\+\)\(.*\)$','\3','')." " - let cnt = cnt + 1 - let oviewer = viewer - " call Decho("!exe: viewer<".viewer."> viewopt<".viewopt.">",'~'.expand("<slnum>")) - endwhile - else - let viewer = g:netrw_browsex_viewer - let viewopt = "" - endif - " call Decho("viewer<".viewer."> viewopt<".viewopt.">",'~'.expand("<slnum>")) - return viewer .. ' ' .. viewopt - else - if !exists('s:os_viewer') - call netrw#ErrorMsg(s:ERROR,"No program to open this path found. See :help Open for more information.",106) - else - return s:os_viewer - endif - endif -endfun - -fun! netrw#Open(file) abort - call netrw#Launch(s:viewer() .. ' ' .. shellescape(a:file, 1)) -endf - -if !exists('g:netrw_regex_url') - let g:netrw_regex_url = '\%(\%(http\|ftp\|irc\)s\?\|file\)://\S\{-}' -endif - -" --------------------------------------------------------------------- -" netrw#BrowseX: (implements "x" and "gx") executes a special "viewer" script or program for the {{{2 -" given filename; typically this means given their extension. -" 0=local, 1=remote -fun! netrw#BrowseX(fname,remote) - if a:remote == 1 && a:fname !~ '^https\=:' && a:fname =~ '/$' - " remote directory, not a webpage access, looks like an attempt to do a directory listing - norm! gf - endif - - if exists("g:netrw_browsex_viewer") && exists("g:netrw_browsex_support_remote") && !g:netrw_browsex_support_remote - let remote = a:remote - else - let remote = 0 - endif - - let ykeep = @@ - let screenposn = winsaveview() - - " need to save and restore aw setting as gx can invoke this function from non-netrw buffers - let awkeep = &aw - set noaw - - " special core dump handler - if a:fname =~ '/core\(\.\d\+\)\=$' - if exists("g:Netrw_corehandler") - if type(g:Netrw_corehandler) == 2 - " g:Netrw_corehandler is a function reference (see :help Funcref) - call g:Netrw_corehandler(s:NetrwFile(a:fname)) - elseif type(g:Netrw_corehandler) == 3 - " g:Netrw_corehandler is a List of function references (see :help Funcref) - for Fncref in g:Netrw_corehandler - if type(Fncref) == 2 - call Fncref(a:fname) - endif - endfor - endif - call winrestview(screenposn) - let @@= ykeep - let &aw= awkeep - return - endif - endif - - " set up the filename - " (lower case the extension, make a local copy of a remote file) - let exten= substitute(a:fname,'.*\.\(.\{-}\)','\1','e') - if has("win32") - let exten= substitute(exten,'^.*$','\L&\E','') - endif - if exten =~ "[\\/]" - let exten= "" - endif - - if remote == 1 - " create a local copy - setl bh=delete - call netrw#NetRead(3,a:fname) - " attempt to rename tempfile - let basename= substitute(a:fname,'^\(.*\)/\(.*\)\.\([^.]*\)$','\2','') - let newname = substitute(s:netrw_tmpfile,'^\(.*\)/\(.*\)\.\([^.]*\)$','\1/'.basename.'.\3','') - if s:netrw_tmpfile != newname && newname != "" - if rename(s:netrw_tmpfile,newname) == 0 - " renaming succeeded - let fname= newname - else - " renaming failed - let fname= s:netrw_tmpfile - endif - else - let fname= s:netrw_tmpfile - endif - else - let fname= a:fname - " special ~ handler for local - if fname =~ '^\~' && expand("$HOME") != "" - let fname= s:NetrwFile(substitute(fname,'^\~',expand("$HOME"),'')) - endif - endif - - " although shellescape(..., 1) is used in netrw#Open(), it's insufficient - call netrw#Open(escape(fname, '#%')) - - " cleanup: remove temporary file, - " delete current buffer if success with handler, - " return to prior buffer (directory listing) - " Feb 12, 2008: had to de-activate removal of - " temporary file because it wasn't getting seen. -" if remote == 1 && fname != a:fname -" call s:NetrwDelete(fname) -" endif - - if remote == 1 - setl bh=delete bt=nofile - if g:netrw_use_noswf - setl noswf - endif - exe "sil! NetrwKeepj norm! \<c-o>" - endif - call winrestview(screenposn) - let @@ = ykeep - let &aw= awkeep -endfun - -" --------------------------------------------------------------------- -" netrw#GX: gets word under cursor for gx support {{{2 -" See also: netrw#BrowseXVis -" netrw#BrowseX -fun! netrw#GX() -" call Dfunc("netrw#GX()") - if &ft == "netrw" - let fname= s:NetrwGetWord() - else - let fname= exists("g:netrw_gx")? expand(g:netrw_gx) : s:GetURL() - endif -" call Dret("netrw#GX <".fname.">") - return fname -endfun - -fun! s:GetURL() abort - let URL = '' - if exists('*Netrw_get_URL_' .. &filetype) - let URL = call('Netrw_get_URL_' .. &filetype, []) - endif - if !empty(URL) | return URL | endif - " URLs end in letter, digit or forward slash - let URL = matchstr(expand("<cWORD>"), '\<' .. g:netrw_regex_url .. '\ze[^A-Za-z0-9/]*$') - if !empty(URL) | return URL | endif - - " Is it a file in the current work dir ... - let file = expand("<cfile>") - if filereadable(file) | return file | endif - " ... or in that of the current buffer? - let path = fnamemodify(expand('%'), ':p') - if isdirectory(path) - let dir = path - elseif filereadable(path) - let dir = fnamemodify(path, ':h') - endif - if exists('dir') && filereadable(dir..'/'..file) | return dir..'/'..file | endif - - return '' -endf - -" --------------------------------------------------------------------- -" netrw#BrowseXVis: used by gx in visual mode to select a file for browsing {{{2 -fun! netrw#BrowseXVis() - let dict={} - let dict.a=[getreg('a'), getregtype('a')] - norm! gv"ay - let gxfile= @a - call s:RestoreRegister(dict) - call netrw#BrowseX(gxfile,netrw#CheckIfRemote(gxfile)) -endfun - -" --------------------------------------------------------------------- -" s:NetrwBufRename: renames a buffer without the side effect of retaining an unlisted buffer having the old name {{{2 -" Using the file command on a "[No Name]" buffer does not seem to cause the old "[No Name]" buffer -" to become an unlisted buffer, so in that case don't bwipe it. -fun! s:NetrwBufRename(newname) -" call Dfunc("s:NetrwBufRename(newname<".a:newname.">) buf(%)#".bufnr("%")."<".bufname(bufnr("%")).">") -" call Dredir("ls!","s:NetrwBufRename (before rename)") - let oldbufname= bufname(bufnr("%")) -" call Decho("buf#".bufnr("%").": oldbufname<".oldbufname.">",'~'.expand("<slnum>")) - - if oldbufname != a:newname -" call Decho("do buffer rename: oldbufname<".oldbufname."> ≠ a:newname<".a:newname.">",'~'.expand("<slnum>")) - let b:junk= 1 -" call Decho("rename buffer: sil! keepj keepalt file ".fnameescape(a:newname),'~'.expand("<slnum>")) - exe 'sil! keepj keepalt file '.fnameescape(a:newname) -" call Dredir("ls!","s:NetrwBufRename (before bwipe)~".expand("<slnum>")) - let oldbufnr= bufnr(oldbufname) -" call Decho("oldbufname<".oldbufname."> oldbufnr#".oldbufnr,'~'.expand("<slnum>")) -" call Decho("bufnr(%)=".bufnr("%"),'~'.expand("<slnum>")) - if oldbufname != "" && oldbufnr != -1 && oldbufnr != bufnr("%") -" call Decho("bwipe ".oldbufnr,'~'.expand("<slnum>")) - exe "bwipe! ".oldbufnr -" else " Decho -" call Decho("did *not* bwipe buf#".oldbufnr,'~'.expand("<slnum>")) -" call Decho("..reason: if oldbufname<".oldbufname."> is empty",'~'.expand("<slnum>"))" -" call Decho("..reason: if oldbufnr#".oldbufnr." is -1",'~'.expand("<slnum>"))" -" call Decho("..reason: if oldbufnr#".oldbufnr." != bufnr(%)#".bufnr("%"),'~'.expand("<slnum>"))" - endif -" call Dredir("ls!","s:NetrwBufRename (after rename)") -" else " Decho -" call Decho("oldbufname<".oldbufname."> == a:newname: did *not* rename",'~'.expand("<slnum>")) - endif - -" call Dret("s:NetrwBufRename : buf#".bufnr("%").": oldname<".oldbufname."> newname<".a:newname."> expand(%)<".expand("%").">") -endfun - -" --------------------------------------------------------------------- -" netrw#CheckIfRemote: returns 1 if current file looks like an url, 0 else {{{2 -fun! netrw#CheckIfRemote(...) -" call Dfunc("netrw#CheckIfRemote() a:0=".a:0) - if a:0 > 0 - let curfile= a:1 - else - let curfile= expand("%") - endif - - " Ignore terminal buffers - if &buftype ==# 'terminal' - return 0 - endif -" call Decho("curfile<".curfile.">") - if curfile =~ '^\a\{3,}://' -" call Dret("netrw#CheckIfRemote 1") - return 1 - else -" call Dret("netrw#CheckIfRemote 0") - return 0 - endif -endfun - -" --------------------------------------------------------------------- -" s:NetrwChgPerm: (implements "gp") change file permission {{{2 -fun! s:NetrwChgPerm(islocal,curdir) - let ykeep = @@ - call inputsave() - let newperm= input("Enter new permission: ") - call inputrestore() - let chgperm= substitute(g:netrw_chgperm,'\<FILENAME\>',s:ShellEscape(expand("<cfile>")),'') - let chgperm= substitute(chgperm,'\<PERM\>',s:ShellEscape(newperm),'') - call system(chgperm) - if v:shell_error != 0 - NetrwKeepj call netrw#ErrorMsg(1,"changing permission on file<".expand("<cfile>")."> seems to have failed",75) - endif - if a:islocal - NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) - endif - let @@= ykeep -endfun - -" --------------------------------------------------------------------- -" s:CheckIfKde: checks if kdeinit is running {{{2 -" Returns 0: kdeinit not running -" 1: kdeinit is running -fun! s:CheckIfKde() -" call Dfunc("s:CheckIfKde()") - " seems kde systems often have gnome-open due to dependencies, even though - " gnome-open's subsidiary display tools are largely absent. Kde systems - " usually have "kdeinit" running, though... (tnx Mikolaj Machowski) - if !exists("s:haskdeinit") - if has("unix") && executable("ps") && !has("win32unix") - let s:haskdeinit= system("ps -e") =~ '\<kdeinit' - if v:shell_error - let s:haskdeinit = 0 - endif - else - let s:haskdeinit= 0 - endif -" call Decho("setting s:haskdeinit=".s:haskdeinit,'~'.expand("<slnum>")) - endif - -" call Dret("s:CheckIfKde ".s:haskdeinit) - return s:haskdeinit -endfun - -" --------------------------------------------------------------------- -" s:NetrwClearExplore: clear explore variables (if any) {{{2 -fun! s:NetrwClearExplore() -" call Dfunc("s:NetrwClearExplore()") - 2match none - if exists("s:explore_match") |unlet s:explore_match |endif - if exists("s:explore_indx") |unlet s:explore_indx |endif - if exists("s:netrw_explore_prvdir") |unlet s:netrw_explore_prvdir |endif - if exists("s:dirstarstar") |unlet s:dirstarstar |endif - if exists("s:explore_prvdir") |unlet s:explore_prvdir |endif - if exists("w:netrw_explore_indx") |unlet w:netrw_explore_indx |endif - if exists("w:netrw_explore_listlen")|unlet w:netrw_explore_listlen|endif - if exists("w:netrw_explore_list") |unlet w:netrw_explore_list |endif - if exists("w:netrw_explore_bufnr") |unlet w:netrw_explore_bufnr |endif -" redraw! -" call Dret("s:NetrwClearExplore") -endfun - -" --------------------------------------------------------------------- -" s:NetrwEditBuf: decides whether or not to use keepalt to edit a buffer {{{2 -fun! s:NetrwEditBuf(bufnum) -" call Dfunc("s:NetrwEditBuf(fname<".a:bufnum.">)") - if exists("g:netrw_altfile") && g:netrw_altfile && &ft == "netrw" -" call Decho("exe sil! NetrwKeepj keepalt noswapfile b ".fnameescape(a:bufnum)) - exe "sil! NetrwKeepj keepalt noswapfile b ".fnameescape(a:bufnum) - else -" call Decho("exe sil! NetrwKeepj noswapfile b ".fnameescape(a:bufnum)) - exe "sil! NetrwKeepj noswapfile b ".fnameescape(a:bufnum) - endif -" call Dret("s:NetrwEditBuf") -endfun - -" --------------------------------------------------------------------- -" s:NetrwEditFile: decides whether or not to use keepalt to edit a file {{{2 -" NetrwKeepj [keepalt] <OPT> <CMD> <FILENAME> -fun! s:NetrwEditFile(cmd,opt,fname) -" call Dfunc("s:NetrwEditFile(cmd<".a:cmd.">,opt<".a:opt.">,fname<".a:fname.">) ft<".&ft.">") - if exists("g:netrw_altfile") && g:netrw_altfile && &ft == "netrw" -" call Decho("exe NetrwKeepj keepalt ".a:opt." ".a:cmd." ".fnameescape(a:fname)) - exe "NetrwKeepj keepalt ".a:opt." ".a:cmd." ".fnameescape(a:fname) - else -" call Decho("exe NetrwKeepj ".a:opt." ".a:cmd." ".fnameescape(a:fname)) - if a:cmd =~# 'e\%[new]!' && !&hidden && getbufvar(bufname('%'), '&modified', 0) - call setbufvar(bufname('%'), '&bufhidden', 'hide') - endif - exe "NetrwKeepj ".a:opt." ".a:cmd." ".fnameescape(a:fname) - endif -" call Dret("s:NetrwEditFile") -endfun - -" --------------------------------------------------------------------- -" s:NetrwExploreListUniq: {{{2 -fun! s:NetrwExploreListUniq(explist) - " this assumes that the list is already sorted - let newexplist= [] - for member in a:explist - if !exists("uniqmember") || member != uniqmember - let uniqmember = member - let newexplist = newexplist + [ member ] - endif - endfor - return newexplist -endfun - -" --------------------------------------------------------------------- -" s:NetrwForceChgDir: (gd support) Force treatment as a directory {{{2 -fun! s:NetrwForceChgDir(islocal,newdir) - let ykeep= @@ - if a:newdir !~ '/$' - " ok, looks like force is needed to get directory-style treatment - if a:newdir =~ '@$' - let newdir= substitute(a:newdir,'@$','/','') - elseif a:newdir =~ '[*=|\\]$' - let newdir= substitute(a:newdir,'.$','/','') - else - let newdir= a:newdir.'/' - endif - else - " should already be getting treatment as a directory - let newdir= a:newdir - endif - let newdir= s:NetrwBrowseChgDir(a:islocal,newdir,0) - call s:NetrwBrowse(a:islocal,newdir) - let @@= ykeep -endfun - -" --------------------------------------------------------------------- -" s:NetrwGlob: does glob() if local, remote listing otherwise {{{2 -" 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 - let keep_liststyle = w:netrw_liststyle - let w:netrw_liststyle = s:THINLIST - if s:NetrwRemoteListing() == 0 - keepj keepalt %s@/@@ - let filelist= getline(1,$) - q! - else - " remote listing error -- leave treedict unchanged - let filelist= w:netrw_treedict[a:direntry] - endif - let w:netrw_liststyle= keep_liststyle - else - let path= s:ComposePath(fnameescape(a:direntry), a:expr) - if has("win32") - " escape [ so it is not detected as wildcard character, see :h wildcard - let path= substitute(path, '[', '[[]', 'g') - endif - if v:version > 704 || (v:version == 704 && has("patch656")) - let filelist= glob(path,0,1,1) - else - let filelist= glob(path,0,1) - endif - if a:pare - let filelist= map(filelist,'substitute(v:val, "^.*/", "", "")') - endif - endif - return filelist -endfun - -" --------------------------------------------------------------------- -" s:NetrwForceFile: (gf support) Force treatment as a file {{{2 -fun! s:NetrwForceFile(islocal,newfile) - if a:newfile =~ '[/@*=|\\]$' - let newfile= substitute(a:newfile,'.$','','') - else - let newfile= a:newfile - endif - if a:islocal - call s:NetrwBrowseChgDir(a:islocal,newfile,0) - else - call s:NetrwBrowse(a:islocal,s:NetrwBrowseChgDir(a:islocal,newfile,0)) - endif -endfun - -" --------------------------------------------------------------------- -" s:NetrwHide: this function is invoked by the "a" map for browsing {{{2 -" and switches the hiding mode. The actual hiding is done by -" s:NetrwListHide(). -" g:netrw_hide= 0: show all -" 1: show not-hidden files -" 2: show hidden files only -fun! s:NetrwHide(islocal) - let ykeep= @@ - let svpos= winsaveview() - - if exists("s:netrwmarkfilelist_{bufnr('%')}") - - " hide the files in the markfile list - for fname in s:netrwmarkfilelist_{bufnr("%")} - if match(g:netrw_list_hide,'\<'.fname.'\>') != -1 - " remove fname from hiding list - let g:netrw_list_hide= substitute(g:netrw_list_hide,'..\<'.escape(fname,g:netrw_fname_escape).'\>..','','') - let g:netrw_list_hide= substitute(g:netrw_list_hide,',,',',','g') - let g:netrw_list_hide= substitute(g:netrw_list_hide,'^,\|,$','','') - else - " append fname to hiding list - if exists("g:netrw_list_hide") && g:netrw_list_hide != "" - let g:netrw_list_hide= g:netrw_list_hide.',\<'.escape(fname,g:netrw_fname_escape).'\>' - else - let g:netrw_list_hide= '\<'.escape(fname,g:netrw_fname_escape).'\>' - endif - endif - endfor - NetrwKeepj call s:NetrwUnmarkList(bufnr("%"),b:netrw_curdir) - let g:netrw_hide= 1 - - else - - " switch between show-all/show-not-hidden/show-hidden - let g:netrw_hide=(g:netrw_hide+1)%3 - exe "NetrwKeepj norm! 0" - if g:netrw_hide && g:netrw_list_hide == "" - NetrwKeepj call netrw#ErrorMsg(s:WARNING,"your hiding list is empty!",49) - let @@= ykeep - return - endif - endif - - NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) - NetrwKeepj call winrestview(svpos) - let @@= ykeep -endfun - -" --------------------------------------------------------------------- -" s:NetrwHideEdit: allows user to edit the file/directory hiding list {{{2 -fun! s:NetrwHideEdit(islocal) - let ykeep= @@ - " save current cursor position - let svpos= winsaveview() - - " get new hiding list from user - call inputsave() - let newhide= input("Edit Hiding List: ",g:netrw_list_hide) - call inputrestore() - let g:netrw_list_hide= newhide - - " refresh the listing - sil NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,"./",0)) - - " restore cursor position - call winrestview(svpos) - let @@= ykeep -endfun - -" --------------------------------------------------------------------- -" s:NetrwHidden: invoked by "gh" {{{2 -fun! s:NetrwHidden(islocal) - let ykeep= @@ - " save current position - let svpos = winsaveview() - - if g:netrw_list_hide =~ '\(^\|,\)\\(^\\|\\s\\s\\)\\zs\\.\\S\\+' - " remove .file pattern from hiding list - let g:netrw_list_hide= substitute(g:netrw_list_hide,'\(^\|,\)\\(^\\|\\s\\s\\)\\zs\\.\\S\\+','','') - elseif s:Strlen(g:netrw_list_hide) >= 1 - let g:netrw_list_hide= g:netrw_list_hide . ',\(^\|\s\s\)\zs\.\S\+' - else - let g:netrw_list_hide= '\(^\|\s\s\)\zs\.\S\+' - endif - if g:netrw_list_hide =~ '^,' - let g:netrw_list_hide= strpart(g:netrw_list_hide,1) - endif - - " refresh screen and return to saved position - NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) - NetrwKeepj call winrestview(svpos) - let @@= ykeep -endfun - -" --------------------------------------------------------------------- -" s:NetrwHome: this function determines a "home" for saving bookmarks and history {{{2 -fun! s:NetrwHome() - if exists("g:netrw_home") - let home= expand(g:netrw_home) - else - let home = stdpath('data') - endif - " insure that the home directory exists - if g:netrw_dirhistmax > 0 && !isdirectory(s:NetrwFile(home)) -" call Decho("insure that the home<".home."> directory exists") - if exists("g:netrw_mkdir") -" call Decho("call system(".g:netrw_mkdir." ".s:ShellEscape(s:NetrwFile(home)).")") - call system(g:netrw_mkdir." ".s:ShellEscape(s:NetrwFile(home))) - else -" call Decho("mkdir(".home.")") - call mkdir(home) - endif - endif - let g:netrw_home= home - return home -endfun - -" --------------------------------------------------------------------- -" s:NetrwLeftmouse: handles the <leftmouse> when in a netrw browsing window {{{2 -fun! s:NetrwLeftmouse(islocal) - if exists("s:netrwdrag") - return - endif - if &ft != "netrw" - return - endif - - let ykeep= @@ - " check if the status bar was clicked on instead of a file/directory name - while getchar(0) != 0 - "clear the input stream - endwhile - call feedkeys("\<LeftMouse>") - let c = getchar() - let mouse_lnum = v:mouse_lnum - let wlastline = line('w$') - let lastline = line('$') - if mouse_lnum >= wlastline + 1 || v:mouse_win != winnr() - " appears to be a status bar leftmouse click - let @@= ykeep - return - endif - " Dec 04, 2013: following test prevents leftmouse selection/deselection of directories and files in treelist mode - " Windows are separated by vertical separator bars - but the mouse seems to be doing what it should when dragging that bar - " without this test when its disabled. - " May 26, 2014: edit file, :Lex, resize window -- causes refresh. Reinstated a modified test. See if problems develop. - if v:mouse_col > virtcol('.') - let @@= ykeep - return - endif - - if a:islocal - if exists("b:netrw_curdir") - NetrwKeepj call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,s:NetrwGetWord(),1)) - endif - else - if exists("b:netrw_curdir") - NetrwKeepj call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,s:NetrwGetWord(),1)) - endif - endif - let @@= ykeep -endfun - -" --------------------------------------------------------------------- -" s:NetrwCLeftmouse: used to select a file/directory for a target {{{2 -fun! s:NetrwCLeftmouse(islocal) - if &ft != "netrw" - return - endif - call s:NetrwMarkFileTgt(a:islocal) -endfun - -" --------------------------------------------------------------------- -" s:NetrwServerEdit: edit file in a server gvim, usually NETRWSERVER (implements <c-r>){{{2 -" a:islocal=0 : <c-r> not used, remote -" a:islocal=1 : <c-r> not used, local -" a:islocal=2 : <c-r> used, remote -" a:islocal=3 : <c-r> used, local -fun! s:NetrwServerEdit(islocal,fname) -" call Dfunc("s:NetrwServerEdit(islocal=".a:islocal.",fname<".a:fname.">)") - let islocal = a:islocal%2 " =0: remote =1: local - let ctrlr = a:islocal >= 2 " =0: <c-r> not used =1: <c-r> used - - if (islocal && isdirectory(s:NetrwFile(a:fname))) || (!islocal && a:fname =~ '/$') - " handle directories in the local window -- not in the remote vim server - " user must have closed the NETRWSERVER window. Treat as normal editing from netrw. - let g:netrw_browse_split= 0 - if exists("s:netrw_browse_split") && exists("s:netrw_browse_split_".winnr()) - let g:netrw_browse_split= s:netrw_browse_split_{winnr()} - unlet s:netrw_browse_split_{winnr()} - endif - call s:NetrwBrowse(islocal,s:NetrwBrowseChgDir(islocal,a:fname,0)) - return - endif - - if has("clientserver") && executable("gvim") - - if exists("g:netrw_browse_split") && type(g:netrw_browse_split) == 3 - let srvrname = g:netrw_browse_split[0] - let tabnum = g:netrw_browse_split[1] - let winnum = g:netrw_browse_split[2] - - if serverlist() !~ '\<'.srvrname.'\>' - if !ctrlr - " user must have closed the server window and the user did not use <c-r>, but - " used something like <cr>. - if exists("g:netrw_browse_split") - unlet g:netrw_browse_split - endif - let g:netrw_browse_split= 0 - if exists("s:netrw_browse_split_".winnr()) - let g:netrw_browse_split= s:netrw_browse_split_{winnr()} - endif - call s:NetrwBrowseChgDir(islocal,a:fname,0) - return - - elseif has("win32") && executable("start") - " start up remote netrw server under windows - call system("start gvim --servername ".srvrname) - - else - " start up remote netrw server under linux - call system("gvim --servername ".srvrname) - endif - endif - - call remote_send(srvrname,":tabn ".tabnum."\<cr>") - call remote_send(srvrname,":".winnum."wincmd w\<cr>") - call remote_send(srvrname,":e ".fnameescape(s:NetrwFile(a:fname))."\<cr>") - else - - if serverlist() !~ '\<'.g:netrw_servername.'\>' - - if !ctrlr - if exists("g:netrw_browse_split") - unlet g:netrw_browse_split - endif - let g:netrw_browse_split= 0 - call s:NetrwBrowse(islocal,s:NetrwBrowseChgDir(islocal,a:fname,0)) - return - - else - if has("win32") && executable("start") - " start up remote netrw server under windows - call system("start gvim --servername ".g:netrw_servername) - else - " start up remote netrw server under linux - call system("gvim --servername ".g:netrw_servername) - endif - endif - endif - - while 1 - try - call remote_send(g:netrw_servername,":e ".fnameescape(s:NetrwFile(a:fname))."\<cr>") - break - catch /^Vim\%((\a\+)\)\=:E241/ - sleep 200m - endtry - endwhile - - if exists("g:netrw_browse_split") - if type(g:netrw_browse_split) != 3 - let s:netrw_browse_split_{winnr()}= g:netrw_browse_split - endif - unlet g:netrw_browse_split - endif - let g:netrw_browse_split= [g:netrw_servername,1,1] - endif - - else - call netrw#ErrorMsg(s:ERROR,"you need a gui-capable vim and client-server to use <ctrl-r>",98) - endif - -endfun - -" --------------------------------------------------------------------- -" s:NetrwSLeftmouse: marks the file under the cursor. May be dragged to select additional files {{{2 -fun! s:NetrwSLeftmouse(islocal) - if &ft != "netrw" - return - endif -" call Dfunc("s:NetrwSLeftmouse(islocal=".a:islocal.")") - - let s:ngw= s:NetrwGetWord() - call s:NetrwMarkFile(a:islocal,s:ngw) - -" call Dret("s:NetrwSLeftmouse") -endfun - -" --------------------------------------------------------------------- -" s:NetrwSLeftdrag: invoked via a shift-leftmouse and dragging {{{2 -" Used to mark multiple files. -fun! s:NetrwSLeftdrag(islocal) -" call Dfunc("s:NetrwSLeftdrag(islocal=".a:islocal.")") - if !exists("s:netrwdrag") - let s:netrwdrag = winnr() - if a:islocal - nno <silent> <s-leftrelease> <leftmouse>:<c-u>call <SID>NetrwSLeftrelease(1)<cr> - else - nno <silent> <s-leftrelease> <leftmouse>:<c-u>call <SID>NetrwSLeftrelease(0)<cr> - endif - endif - let ngw = s:NetrwGetWord() - if !exists("s:ngw") || s:ngw != ngw - call s:NetrwMarkFile(a:islocal,ngw) - endif - let s:ngw= ngw -" call Dret("s:NetrwSLeftdrag : s:netrwdrag=".s:netrwdrag." buf#".bufnr("%")) -endfun - -" --------------------------------------------------------------------- -" s:NetrwSLeftrelease: terminates shift-leftmouse dragging {{{2 -fun! s:NetrwSLeftrelease(islocal) -" call Dfunc("s:NetrwSLeftrelease(islocal=".a:islocal.") s:netrwdrag=".s:netrwdrag." buf#".bufnr("%")) - if exists("s:netrwdrag") - nunmap <s-leftrelease> - let ngw = s:NetrwGetWord() - if !exists("s:ngw") || s:ngw != ngw - call s:NetrwMarkFile(a:islocal,ngw) - endif - if exists("s:ngw") - unlet s:ngw - endif - unlet s:netrwdrag - endif -" call Dret("s:NetrwSLeftrelease") -endfun - -" --------------------------------------------------------------------- -" s:NetrwListHide: uses [range]g~...~d to delete files that match {{{2 -" comma-separated patterns given in g:netrw_list_hide -fun! s:NetrwListHide() -" call Dfunc("s:NetrwListHide() g:netrw_hide=".g:netrw_hide." g:netrw_list_hide<".g:netrw_list_hide.">") -" call Decho("initial: ".string(getline(w:netrw_bannercnt,'$'))) - let ykeep= @@ - - " find a character not in the "hide" string to use as a separator for :g and :v commands - " How-it-works: take the hiding command, convert it into a range. - " Duplicate characters don't matter. - " Remove all such characters from the '/~@#...890' string. - " Use the first character left as a separator character. -" call Decho("find a character not in the hide string to use as a separator",'~'.expand("<slnum>")) - let listhide= g:netrw_list_hide - let sep = strpart(substitute('~@#$%^&*{};:,<.>?|1234567890','['.escape(listhide,'-]^\').']','','ge'),1,1) -" call Decho("sep<".sep."> (sep not in hide string)",'~'.expand("<slnum>")) - - while listhide != "" - if listhide =~ ',' - let hide = substitute(listhide,',.*$','','e') - let listhide = substitute(listhide,'^.\{-},\(.*\)$','\1','e') - else - let hide = listhide - let listhide = "" - endif -" call Decho("..extracted pattern from listhide: hide<".hide."> g:netrw_sort_by<".g:netrw_sort_by.'>','~'.expand("<slnum>")) - if g:netrw_sort_by =~ '^[ts]' - if hide =~ '^\^' -" call Decho("..modify hide to handle a \"^...\" pattern",'~'.expand("<slnum>")) - let hide= substitute(hide,'^\^','^\(\\d\\+/\)','') - elseif hide =~ '^\\(\^' - let hide= substitute(hide,'^\\(\^','\\(^\\(\\d\\+/\\)','') - endif -" call Decho("..hide<".hide."> listhide<".listhide.'>','~'.expand("<slnum>")) - endif - - " Prune the list by hiding any files which match -" call Decho("..prune the list by hiding any files which ".((g:netrw_hide == 1)? "" : "don't")."match hide<".hide.">") - if g:netrw_hide == 1 -" call Decho("..hiding<".hide.">",'~'.expand("<slnum>")) - exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$g'.sep.hide.sep.'d' - elseif g:netrw_hide == 2 -" call Decho("..showing<".hide.">",'~'.expand("<slnum>")) - exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$g'.sep.hide.sep.'s@^@ /-KEEP-/ @' - endif -" call Decho("..result: ".string(getline(w:netrw_bannercnt,'$')),'~'.expand("<slnum>")) - endwhile - - if g:netrw_hide == 2 - exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$v@^ /-KEEP-/ @d' -" call Decho("..v KEEP: ".string(getline(w:netrw_bannercnt,'$')),'~'.expand("<slnum>")) - exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s@^\%( /-KEEP-/ \)\+@@e' -" call Decho("..g KEEP: ".string(getline(w:netrw_bannercnt,'$')),'~'.expand("<slnum>")) - endif - - " remove any blank lines that have somehow remained. - " This seems to happen under Windows. - exe 'sil! NetrwKeepj 1,$g@^\s*$@d' - - let @@= ykeep -" call Dret("s:NetrwListHide") -endfun - -" --------------------------------------------------------------------- -" s:NetrwMakeDir: this function makes a directory (both local and remote) {{{2 -" implements the "d" mapping. -fun! s:NetrwMakeDir(usrhost) - - let ykeep= @@ - " get name of new directory from user. A bare <CR> will skip. - " if its currently a directory, also request will be skipped, but with - " a message. - call inputsave() - let newdirname= input("Please give directory name: ") - call inputrestore() - - if newdirname == "" - let @@= ykeep - return - endif - - if a:usrhost == "" - - " Local mkdir: - " sanity checks - let fullnewdir= b:netrw_curdir.'/'.newdirname - if isdirectory(s:NetrwFile(fullnewdir)) - if !exists("g:netrw_quiet") - NetrwKeepj call netrw#ErrorMsg(s:WARNING,"<".newdirname."> is already a directory!",24) - endif - let @@= ykeep - return - endif - if s:FileReadable(fullnewdir) - if !exists("g:netrw_quiet") - NetrwKeepj call netrw#ErrorMsg(s:WARNING,"<".newdirname."> is already a file!",25) - endif - let @@= ykeep - return - endif - - " requested new local directory is neither a pre-existing file or - " directory, so make it! - if exists("*mkdir") - if has("unix") - call mkdir(fullnewdir,"p",xor(0777, system("umask"))) - else - call mkdir(fullnewdir,"p") - endif - else - let netrw_origdir= s:NetrwGetcwd(1) - if s:NetrwLcd(b:netrw_curdir) - return - endif - call s:NetrwExe("sil! !".g:netrw_localmkdir.g:netrw_localmkdiropt.' '.s:ShellEscape(newdirname,1)) - if v:shell_error != 0 - let @@= ykeep - call netrw#ErrorMsg(s:ERROR,"consider setting g:netrw_localmkdir<".g:netrw_localmkdir."> to something that works",80) - return - endif - if !g:netrw_keepdir - if s:NetrwLcd(netrw_origdir) - return - endif - endif - endif - - if v:shell_error == 0 - " refresh listing - let svpos= winsaveview() - call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0)) - call winrestview(svpos) - elseif !exists("g:netrw_quiet") - call netrw#ErrorMsg(s:ERROR,"unable to make directory<".newdirname.">",26) - endif - - elseif !exists("b:netrw_method") || b:netrw_method == 4 - " Remote mkdir: using ssh - let mkdircmd = s:MakeSshCmd(g:netrw_mkdir_cmd) - let newdirname= substitute(b:netrw_curdir,'^\%(.\{-}/\)\{3}\(.*\)$','\1','').newdirname - call s:NetrwExe("sil! !".mkdircmd." ".s:ShellEscape(newdirname,1)) - if v:shell_error == 0 - " refresh listing - let svpos= winsaveview() - NetrwKeepj call s:NetrwRefresh(0,s:NetrwBrowseChgDir(0,'./',0)) - NetrwKeepj call winrestview(svpos) - elseif !exists("g:netrw_quiet") - NetrwKeepj call netrw#ErrorMsg(s:ERROR,"unable to make directory<".newdirname.">",27) - endif - - elseif b:netrw_method == 2 - " Remote mkdir: using ftp+.netrc - let svpos= winsaveview() - if exists("b:netrw_fname") - let remotepath= b:netrw_fname - else - let remotepath= "" - endif - call s:NetrwRemoteFtpCmd(remotepath,g:netrw_remote_mkdir.' "'.newdirname.'"') - NetrwKeepj call s:NetrwRefresh(0,s:NetrwBrowseChgDir(0,'./',0)) - NetrwKeepj call winrestview(svpos) - - elseif b:netrw_method == 3 - " Remote mkdir: using ftp + machine, id, passwd, and fname (ie. no .netrc) - let svpos= winsaveview() - if exists("b:netrw_fname") - let remotepath= b:netrw_fname - else - let remotepath= "" - endif - call s:NetrwRemoteFtpCmd(remotepath,g:netrw_remote_mkdir.' "'.newdirname.'"') - NetrwKeepj call s:NetrwRefresh(0,s:NetrwBrowseChgDir(0,'./',0)) - NetrwKeepj call winrestview(svpos) - endif - - let @@= ykeep -endfun - -" --------------------------------------------------------------------- -" s:TreeSqueezeDir: allows a shift-cr (gvim only) to squeeze the current tree-listing directory {{{2 -fun! s:TreeSqueezeDir(islocal) - if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict") - " its a tree-listing style - let curdepth = substitute(getline('.'),'^\(\%('.s:treedepthstring.'\)*\)[^'.s:treedepthstring.'].\{-}$','\1','e') - let stopline = (exists("w:netrw_bannercnt")? (w:netrw_bannercnt + 1) : 1) - let depth = strchars(substitute(curdepth,' ','','g')) - let srch = -1 - if depth >= 2 - NetrwKeepj norm! 0 - let curdepthm1= substitute(curdepth,'^'.s:treedepthstring,'','') - let srch = search('^'.curdepthm1.'\%('.s:treedepthstring.'\)\@!','bW',stopline) - elseif depth == 1 - NetrwKeepj norm! 0 - let treedepthchr= substitute(s:treedepthstring,' ','','') - let srch = search('^[^'.treedepthchr.']','bW',stopline) - endif - if srch > 0 - call s:NetrwBrowse(a:islocal,s:NetrwBrowseChgDir(a:islocal,s:NetrwGetWord(),1)) - exe srch - endif - endif -endfun - -" --------------------------------------------------------------------- -" s:NetrwMaps: {{{2 -fun! s:NetrwMaps(islocal) - - " mouse <Plug> maps: {{{3 - if g:netrw_mousemaps && g:netrw_retmap -" call Decho("set up Rexplore 2-leftmouse",'~'.expand("<slnum>")) - if !hasmapto("<Plug>NetrwReturn") - if maparg("<2-leftmouse>","n") == "" || maparg("<2-leftmouse>","n") =~ '^-$' - nmap <unique> <silent> <2-leftmouse> <Plug>NetrwReturn - elseif maparg("<c-leftmouse>","n") == "" - nmap <unique> <silent> <c-leftmouse> <Plug>NetrwReturn - endif - endif - nno <silent> <Plug>NetrwReturn :Rexplore<cr> - endif - - " generate default <Plug> maps {{{3 - if !hasmapto('<Plug>NetrwHide') |nmap <buffer> <silent> <nowait> a <Plug>NetrwHide_a|endif - if !hasmapto('<Plug>NetrwBrowseUpDir') |nmap <buffer> <silent> <nowait> - <Plug>NetrwBrowseUpDir|endif - if !hasmapto('<Plug>NetrwOpenFile') |nmap <buffer> <silent> <nowait> % <Plug>NetrwOpenFile|endif - if !hasmapto('<Plug>NetrwBadd_cb') |nmap <buffer> <silent> <nowait> cb <Plug>NetrwBadd_cb|endif - if !hasmapto('<Plug>NetrwBadd_cB') |nmap <buffer> <silent> <nowait> cB <Plug>NetrwBadd_cB|endif - if !hasmapto('<Plug>NetrwLcd') |nmap <buffer> <silent> <nowait> cd <Plug>NetrwLcd|endif - if !hasmapto('<Plug>NetrwSetChgwin') |nmap <buffer> <silent> <nowait> C <Plug>NetrwSetChgwin|endif - if !hasmapto('<Plug>NetrwRefresh') |nmap <buffer> <silent> <nowait> <c-l> <Plug>NetrwRefresh|endif - if !hasmapto('<Plug>NetrwLocalBrowseCheck') |nmap <buffer> <silent> <nowait> <cr> <Plug>NetrwLocalBrowseCheck|endif - if !hasmapto('<Plug>NetrwServerEdit') |nmap <buffer> <silent> <nowait> <c-r> <Plug>NetrwServerEdit|endif - if !hasmapto('<Plug>NetrwMakeDir') |nmap <buffer> <silent> <nowait> d <Plug>NetrwMakeDir|endif - if !hasmapto('<Plug>NetrwBookHistHandler_gb')|nmap <buffer> <silent> <nowait> gb <Plug>NetrwBookHistHandler_gb|endif - - if a:islocal - " local normal-mode maps {{{3 - nnoremap <buffer> <silent> <Plug>NetrwHide_a :<c-u>call <SID>NetrwHide(1)<cr> - nnoremap <buffer> <silent> <Plug>NetrwBrowseUpDir :<c-u>call <SID>NetrwBrowseUpDir(1)<cr> - nnoremap <buffer> <silent> <Plug>NetrwOpenFile :<c-u>call <SID>NetrwOpenFile(1)<cr> - nnoremap <buffer> <silent> <Plug>NetrwBadd_cb :<c-u>call <SID>NetrwBadd(1,0)<cr> - nnoremap <buffer> <silent> <Plug>NetrwBadd_cB :<c-u>call <SID>NetrwBadd(1,1)<cr> - nnoremap <buffer> <silent> <Plug>NetrwLcd :<c-u>call <SID>NetrwLcd(b:netrw_curdir)<cr> - nnoremap <buffer> <silent> <Plug>NetrwSetChgwin :<c-u>call <SID>NetrwSetChgwin()<cr> - nnoremap <buffer> <silent> <Plug>NetrwLocalBrowseCheck :<c-u>call netrw#LocalBrowseCheck(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),1))<cr> - nnoremap <buffer> <silent> <Plug>NetrwServerEdit :<c-u>call <SID>NetrwServerEdit(3,<SID>NetrwGetWord())<cr> - nnoremap <buffer> <silent> <Plug>NetrwMakeDir :<c-u>call <SID>NetrwMakeDir("")<cr> - nnoremap <buffer> <silent> <Plug>NetrwBookHistHandler_gb :<c-u>call <SID>NetrwBookHistHandler(1,b:netrw_curdir)<cr> -" --------------------------------------------------------------------- - nnoremap <buffer> <silent> <nowait> gd :<c-u>call <SID>NetrwForceChgDir(1,<SID>NetrwGetWord())<cr> - nnoremap <buffer> <silent> <nowait> gf :<c-u>call <SID>NetrwForceFile(1,<SID>NetrwGetWord())<cr> - nnoremap <buffer> <silent> <nowait> gh :<c-u>call <SID>NetrwHidden(1)<cr> - nnoremap <buffer> <silent> <nowait> gn :<c-u>call netrw#SetTreetop(0,<SID>NetrwGetWord())<cr> - nnoremap <buffer> <silent> <nowait> gp :<c-u>call <SID>NetrwChgPerm(1,b:netrw_curdir)<cr> - nnoremap <buffer> <silent> <nowait> I :<c-u>call <SID>NetrwBannerCtrl(1)<cr> - nnoremap <buffer> <silent> <nowait> i :<c-u>call <SID>NetrwListStyle(1)<cr> - nnoremap <buffer> <silent> <nowait> ma :<c-u>call <SID>NetrwMarkFileArgList(1,0)<cr> - nnoremap <buffer> <silent> <nowait> mA :<c-u>call <SID>NetrwMarkFileArgList(1,1)<cr> - nnoremap <buffer> <silent> <nowait> mb :<c-u>call <SID>NetrwBookHistHandler(0,b:netrw_curdir)<cr> - nnoremap <buffer> <silent> <nowait> mB :<c-u>call <SID>NetrwBookHistHandler(6,b:netrw_curdir)<cr> - nnoremap <buffer> <silent> <nowait> mc :<c-u>call <SID>NetrwMarkFileCopy(1)<cr> - nnoremap <buffer> <silent> <nowait> md :<c-u>call <SID>NetrwMarkFileDiff(1)<cr> - nnoremap <buffer> <silent> <nowait> me :<c-u>call <SID>NetrwMarkFileEdit(1)<cr> - nnoremap <buffer> <silent> <nowait> mf :<c-u>call <SID>NetrwMarkFile(1,<SID>NetrwGetWord())<cr> - nnoremap <buffer> <silent> <nowait> mF :<c-u>call <SID>NetrwUnmarkList(bufnr("%"),b:netrw_curdir)<cr> - nnoremap <buffer> <silent> <nowait> mg :<c-u>call <SID>NetrwMarkFileGrep(1)<cr> - nnoremap <buffer> <silent> <nowait> mh :<c-u>call <SID>NetrwMarkHideSfx(1)<cr> - nnoremap <buffer> <silent> <nowait> mm :<c-u>call <SID>NetrwMarkFileMove(1)<cr> - "nnoremap <buffer> <silent> <nowait> mp :<c-u>call <SID>NetrwMarkFilePrint(1)<cr> - nnoremap <buffer> <silent> <nowait> mr :<c-u>call <SID>NetrwMarkFileRegexp(1)<cr> - nnoremap <buffer> <silent> <nowait> ms :<c-u>call <SID>NetrwMarkFileSource(1)<cr> - nnoremap <buffer> <silent> <nowait> mT :<c-u>call <SID>NetrwMarkFileTag(1)<cr> - nnoremap <buffer> <silent> <nowait> mt :<c-u>call <SID>NetrwMarkFileTgt(1)<cr> - nnoremap <buffer> <silent> <nowait> mu :<c-u>call <SID>NetrwUnMarkFile(1)<cr> - nnoremap <buffer> <silent> <nowait> mv :<c-u>call <SID>NetrwMarkFileVimCmd(1)<cr> - nnoremap <buffer> <silent> <nowait> mx :<c-u>call <SID>NetrwMarkFileExe(1,0)<cr> - nnoremap <buffer> <silent> <nowait> mX :<c-u>call <SID>NetrwMarkFileExe(1,1)<cr> - nnoremap <buffer> <silent> <nowait> mz :<c-u>call <SID>NetrwMarkFileCompress(1)<cr> - nnoremap <buffer> <silent> <nowait> O :<c-u>call <SID>NetrwObtain(1)<cr> - nnoremap <buffer> <silent> <nowait> o :call <SID>NetrwSplit(3)<cr> - nnoremap <buffer> <silent> <nowait> p :<c-u>call <SID>NetrwPreview(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),1,1))<cr> - nnoremap <buffer> <silent> <nowait> P :<c-u>call <SID>NetrwPrevWinOpen(1)<cr> - nnoremap <buffer> <silent> <nowait> qb :<c-u>call <SID>NetrwBookHistHandler(2,b:netrw_curdir)<cr> - 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> 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> - nnoremap <buffer> <silent> <nowait> t :call <SID>NetrwSplit(4)<cr> - nnoremap <buffer> <silent> <nowait> Th :<c-u>call <SID>NetrwSetTgt(1,'h',v:count)<cr> - nnoremap <buffer> <silent> <nowait> u :<c-u>call <SID>NetrwBookHistHandler(4,expand("%"))<cr> - nnoremap <buffer> <silent> <nowait> U :<c-u>call <SID>NetrwBookHistHandler(5,expand("%"))<cr> - nnoremap <buffer> <silent> <nowait> v :call <SID>NetrwSplit(5)<cr> - nnoremap <buffer> <silent> <nowait> x :<c-u>call netrw#BrowseX(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),1,0),0)"<cr> - nnoremap <buffer> <silent> <nowait> X :<c-u>call <SID>NetrwLocalExecute(expand("<cword>"))"<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,'./',0))<cr> - if !hasmapto('<Plug>NetrwHideEdit') - nmap <buffer> <unique> <c-h> <Plug>NetrwHideEdit - endif - nnoremap <buffer> <silent> <Plug>NetrwHideEdit :call <SID>NetrwHideEdit(1)<cr> - if !hasmapto('<Plug>NetrwRefresh') - nmap <buffer> <unique> <c-l> <Plug>NetrwRefresh - endif - nnoremap <buffer> <silent> <Plug>NetrwRefresh <c-l>:call <SID>NetrwRefresh(1,<SID>NetrwBrowseChgDir(1,(exists("w:netrw_liststyle") && exists("w:netrw_treetop") && w:netrw_liststyle == 3)? w:netrw_treetop : './',0))<cr> - if s:didstarstar || !mapcheck("<s-down>","n") - nnoremap <buffer> <silent> <s-down> :Nexplore<cr> - endif - if s:didstarstar || !mapcheck("<s-up>","n") - nnoremap <buffer> <silent> <s-up> :Pexplore<cr> - endif - if !hasmapto('<Plug>NetrwTreeSqueeze') - nmap <buffer> <silent> <nowait> <s-cr> <Plug>NetrwTreeSqueeze - endif - nnoremap <buffer> <silent> <Plug>NetrwTreeSqueeze :call <SID>TreeSqueezeDir(1)<cr> - let mapsafecurdir = escape(b:netrw_curdir, s:netrw_map_escape) - if g:netrw_mousemaps == 1 - nmap <buffer> <leftmouse> <Plug>NetrwLeftmouse - nmap <buffer> <c-leftmouse> <Plug>NetrwCLeftmouse - nmap <buffer> <middlemouse> <Plug>NetrwMiddlemouse - nmap <buffer> <s-leftmouse> <Plug>NetrwSLeftmouse - nmap <buffer> <s-leftdrag> <Plug>NetrwSLeftdrag - nmap <buffer> <2-leftmouse> <Plug>Netrw2Leftmouse - imap <buffer> <leftmouse> <Plug>ILeftmouse - imap <buffer> <middlemouse> <Plug>IMiddlemouse - nno <buffer> <silent> <Plug>NetrwLeftmouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwLeftmouse(1)<cr> - nno <buffer> <silent> <Plug>NetrwCLeftmouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwCLeftmouse(1)<cr> - nno <buffer> <silent> <Plug>NetrwMiddlemouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwPrevWinOpen(1)<cr> - nno <buffer> <silent> <Plug>NetrwSLeftmouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwSLeftmouse(1)<cr> - nno <buffer> <silent> <Plug>NetrwSLeftdrag :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwSLeftdrag(1)<cr> - nmap <buffer> <silent> <Plug>Netrw2Leftmouse - - exe 'nnoremap <buffer> <silent> <rightmouse> :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' - exe 'vnoremap <buffer> <silent> <rightmouse> :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' - endif - exe 'nnoremap <buffer> <silent> <nowait> <del> :call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' - exe 'nnoremap <buffer> <silent> <nowait> D :call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' - exe 'nnoremap <buffer> <silent> <nowait> R :call <SID>NetrwLocalRename("'.mapsafecurdir.'")<cr>' - exe 'nnoremap <buffer> <silent> <nowait> d :call <SID>NetrwMakeDir("")<cr>' - exe 'vnoremap <buffer> <silent> <nowait> <del> :call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' - exe 'vnoremap <buffer> <silent> <nowait> D :call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' - exe 'vnoremap <buffer> <silent> <nowait> R :call <SID>NetrwLocalRename("'.mapsafecurdir.'")<cr>' - nnoremap <buffer> <F1> :he netrw-quickhelp<cr> - - " support user-specified maps - call netrw#UserMaps(1) - - else - " remote normal-mode maps {{{3 - call s:RemotePathAnalysis(b:netrw_curdir) - nnoremap <buffer> <silent> <Plug>NetrwHide_a :<c-u>call <SID>NetrwHide(0)<cr> - nnoremap <buffer> <silent> <Plug>NetrwBrowseUpDir :<c-u>call <SID>NetrwBrowseUpDir(0)<cr> - nnoremap <buffer> <silent> <Plug>NetrwOpenFile :<c-u>call <SID>NetrwOpenFile(0)<cr> - nnoremap <buffer> <silent> <Plug>NetrwBadd_cb :<c-u>call <SID>NetrwBadd(0,0)<cr> - nnoremap <buffer> <silent> <Plug>NetrwBadd_cB :<c-u>call <SID>NetrwBadd(0,1)<cr> - nnoremap <buffer> <silent> <Plug>NetrwLcd :<c-u>call <SID>NetrwLcd(b:netrw_curdir)<cr> - nnoremap <buffer> <silent> <Plug>NetrwSetChgwin :<c-u>call <SID>NetrwSetChgwin()<cr> - nnoremap <buffer> <silent> <Plug>NetrwRefresh :<c-u>call <SID>NetrwRefresh(0,<SID>NetrwBrowseChgDir(0,'./',0))<cr> - nnoremap <buffer> <silent> <Plug>NetrwLocalBrowseCheck :<c-u>call <SID>NetrwBrowse(0,<SID>NetrwBrowseChgDir(0,<SID>NetrwGetWord(),1))<cr> - nnoremap <buffer> <silent> <Plug>NetrwServerEdit :<c-u>call <SID>NetrwServerEdit(2,<SID>NetrwGetWord())<cr> - nnoremap <buffer> <silent> <Plug>NetrwBookHistHandler_gb :<c-u>call <SID>NetrwBookHistHandler(1,b:netrw_curdir)<cr> -" --------------------------------------------------------------------- - nnoremap <buffer> <silent> <nowait> gd :<c-u>call <SID>NetrwForceChgDir(0,<SID>NetrwGetWord())<cr> - nnoremap <buffer> <silent> <nowait> gf :<c-u>call <SID>NetrwForceFile(0,<SID>NetrwGetWord())<cr> - nnoremap <buffer> <silent> <nowait> gh :<c-u>call <SID>NetrwHidden(0)<cr> - nnoremap <buffer> <silent> <nowait> gp :<c-u>call <SID>NetrwChgPerm(0,b:netrw_curdir)<cr> - nnoremap <buffer> <silent> <nowait> I :<c-u>call <SID>NetrwBannerCtrl(1)<cr> - nnoremap <buffer> <silent> <nowait> i :<c-u>call <SID>NetrwListStyle(0)<cr> - nnoremap <buffer> <silent> <nowait> ma :<c-u>call <SID>NetrwMarkFileArgList(0,0)<cr> - nnoremap <buffer> <silent> <nowait> mA :<c-u>call <SID>NetrwMarkFileArgList(0,1)<cr> - nnoremap <buffer> <silent> <nowait> mb :<c-u>call <SID>NetrwBookHistHandler(0,b:netrw_curdir)<cr> - nnoremap <buffer> <silent> <nowait> mB :<c-u>call <SID>NetrwBookHistHandler(6,b:netrw_curdir)<cr> - nnoremap <buffer> <silent> <nowait> mc :<c-u>call <SID>NetrwMarkFileCopy(0)<cr> - nnoremap <buffer> <silent> <nowait> md :<c-u>call <SID>NetrwMarkFileDiff(0)<cr> - nnoremap <buffer> <silent> <nowait> me :<c-u>call <SID>NetrwMarkFileEdit(0)<cr> - nnoremap <buffer> <silent> <nowait> mf :<c-u>call <SID>NetrwMarkFile(0,<SID>NetrwGetWord())<cr> - nnoremap <buffer> <silent> <nowait> mF :<c-u>call <SID>NetrwUnmarkList(bufnr("%"),b:netrw_curdir)<cr> - nnoremap <buffer> <silent> <nowait> mg :<c-u>call <SID>NetrwMarkFileGrep(0)<cr> - nnoremap <buffer> <silent> <nowait> mh :<c-u>call <SID>NetrwMarkHideSfx(0)<cr> - nnoremap <buffer> <silent> <nowait> mm :<c-u>call <SID>NetrwMarkFileMove(0)<cr> - "nnoremap <buffer> <silent> <nowait> mp :<c-u>call <SID>NetrwMarkFilePrint(0)<cr> - nnoremap <buffer> <silent> <nowait> mr :<c-u>call <SID>NetrwMarkFileRegexp(0)<cr> - nnoremap <buffer> <silent> <nowait> ms :<c-u>call <SID>NetrwMarkFileSource(0)<cr> - nnoremap <buffer> <silent> <nowait> mT :<c-u>call <SID>NetrwMarkFileTag(0)<cr> - nnoremap <buffer> <silent> <nowait> mt :<c-u>call <SID>NetrwMarkFileTgt(0)<cr> - nnoremap <buffer> <silent> <nowait> mu :<c-u>call <SID>NetrwUnMarkFile(0)<cr> - nnoremap <buffer> <silent> <nowait> mv :<c-u>call <SID>NetrwMarkFileVimCmd(0)<cr> - nnoremap <buffer> <silent> <nowait> mx :<c-u>call <SID>NetrwMarkFileExe(0,0)<cr> - nnoremap <buffer> <silent> <nowait> mX :<c-u>call <SID>NetrwMarkFileExe(0,1)<cr> - nnoremap <buffer> <silent> <nowait> mz :<c-u>call <SID>NetrwMarkFileCompress(0)<cr> - nnoremap <buffer> <silent> <nowait> O :<c-u>call <SID>NetrwObtain(0)<cr> - nnoremap <buffer> <silent> <nowait> o :call <SID>NetrwSplit(0)<cr> - nnoremap <buffer> <silent> <nowait> p :<c-u>call <SID>NetrwPreview(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),1,1))<cr> - nnoremap <buffer> <silent> <nowait> P :<c-u>call <SID>NetrwPrevWinOpen(0)<cr> - nnoremap <buffer> <silent> <nowait> qb :<c-u>call <SID>NetrwBookHistHandler(2,b:netrw_curdir)<cr> - 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,'./',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> - nnoremap <buffer> <silent> <nowait> t :call <SID>NetrwSplit(1)<cr> - nnoremap <buffer> <silent> <nowait> Th :<c-u>call <SID>NetrwSetTgt(0,'h',v:count)<cr> - nnoremap <buffer> <silent> <nowait> u :<c-u>call <SID>NetrwBookHistHandler(4,b:netrw_curdir)<cr> - nnoremap <buffer> <silent> <nowait> U :<c-u>call <SID>NetrwBookHistHandler(5,b:netrw_curdir)<cr> - nnoremap <buffer> <silent> <nowait> v :call <SID>NetrwSplit(2)<cr> - nnoremap <buffer> <silent> <nowait> x :<c-u>call netrw#BrowseX(<SID>NetrwBrowseChgDir(0,<SID>NetrwGetWord(),1),1)<cr> - nmap <buffer> <nowait> gx x - if !hasmapto('<Plug>NetrwHideEdit') - nmap <buffer> <c-h> <Plug>NetrwHideEdit - endif - nnoremap <buffer> <silent> <Plug>NetrwHideEdit :call <SID>NetrwHideEdit(0)<cr> - if !hasmapto('<Plug>NetrwRefresh') - nmap <buffer> <c-l> <Plug>NetrwRefresh - endif - if !hasmapto('<Plug>NetrwTreeSqueeze') - nmap <buffer> <silent> <nowait> <s-cr> <Plug>NetrwTreeSqueeze - endif - nnoremap <buffer> <silent> <Plug>NetrwTreeSqueeze :call <SID>TreeSqueezeDir(0)<cr> - - let mapsafepath = escape(s:path, s:netrw_map_escape) - let mapsafeusermach = escape(((s:user == "")? "" : s:user."@").s:machine, s:netrw_map_escape) - - nnoremap <buffer> <silent> <Plug>NetrwRefresh :call <SID>NetrwRefresh(0,<SID>NetrwBrowseChgDir(0,'./',0))<cr> - if g:netrw_mousemaps == 1 - nmap <buffer> <leftmouse> <Plug>NetrwLeftmouse - nno <buffer> <silent> <Plug>NetrwLeftmouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwLeftmouse(0)<cr> - nmap <buffer> <c-leftmouse> <Plug>NetrwCLeftmouse - nno <buffer> <silent> <Plug>NetrwCLeftmouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwCLeftmouse(0)<cr> - nmap <buffer> <s-leftmouse> <Plug>NetrwSLeftmouse - nno <buffer> <silent> <Plug>NetrwSLeftmouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwSLeftmouse(0)<cr> - nmap <buffer> <s-leftdrag> <Plug>NetrwSLeftdrag - nno <buffer> <silent> <Plug>NetrwSLeftdrag :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwSLeftdrag(0)<cr> - nmap <middlemouse> <Plug>NetrwMiddlemouse - nno <buffer> <silent> <middlemouse> <Plug>NetrwMiddlemouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwPrevWinOpen(0)<cr> - nmap <buffer> <2-leftmouse> <Plug>Netrw2Leftmouse - nmap <buffer> <silent> <Plug>Netrw2Leftmouse - - imap <buffer> <leftmouse> <Plug>ILeftmouse - imap <buffer> <middlemouse> <Plug>IMiddlemouse - imap <buffer> <s-leftmouse> <Plug>ISLeftmouse - exe 'nnoremap <buffer> <silent> <rightmouse> :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' - exe 'vnoremap <buffer> <silent> <rightmouse> :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' - endif - exe 'nnoremap <buffer> <silent> <nowait> <del> :call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' - exe 'nnoremap <buffer> <silent> <nowait> d :call <SID>NetrwMakeDir("'.mapsafeusermach.'")<cr>' - exe 'nnoremap <buffer> <silent> <nowait> D :call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' - exe 'nnoremap <buffer> <silent> <nowait> R :call <SID>NetrwRemoteRename("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' - exe 'vnoremap <buffer> <silent> <nowait> <del> :call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' - exe 'vnoremap <buffer> <silent> <nowait> D :call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' - exe 'vnoremap <buffer> <silent> <nowait> R :call <SID>NetrwRemoteRename("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' - nnoremap <buffer> <F1> :he netrw-quickhelp<cr> - - " support user-specified maps - call netrw#UserMaps(0) - endif " }}}3 -endfun - -" --------------------------------------------------------------------- -" s:NetrwCommands: set up commands {{{2 -" If -buffer, the command is only available from within netrw buffers -" Otherwise, the command is available from any window, so long as netrw -" has been used at least once in the session. -fun! s:NetrwCommands(islocal) -" call Dfunc("s:NetrwCommands(islocal=".a:islocal.")") - - com! -nargs=* -complete=file -bang NetrwMB call s:NetrwBookmark(<bang>0,<f-args>) - com! -nargs=* NetrwC call s:NetrwSetChgwin(<q-args>) - com! Rexplore if exists("w:netrw_rexlocal")|call s:NetrwRexplore(w:netrw_rexlocal,exists("w:netrw_rexdir")? w:netrw_rexdir : ".")|else|call netrw#ErrorMsg(s:WARNING,"win#".winnr()." not a former netrw window",79)|endif - if a:islocal - com! -buffer -nargs=+ -complete=file MF call s:NetrwMarkFiles(1,<f-args>) - else - com! -buffer -nargs=+ -complete=file MF call s:NetrwMarkFiles(0,<f-args>) - endif - com! -buffer -nargs=? -complete=file MT call s:NetrwMarkTarget(<q-args>) - -" call Dret("s:NetrwCommands") -endfun - -" --------------------------------------------------------------------- -" s:NetrwMarkFiles: apply s:NetrwMarkFile() to named file(s) {{{2 -" glob()ing only works with local files -fun! s:NetrwMarkFiles(islocal,...) -" call Dfunc("s:NetrwMarkFiles(islocal=".a:islocal."...) a:0=".a:0) - let curdir = s:NetrwGetCurdir(a:islocal) - let i = 1 - while i <= a:0 - if a:islocal - if v:version > 704 || (v:version == 704 && has("patch656")) - let mffiles= glob(a:{i},0,1,1) - else - let mffiles= glob(a:{i},0,1) - endif - else - let mffiles= [a:{i}] - endif -" call Decho("mffiles".string(mffiles),'~'.expand("<slnum>")) - for mffile in mffiles -" call Decho("mffile<".mffile.">",'~'.expand("<slnum>")) - call s:NetrwMarkFile(a:islocal,mffile) - endfor - let i= i + 1 - endwhile -" call Dret("s:NetrwMarkFiles") -endfun - -" --------------------------------------------------------------------- -" s:NetrwMarkTarget: implements :MT (mark target) {{{2 -fun! s:NetrwMarkTarget(...) - if a:0 == 0 || (a:0 == 1 && a:1 == "") - let curdir = s:NetrwGetCurdir(1) - let tgt = b:netrw_curdir - else - let curdir = s:NetrwGetCurdir((a:1 =~ '^\a\{3,}://')? 0 : 1) - let tgt = a:1 - endif - let s:netrwmftgt = tgt - let s:netrwmftgt_islocal = tgt !~ '^\a\{3,}://' - let curislocal = b:netrw_curdir !~ '^\a\{3,}://' - let svpos = winsaveview() - call s:NetrwRefresh(curislocal,s:NetrwBrowseChgDir(curislocal,'./',0)) - call winrestview(svpos) -endfun - -" --------------------------------------------------------------------- -" s:NetrwMarkFile: (invoked by mf) This function is used to both {{{2 -" mark and unmark files. If a markfile list exists, -" then the rename and delete functions will use it instead -" of whatever may happen to be under the cursor at that -" moment. When the mouse and gui are available, -" shift-leftmouse may also be used to mark files. -" -" Creates two lists -" s:netrwmarkfilelist -- holds complete paths to all marked files -" s:netrwmarkfilelist_# -- holds list of marked files in current-buffer's directory (#==bufnr()) -" -" Creates a marked file match string -" s:netrwmarfilemtch_# -- used with 2match to display marked files -" -" Creates a buffer version of islocal -" b:netrw_islocal -fun! s:NetrwMarkFile(islocal,fname) -" call Dfunc("s:NetrwMarkFile(islocal=".a:islocal." fname<".a:fname.">)") -" call Decho("bufnr(%)=".bufnr("%").": ".bufname("%"),'~'.expand("<slnum>")) - - " sanity check - if empty(a:fname) -" call Dret("s:NetrwMarkFile : empty fname") - return - endif - let curdir = s:NetrwGetCurdir(a:islocal) - - let ykeep = @@ - let curbufnr= bufnr("%") - let leader= '\%(^\|\s\)\zs' - if a:fname =~ '\a$' - let trailer = '\>[@=|\/\*]\=\ze\%( \|\t\|$\)' - else - let trailer = '[@=|\/\*]\=\ze\%( \|\t\|$\)' - endif - - if exists("s:netrwmarkfilelist_".curbufnr) - " markfile list pre-exists -" call Decho("case s:netrwmarkfilelist_".curbufnr." already exists",'~'.expand("<slnum>")) -" call Decho("starting s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>")) -" call Decho("starting s:netrwmarkfilemtch_".curbufnr."<".s:netrwmarkfilemtch_{curbufnr}.">",'~'.expand("<slnum>")) - let b:netrw_islocal= a:islocal - - if index(s:netrwmarkfilelist_{curbufnr},a:fname) == -1 - " append filename to buffer's markfilelist -" call Decho("append filename<".a:fname."> to local markfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>")) - call add(s:netrwmarkfilelist_{curbufnr},a:fname) - let s:netrwmarkfilemtch_{curbufnr}= s:netrwmarkfilemtch_{curbufnr}.'\|'.leader.escape(a:fname,g:netrw_markfileesc).trailer - - else - " remove filename from buffer's markfilelist -" call Decho("remove filename<".a:fname."> from local markfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>")) - call filter(s:netrwmarkfilelist_{curbufnr},'v:val != a:fname') - if s:netrwmarkfilelist_{curbufnr} == [] - " local markfilelist is empty; remove it entirely -" call Decho("markfile list now empty",'~'.expand("<slnum>")) - call s:NetrwUnmarkList(curbufnr,curdir) - else - " rebuild match list to display markings correctly -" call Decho("rebuild s:netrwmarkfilemtch_".curbufnr,'~'.expand("<slnum>")) - let s:netrwmarkfilemtch_{curbufnr}= "" - let first = 1 - for fname in s:netrwmarkfilelist_{curbufnr} - if first - let s:netrwmarkfilemtch_{curbufnr}= s:netrwmarkfilemtch_{curbufnr}.leader.escape(fname,g:netrw_markfileesc).trailer - else - let s:netrwmarkfilemtch_{curbufnr}= s:netrwmarkfilemtch_{curbufnr}.'\|'.leader.escape(fname,g:netrw_markfileesc).trailer - endif - let first= 0 - endfor -" call Decho("ending s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>")) - endif - endif - - else - " initialize new markfilelist -" call Decho("case: initialize new markfilelist",'~'.expand("<slnum>")) - -" call Decho("add fname<".a:fname."> to new markfilelist_".curbufnr,'~'.expand("<slnum>")) - let s:netrwmarkfilelist_{curbufnr}= [] - call add(s:netrwmarkfilelist_{curbufnr},substitute(a:fname,'[|@]$','','')) -" call Decho("ending s:netrwmarkfilelist_{curbufnr}<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>")) - - " build initial markfile matching pattern - if a:fname =~ '/$' - let s:netrwmarkfilemtch_{curbufnr}= leader.escape(a:fname,g:netrw_markfileesc) - else - let s:netrwmarkfilemtch_{curbufnr}= leader.escape(a:fname,g:netrw_markfileesc).trailer - endif -" call Decho("ending s:netrwmarkfilemtch_".curbufnr."<".s:netrwmarkfilemtch_{curbufnr}.">",'~'.expand("<slnum>")) - endif - - " handle global markfilelist - if exists("s:netrwmarkfilelist") - let dname= s:ComposePath(b:netrw_curdir,a:fname) - if index(s:netrwmarkfilelist,dname) == -1 - " append new filename to global markfilelist - call add(s:netrwmarkfilelist,s:ComposePath(b:netrw_curdir,a:fname)) -" call Decho("append filename<".a:fname."> to global s:markfilelist<".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>")) - else - " remove new filename from global markfilelist -" call Decho("remove new filename from global s:markfilelist",'~'.expand("<slnum>")) -" call Decho("..filter(".string(s:netrwmarkfilelist).",'v:val != '.".dname.")",'~'.expand("<slnum>")) - call filter(s:netrwmarkfilelist,'v:val != "'.dname.'"') -" call Decho("..ending s:netrwmarkfilelist <".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>")) - if s:netrwmarkfilelist == [] -" call Decho("s:netrwmarkfilelist is empty; unlet it",'~'.expand("<slnum>")) - unlet s:netrwmarkfilelist - endif - endif - else - " initialize new global-directory markfilelist - let s:netrwmarkfilelist= [] - call add(s:netrwmarkfilelist,s:ComposePath(b:netrw_curdir,a:fname)) -" call Decho("init s:netrwmarkfilelist<".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>")) - endif - - " set up 2match'ing to netrwmarkfilemtch_# list - if has("syntax") && exists("g:syntax_on") && g:syntax_on - if exists("s:netrwmarkfilemtch_{curbufnr}") && s:netrwmarkfilemtch_{curbufnr} != "" -" " call Decho("exe 2match netrwMarkFile /".s:netrwmarkfilemtch_{curbufnr}."/",'~'.expand("<slnum>")) - if exists("g:did_drchip_netrwlist_syntax") - exe "2match netrwMarkFile /".s:netrwmarkfilemtch_{curbufnr}."/" - endif - else -" " call Decho("2match none",'~'.expand("<slnum>")) - 2match none - endif - endif - let @@= ykeep -" call Decho("s:netrwmarkfilelist[".(exists("s:netrwmarkfilelist")? string(s:netrwmarkfilelist) : "")."] (avail in all buffers)",'~'.expand("<slnum>")) -" call Dret("s:NetrwMarkFile : s:netrwmarkfilelist_".curbufnr."<".(exists("s:netrwmarkfilelist_{curbufnr}")? string(s:netrwmarkfilelist_{curbufnr}) : " doesn't exist")."> (buf#".curbufnr."list)") -endfun - -" --------------------------------------------------------------------- -" s:NetrwMarkFileArgList: ma: move the marked file list to the argument list (tomflist=0) {{{2 -" mA: move the argument list to marked file list (tomflist=1) -" Uses the global marked file list -fun! s:NetrwMarkFileArgList(islocal,tomflist) - let svpos = winsaveview() - let curdir = s:NetrwGetCurdir(a:islocal) - let curbufnr = bufnr("%") - - if a:tomflist - " mA: move argument list to marked file list - while argc() - let fname= argv(0) - exe "argdel ".fnameescape(fname) - call s:NetrwMarkFile(a:islocal,fname) - endwhile - - else - " ma: move marked file list to argument list - if exists("s:netrwmarkfilelist") - - " for every filename in the marked list - for fname in s:netrwmarkfilelist - exe "argadd ".fnameescape(fname) - endfor " for every file in the marked list - - " unmark list and refresh - call s:NetrwUnmarkList(curbufnr,curdir) - NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) - NetrwKeepj call winrestview(svpos) - endif - endif - -endfun - -" --------------------------------------------------------------------- -" s:NetrwMarkFileCompress: (invoked by mz) This function is used to {{{2 -" compress/decompress files using the programs -" in g:netrw_compress and g:netrw_uncompress, -" using g:netrw_compress_suffix to know which to -" do. By default: -" g:netrw_compress = "gzip" -" g:netrw_decompress = { ".gz" : "gunzip" , ".bz2" : "bunzip2" , ".zip" : "unzip" , ".tar" : "tar -xf", ".xz" : "unxz"} -fun! s:NetrwMarkFileCompress(islocal) - let svpos = winsaveview() - let curdir = s:NetrwGetCurdir(a:islocal) - let curbufnr = bufnr("%") - - " sanity check - if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) - NetrwKeepj call netrw#ErrorMsg(2,"there are no marked files in this window (:help netrw-mf)",66) - return - endif - - if exists("s:netrwmarkfilelist_{curbufnr}") && exists("g:netrw_compress") && exists("g:netrw_decompress") - - " for every filename in the marked list - for fname in s:netrwmarkfilelist_{curbufnr} - let sfx= substitute(fname,'^.\{-}\(\.\a\+\)$','\1','') - if exists("g:netrw_decompress['".sfx."']") - " fname has a suffix indicating that its compressed; apply associated decompression routine - let exe= g:netrw_decompress[sfx] - let exe= netrw#WinPath(exe) - if a:islocal - if g:netrw_keepdir - let fname= s:ShellEscape(s:ComposePath(curdir,fname)) - endif - call system(exe." ".fname) - if v:shell_error - NetrwKeepj call netrw#ErrorMsg(s:WARNING,"unable to apply<".exe."> to file<".fname.">",50) - endif - else - let fname= s:ShellEscape(b:netrw_curdir.fname,1) - NetrwKeepj call s:RemoteSystem(exe." ".fname) - endif - - endif - unlet sfx - - if exists("exe") - unlet exe - elseif a:islocal - " fname not a compressed file, so compress it - call system(netrw#WinPath(g:netrw_compress)." ".s:ShellEscape(s:ComposePath(b:netrw_curdir,fname))) - if v:shell_error - call netrw#ErrorMsg(s:WARNING,"consider setting g:netrw_compress<".g:netrw_compress."> to something that works",104) - endif - else - " fname not a compressed file, so compress it - NetrwKeepj call s:RemoteSystem(netrw#WinPath(g:netrw_compress)." ".s:ShellEscape(fname)) - endif - endfor " for every file in the marked list - - call s:NetrwUnmarkList(curbufnr,curdir) - NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) - NetrwKeepj call winrestview(svpos) - endif -endfun - -" --------------------------------------------------------------------- -" s:NetrwMarkFileCopy: (invoked by mc) copy marked files to target {{{2 -" If no marked files, then set up directory as the -" target. Currently does not support copying entire -" directories. Uses the local-buffer marked file list. -" Returns 1=success (used by NetrwMarkFileMove()) -" 0=failure -fun! s:NetrwMarkFileCopy(islocal,...) -" call Dfunc("s:NetrwMarkFileCopy(islocal=".a:islocal.") target<".(exists("s:netrwmftgt")? s:netrwmftgt : '---')."> a:0=".a:0) - - let curdir = s:NetrwGetCurdir(a:islocal) - let curbufnr = bufnr("%") - if b:netrw_curdir !~ '/$' - if !exists("b:netrw_curdir") - let b:netrw_curdir= curdir - endif - let b:netrw_curdir= b:netrw_curdir."/" - endif - - " sanity check - if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) - NetrwKeepj call netrw#ErrorMsg(2,"there are no marked files in this window (:help netrw-mf)",66) -" call Dret("s:NetrwMarkFileCopy") - return - endif -" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>")) - - if !exists("s:netrwmftgt") - NetrwKeepj call netrw#ErrorMsg(s:ERROR,"your marked file target is empty! (:help netrw-mt)",67) -" call Dret("s:NetrwMarkFileCopy 0") - return 0 - endif -" call Decho("sanity chk passed: s:netrwmftgt<".s:netrwmftgt.">",'~'.expand("<slnum>")) - - if a:islocal && s:netrwmftgt_islocal - " Copy marked files, local directory to local directory -" call Decho("copy from local to local",'~'.expand("<slnum>")) - if !executable(g:netrw_localcopycmd) - call netrw#ErrorMsg(s:ERROR,"g:netrw_localcopycmd<".g:netrw_localcopycmd."> not executable on your system, aborting",91) -" call Dfunc("s:NetrwMarkFileMove : g:netrw_localcopycmd<".g:netrw_localcopycmd."> n/a!") - return - endif - - " copy marked files while within the same directory (ie. allow renaming) - if s:StripTrailingSlash(simplify(s:netrwmftgt)) == s:StripTrailingSlash(simplify(b:netrw_curdir)) - if len(s:netrwmarkfilelist_{bufnr('%')}) == 1 - " only one marked file -" call Decho("case: only one marked file",'~'.expand("<slnum>")) - let args = s:ShellEscape(b:netrw_curdir.s:netrwmarkfilelist_{bufnr('%')}[0]) - let oldname = s:netrwmarkfilelist_{bufnr('%')}[0] - elseif a:0 == 1 -" call Decho("case: handling one input argument",'~'.expand("<slnum>")) - " this happens when the next case was used to recursively call s:NetrwMarkFileCopy() - let args = s:ShellEscape(b:netrw_curdir.a:1) - let oldname = a:1 - else - " copy multiple marked files inside the same directory -" call Decho("case: handling a multiple marked files",'~'.expand("<slnum>")) - let s:recursive= 1 - for oldname in s:netrwmarkfilelist_{bufnr("%")} - let ret= s:NetrwMarkFileCopy(a:islocal,oldname) - if ret == 0 - break - endif - endfor - unlet s:recursive - call s:NetrwUnmarkList(curbufnr,curdir) -" call Dret("s:NetrwMarkFileCopy ".ret) - return ret - endif - - call inputsave() - let newname= input("Copy ".oldname." to : ",oldname,"file") - call inputrestore() - if newname == "" -" call Dret("s:NetrwMarkFileCopy 0") - return 0 - endif - let args= s:ShellEscape(oldname) - let tgt = s:ShellEscape(s:netrwmftgt.'/'.newname) - else - let args= join(map(deepcopy(s:netrwmarkfilelist_{bufnr('%')}),"s:ShellEscape(b:netrw_curdir.\"/\".v:val)")) - let tgt = s:ShellEscape(s:netrwmftgt) - endif - if !g:netrw_cygwin && has("win32") - let args= substitute(args,'/','\\','g') - let tgt = substitute(tgt, '/','\\','g') - endif - if args =~ "'" |let args= substitute(args,"'\\(.*\\)'",'\1','')|endif - if tgt =~ "'" |let tgt = substitute(tgt ,"'\\(.*\\)'",'\1','')|endif - if args =~ '//'|let args= substitute(args,'//','/','g')|endif - if tgt =~ '//'|let tgt = substitute(tgt ,'//','/','g')|endif -" call Decho("args <".args.">",'~'.expand("<slnum>")) -" call Decho("tgt <".tgt.">",'~'.expand("<slnum>")) - if isdirectory(s:NetrwFile(args)) -" call Decho("args<".args."> is a directory",'~'.expand("<slnum>")) - let copycmd= g:netrw_localcopydircmd -" call Decho("using copydircmd<".copycmd.">",'~'.expand("<slnum>")) - if !g:netrw_cygwin && has("win32") - " window's xcopy doesn't copy a directory to a target properly. Instead, it copies a directory's - " contents to a target. One must append the source directory name to the target to get xcopy to - " do the right thing. - let tgt= tgt.'\'.substitute(a:1,'^.*[\\/]','','') -" call Decho("modified tgt for xcopy",'~'.expand("<slnum>")) - endif - else - let copycmd= g:netrw_localcopycmd - endif - if g:netrw_localcopycmd =~ '\s' - let copycmd = substitute(copycmd,'\s.*$','','') - let copycmdargs = substitute(copycmd,'^.\{-}\(\s.*\)$','\1','') - let copycmd = netrw#WinPath(copycmd).copycmdargs - else - let copycmd = netrw#WinPath(copycmd) - endif -" call Decho("args <".args.">",'~'.expand("<slnum>")) -" call Decho("tgt <".tgt.">",'~'.expand("<slnum>")) -" call Decho("copycmd<".copycmd.">",'~'.expand("<slnum>")) -" call Decho("system(".copycmd." '".args."' '".tgt."')",'~'.expand("<slnum>")) - call system(copycmd.g:netrw_localcopycmdopt." '".args."' '".tgt."'") - if v:shell_error != 0 - if exists("b:netrw_curdir") && b:netrw_curdir != getcwd() && g:netrw_keepdir - call netrw#ErrorMsg(s:ERROR,"copy failed; perhaps due to vim's current directory<".getcwd()."> not matching netrw's (".b:netrw_curdir.") (see :help netrw-cd)",101) - else - call netrw#ErrorMsg(s:ERROR,"tried using g:netrw_localcopycmd<".g:netrw_localcopycmd.">; it doesn't work!",80) - endif -" call Dret("s:NetrwMarkFileCopy 0 : failed: system(".g:netrw_localcopycmd." ".args." ".s:ShellEscape(s:netrwmftgt)) - return 0 - endif - - elseif a:islocal && !s:netrwmftgt_islocal - " Copy marked files, local directory to remote directory -" call Decho("copy from local to remote",'~'.expand("<slnum>")) - NetrwKeepj call s:NetrwUpload(s:netrwmarkfilelist_{bufnr('%')},s:netrwmftgt) - - elseif !a:islocal && s:netrwmftgt_islocal - " Copy marked files, remote directory to local directory -" call Decho("copy from remote to local",'~'.expand("<slnum>")) - NetrwKeepj call netrw#Obtain(a:islocal,s:netrwmarkfilelist_{bufnr('%')},s:netrwmftgt) - - elseif !a:islocal && !s:netrwmftgt_islocal - " Copy marked files, remote directory to remote directory -" call Decho("copy from remote to remote",'~'.expand("<slnum>")) - let curdir = getcwd() - let tmpdir = s:GetTempfile("") - if tmpdir !~ '/' - let tmpdir= curdir."/".tmpdir - endif - if exists("*mkdir") - call mkdir(tmpdir) - else - call s:NetrwExe("sil! !".g:netrw_localmkdir.g:netrw_localmkdiropt.' '.s:ShellEscape(tmpdir,1)) - if v:shell_error != 0 - call netrw#ErrorMsg(s:WARNING,"consider setting g:netrw_localmkdir<".g:netrw_localmkdir."> to something that works",80) -" call Dret("s:NetrwMarkFileCopy : failed: sil! !".g:netrw_localmkdir.' '.s:ShellEscape(tmpdir,1) ) - return - endif - endif - if isdirectory(s:NetrwFile(tmpdir)) - if s:NetrwLcd(tmpdir) -" call Dret("s:NetrwMarkFileCopy : lcd failure") - return - endif - NetrwKeepj call netrw#Obtain(a:islocal,s:netrwmarkfilelist_{bufnr('%')},tmpdir) - let localfiles= map(deepcopy(s:netrwmarkfilelist_{bufnr('%')}),'substitute(v:val,"^.*/","","")') - NetrwKeepj call s:NetrwUpload(localfiles,s:netrwmftgt) - if getcwd() == tmpdir - for fname in s:netrwmarkfilelist_{bufnr('%')} - NetrwKeepj call s:NetrwDelete(fname) - endfor - if s:NetrwLcd(curdir) -" call Dret("s:NetrwMarkFileCopy : lcd failure") - return - endif - if delete(tmpdir,"d") - call netrw#ErrorMsg(s:ERROR,"unable to delete directory <".tmpdir.">!",103) - endif - else - if s:NetrwLcd(curdir) -" call Dret("s:NetrwMarkFileCopy : lcd failure") - return - endif - endif - endif - endif - - " ------- - " 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>")) -" call Decho(" curdir =".curdir,'~'.expand("<slnum>")) -" call Decho(" a:islocal =".a:islocal,'~'.expand("<slnum>")) -" call Decho(" curbufnr =".curbufnr,'~'.expand("<slnum>")) - if exists("s:recursive") -" call Decho(" s:recursive =".s:recursive,'~'.expand("<slnum>")) - else -" call Decho(" s:recursive =n/a",'~'.expand("<slnum>")) - endif - " see s:LocalFastBrowser() for g:netrw_fastbrowse interpretation (refreshing done for both slow and medium) - if g:netrw_fastbrowse <= 1 - NetrwKeepj call s:LocalBrowseRefresh() - else - " refresh local and targets for fast browsing - if !exists("s:recursive") - " remove markings from local buffer -" call Decho(" remove markings from local buffer",'~'.expand("<slnum>")) - NetrwKeepj call s:NetrwUnmarkList(curbufnr,curdir) - endif - - " refresh buffers - if s:netrwmftgt_islocal -" call Decho(" refresh s:netrwmftgt=".s:netrwmftgt,'~'.expand("<slnum>")) - NetrwKeepj call s:NetrwRefreshDir(s:netrwmftgt_islocal,s:netrwmftgt) - endif - if a:islocal && s:netrwmftgt != curdir -" call Decho(" refresh curdir=".curdir,'~'.expand("<slnum>")) - NetrwKeepj call s:NetrwRefreshDir(a:islocal,curdir) - endif - endif - -" call Dret("s:NetrwMarkFileCopy 1") - return 1 -endfun - -" --------------------------------------------------------------------- -" s:NetrwMarkFileDiff: (invoked by md) This function is used to {{{2 -" invoke vim's diff mode on the marked files. -" Either two or three files can be so handled. -" Uses the global marked file list. -fun! s:NetrwMarkFileDiff(islocal) -" call Dfunc("s:NetrwMarkFileDiff(islocal=".a:islocal.") b:netrw_curdir<".b:netrw_curdir.">") - let curbufnr= bufnr("%") - - " sanity check - if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) - NetrwKeepj call netrw#ErrorMsg(2,"there are no marked files in this window (:help netrw-mf)",66) -" call Dret("s:NetrwMarkFileDiff") - return - endif - let curdir= s:NetrwGetCurdir(a:islocal) -" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>")) - - if exists("s:netrwmarkfilelist_{".curbufnr."}") - let cnt = 0 - for fname in s:netrwmarkfilelist - let cnt= cnt + 1 - if cnt == 1 -" call Decho("diffthis: fname<".fname.">",'~'.expand("<slnum>")) - exe "NetrwKeepj e ".fnameescape(fname) - diffthis - elseif cnt == 2 || cnt == 3 - below vsplit -" call Decho("diffthis: ".fname,'~'.expand("<slnum>")) - exe "NetrwKeepj e ".fnameescape(fname) - diffthis - else - break - endif - endfor - call s:NetrwUnmarkList(curbufnr,curdir) - endif - -" call Dret("s:NetrwMarkFileDiff") -endfun - -" --------------------------------------------------------------------- -" s:NetrwMarkFileEdit: (invoked by me) put marked files on arg list and start editing them {{{2 -" Uses global markfilelist -fun! s:NetrwMarkFileEdit(islocal) -" call Dfunc("s:NetrwMarkFileEdit(islocal=".a:islocal.")") - - let curdir = s:NetrwGetCurdir(a:islocal) - let curbufnr = bufnr("%") - - " sanity check - if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) - NetrwKeepj call netrw#ErrorMsg(2,"there are no marked files in this window (:help netrw-mf)",66) -" call Dret("s:NetrwMarkFileEdit") - return - endif -" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>")) - - if exists("s:netrwmarkfilelist_{curbufnr}") - call s:SetRexDir(a:islocal,curdir) - let flist= join(map(deepcopy(s:netrwmarkfilelist), "fnameescape(v:val)")) - " unmark markedfile list -" call s:NetrwUnmarkList(curbufnr,curdir) - call s:NetrwUnmarkAll() -" call Decho("exe sil args ".flist,'~'.expand("<slnum>")) - exe "sil args ".flist - endif - echo "(use :bn, :bp to navigate files; :Rex to return)" - -" call Dret("s:NetrwMarkFileEdit") -endfun - -" --------------------------------------------------------------------- -" s:NetrwMarkFileQFEL: convert a quickfix-error or location list into a marked file list {{{2 -fun! s:NetrwMarkFileQFEL(islocal,qfel) -" call Dfunc("s:NetrwMarkFileQFEL(islocal=".a:islocal.",qfel)") - call s:NetrwUnmarkAll() - let curbufnr= bufnr("%") - - if !empty(a:qfel) - for entry in a:qfel - let bufnmbr= entry["bufnr"] -" call Decho("bufname(".bufnmbr.")<".bufname(bufnmbr)."> line#".entry["lnum"]." text=".entry["text"],'~'.expand("<slnum>")) - if !exists("s:netrwmarkfilelist_{curbufnr}") -" call Decho("case: no marked file list",'~'.expand("<slnum>")) - call s:NetrwMarkFile(a:islocal,bufname(bufnmbr)) - elseif index(s:netrwmarkfilelist_{curbufnr},bufname(bufnmbr)) == -1 - " s:NetrwMarkFile will remove duplicate entries from the marked file list. - " So, this test lets two or more hits on the same pattern to be ignored. -" call Decho("case: ".bufname(bufnmbr)." not currently in marked file list",'~'.expand("<slnum>")) - call s:NetrwMarkFile(a:islocal,bufname(bufnmbr)) - else -" call Decho("case: ".bufname(bufnmbr)." already in marked file list",'~'.expand("<slnum>")) - endif - endfor - echo "(use me to edit marked files)" - else - call netrw#ErrorMsg(s:WARNING,"can't convert quickfix error list; its empty!",92) - endif - -" call Dret("s:NetrwMarkFileQFEL") -endfun - -" --------------------------------------------------------------------- -" s:NetrwMarkFileExe: (invoked by mx and mX) execute arbitrary system command on marked files {{{2 -" mx enbloc=0: Uses the local marked-file list, applies command to each file individually -" mX enbloc=1: Uses the global marked-file list, applies command to entire list -fun! s:NetrwMarkFileExe(islocal,enbloc) - let svpos = winsaveview() - let curdir = s:NetrwGetCurdir(a:islocal) - let curbufnr = bufnr("%") - - if a:enbloc == 0 - " individually apply command to files, one at a time - " sanity check - if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) - NetrwKeepj call netrw#ErrorMsg(2,"there are no marked files in this window (:help netrw-mf)",66) - return - endif - - if exists("s:netrwmarkfilelist_{curbufnr}") - " get the command - call inputsave() - let cmd= input("Enter command: ","","file") - call inputrestore() - if cmd == "" - return - endif - - " apply command to marked files, individually. Substitute: filename -> % - " If no %, then append a space and the filename to the command - for fname in s:netrwmarkfilelist_{curbufnr} - if a:islocal - if g:netrw_keepdir - let fname= s:ShellEscape(netrw#WinPath(s:ComposePath(curdir,fname))) - endif - else - let fname= s:ShellEscape(netrw#WinPath(b:netrw_curdir.fname)) - endif - if cmd =~ '%' - let xcmd= substitute(cmd,'%',fname,'g') - else - let xcmd= cmd.' '.fname - endif - if a:islocal - let ret= system(xcmd) - else - let ret= s:RemoteSystem(xcmd) - endif - if v:shell_error < 0 - NetrwKeepj call netrw#ErrorMsg(s:ERROR,"command<".xcmd."> failed, aborting",54) - break - else - if ret !=# '' - echo "\n" - " skip trailing new line - echo ret[0:-2] - else - echo ret - endif - endif - endfor - - " unmark marked file list - call s:NetrwUnmarkList(curbufnr,curdir) - - " refresh the listing - NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) - NetrwKeepj call winrestview(svpos) - else - NetrwKeepj call netrw#ErrorMsg(s:ERROR,"no files marked!",59) - endif - - else " apply command to global list of files, en bloc - - call inputsave() - let cmd= input("Enter command: ","","file") - call inputrestore() - if cmd == "" - return - endif - if cmd =~ '%' - let cmd= substitute(cmd,'%',join(map(s:netrwmarkfilelist,'s:ShellEscape(v:val)'),' '),'g') - else - let cmd= cmd.' '.join(map(s:netrwmarkfilelist,'s:ShellEscape(v:val)'),' ') - endif - if a:islocal - call system(cmd) - if v:shell_error < 0 - NetrwKeepj call netrw#ErrorMsg(s:ERROR,"command<".xcmd."> failed, aborting",54) - endif - else - let ret= s:RemoteSystem(cmd) - endif - call s:NetrwUnmarkAll() - - " refresh the listing - NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) - NetrwKeepj call winrestview(svpos) - - endif -endfun - -" --------------------------------------------------------------------- -" s:NetrwMarkHideSfx: (invoked by mh) (un)hide files having same suffix -" as the marked file(s) (toggles suffix presence) -" Uses the local marked file list. -fun! s:NetrwMarkHideSfx(islocal) - let svpos = winsaveview() - let curbufnr = bufnr("%") - - " s:netrwmarkfilelist_{curbufnr}: the List of marked files - if exists("s:netrwmarkfilelist_{curbufnr}") - - for fname in s:netrwmarkfilelist_{curbufnr} - " construct suffix pattern - if fname =~ '\.' - let sfxpat= "^.*".substitute(fname,'^.*\(\.[^. ]\+\)$','\1','') - else - let sfxpat= '^\%(\%(\.\)\@!.\)*$' - endif - " determine if its in the hiding list or not - let inhidelist= 0 - if g:netrw_list_hide != "" - let itemnum = 0 - let hidelist= split(g:netrw_list_hide,',') - for hidepat in hidelist - if sfxpat == hidepat - let inhidelist= 1 - break - endif - let itemnum= itemnum + 1 - endfor - endif - if inhidelist - " remove sfxpat from list - call remove(hidelist,itemnum) - let g:netrw_list_hide= join(hidelist,",") - elseif g:netrw_list_hide != "" - " append sfxpat to non-empty list - let g:netrw_list_hide= g:netrw_list_hide.",".sfxpat - else - " set hiding list to sfxpat - let g:netrw_list_hide= sfxpat - endif - endfor - - " refresh the listing - NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) - NetrwKeepj call winrestview(svpos) - else - NetrwKeepj call netrw#ErrorMsg(s:ERROR,"no files marked!",59) - endif -endfun - -" --------------------------------------------------------------------- -" s:NetrwMarkFileVimCmd: (invoked by mv) execute arbitrary vim command on marked files, one at a time {{{2 -" Uses the local marked-file list. -fun! s:NetrwMarkFileVimCmd(islocal) - let svpos = winsaveview() - let curdir = s:NetrwGetCurdir(a:islocal) - let curbufnr = bufnr("%") - - " sanity check - if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) - NetrwKeepj call netrw#ErrorMsg(2,"there are no marked files in this window (:help netrw-mf)",66) - return - endif - - if exists("s:netrwmarkfilelist_{curbufnr}") - " get the command - call inputsave() - let cmd= input("Enter vim command: ","","file") - call inputrestore() - if cmd == "" - return - endif - - " apply command to marked files. Substitute: filename -> % - " If no %, then append a space and the filename to the command - for fname in s:netrwmarkfilelist_{curbufnr} - if a:islocal - 1split - exe "sil! NetrwKeepj keepalt e ".fnameescape(fname) - exe cmd - exe "sil! keepalt wq!" - else - echo "sorry, \"mv\" not supported yet for remote files" - endif - endfor - - " unmark marked file list - call s:NetrwUnmarkList(curbufnr,curdir) - - " refresh the listing - NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) - NetrwKeepj call winrestview(svpos) - else - NetrwKeepj call netrw#ErrorMsg(s:ERROR,"no files marked!",59) - endif -endfun - -" --------------------------------------------------------------------- -" s:NetrwMarkHideSfx: (invoked by mh) (un)hide files having same suffix -" as the marked file(s) (toggles suffix presence) -" Uses the local marked file list. -fun! s:NetrwMarkHideSfx(islocal) - let svpos = winsaveview() - let curbufnr = bufnr("%") - - " s:netrwmarkfilelist_{curbufnr}: the List of marked files - if exists("s:netrwmarkfilelist_{curbufnr}") - - for fname in s:netrwmarkfilelist_{curbufnr} - " construct suffix pattern - if fname =~ '\.' - let sfxpat= "^.*".substitute(fname,'^.*\(\.[^. ]\+\)$','\1','') - else - let sfxpat= '^\%(\%(\.\)\@!.\)*$' - endif - " determine if its in the hiding list or not - let inhidelist= 0 - if g:netrw_list_hide != "" - let itemnum = 0 - let hidelist= split(g:netrw_list_hide,',') - for hidepat in hidelist - if sfxpat == hidepat - let inhidelist= 1 - break - endif - let itemnum= itemnum + 1 - endfor - endif - if inhidelist - " remove sfxpat from list - call remove(hidelist,itemnum) - let g:netrw_list_hide= join(hidelist,",") - elseif g:netrw_list_hide != "" - " append sfxpat to non-empty list - let g:netrw_list_hide= g:netrw_list_hide.",".sfxpat - else - " set hiding list to sfxpat - let g:netrw_list_hide= sfxpat - endif - endfor - - " refresh the listing - NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) - NetrwKeepj call winrestview(svpos) - else - NetrwKeepj call netrw#ErrorMsg(s:ERROR,"no files marked!",59) - endif -endfun - -" --------------------------------------------------------------------- -" s:NetrwMarkFileGrep: (invoked by mg) This function applies vimgrep to marked files {{{2 -" Uses the global markfilelist -fun! s:NetrwMarkFileGrep(islocal) -" call Dfunc("s:NetrwMarkFileGrep(islocal=".a:islocal.")") - let svpos = winsaveview() -" call Decho("saving posn to svpos<".string(svpos).">",'~'.expand("<slnum>")) - let curbufnr = bufnr("%") - let curdir = s:NetrwGetCurdir(a:islocal) - - if exists("s:netrwmarkfilelist") -" call Decho("using s:netrwmarkfilelist".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>")) - let netrwmarkfilelist= join(map(deepcopy(s:netrwmarkfilelist), "fnameescape(v:val)")) -" call Decho("keeping copy of s:netrwmarkfilelist in function-local variable,'~'.expand("<slnum>"))" - call s:NetrwUnmarkAll() - else -" call Decho('no marked files, using "*"','~'.expand("<slnum>")) - let netrwmarkfilelist= "*" - endif - - " ask user for pattern -" call Decho("ask user for search pattern",'~'.expand("<slnum>")) - call inputsave() - let pat= input("Enter pattern: ","") - call inputrestore() - let patbang = "" - if pat =~ '^!' - let patbang = "!" - let pat = strpart(pat,2) - endif - if pat =~ '^\i' - let pat = escape(pat,'/') - let pat = '/'.pat.'/' - else - let nonisi = pat[0] - endif - - " use vimgrep for both local and remote -" call Decho("exe vimgrep".patbang." ".pat." ".netrwmarkfilelist,'~'.expand("<slnum>")) - try - exe "NetrwKeepj noautocmd vimgrep".patbang." ".pat." ".netrwmarkfilelist - catch /^Vim\%((\a\+)\)\=:E480/ - NetrwKeepj call netrw#ErrorMsg(s:WARNING,"no match with pattern<".pat.">",76) -" call Dret("s:NetrwMarkFileGrep : unable to find pattern<".pat.">") - return - endtry - echo "(use :cn, :cp to navigate, :Rex to return)" - - 2match none -" call Decho("restoring posn to svpos<".string(svpos).">",'~'.expand("<slnum>")) - NetrwKeepj call winrestview(svpos) - - 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$' - call s:NetrwMarkFileQFEL(a:islocal,getqflist()) - endif - endif - -" call Dret("s:NetrwMarkFileGrep") -endfun - -" --------------------------------------------------------------------- -" s:NetrwMarkFileMove: (invoked by mm) execute arbitrary command on marked files, one at a time {{{2 -" uses the global marked file list -" s:netrwmfloc= 0: target directory is remote -" = 1: target directory is local -fun! s:NetrwMarkFileMove(islocal) -" call Dfunc("s:NetrwMarkFileMove(islocal=".a:islocal.")") - let curdir = s:NetrwGetCurdir(a:islocal) - let curbufnr = bufnr("%") - - " sanity check - if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) - NetrwKeepj call netrw#ErrorMsg(2,"there are no marked files in this window (:help netrw-mf)",66) -" call Dret("s:NetrwMarkFileMove") - return - endif -" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>")) - - if !exists("s:netrwmftgt") - NetrwKeepj call netrw#ErrorMsg(2,"your marked file target is empty! (:help netrw-mt)",67) -" call Dret("s:NetrwMarkFileCopy 0") - return 0 - endif -" call Decho("sanity chk passed: s:netrwmftgt<".s:netrwmftgt.">",'~'.expand("<slnum>")) - - if a:islocal && s:netrwmftgt_islocal - " move: local -> local -" call Decho("move from local to local",'~'.expand("<slnum>")) -" call Decho("local to local move",'~'.expand("<slnum>")) - if !executable(g:netrw_localmovecmd) - call netrw#ErrorMsg(s:ERROR,"g:netrw_localmovecmd<".g:netrw_localmovecmd."> not executable on your system, aborting",90) -" call Dfunc("s:NetrwMarkFileMove : g:netrw_localmovecmd<".g:netrw_localmovecmd."> n/a!") - return - endif - let tgt = s:ShellEscape(s:netrwmftgt) -" call Decho("tgt<".tgt.">",'~'.expand("<slnum>")) - if !g:netrw_cygwin && has("win32") - let tgt= substitute(tgt, '/','\\','g') -" call Decho("windows exception: tgt<".tgt.">",'~'.expand("<slnum>")) - if g:netrw_localmovecmd =~ '\s' - let movecmd = substitute(g:netrw_localmovecmd,'\s.*$','','') - let movecmdargs = substitute(g:netrw_localmovecmd,'^.\{-}\(\s.*\)$','\1','') - let movecmd = netrw#WinPath(movecmd).movecmdargs -" call Decho("windows exception: movecmd<".movecmd."> (#1: had a space)",'~'.expand("<slnum>")) - else - let movecmd = netrw#WinPath(g:netrw_localmovecmd) -" call Decho("windows exception: movecmd<".movecmd."> (#2: no space)",'~'.expand("<slnum>")) - endif - else - let movecmd = netrw#WinPath(g:netrw_localmovecmd) -" call Decho("movecmd<".movecmd."> (#3 linux or cygwin)",'~'.expand("<slnum>")) - endif - for fname in s:netrwmarkfilelist_{bufnr("%")} - if g:netrw_keepdir - " Jul 19, 2022: fixing file move when g:netrw_keepdir is 1 - let fname= b:netrw_curdir."/".fname - endif - if !g:netrw_cygwin && has("win32") - let fname= substitute(fname,'/','\\','g') - endif -" call Decho("system(".movecmd." ".s:ShellEscape(fname)." ".tgt.")",'~'.expand("<slnum>")) - let ret= system(movecmd.g:netrw_localmovecmdopt." ".s:ShellEscape(fname)." ".tgt) - if v:shell_error != 0 - if exists("b:netrw_curdir") && b:netrw_curdir != getcwd() && !g:netrw_keepdir - call netrw#ErrorMsg(s:ERROR,"move failed; perhaps due to vim's current directory<".getcwd()."> not matching netrw's (".b:netrw_curdir.") (see :help netrw-cd)",100) - else - call netrw#ErrorMsg(s:ERROR,"tried using g:netrw_localmovecmd<".g:netrw_localmovecmd.">; it doesn't work!",54) - endif - break - endif - endfor - - elseif a:islocal && !s:netrwmftgt_islocal - " move: local -> remote -" call Decho("move from local to remote",'~'.expand("<slnum>")) -" call Decho("copy",'~'.expand("<slnum>")) - let mflist= s:netrwmarkfilelist_{bufnr("%")} - NetrwKeepj call s:NetrwMarkFileCopy(a:islocal) -" call Decho("remove",'~'.expand("<slnum>")) - for fname in mflist - let barefname = substitute(fname,'^\(.*/\)\(.\{-}\)$','\2','') - let ok = s:NetrwLocalRmFile(b:netrw_curdir,barefname,1) - endfor - unlet mflist - - elseif !a:islocal && s:netrwmftgt_islocal - " move: remote -> local -" call Decho("move from remote to local",'~'.expand("<slnum>")) -" call Decho("copy",'~'.expand("<slnum>")) - let mflist= s:netrwmarkfilelist_{bufnr("%")} - NetrwKeepj call s:NetrwMarkFileCopy(a:islocal) -" call Decho("remove",'~'.expand("<slnum>")) - for fname in mflist - let barefname = substitute(fname,'^\(.*/\)\(.\{-}\)$','\2','') - let ok = s:NetrwRemoteRmFile(b:netrw_curdir,barefname,1) - endfor - unlet mflist - - elseif !a:islocal && !s:netrwmftgt_islocal - " move: remote -> remote -" call Decho("move from remote to remote",'~'.expand("<slnum>")) -" call Decho("copy",'~'.expand("<slnum>")) - let mflist= s:netrwmarkfilelist_{bufnr("%")} - NetrwKeepj call s:NetrwMarkFileCopy(a:islocal) -" call Decho("remove",'~'.expand("<slnum>")) - for fname in mflist - let barefname = substitute(fname,'^\(.*/\)\(.\{-}\)$','\2','') - let ok = s:NetrwRemoteRmFile(b:netrw_curdir,barefname,1) - endfor - unlet mflist - endif - - " ------- - " cleanup - " ------- -" call Decho("cleanup",'~'.expand("<slnum>")) - - " remove markings from local buffer - call s:NetrwUnmarkList(curbufnr,curdir) " remove markings from local buffer - - " refresh buffers - if !s:netrwmftgt_islocal -" call Decho("refresh netrwmftgt<".s:netrwmftgt.">",'~'.expand("<slnum>")) - NetrwKeepj call s:NetrwRefreshDir(s:netrwmftgt_islocal,s:netrwmftgt) - endif - if a:islocal -" call Decho("refresh b:netrw_curdir<".b:netrw_curdir.">",'~'.expand("<slnum>")) - NetrwKeepj call s:NetrwRefreshDir(a:islocal,b:netrw_curdir) - endif - if g:netrw_fastbrowse <= 1 -" call Decho("since g:netrw_fastbrowse=".g:netrw_fastbrowse.", perform shell cmd refresh",'~'.expand("<slnum>")) - NetrwKeepj call s:LocalBrowseRefresh() - endif - -" call Dret("s:NetrwMarkFileMove") -endfun - -" --------------------------------------------------------------------- -" s:NetrwMarkFilePrint: (invoked by mp) This function prints marked files {{{2 -" using the hardcopy command. Local marked-file list only. -fun! s:NetrwMarkFilePrint(islocal) -" call Dfunc("s:NetrwMarkFilePrint(islocal=".a:islocal.")") - let curbufnr= bufnr("%") - - " sanity check - if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) - NetrwKeepj call netrw#ErrorMsg(2,"there are no marked files in this window (:help netrw-mf)",66) -" call Dret("s:NetrwMarkFilePrint") - return - endif -" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>")) - let curdir= s:NetrwGetCurdir(a:islocal) - - if exists("s:netrwmarkfilelist_{curbufnr}") - let netrwmarkfilelist = s:netrwmarkfilelist_{curbufnr} - call s:NetrwUnmarkList(curbufnr,curdir) - for fname in netrwmarkfilelist - if a:islocal - if g:netrw_keepdir - let fname= s:ComposePath(curdir,fname) - endif - else - let fname= curdir.fname - endif - 1split - " the autocmds will handle both local and remote files -" call Decho("exe sil e ".escape(fname,' '),'~'.expand("<slnum>")) - exe "sil NetrwKeepj e ".fnameescape(fname) -" call Decho("hardcopy",'~'.expand("<slnum>")) - hardcopy - q - endfor - 2match none - endif -" call Dret("s:NetrwMarkFilePrint") -endfun - -" --------------------------------------------------------------------- -" s:NetrwMarkFileRegexp: (invoked by mr) This function is used to mark {{{2 -" files when given a regexp (for which a prompt is -" issued) (matches to name of files). -fun! s:NetrwMarkFileRegexp(islocal) -" call Dfunc("s:NetrwMarkFileRegexp(islocal=".a:islocal.")") - - " get the regular expression - call inputsave() - let regexp= input("Enter regexp: ","","file") - call inputrestore() - - if a:islocal - let curdir= s:NetrwGetCurdir(a:islocal) -" call Decho("curdir<".fnameescape(curdir).">") - " 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 || (v:version == 704 && has("patch656")) - let filelist= glob(s:ComposePath(dirname,regexp),0,1,1) - else - let files = glob(s:ComposePath(dirname,regexp),0,0) - let filelist= split(files,"\n") - endif -" call Decho("files<".string(filelist).">",'~'.expand("<slnum>")) - - " mark the list of files - for fname in filelist - if fname =~ '^'.fnameescape(curdir) -" call Decho("fname<".substitute(fname,'^'.fnameescape(curdir).'/','','').">",'~'.expand("<slnum>")) - NetrwKeepj call s:NetrwMarkFile(a:islocal,substitute(fname,'^'.fnameescape(curdir).'/','','')) - else -" call Decho("fname<".fname.">",'~'.expand("<slnum>")) - NetrwKeepj call s:NetrwMarkFile(a:islocal,substitute(fname,'^.*/','','')) - endif - endfor - - else -" call Decho("handle remote regexp",'~'.expand("<slnum>")) - - " convert displayed listing into a filelist - let eikeep = &ei - let areg = @a - sil NetrwKeepj %y a - setl ei=all ma -" call Decho("setl ei=all ma",'~'.expand("<slnum>")) - 1split - NetrwKeepj call s:NetrwEnew() - NetrwKeepj call s:NetrwOptionsSafe(a:islocal) - sil NetrwKeepj norm! "ap - NetrwKeepj 2 - let bannercnt= search('^" =====','W') - exe "sil NetrwKeepj 1,".bannercnt."d" - setl bt=nofile - if g:netrw_liststyle == s:LONGLIST - sil NetrwKeepj %s/\s\{2,}\S.*$//e - call histdel("/",-1) - elseif g:netrw_liststyle == s:WIDELIST - sil NetrwKeepj %s/\s\{2,}/\r/ge - call histdel("/",-1) - elseif g:netrw_liststyle == s:TREELIST - exe 'sil NetrwKeepj %s/^'.s:treedepthstring.' //e' - sil! NetrwKeepj g/^ .*$/d - call histdel("/",-1) - call histdel("/",-1) - endif - " convert regexp into the more usual glob-style format - let regexp= substitute(regexp,'\*','.*','g') -" call Decho("regexp<".regexp.">",'~'.expand("<slnum>")) - exe "sil! NetrwKeepj v/".escape(regexp,'/')."/d" - call histdel("/",-1) - let filelist= getline(1,line("$")) - q! - for filename in filelist - NetrwKeepj call s:NetrwMarkFile(a:islocal,substitute(filename,'^.*/','','')) - endfor - unlet filelist - let @a = areg - let &ei = eikeep - endif - echo " (use me to edit marked files)" - -" call Dret("s:NetrwMarkFileRegexp") -endfun - -" --------------------------------------------------------------------- -" s:NetrwMarkFileSource: (invoked by ms) This function sources marked files {{{2 -" Uses the local marked file list. -fun! s:NetrwMarkFileSource(islocal) -" call Dfunc("s:NetrwMarkFileSource(islocal=".a:islocal.")") - let curbufnr= bufnr("%") - - " sanity check - if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) - NetrwKeepj call netrw#ErrorMsg(2,"there are no marked files in this window (:help netrw-mf)",66) -" call Dret("s:NetrwMarkFileSource") - return - endif -" call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>")) - let curdir= s:NetrwGetCurdir(a:islocal) - - if exists("s:netrwmarkfilelist_{curbufnr}") - let netrwmarkfilelist = s:netrwmarkfilelist_{bufnr("%")} - call s:NetrwUnmarkList(curbufnr,curdir) - for fname in netrwmarkfilelist - if a:islocal - if g:netrw_keepdir - let fname= s:ComposePath(curdir,fname) - endif - else - let fname= curdir.fname - endif - " the autocmds will handle sourcing both local and remote files -" call Decho("exe so ".fnameescape(fname),'~'.expand("<slnum>")) - exe "so ".fnameescape(fname) - endfor - 2match none - endif -" call Dret("s:NetrwMarkFileSource") -endfun - -" --------------------------------------------------------------------- -" s:NetrwMarkFileTag: (invoked by mT) This function applies g:netrw_ctags to marked files {{{2 -" Uses the global markfilelist -fun! s:NetrwMarkFileTag(islocal) - let svpos = winsaveview() - let curdir = s:NetrwGetCurdir(a:islocal) - let curbufnr = bufnr("%") - - " sanity check - if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) - NetrwKeepj call netrw#ErrorMsg(2,"there are no marked files in this window (:help netrw-mf)",66) - return - endif - - if exists("s:netrwmarkfilelist") - let netrwmarkfilelist= join(map(deepcopy(s:netrwmarkfilelist), "s:ShellEscape(v:val,".!a:islocal.")")) - call s:NetrwUnmarkAll() - - if a:islocal - - call system(g:netrw_ctags." ".netrwmarkfilelist) - if v:shell_error - call netrw#ErrorMsg(s:ERROR,"g:netrw_ctags<".g:netrw_ctags."> is not executable!",51) - endif - - else - let cmd = s:RemoteSystem(g:netrw_ctags." ".netrwmarkfilelist) - call netrw#Obtain(a:islocal,"tags") - let curdir= b:netrw_curdir - 1split - NetrwKeepj e tags - let path= substitute(curdir,'^\(.*\)/[^/]*$','\1/','') - exe 'NetrwKeepj %s/\t\(\S\+\)\t/\t'.escape(path,"/\n\r\\").'\1\t/e' - call histdel("/",-1) - wq! - endif - 2match none - call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) - call winrestview(svpos) - endif -endfun - -" --------------------------------------------------------------------- -" s:NetrwMarkFileTgt: (invoked by mt) This function sets up a marked file target {{{2 -" Sets up two variables, -" s:netrwmftgt : holds the target directory -" s:netrwmftgt_islocal : 0=target directory is remote -" 1=target directory is local -fun! s:NetrwMarkFileTgt(islocal) - let svpos = winsaveview() - let curdir = s:NetrwGetCurdir(a:islocal) - let hadtgt = exists("s:netrwmftgt") - if !exists("w:netrw_bannercnt") - let w:netrw_bannercnt= b:netrw_bannercnt - endif - - " set up target - if line(".") < w:netrw_bannercnt - " if cursor in banner region, use b:netrw_curdir for the target unless its already the target - if exists("s:netrwmftgt") && exists("s:netrwmftgt_islocal") && s:netrwmftgt == b:netrw_curdir - unlet s:netrwmftgt s:netrwmftgt_islocal - if g:netrw_fastbrowse <= 1 - call s:LocalBrowseRefresh() - endif - call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) - call winrestview(svpos) - return - else - let s:netrwmftgt= b:netrw_curdir - endif - - else - " get word under cursor. - " * If directory, use it for the target. - " * If file, use b:netrw_curdir for the target - let curword= s:NetrwGetWord() - let tgtdir = s:ComposePath(curdir,curword) - if a:islocal && isdirectory(s:NetrwFile(tgtdir)) - let s:netrwmftgt = tgtdir - elseif !a:islocal && tgtdir =~ '/$' - let s:netrwmftgt = tgtdir - else - let s:netrwmftgt = curdir - endif - endif - if a:islocal - " simplify the target (eg. /abc/def/../ghi -> /abc/ghi) - let s:netrwmftgt= simplify(s:netrwmftgt) - endif - if g:netrw_cygwin - let s:netrwmftgt= substitute(system("cygpath ".s:ShellEscape(s:netrwmftgt)),'\n$','','') - let s:netrwmftgt= substitute(s:netrwmftgt,'\n$','','') - endif - let s:netrwmftgt_islocal= a: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 s:LocalBrowseRefresh() - endif -" call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) - if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST - call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,w:netrw_treetop,0)) - else - call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) - endif - call winrestview(svpos) - if !hadtgt - sil! NetrwKeepj norm! j - endif -endfun - -" --------------------------------------------------------------------- -" s:NetrwGetCurdir: gets current directory and sets up b:netrw_curdir if necessary {{{2 -fun! s:NetrwGetCurdir(islocal) -" call Dfunc("s:NetrwGetCurdir(islocal=".a:islocal.")") - - if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST - let b:netrw_curdir = s:NetrwTreePath(w:netrw_treetop) -" call Decho("set b:netrw_curdir<".b:netrw_curdir."> (used s:NetrwTreeDir)",'~'.expand("<slnum>")) - elseif !exists("b:netrw_curdir") - let b:netrw_curdir= getcwd() -" 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",'~'.expand("<slnum>")) - if b:netrw_curdir !~ '\<\a\{3,}://' - let curdir= b:netrw_curdir -" call Decho("g:netrw_keepdir=".g:netrw_keepdir,'~'.expand("<slnum>")) - if g:netrw_keepdir == 0 - call s:NetrwLcd(curdir) - endif - endif - -" call Dret("s:NetrwGetCurdir <".curdir.">") - return b:netrw_curdir -endfun - -" --------------------------------------------------------------------- -" s:NetrwOpenFile: query user for a filename and open it {{{2 -fun! s:NetrwOpenFile(islocal) -" call Dfunc("s:NetrwOpenFile(islocal=".a:islocal.")") - let ykeep= @@ - call inputsave() - let fname= input("Enter filename: ") - call inputrestore() -" call Decho("(s:NetrwOpenFile) fname<".fname.">",'~'.expand("<slnum>")) - - " determine if Lexplore is in use - if exists("t:netrw_lexbufnr") - " check if t:netrw_lexbufnr refers to a netrw window -" call Decho("(s:netrwOpenFile) ..t:netrw_lexbufnr=".t:netrw_lexbufnr,'~'.expand("<slnum>")) - let lexwinnr = bufwinnr(t:netrw_lexbufnr) - if lexwinnr != -1 && exists("g:netrw_chgwin") && g:netrw_chgwin != -1 -" call Decho("(s:netrwOpenFile) ..Lexplore in use",'~'.expand("<slnum>")) - exe "NetrwKeepj keepalt ".g:netrw_chgwin."wincmd w" - exe "NetrwKeepj e ".fnameescape(fname) - let @@= ykeep -" call Dret("s:NetrwOpenFile : creating a file with Lexplore mode") - endif - endif - - " Does the filename contain a path? - if fname !~ '[/\\]' - if exists("b:netrw_curdir") - if exists("g:netrw_quiet") - let netrw_quiet_keep = g:netrw_quiet - endif - let g:netrw_quiet = 1 - " save position for benefit of Rexplore - let s:rexposn_{bufnr("%")}= winsaveview() -" call Decho("saving posn to s:rexposn_".bufnr("%")."<".string(s:rexposn_{bufnr("%")}).">",'~'.expand("<slnum>")) - if b:netrw_curdir =~ '/$' - exe "NetrwKeepj e ".fnameescape(b:netrw_curdir.fname) - else - exe "e ".fnameescape(b:netrw_curdir."/".fname) - endif - if exists("netrw_quiet_keep") - let g:netrw_quiet= netrw_quiet_keep - else - unlet g:netrw_quiet - endif - endif - else - exe "NetrwKeepj e ".fnameescape(fname) - endif - let @@= ykeep -" call Dret("s:NetrwOpenFile") -endfun - -" --------------------------------------------------------------------- -" netrw#Shrink: shrinks/expands a netrw or Lexplorer window {{{2 -" For the mapping to this function be made via -" netrwPlugin, you'll need to have had -" g:netrw_usetab set to non-zero. -fun! netrw#Shrink() -" call Dfunc("netrw#Shrink() ft<".&ft."> winwidth=".winwidth(0)." lexbuf#".((exists("t:netrw_lexbufnr"))? t:netrw_lexbufnr : 'n/a')) - let curwin = winnr() - let wiwkeep = &wiw - set wiw=1 - - if &ft == "netrw" - if winwidth(0) > g:netrw_wiw - let t:netrw_winwidth= winwidth(0) - exe "vert resize ".g:netrw_wiw - wincmd l - if winnr() == curwin - wincmd h - endif -" call Decho("vert resize 0",'~'.expand("<slnum>")) - else - exe "vert resize ".t:netrw_winwidth -" call Decho("vert resize ".t:netrw_winwidth,'~'.expand("<slnum>")) - endif - - elseif exists("t:netrw_lexbufnr") - exe bufwinnr(t:netrw_lexbufnr)."wincmd w" - if winwidth(bufwinnr(t:netrw_lexbufnr)) > g:netrw_wiw - let t:netrw_winwidth= winwidth(0) - exe "vert resize ".g:netrw_wiw - wincmd l - if winnr() == curwin - wincmd h - endif -" call Decho("vert resize 0",'~'.expand("<slnum>")) - elseif winwidth(bufwinnr(t:netrw_lexbufnr)) >= 0 - exe "vert resize ".t:netrw_winwidth -" call Decho("vert resize ".t:netrw_winwidth,'~'.expand("<slnum>")) - else - call netrw#Lexplore(0,0) - endif - - else - call netrw#Lexplore(0,0) - endif - let wiw= wiwkeep - -" call Dret("netrw#Shrink") -endfun - -" --------------------------------------------------------------------- -" s:NetSortSequence: allows user to edit the sorting sequence {{{2 -fun! s:NetSortSequence(islocal) - let ykeep= @@ - let svpos= winsaveview() - call inputsave() - let newsortseq= input("Edit Sorting Sequence: ",g:netrw_sort_sequence) - call inputrestore() - - " refresh the listing - let g:netrw_sort_sequence= newsortseq - NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) - NetrwKeepj call winrestview(svpos) - let @@= ykeep -endfun - -" --------------------------------------------------------------------- -" s:NetrwUnmarkList: delete local marked file list and remove their contents from the global marked-file list {{{2 -" User access provided by the <mF> mapping. (see :help netrw-mF) -" Used by many MarkFile functions. -fun! s:NetrwUnmarkList(curbufnr,curdir) -" call Dfunc("s:NetrwUnmarkList(curbufnr=".a:curbufnr." curdir<".a:curdir.">)") - - " remove all files in local marked-file list from global list - if exists("s:netrwmarkfilelist") - for mfile in s:netrwmarkfilelist_{a:curbufnr} - let dfile = s:ComposePath(a:curdir,mfile) " prepend directory to mfile - let idx = index(s:netrwmarkfilelist,dfile) " get index in list of dfile - call remove(s:netrwmarkfilelist,idx) " remove from global list - endfor - if s:netrwmarkfilelist == [] - unlet s:netrwmarkfilelist - endif - - " getting rid of the local marked-file lists is easy - unlet s:netrwmarkfilelist_{a:curbufnr} - endif - if exists("s:netrwmarkfilemtch_{a:curbufnr}") - unlet s:netrwmarkfilemtch_{a:curbufnr} - endif - 2match none -" call Dret("s:NetrwUnmarkList") -endfun - -" --------------------------------------------------------------------- -" s:NetrwUnmarkAll: remove the global marked file list and all local ones {{{2 -fun! s:NetrwUnmarkAll() -" call Dfunc("s:NetrwUnmarkAll()") - if exists("s:netrwmarkfilelist") - unlet s:netrwmarkfilelist - endif - sil call s:NetrwUnmarkAll2() - 2match none -" call Dret("s:NetrwUnmarkAll") -endfun - -" --------------------------------------------------------------------- -" s:NetrwUnmarkAll2: unmark all files from all buffers {{{2 -fun! s:NetrwUnmarkAll2() -" call Dfunc("s:NetrwUnmarkAll2()") - redir => netrwmarkfilelist_let - let - redir END - let netrwmarkfilelist_list= split(netrwmarkfilelist_let,'\n') " convert let string into a let list - call filter(netrwmarkfilelist_list,"v:val =~ '^s:netrwmarkfilelist_'") " retain only those vars that start as s:netrwmarkfilelist_ - call map(netrwmarkfilelist_list,"substitute(v:val,'\\s.*$','','')") " remove what the entries are equal to - for flist in netrwmarkfilelist_list - let curbufnr= substitute(flist,'s:netrwmarkfilelist_','','') - unlet s:netrwmarkfilelist_{curbufnr} - unlet s:netrwmarkfilemtch_{curbufnr} - endfor -" call Dret("s:NetrwUnmarkAll2") -endfun - -" --------------------------------------------------------------------- -" s:NetrwUnMarkFile: called via mu map; unmarks *all* marked files, both global and buffer-local {{{2 -" -" Marked files are in two types of lists: -" s:netrwmarkfilelist -- holds complete paths to all marked files -" s:netrwmarkfilelist_# -- holds list of marked files in current-buffer's directory (#==bufnr()) -" -" Marked files suitable for use with 2match are in: -" s:netrwmarkfilemtch_# -- used with 2match to display marked files -fun! s:NetrwUnMarkFile(islocal) - let svpos = winsaveview() - let curbufnr = bufnr("%") - - " unmark marked file list - " (although I expect s:NetrwUpload() to do it, I'm just making sure) - if exists("s:netrwmarkfilelist") -" " call Decho("unlet'ing: s:netrwmarkfilelist",'~'.expand("<slnum>")) - unlet s:netrwmarkfilelist - endif - - let ibuf= 1 - while ibuf < bufnr("$") - if exists("s:netrwmarkfilelist_".ibuf) - unlet s:netrwmarkfilelist_{ibuf} - unlet s:netrwmarkfilemtch_{ibuf} - endif - let ibuf = ibuf + 1 - endwhile - 2match none - -" call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) -call winrestview(svpos) -endfun - -" --------------------------------------------------------------------- -" s:NetrwMenu: generates the menu for gvim and netrw {{{2 -fun! s:NetrwMenu(domenu) - - if !exists("g:NetrwMenuPriority") - let g:NetrwMenuPriority= 80 - endif - - if has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu -" call Dfunc("NetrwMenu(domenu=".a:domenu.")") - - if !exists("s:netrw_menu_enabled") && a:domenu -" call Decho("initialize menu",'~'.expand("<slnum>")) - let s:netrw_menu_enabled= 1 - exe 'sil! menu '.g:NetrwMenuPriority.'.1 '.g:NetrwTopLvlMenu.'Help<tab><F1> <F1>' - exe 'sil! menu '.g:NetrwMenuPriority.'.5 '.g:NetrwTopLvlMenu.'-Sep1- :' - exe 'sil! menu '.g:NetrwMenuPriority.'.6 '.g:NetrwTopLvlMenu.'Go\ Up\ Directory<tab>- -' - exe 'sil! menu '.g:NetrwMenuPriority.'.7 '.g:NetrwTopLvlMenu.'Apply\ Special\ Viewer<tab>x x' - if g:netrw_dirhistmax > 0 - exe 'sil! menu '.g:NetrwMenuPriority.'.8.1 '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History.Bookmark\ Current\ Directory<tab>mb mb' - exe 'sil! menu '.g:NetrwMenuPriority.'.8.4 '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History.Goto\ Prev\ Dir\ (History)<tab>u u' - exe 'sil! menu '.g:NetrwMenuPriority.'.8.5 '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History.Goto\ Next\ Dir\ (History)<tab>U U' - exe 'sil! menu '.g:NetrwMenuPriority.'.8.6 '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History.List<tab>qb qb' - else - exe 'sil! menu '.g:NetrwMenuPriority.'.8 '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History :echo "(disabled)"'."\<cr>" - endif - exe 'sil! menu '.g:NetrwMenuPriority.'.9.1 '.g:NetrwTopLvlMenu.'Browsing\ Control.Horizontal\ Split<tab>o o' - exe 'sil! menu '.g:NetrwMenuPriority.'.9.2 '.g:NetrwTopLvlMenu.'Browsing\ Control.Vertical\ Split<tab>v v' - exe 'sil! menu '.g:NetrwMenuPriority.'.9.3 '.g:NetrwTopLvlMenu.'Browsing\ Control.New\ Tab<tab>t t' - exe 'sil! menu '.g:NetrwMenuPriority.'.9.4 '.g:NetrwTopLvlMenu.'Browsing\ Control.Preview<tab>p p' - exe 'sil! menu '.g:NetrwMenuPriority.'.9.5 '.g:NetrwTopLvlMenu.'Browsing\ Control.Edit\ File\ Hiding\ List<tab><ctrl-h>'." \<c-h>'" - exe 'sil! menu '.g:NetrwMenuPriority.'.9.6 '.g:NetrwTopLvlMenu.'Browsing\ Control.Edit\ Sorting\ Sequence<tab>S S' - exe 'sil! menu '.g:NetrwMenuPriority.'.9.7 '.g:NetrwTopLvlMenu.'Browsing\ Control.Quick\ Hide/Unhide\ Dot\ Files<tab>'."gh gh" - exe 'sil! menu '.g:NetrwMenuPriority.'.9.8 '.g:NetrwTopLvlMenu.'Browsing\ Control.Refresh\ Listing<tab>'."<ctrl-l> \<c-l>" - exe 'sil! menu '.g:NetrwMenuPriority.'.9.9 '.g:NetrwTopLvlMenu.'Browsing\ Control.Settings/Options<tab>:NetrwSettings '.":NetrwSettings\<cr>" - exe 'sil! menu '.g:NetrwMenuPriority.'.10 '.g:NetrwTopLvlMenu.'Delete\ File/Directory<tab>D D' - exe 'sil! menu '.g:NetrwMenuPriority.'.11.1 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.Create\ New\ File<tab>% %' - exe 'sil! menu '.g:NetrwMenuPriority.'.11.1 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.In\ Current\ Window<tab><cr> '."\<cr>" - exe 'sil! menu '.g:NetrwMenuPriority.'.11.2 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.Preview\ File/Directory<tab>p p' - exe 'sil! menu '.g:NetrwMenuPriority.'.11.3 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.In\ Previous\ Window<tab>P P' - exe 'sil! menu '.g:NetrwMenuPriority.'.11.4 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.In\ New\ Window<tab>o o' - exe 'sil! menu '.g:NetrwMenuPriority.'.11.5 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.In\ New\ Tab<tab>t t' - exe 'sil! menu '.g:NetrwMenuPriority.'.11.5 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.In\ New\ Vertical\ Window<tab>v v' - exe 'sil! menu '.g:NetrwMenuPriority.'.12.1 '.g:NetrwTopLvlMenu.'Explore.Directory\ Name :Explore ' - exe 'sil! menu '.g:NetrwMenuPriority.'.12.2 '.g:NetrwTopLvlMenu.'Explore.Filenames\ Matching\ Pattern\ (curdir\ only)<tab>:Explore\ */ :Explore */' - exe 'sil! menu '.g:NetrwMenuPriority.'.12.2 '.g:NetrwTopLvlMenu.'Explore.Filenames\ Matching\ Pattern\ (+subdirs)<tab>:Explore\ **/ :Explore **/' - exe 'sil! menu '.g:NetrwMenuPriority.'.12.3 '.g:NetrwTopLvlMenu.'Explore.Files\ Containing\ String\ Pattern\ (curdir\ only)<tab>:Explore\ *// :Explore *//' - exe 'sil! menu '.g:NetrwMenuPriority.'.12.4 '.g:NetrwTopLvlMenu.'Explore.Files\ Containing\ String\ Pattern\ (+subdirs)<tab>:Explore\ **// :Explore **//' - exe 'sil! menu '.g:NetrwMenuPriority.'.12.4 '.g:NetrwTopLvlMenu.'Explore.Next\ Match<tab>:Nexplore :Nexplore<cr>' - exe 'sil! menu '.g:NetrwMenuPriority.'.12.4 '.g:NetrwTopLvlMenu.'Explore.Prev\ Match<tab>:Pexplore :Pexplore<cr>' - exe 'sil! menu '.g:NetrwMenuPriority.'.13 '.g:NetrwTopLvlMenu.'Make\ Subdirectory<tab>d d' - exe 'sil! menu '.g:NetrwMenuPriority.'.14.1 '.g:NetrwTopLvlMenu.'Marked\ Files.Mark\ File<tab>mf mf' - exe 'sil! menu '.g:NetrwMenuPriority.'.14.2 '.g:NetrwTopLvlMenu.'Marked\ Files.Mark\ Files\ by\ Regexp<tab>mr mr' - exe 'sil! menu '.g:NetrwMenuPriority.'.14.3 '.g:NetrwTopLvlMenu.'Marked\ Files.Hide-Show-List\ Control<tab>a a' - exe 'sil! menu '.g:NetrwMenuPriority.'.14.4 '.g:NetrwTopLvlMenu.'Marked\ Files.Copy\ To\ Target<tab>mc mc' - exe 'sil! menu '.g:NetrwMenuPriority.'.14.5 '.g:NetrwTopLvlMenu.'Marked\ Files.Delete<tab>D D' - exe 'sil! menu '.g:NetrwMenuPriority.'.14.6 '.g:NetrwTopLvlMenu.'Marked\ Files.Diff<tab>md md' - exe 'sil! menu '.g:NetrwMenuPriority.'.14.7 '.g:NetrwTopLvlMenu.'Marked\ Files.Edit<tab>me me' - exe 'sil! menu '.g:NetrwMenuPriority.'.14.8 '.g:NetrwTopLvlMenu.'Marked\ Files.Exe\ Cmd<tab>mx mx' - exe 'sil! menu '.g:NetrwMenuPriority.'.14.9 '.g:NetrwTopLvlMenu.'Marked\ Files.Move\ To\ Target<tab>mm mm' - exe 'sil! menu '.g:NetrwMenuPriority.'.14.10 '.g:NetrwTopLvlMenu.'Marked\ Files.Obtain<tab>O O' - exe 'sil! menu '.g:NetrwMenuPriority.'.14.11 '.g:NetrwTopLvlMenu.'Marked\ Files.Print<tab>mp mp' - exe 'sil! menu '.g:NetrwMenuPriority.'.14.12 '.g:NetrwTopLvlMenu.'Marked\ Files.Replace<tab>R R' - exe 'sil! menu '.g:NetrwMenuPriority.'.14.13 '.g:NetrwTopLvlMenu.'Marked\ Files.Set\ Target<tab>mt mt' - exe 'sil! menu '.g:NetrwMenuPriority.'.14.14 '.g:NetrwTopLvlMenu.'Marked\ Files.Tag<tab>mT mT' - exe 'sil! menu '.g:NetrwMenuPriority.'.14.15 '.g:NetrwTopLvlMenu.'Marked\ Files.Zip/Unzip/Compress/Uncompress<tab>mz mz' - exe 'sil! menu '.g:NetrwMenuPriority.'.15 '.g:NetrwTopLvlMenu.'Obtain\ File<tab>O O' - exe 'sil! menu '.g:NetrwMenuPriority.'.16.1.1 '.g:NetrwTopLvlMenu.'Style.Listing.thin<tab>i :let w:netrw_liststyle=0<cr><c-L>' - exe 'sil! menu '.g:NetrwMenuPriority.'.16.1.1 '.g:NetrwTopLvlMenu.'Style.Listing.long<tab>i :let w:netrw_liststyle=1<cr><c-L>' - exe 'sil! menu '.g:NetrwMenuPriority.'.16.1.1 '.g:NetrwTopLvlMenu.'Style.Listing.wide<tab>i :let w:netrw_liststyle=2<cr><c-L>' - exe 'sil! menu '.g:NetrwMenuPriority.'.16.1.1 '.g:NetrwTopLvlMenu.'Style.Listing.tree<tab>i :let w:netrw_liststyle=3<cr><c-L>' - exe 'sil! menu '.g:NetrwMenuPriority.'.16.2.1 '.g:NetrwTopLvlMenu.'Style.Normal-Hide-Show.Show\ All<tab>a :let g:netrw_hide=0<cr><c-L>' - exe 'sil! menu '.g:NetrwMenuPriority.'.16.2.3 '.g:NetrwTopLvlMenu.'Style.Normal-Hide-Show.Normal<tab>a :let g:netrw_hide=1<cr><c-L>' - exe 'sil! menu '.g:NetrwMenuPriority.'.16.2.2 '.g:NetrwTopLvlMenu.'Style.Normal-Hide-Show.Hidden\ Only<tab>a :let g:netrw_hide=2<cr><c-L>' - exe 'sil! menu '.g:NetrwMenuPriority.'.16.3 '.g:NetrwTopLvlMenu.'Style.Reverse\ Sorting\ Order<tab>'."r r" - exe 'sil! menu '.g:NetrwMenuPriority.'.16.4.1 '.g:NetrwTopLvlMenu.'Style.Sorting\ Method.Name<tab>s :let g:netrw_sort_by="name"<cr><c-L>' - exe 'sil! menu '.g:NetrwMenuPriority.'.16.4.2 '.g:NetrwTopLvlMenu.'Style.Sorting\ Method.Time<tab>s :let g:netrw_sort_by="time"<cr><c-L>' - exe 'sil! menu '.g:NetrwMenuPriority.'.16.4.3 '.g:NetrwTopLvlMenu.'Style.Sorting\ Method.Size<tab>s :let g:netrw_sort_by="size"<cr><c-L>' - exe 'sil! menu '.g:NetrwMenuPriority.'.16.4.3 '.g:NetrwTopLvlMenu.'Style.Sorting\ Method.Exten<tab>s :let g:netrw_sort_by="exten"<cr><c-L>' - exe 'sil! menu '.g:NetrwMenuPriority.'.17 '.g:NetrwTopLvlMenu.'Rename\ File/Directory<tab>R R' - exe 'sil! menu '.g:NetrwMenuPriority.'.18 '.g:NetrwTopLvlMenu.'Set\ Current\ Directory<tab>c c' - let s:netrw_menucnt= 28 - call s:NetrwBookmarkMenu() " provide some history! uses priorities 2,3, reserves 4, 8.2.x - call s:NetrwTgtMenu() " let bookmarks and history be easy targets - - elseif !a:domenu - let s:netrwcnt = 0 - let curwin = winnr() - windo if getline(2) =~# "Netrw" | let s:netrwcnt= s:netrwcnt + 1 | endif - exe curwin."wincmd w" - - if s:netrwcnt <= 1 -" call Decho("clear menus",'~'.expand("<slnum>")) - exe 'sil! unmenu '.g:NetrwTopLvlMenu -" call Decho('exe sil! unmenu '.g:NetrwTopLvlMenu.'*','~'.expand("<slnum>")) - sil! unlet s:netrw_menu_enabled - endif - endif -" call Dret("NetrwMenu") - return - endif - -endfun - -" --------------------------------------------------------------------- -" s:NetrwObtain: obtain file under cursor or from markfile list {{{2 -" Used by the O maps (as <SID>NetrwObtain()) -fun! s:NetrwObtain(islocal) -" call Dfunc("NetrwObtain(islocal=".a:islocal.")") - - let ykeep= @@ - if exists("s:netrwmarkfilelist_{bufnr('%')}") - let islocal= s:netrwmarkfilelist_{bufnr('%')}[1] !~ '^\a\{3,}://' - call netrw#Obtain(islocal,s:netrwmarkfilelist_{bufnr('%')}) - call s:NetrwUnmarkList(bufnr('%'),b:netrw_curdir) - else - call netrw#Obtain(a:islocal,s:NetrwGetWord()) - endif - let @@= ykeep - -" call Dret("NetrwObtain") -endfun - -" --------------------------------------------------------------------- -" s:NetrwPrevWinOpen: open file/directory in previous window. {{{2 -" If there's only one window, then the window will first be split. -" Returns: -" choice = 0 : didn't have to choose -" choice = 1 : saved modified file in window first -" choice = 2 : didn't save modified file, opened window -" choice = 3 : cancel open -fun! s:NetrwPrevWinOpen(islocal) - let ykeep= @@ - " grab a copy of the b:netrw_curdir to pass it along to newly split windows - let curdir = b:netrw_curdir - - " get last window number and the word currently under the cursor - let origwin = winnr() - let lastwinnr = winnr("$") - let curword = s:NetrwGetWord() - let choice = 0 - let s:prevwinopen= 1 " lets s:NetrwTreeDir() know that NetrwPrevWinOpen called it (s:NetrwTreeDir() will unlet s:prevwinopen) - let s:treedir = s:NetrwTreeDir(a:islocal) - let curdir = s:treedir - - let didsplit = 0 - if lastwinnr == 1 - " if only one window, open a new one first - " g:netrw_preview=0: preview window shown in a horizontally split window - " g:netrw_preview=1: preview window shown in a vertically split window - if g:netrw_preview - " vertically split preview window - let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize - exe (g:netrw_alto? "top " : "bot ")."vert ".winsz."wincmd s" - else - " horizontally split preview window - let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winheight(0))/100 : -g:netrw_winsize - exe (g:netrw_alto? "bel " : "abo ").winsz."wincmd s" - endif - let didsplit = 1 - - else - NetrwKeepj call s:SaveBufVars() - let eikeep= &ei - setl ei=all - wincmd p - - if exists("s:lexplore_win") && s:lexplore_win == winnr() - " whoops -- user trying to open file in the Lexplore window. - " Use Lexplore's opening-file window instead. -" exe g:netrw_chgwin."wincmd w" - wincmd p - call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,s:NetrwGetWord(),1)) - endif - - " prevwinnr: the window number of the "prev" window - " prevbufnr: the buffer number of the buffer in the "prev" window - " bnrcnt : the qty of windows open on the "prev" buffer - let prevwinnr = winnr() - let prevbufnr = bufnr("%") - let prevbufname = bufname("%") - let prevmod = &mod - let bnrcnt = 0 - NetrwKeepj call s:RestoreBufVars() - - " if the previous window's buffer has been changed (ie. its modified flag is set), - " and it doesn't appear in any other extant window, then ask the - " user if s/he wants to abandon modifications therein. - if prevmod - windo if winbufnr(0) == prevbufnr | let bnrcnt=bnrcnt+1 | endif - exe prevwinnr."wincmd w" - - if bnrcnt == 1 && &hidden == 0 - " only one copy of the modified buffer in a window, and - " hidden not set, so overwriting will lose the modified file. Ask first... - let choice = confirm("Save modified buffer<".prevbufname."> first?","&Yes\n&No\n&Cancel") - let &ei= eikeep - - if choice == 1 - " Yes -- write file & then browse - let v:errmsg= "" - sil w - if v:errmsg != "" - call netrw#ErrorMsg(s:ERROR,"unable to write <".(exists("prevbufname")? prevbufname : 'n/a').">!",30) - exe origwin."wincmd w" - let &ei = eikeep - let @@ = ykeep - return choice - endif - - elseif choice == 2 - " No -- don't worry about changed file, just browse anyway - echomsg "**note** changes to ".prevbufname." abandoned" - - else - " Cancel -- don't do this - exe origwin."wincmd w" - let &ei= eikeep - let @@ = ykeep - return choice - endif - endif - endif - let &ei= eikeep - endif - - " restore b:netrw_curdir (window split/enew may have lost it) - let b:netrw_curdir= curdir - if a:islocal < 2 - if a:islocal - call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(a:islocal,curword,0)) - else - call s:NetrwBrowse(a:islocal,s:NetrwBrowseChgDir(a:islocal,curword,0)) - endif - endif - let @@= ykeep - return choice -endfun - -" --------------------------------------------------------------------- -" s:NetrwUpload: load fname to tgt (used by NetrwMarkFileCopy()) {{{2 -" Always assumed to be local -> remote -" call s:NetrwUpload(filename, target) -" call s:NetrwUpload(filename, target, fromdirectory) -fun! s:NetrwUpload(fname,tgt,...) -" call Dfunc("s:NetrwUpload(fname<".((type(a:fname) == 1)? a:fname : string(a:fname))."> tgt<".a:tgt.">) a:0=".a:0) - - if a:tgt =~ '^\a\{3,}://' - let tgtdir= substitute(a:tgt,'^\a\{3,}://[^/]\+/\(.\{-}\)$','\1','') - else - let tgtdir= substitute(a:tgt,'^\(.*\)/[^/]*$','\1','') - endif -" call Decho("tgtdir<".tgtdir.">",'~'.expand("<slnum>")) - - if a:0 > 0 - let fromdir= a:1 - else - let fromdir= getcwd() - endif -" call Decho("fromdir<".fromdir.">",'~'.expand("<slnum>")) - - if type(a:fname) == 1 - " handle uploading a single file using NetWrite -" call Decho("handle uploading a single file via NetWrite",'~'.expand("<slnum>")) - 1split -" call Decho("exe e ".fnameescape(s:NetrwFile(a:fname)),'~'.expand("<slnum>")) - exe "NetrwKeepj e ".fnameescape(s:NetrwFile(a:fname)) -" call Decho("now locally editing<".expand("%").">, has ".line("$")." lines",'~'.expand("<slnum>")) - if a:tgt =~ '/$' - let wfname= substitute(a:fname,'^.*/','','') -" call Decho("exe w! ".fnameescape(wfname),'~'.expand("<slnum>")) - exe "w! ".fnameescape(a:tgt.wfname) - else -" call Decho("writing local->remote: exe w ".fnameescape(a:tgt),'~'.expand("<slnum>")) - exe "w ".fnameescape(a:tgt) -" call Decho("done writing local->remote",'~'.expand("<slnum>")) - endif - q! - - elseif type(a:fname) == 3 - " handle uploading a list of files via scp -" call Decho("handle uploading a list of files via scp",'~'.expand("<slnum>")) - let curdir= getcwd() - if a:tgt =~ '^scp:' - if s:NetrwLcd(fromdir) -" call Dret("s:NetrwUpload : lcd failure") - return - endif - let filelist= deepcopy(s:netrwmarkfilelist_{bufnr('%')}) - let args = join(map(filelist,"s:ShellEscape(v:val, 1)")) - if exists("g:netrw_port") && g:netrw_port != "" - let useport= " ".g:netrw_scpport." ".g:netrw_port - else - let useport= "" - endif - let machine = substitute(a:tgt,'^scp://\([^/:]\+\).*$','\1','') - let tgt = substitute(a:tgt,'^scp://[^/]\+/\(.*\)$','\1','') - call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_scp_cmd.s:ShellEscape(useport,1)." ".args." ".s:ShellEscape(machine.":".tgt,1)) - if s:NetrwLcd(curdir) -" call Dret("s:NetrwUpload : lcd failure") - return - endif - - elseif a:tgt =~ '^ftp:' - call s:NetrwMethod(a:tgt) - - if b:netrw_method == 2 - " handle uploading a list of files via ftp+.netrc - let netrw_fname = b:netrw_fname - sil NetrwKeepj new -" call Decho("filter input window#".winnr(),'~'.expand("<slnum>")) - - NetrwKeepj put =g:netrw_ftpmode -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - - if exists("g:netrw_ftpextracmd") - NetrwKeepj put =g:netrw_ftpextracmd -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - endif - - NetrwKeepj call setline(line("$")+1,'lcd "'.fromdir.'"') -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - - if tgtdir == "" - let tgtdir= '/' - endif - NetrwKeepj call setline(line("$")+1,'cd "'.tgtdir.'"') -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - - for fname in a:fname - NetrwKeepj call setline(line("$")+1,'put "'.s:NetrwFile(fname).'"') -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - endfor - - if exists("g:netrw_port") && g:netrw_port != "" - call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1)) - else -" call Decho("filter input window#".winnr(),'~'.expand("<slnum>")) - call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)) - endif - " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) - sil NetrwKeepj g/Local directory now/d - call histdel("/",-1) - if getline(1) !~ "^$" && !exists("g:netrw_quiet") && getline(1) !~ '^Trying ' - call netrw#ErrorMsg(s:ERROR,getline(1),14) - else - bw!|q - endif - - elseif b:netrw_method == 3 - " upload with ftp + machine, id, passwd, and fname (ie. no .netrc) - let netrw_fname= b:netrw_fname - NetrwKeepj call s:SaveBufVars()|sil NetrwKeepj new|NetrwKeepj call s:RestoreBufVars() - let tmpbufnr= bufnr("%") - setl ff=unix - - if exists("g:netrw_port") && g:netrw_port != "" - NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - else - NetrwKeepj put ='open '.g:netrw_machine -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - endif - - if exists("g:netrw_uid") && g:netrw_uid != "" - if exists("g:netrw_ftp") && g:netrw_ftp == 1 - NetrwKeepj put =g:netrw_uid -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - if exists("s:netrw_passwd") - NetrwKeepj call setline(line("$")+1,'"'.s:netrw_passwd.'"') - endif -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - elseif exists("s:netrw_passwd") - NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"' -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - endif - endif - - NetrwKeepj call setline(line("$")+1,'lcd "'.fromdir.'"') -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - - if exists("b:netrw_fname") && b:netrw_fname != "" - NetrwKeepj call setline(line("$")+1,'cd "'.b:netrw_fname.'"') -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - endif - - if exists("g:netrw_ftpextracmd") - NetrwKeepj put =g:netrw_ftpextracmd -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - endif - - for fname in a:fname - NetrwKeepj call setline(line("$")+1,'put "'.fname.'"') -" call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) - endfor - - " perform ftp: - " -i : turns off interactive prompting from ftp - " -n unix : DON'T use <.netrc>, even though it exists - " -n win32: quit being obnoxious about password - NetrwKeepj norm! 1G"_dd - call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." ".g:netrw_ftp_options) - " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) - sil NetrwKeepj g/Local directory now/d - call histdel("/",-1) - if getline(1) !~ "^$" && !exists("g:netrw_quiet") && getline(1) !~ '^Trying ' - let debugkeep= &debug - setl debug=msg - call netrw#ErrorMsg(s:ERROR,getline(1),15) - let &debug = debugkeep - let mod = 1 - else - bw!|q - endif - elseif !exists("b:netrw_method") || b:netrw_method < 0 -" call Dret("s:#NetrwUpload : unsupported method") - return - endif - else - call netrw#ErrorMsg(s:ERROR,"can't obtain files with protocol from<".a:tgt.">",63) - endif - endif - -" call Dret("s:NetrwUpload") -endfun - -" --------------------------------------------------------------------- -" s:NetrwPreview: supports netrw's "p" map {{{2 -fun! s:NetrwPreview(path) range -" call Dfunc("NetrwPreview(path<".a:path.">)") -" call Decho("g:netrw_alto =".(exists("g:netrw_alto")? g:netrw_alto : 'n/a'),'~'.expand("<slnum>")) -" call Decho("g:netrw_preview=".(exists("g:netrw_preview")? g:netrw_preview : 'n/a'),'~'.expand("<slnum>")) - let ykeep= @@ - NetrwKeepj call s:NetrwOptionsSave("s:") - if a:path !~ '^\*\{1,2}/' && a:path !~ '^\a\{3,}://' - NetrwKeepj call s:NetrwOptionsSafe(1) - else - NetrwKeepj call s:NetrwOptionsSafe(0) - endif - if has("quickfix") -" call Decho("has quickfix",'~'.expand("<slnum>")) - if !isdirectory(s:NetrwFile(a:path)) -" call Decho("good; not previewing a directory",'~'.expand("<slnum>")) - if g:netrw_preview - " vertical split - let pvhkeep = &pvh - let winsz = (g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize - let &pvh = winwidth(0) - winsz -" call Decho("g:netrw_preview: winsz=".winsz." &pvh=".&pvh." (temporarily) g:netrw_winsize=".g:netrw_winsize,'~'.expand("<slnum>")) - else - " horizontal split - let pvhkeep = &pvh - let winsz = (g:netrw_winsize > 0)? (g:netrw_winsize*winheight(0))/100 : -g:netrw_winsize - let &pvh = winheight(0) - winsz -" call Decho("!g:netrw_preview: winsz=".winsz." &pvh=".&pvh." (temporarily) g:netrw_winsize=".g:netrw_winsize,'~'.expand("<slnum>")) - endif - " g:netrw_preview g:netrw_alto - " 1 : vert 1: top -- preview window is vertically split off and on the left - " 1 : vert 0: bot -- preview window is vertically split off and on the right - " 0 : 1: top -- preview window is horizontally split off and on the top - " 0 : 0: bot -- preview window is horizontally split off and on the bottom - " - " Note that the file being previewed is already known to not be a directory, hence we can avoid doing a LocalBrowseCheck() check via - " the BufEnter event set up in netrwPlugin.vim -" call Decho("exe ".(g:netrw_alto? "top " : "bot ").(g:netrw_preview? "vert " : "")."pedit ".fnameescape(a:path),'~'.expand("<slnum>")) - let eikeep = &ei - set ei=BufEnter - exe (g:netrw_alto? "top " : "bot ").(g:netrw_preview? "vert " : "")."pedit ".fnameescape(a:path) - let &ei= eikeep -" call Decho("winnr($)=".winnr("$"),'~'.expand("<slnum>")) - if exists("pvhkeep") - let &pvh= pvhkeep - endif - elseif !exists("g:netrw_quiet") - NetrwKeepj call netrw#ErrorMsg(s:WARNING,"sorry, cannot preview a directory such as <".a:path.">",38) - endif - elseif !exists("g:netrw_quiet") - NetrwKeepj call netrw#ErrorMsg(s:WARNING,"sorry, to preview your vim needs the quickfix feature compiled in",39) - endif - NetrwKeepj call s:NetrwOptionsRestore("s:") - let @@= ykeep -" call Dret("NetrwPreview") -endfun - -" --------------------------------------------------------------------- -" s:NetrwRefresh: {{{2 -fun! s:NetrwRefresh(islocal,dirname) -" call Dfunc("s:NetrwRefresh(islocal<".a:islocal.">,dirname=".a:dirname.") g:netrw_hide=".g:netrw_hide." g:netrw_sort_direction=".g:netrw_sort_direction) - " at the current time (Mar 19, 2007) all calls to NetrwRefresh() call NetrwBrowseChgDir() first. - setl ma noro -" call Decho("setl ma noro",'~'.expand("<slnum>")) -" call Decho("clear buffer<".expand("%")."> with :%d",'~'.expand("<slnum>")) - let ykeep = @@ - if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST - if !exists("w:netrw_treetop") - if exists("b:netrw_curdir") - let w:netrw_treetop= b:netrw_curdir - else - let w:netrw_treetop= getcwd() - endif - endif - NetrwKeepj call s:NetrwRefreshTreeDict(w:netrw_treetop) - endif - - " save the cursor position before refresh. - let screenposn = winsaveview() -" call Decho("saving posn to screenposn<".string(screenposn).">",'~'.expand("<slnum>")) - -" call Decho("win#".winnr().": ".winheight(0)."x".winwidth(0)." curfile<".expand("%").">",'~'.expand("<slnum>")) -" call Decho("clearing buffer prior to refresh",'~'.expand("<slnum>")) - sil! NetrwKeepj %d _ - if a:islocal - NetrwKeepj call netrw#LocalBrowseCheck(a:dirname) - else - NetrwKeepj call s:NetrwBrowse(a:islocal,a:dirname) - endif - - " restore position -" call Decho("restoring posn to screenposn<".string(screenposn).">",'~'.expand("<slnum>")) - NetrwKeepj call winrestview(screenposn) - - " restore file marks - if has("syntax") && exists("g:syntax_on") && g:syntax_on - if exists("s:netrwmarkfilemtch_{bufnr('%')}") && s:netrwmarkfilemtch_{bufnr("%")} != "" -" " call Decho("exe 2match netrwMarkFile /".s:netrwmarkfilemtch_{bufnr("%")}."/",'~'.expand("<slnum>")) - exe "2match netrwMarkFile /".s:netrwmarkfilemtch_{bufnr("%")}."/" - else -" " call Decho("2match none (bufnr(%)=".bufnr("%")."<".bufname("%").">)",'~'.expand("<slnum>")) - 2match none - endif - endif - -" restore - let @@= ykeep -" call Dret("s:NetrwRefresh") -endfun - -" --------------------------------------------------------------------- -" s:NetrwRefreshDir: refreshes a directory by name {{{2 -" Called by NetrwMarkFileCopy() -" Interfaces to s:NetrwRefresh() and s:LocalBrowseRefresh() -fun! s:NetrwRefreshDir(islocal,dirname) - if g:netrw_fastbrowse == 0 - " slowest mode (keep buffers refreshed, local or remote) - let tgtwin= bufwinnr(a:dirname) - - if tgtwin > 0 - " tgtwin is being displayed, so refresh it - let curwin= winnr() - exe tgtwin."wincmd w" - NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) - exe curwin."wincmd w" - - elseif bufnr(a:dirname) > 0 - let bn= bufnr(a:dirname) - exe "sil keepj bd ".bn - endif - - elseif g:netrw_fastbrowse <= 1 - NetrwKeepj call s:LocalBrowseRefresh() - endif -endfun - -" --------------------------------------------------------------------- -" s:NetrwSetChgwin: set g:netrw_chgwin; a <cr> will use the specified -" window number to do its editing in. -" Supports [count]C where the count, if present, is used to specify -" a window to use for editing via the <cr> mapping. -fun! s:NetrwSetChgwin(...) -" call Dfunc("s:NetrwSetChgwin() v:count=".v:count) - if a:0 > 0 -" call Decho("a:1<".a:1.">",'~'.expand("<slnum>")) - if a:1 == "" " :NetrwC win# - let g:netrw_chgwin= winnr() - else " :NetrwC - let g:netrw_chgwin= a:1 - endif - elseif v:count > 0 " [count]C - let g:netrw_chgwin= v:count - else " C - let g:netrw_chgwin= winnr() - endif - echo "editing window now set to window#".g:netrw_chgwin -" call Dret("s:NetrwSetChgwin : g:netrw_chgwin=".g:netrw_chgwin) -endfun - -" --------------------------------------------------------------------- -" s:NetrwSetSort: sets up the sort based on the g:netrw_sort_sequence {{{2 -" What this function does is to compute a priority for the patterns -" in the g:netrw_sort_sequence. It applies a substitute to any -" "files" that satisfy each pattern, putting the priority / in -" front. An "*" pattern handles the default priority. -fun! s:NetrwSetSort() -" call Dfunc("SetSort() bannercnt=".w:netrw_bannercnt) - let ykeep= @@ - if w:netrw_liststyle == s:LONGLIST - let seqlist = substitute(g:netrw_sort_sequence,'\$','\\%(\t\\|\$\\)','ge') - else - let seqlist = g:netrw_sort_sequence - endif - " sanity check -- insure that * appears somewhere - if seqlist == "" - let seqlist= '*' - elseif seqlist !~ '\*' - let seqlist= seqlist.',*' - endif - let priority = 1 - while seqlist != "" - if seqlist =~ ',' - let seq = substitute(seqlist,',.*$','','e') - let seqlist = substitute(seqlist,'^.\{-},\(.*\)$','\1','e') - else - let seq = seqlist - let seqlist = "" - endif - if priority < 10 - let spriority= "00".priority.g:netrw_sepchr - elseif priority < 100 - let spriority= "0".priority.g:netrw_sepchr - else - let spriority= priority.g:netrw_sepchr - endif -" call Decho("priority=".priority." spriority<".spriority."> seq<".seq."> seqlist<".seqlist.">",'~'.expand("<slnum>")) - - " sanity check - if w:netrw_bannercnt > line("$") - " apparently no files were left after a Hiding pattern was used -" call Dret("SetSort : no files left after hiding") - return - endif - if seq == '*' - let starpriority= spriority - else - exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g/'.seq.'/s/^/'.spriority.'/' - call histdel("/",-1) - " sometimes multiple sorting patterns will match the same file or directory. - " The following substitute is intended to remove the excess matches. - exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g/^\d\{3}'.g:netrw_sepchr.'\d\{3}\//s/^\d\{3}'.g:netrw_sepchr.'\(\d\{3}\/\).\@=/\1/e' - NetrwKeepj call histdel("/",-1) - endif - let priority = priority + 1 - endwhile - if exists("starpriority") - exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$v/^\d\{3}'.g:netrw_sepchr.'/s/^/'.starpriority.'/e' - NetrwKeepj call histdel("/",-1) - endif - - " Following line associated with priority -- items that satisfy a priority - " pattern get prefixed by ###/ which permits easy sorting by priority. - " Sometimes files can satisfy multiple priority patterns -- only the latest - " priority pattern needs to be retained. So, at this point, these excess - " priority prefixes need to be removed, but not directories that happen to - " be just digits themselves. - exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$s/^\(\d\{3}'.g:netrw_sepchr.'\)\%(\d\{3}'.g:netrw_sepchr.'\)\+\ze./\1/e' - NetrwKeepj call histdel("/",-1) - let @@= ykeep - -" call Dret("SetSort") -endfun - -" --------------------------------------------------------------------- -" s:NetrwSetTgt: sets the target to the specified choice index {{{2 -" Implements [count]Tb (bookhist<b>) -" [count]Th (bookhist<h>) -" See :help netrw-qb for how to make the choice. -fun! s:NetrwSetTgt(islocal,bookhist,choice) -" call Dfunc("s:NetrwSetTgt(islocal=".a:islocal." bookhist<".a:bookhist."> choice#".a:choice.")") - - if a:bookhist == 'b' - " supports choosing a bookmark as a target using a qb-generated list - let choice= a:choice - 1 - if exists("g:netrw_bookmarklist[".choice."]") - call netrw#MakeTgt(g:netrw_bookmarklist[choice]) - else - echomsg "Sorry, bookmark#".a:choice." doesn't exist!" - endif - - elseif a:bookhist == 'h' - " supports choosing a history stack entry as a target using a qb-generated list - let choice= (a:choice % g:netrw_dirhistmax) + 1 - if exists("g:netrw_dirhist_".choice) - let histentry = g:netrw_dirhist_{choice} - call netrw#MakeTgt(histentry) - else - echomsg "Sorry, history#".a:choice." not available!" - endif - endif - - " refresh the display - if !exists("b:netrw_curdir") - let b:netrw_curdir= getcwd() - endif - call s:NetrwRefresh(a:islocal,b:netrw_curdir) - -" call Dret("s:NetrwSetTgt") -endfun - -" ===================================================================== -" s:NetrwSortStyle: change sorting style (name - time - size - exten) and refresh display {{{2 -fun! s:NetrwSortStyle(islocal) - NetrwKeepj call s:NetrwSaveWordPosn() - let svpos= winsaveview() - - 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,'./',0)) - NetrwKeepj call winrestview(svpos) -endfun - -" --------------------------------------------------------------------- -" s:NetrwSplit: mode {{{2 -" =0 : net and o -" =1 : net and t -" =2 : net and v -" =3 : local and o -" =4 : local and t -" =5 : local and v -fun! s:NetrwSplit(mode) - - let ykeep= @@ - call s:SaveWinVars() - - if a:mode == 0 - " remote and o - let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winheight(0))/100 : -g:netrw_winsize - if winsz == 0|let winsz= ""|endif - exe (g:netrw_alto? "bel " : "abo ").winsz."wincmd s" - let s:didsplit= 1 - NetrwKeepj call s:RestoreWinVars() - NetrwKeepj call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,s:NetrwGetWord(),1)) - unlet s:didsplit - - elseif a:mode == 1 - " remote and t - let newdir = s:NetrwBrowseChgDir(0,s:NetrwGetWord(),1) - tabnew - let s:didsplit= 1 - NetrwKeepj call s:RestoreWinVars() - NetrwKeepj call s:NetrwBrowse(0,newdir) - unlet s:didsplit - - elseif a:mode == 2 - " remote and v - let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize - if winsz == 0|let winsz= ""|endif - exe (g:netrw_altv? "rightb " : "lefta ").winsz."wincmd v" - let s:didsplit= 1 - NetrwKeepj call s:RestoreWinVars() - NetrwKeepj call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,s:NetrwGetWord(),1)) - unlet s:didsplit - - elseif a:mode == 3 - " local and o - let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winheight(0))/100 : -g:netrw_winsize - if winsz == 0|let winsz= ""|endif - exe (g:netrw_alto? "bel " : "abo ").winsz."wincmd s" - let s:didsplit= 1 - NetrwKeepj call s:RestoreWinVars() - NetrwKeepj call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,s:NetrwGetWord(),1)) - unlet s:didsplit - - elseif a:mode == 4 - " local and t - let cursorword = s:NetrwGetWord() - let eikeep = &ei - let netrw_winnr = winnr() - let netrw_line = line(".") - let netrw_col = virtcol(".") - NetrwKeepj norm! H0 - let netrw_hline = line(".") - setl ei=all - exe "NetrwKeepj norm! ".netrw_hline."G0z\<CR>" - exe "NetrwKeepj norm! ".netrw_line."G0".netrw_col."\<bar>" - let &ei = eikeep - let netrw_curdir = s:NetrwTreeDir(0) - tabnew - let b:netrw_curdir = netrw_curdir - let s:didsplit = 1 - NetrwKeepj call s:RestoreWinVars() - NetrwKeepj call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,cursorword,0)) - if &ft == "netrw" - setl ei=all - exe "NetrwKeepj norm! ".netrw_hline."G0z\<CR>" - exe "NetrwKeepj norm! ".netrw_line."G0".netrw_col."\<bar>" - let &ei= eikeep - endif - unlet s:didsplit - - elseif a:mode == 5 - " local and v - let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize - if winsz == 0|let winsz= ""|endif - exe (g:netrw_altv? "rightb " : "lefta ").winsz."wincmd v" - let s:didsplit= 1 - NetrwKeepj call s:RestoreWinVars() - NetrwKeepj call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,s:NetrwGetWord(),1)) - unlet s:didsplit - - else - NetrwKeepj call netrw#ErrorMsg(s:ERROR,"(NetrwSplit) unsupported mode=".a:mode,45) - endif - - let @@= ykeep -endfun - -" --------------------------------------------------------------------- -" s:NetrwTgtMenu: {{{2 -fun! s:NetrwTgtMenu() - if !exists("s:netrw_menucnt") - return - endif -" call Dfunc("s:NetrwTgtMenu()") - - " the following test assures that gvim is running, has menus available, and has menus enabled. - if has("gui") && has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu - if exists("g:NetrwTopLvlMenu") -" call Decho("removing ".g:NetrwTopLvlMenu."Bookmarks menu item(s)",'~'.expand("<slnum>")) - exe 'sil! unmenu '.g:NetrwTopLvlMenu.'Targets' - endif - if !exists("s:netrw_initbookhist") - call s:NetrwBookHistRead() - endif - - " try to cull duplicate entries - let tgtdict={} - - " target bookmarked places - if exists("g:netrw_bookmarklist") && g:netrw_bookmarklist != [] && g:netrw_dirhistmax > 0 -" call Decho("installing bookmarks as easy targets",'~'.expand("<slnum>")) - let cnt= 1 - for bmd in g:netrw_bookmarklist - if has_key(tgtdict,bmd) - let cnt= cnt + 1 - continue - endif - let tgtdict[bmd]= cnt - let ebmd= escape(bmd,g:netrw_menu_escape) - " show bookmarks for goto menu -" call Decho("menu: Targets: ".bmd,'~'.expand("<slnum>")) - exe 'sil! menu <silent> '.g:NetrwMenuPriority.".19.1.".cnt." ".g:NetrwTopLvlMenu.'Targets.'.ebmd." :call netrw#MakeTgt('".bmd."')\<cr>" - let cnt= cnt + 1 - endfor - endif - - " target directory browsing history - if exists("g:netrw_dirhistmax") && g:netrw_dirhistmax > 0 -" call Decho("installing history as easy targets (histmax=".g:netrw_dirhistmax.")",'~'.expand("<slnum>")) - let histcnt = 1 - while histcnt <= g:netrw_dirhistmax - let priority = g:netrw_dirhistcnt + histcnt - if exists("g:netrw_dirhist_{histcnt}") - let histentry = g:netrw_dirhist_{histcnt} - if has_key(tgtdict,histentry) - let histcnt = histcnt + 1 - continue - endif - let tgtdict[histentry] = histcnt - let ehistentry = escape(histentry,g:netrw_menu_escape) -" call Decho("menu: Targets: ".histentry,'~'.expand("<slnum>")) - exe 'sil! menu <silent> '.g:NetrwMenuPriority.".19.2.".priority." ".g:NetrwTopLvlMenu.'Targets.'.ehistentry." :call netrw#MakeTgt('".histentry."')\<cr>" - endif - let histcnt = histcnt + 1 - endwhile - endif - endif -" call Dret("s:NetrwTgtMenu") -endfun - -" --------------------------------------------------------------------- -" s:NetrwTreeDir: determine tree directory given current cursor position {{{2 -" (full path directory with trailing slash returned) -fun! s:NetrwTreeDir(islocal) - - if exists("s:treedir") && exists("s:prevwinopen") - " s:NetrwPrevWinOpen opens a "previous" window -- and thus needs to and does call s:NetrwTreeDir early - let treedir= s:treedir - unlet s:treedir - unlet s:prevwinopen - return treedir - endif - if exists("s:prevwinopen") - unlet s:prevwinopen - endif - - if !exists("b:netrw_curdir") || b:netrw_curdir == "" - let b:netrw_curdir= getcwd() - endif - let treedir = b:netrw_curdir - let s:treecurpos= winsaveview() - - if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST - - " extract tree directory if on a line specifying a subdirectory (ie. ends with "/") - let curline= substitute(getline('.'),"\t -->.*$",'','') - if curline =~ '/$' - let treedir= substitute(getline('.'),'^\%('.s:treedepthstring.'\)*\([^'.s:treedepthstring.'].\{-}\)$','\1','e') - elseif curline =~ '@$' - let potentialdir= resolve(s:NetrwTreePath(w:netrw_treetop)) - else - let treedir= "" - endif - - " detect user attempting to close treeroot - if curline !~ '^'.s:treedepthstring && getline('.') != '..' - " now force a refresh - sil! NetrwKeepj %d _ - return b:netrw_curdir - endif - - " COMBAK: a symbolic link may point anywhere -- so it will be used to start a new treetop -" if a:islocal && curline =~ '@$' && isdirectory(s:NetrwFile(potentialdir)) -" let newdir = w:netrw_treetop.'/'.potentialdir - if a:islocal && curline =~ '@$' - if isdirectory(s:NetrwFile(potentialdir)) - let treedir = potentialdir - let w:netrw_treetop = treedir - endif - else - let potentialdir= s:NetrwFile(substitute(curline,'^'.s:treedepthstring.'\+ \(.*\)@$','\1','')) - let treedir = s:NetrwTreePath(w:netrw_treetop) - endif - endif - - " sanity maintenance: keep those //s away... - let treedir= substitute(treedir,'//$','/','') - return treedir -endfun - -" --------------------------------------------------------------------- -" s:NetrwTreeDisplay: recursive tree display {{{2 -fun! s:NetrwTreeDisplay(dir,depth) - " ensure that there are no folds - setl nofen - - " install ../ and shortdir - if a:depth == "" - call setline(line("$")+1,'../') - endif - if a:dir =~ '^\a\{3,}://' - if a:dir == w:netrw_treetop - let shortdir= a:dir - else - let shortdir= substitute(a:dir,'^.*/\([^/]\+\)/$','\1/','e') - endif - call setline(line("$")+1,a:depth.shortdir) - else - let shortdir= substitute(a:dir,'^.*/','','e') - call setline(line("$")+1,a:depth.shortdir.'/') - endif - " append a / to dir if its missing one - let dir= a:dir - - " display subtrees (if any) - let depth= s:treedepthstring.a:depth - - " implement g:netrw_hide for tree listings (uses g:netrw_list_hide) - if g:netrw_hide == 1 - " hide given patterns - let listhide= split(g:netrw_list_hide,',') - for pat in listhide - call filter(w:netrw_treedict[dir],'v:val !~ "'.escape(pat,'\\').'"') - endfor - - elseif g:netrw_hide == 2 - " show given patterns (only) - let listhide= split(g:netrw_list_hide,',') - let entries=[] - for entry in w:netrw_treedict[dir] - for pat in listhide - if entry =~ pat - call add(entries,entry) - break - endif - endfor - endfor - let w:netrw_treedict[dir]= entries - endif - if depth != "" - " always remove "." and ".." entries when there's depth - call filter(w:netrw_treedict[dir],'v:val !~ "\\.\\.$"') - call filter(w:netrw_treedict[dir],'v:val !~ "\\.\\./$"') - call filter(w:netrw_treedict[dir],'v:val !~ "\\.$"') - call filter(w:netrw_treedict[dir],'v:val !~ "\\./$"') - endif - - for entry in w:netrw_treedict[dir] - if dir =~ '/$' - let direntry= substitute(dir.entry,'[@/]$','','e') - else - let direntry= substitute(dir.'/'.entry,'[@/]$','','e') - endif - if entry =~ '/$' && has_key(w:netrw_treedict,direntry) - NetrwKeepj call s:NetrwTreeDisplay(direntry,depth) - elseif entry =~ '/$' && has_key(w:netrw_treedict,direntry.'/') - NetrwKeepj call s:NetrwTreeDisplay(direntry.'/',depth) - elseif entry =~ '@$' && has_key(w:netrw_treedict,direntry.'@') - NetrwKeepj call s:NetrwTreeDisplay(direntry.'@',depth) - else - sil! NetrwKeepj call setline(line("$")+1,depth.entry) - endif - endfor -endfun - -" --------------------------------------------------------------------- -" s:NetrwRefreshTreeDict: updates the contents information for a tree (w:netrw_treedict) {{{2 -fun! s:NetrwRefreshTreeDict(dir) - if !exists("w:netrw_treedict") - return - endif - - for entry in w:netrw_treedict[a:dir] - let direntry= substitute(a:dir.'/'.entry,'[@/]$','','e') - - if entry =~ '/$' && has_key(w:netrw_treedict,direntry) - NetrwKeepj call s:NetrwRefreshTreeDict(direntry) - let filelist = s:NetrwLocalListingList(direntry,0) - let w:netrw_treedict[direntry] = sort(filelist) - - elseif entry =~ '/$' && has_key(w:netrw_treedict,direntry.'/') - NetrwKeepj call s:NetrwRefreshTreeDict(direntry.'/') - let filelist = s:NetrwLocalListingList(direntry.'/',0) - let w:netrw_treedict[direntry] = sort(filelist) - - elseif entry =~ '@$' && has_key(w:netrw_treedict,direntry.'@') - NetrwKeepj call s:NetrwRefreshTreeDict(direntry.'/') - let liststar = s:NetrwGlob(direntry.'/','*',1) - let listdotstar= s:NetrwGlob(direntry.'/','.*',1) - - else - endif - endfor -endfun - -" --------------------------------------------------------------------- -" s:NetrwTreeListing: displays tree listing from treetop on down, using NetrwTreeDisplay() {{{2 -" Called by s:PerformListing() -fun! s:NetrwTreeListing(dirname) - if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST - - " update the treetop - if !exists("w:netrw_treetop") - let w:netrw_treetop= a:dirname - let s:netrw_treetop= w:netrw_treetop - " use \V in case the directory contains specials chars like '$' or '~' - elseif (w:netrw_treetop =~ ('^'.'\V'.a:dirname) && s:Strlen(a:dirname) < s:Strlen(w:netrw_treetop)) - \ || a:dirname !~ ('^'.'\V'.w:netrw_treetop) - let w:netrw_treetop= a:dirname - let s:netrw_treetop= w:netrw_treetop - endif - if exists("w:netrw_treetop") - let s:netrw_treetop= w:netrw_treetop - else - let w:netrw_treetop= getcwd() - let s:netrw_treetop= w:netrw_treetop - endif - - if !exists("w:netrw_treedict") - " insure that we have a treedict, albeit empty - let w:netrw_treedict= {} - endif - - " update the dictionary for the current directory - exe "sil! NetrwKeepj keepp ".w:netrw_bannercnt.',$g@^\.\.\=/$@d _' - let w:netrw_treedict[a:dirname]= getline(w:netrw_bannercnt,line("$")) - exe "sil! NetrwKeepj ".w:netrw_bannercnt.",$d _" - - " if past banner, record word - if exists("w:netrw_bannercnt") && line(".") > w:netrw_bannercnt - let fname= expand("<cword>") - else - let fname= "" - endif - - " display from treetop on down - NetrwKeepj call s:NetrwTreeDisplay(w:netrw_treetop,"") - - " remove any blank line remaining as line#1 (happens in treelisting mode with banner suppressed) - while getline(1) =~ '^\s*$' && byte2line(1) > 0 - 1d - endwhile - - exe "setl ".g:netrw_bufsettings - - return - endif -endfun - -" --------------------------------------------------------------------- -" s:NetrwTreePath: returns path to current file/directory in tree listing {{{2 -" Normally, treetop is w:netrw_treetop, but a -" user of the function ( netrw#SetTreetop() ) -" wipes that out prior to calling this function -fun! s:NetrwTreePath(treetop) -" call Dfunc("s:NetrwTreePath(treetop<".a:treetop.">) line#".line(".")."<".getline(".").">") - if line(".") < w:netrw_bannercnt + 2 - let treedir= a:treetop - if treedir !~ '/$' - let treedir= treedir.'/' - endif -" call Dret("s:NetrwTreePath ".treedir." : line#".line(".")." ≤ ".(w:netrw_bannercnt+2)) - return treedir - endif - - let svpos = winsaveview() -" call Decho("saving posn to svpos<".string(svpos).">",'~'.expand("<slnum>")) - let depth = substitute(getline('.'),'^\(\%('.s:treedepthstring.'\)*\)[^'.s:treedepthstring.'].\{-}$','\1','e') -" call Decho("depth<".depth."> 1st subst",'~'.expand("<slnum>")) - let depth = substitute(depth,'^'.s:treedepthstring,'','') -" call Decho("depth<".depth."> 2nd subst (first depth removed)",'~'.expand("<slnum>")) - let curline= getline('.') -" call Decho("curline<".curline.'>','~'.expand("<slnum>")) - if curline =~ '/$' -" call Decho("extract tree directory from current line",'~'.expand("<slnum>")) - let treedir= substitute(curline,'^\%('.s:treedepthstring.'\)*\([^'.s:treedepthstring.'].\{-}\)$','\1','e') -" call Decho("treedir<".treedir.">",'~'.expand("<slnum>")) - elseif curline =~ '@\s\+-->' -" call Decho("extract tree directory using symbolic link",'~'.expand("<slnum>")) - let treedir= substitute(curline,'^\%('.s:treedepthstring.'\)*\([^'.s:treedepthstring.'].\{-}\)$','\1','e') - let treedir= substitute(treedir,'@\s\+-->.*$','','e') -" call Decho("treedir<".treedir.">",'~'.expand("<slnum>")) - else -" call Decho("do not extract tree directory from current line and set treedir to empty",'~'.expand("<slnum>")) - let treedir= "" - endif - " construct treedir by searching backwards at correct depth -" call Decho("construct treedir by searching backwards for correct depth",'~'.expand("<slnum>")) -" call Decho("initial treedir<".treedir."> depth<".depth.">",'~'.expand("<slnum>")) - while depth != "" && search('^'.depth.'[^'.s:treedepthstring.'].\{-}/$','bW') - let dirname= substitute(getline('.'),'^\('.s:treedepthstring.'\)*','','e') - let treedir= dirname.treedir - let depth = substitute(depth,'^'.s:treedepthstring,'','') -" call Decho("constructing treedir<".treedir.">: dirname<".dirname."> while depth<".depth.">",'~'.expand("<slnum>")) - endwhile -" call Decho("treedir#1<".treedir.">",'~'.expand("<slnum>")) - if a:treetop =~ '/$' - let treedir= a:treetop.treedir - else - let treedir= a:treetop.'/'.treedir - endif -" call Decho("treedir#2<".treedir.">",'~'.expand("<slnum>")) - let treedir= substitute(treedir,'//$','/','') -" call Decho("treedir#3<".treedir.">",'~'.expand("<slnum>")) -" call Decho("restoring posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))" - call winrestview(svpos) -" call Dret("s:NetrwTreePath <".treedir.">") - return treedir -endfun - -" --------------------------------------------------------------------- -" s:NetrwWideListing: {{{2 -fun! s:NetrwWideListing() - - if w:netrw_liststyle == s:WIDELIST -" call Dfunc("NetrwWideListing() w:netrw_liststyle=".w:netrw_liststyle.' fo='.&fo.' l:fo='.&l:fo) - " look for longest filename (cpf=characters per filename) - " cpf: characters per filename - " fpl: filenames per line - " fpc: filenames per column - setl ma noro - let dict={} - " save the unnamed register and register 0-9 and a - let dict.a=[getreg('a'), getregtype('a')] - for i in range(0, 9) - let dict[i] = [getreg(i), getregtype(i)] - endfor - let dict.unnamed = [getreg(''), getregtype('')] -" call Decho("setl ma noro",'~'.expand("<slnum>")) - let b:netrw_cpf= 0 - if line("$") >= w:netrw_bannercnt - " determine the maximum filename size; use that to set cpf - exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g/^./if virtcol("$") > b:netrw_cpf|let b:netrw_cpf= virtcol("$")|endif' - NetrwKeepj call histdel("/",-1) - else - " restore stored registers - call s:RestoreRegister(dict) -" call Dret("NetrwWideListing") - return - endif - " allow for two spaces to separate columns - let b:netrw_cpf= b:netrw_cpf + 2 -" call Decho("b:netrw_cpf=max_filename_length+2=".b:netrw_cpf,'~'.expand("<slnum>")) - - " determine qty files per line (fpl) - let w:netrw_fpl= winwidth(0)/b:netrw_cpf - if w:netrw_fpl <= 0 - let w:netrw_fpl= 1 - endif -" call Decho("fpl= [winwidth=".winwidth(0)."]/[b:netrw_cpf=".b:netrw_cpf.']='.w:netrw_fpl,'~'.expand("<slnum>")) - - " make wide display - " fpc: files per column of wide listing - exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$s/^.*$/\=escape(printf("%-'.b:netrw_cpf.'S",submatch(0)),"\\")/' - NetrwKeepj call histdel("/",-1) - let fpc = (line("$") - w:netrw_bannercnt + w:netrw_fpl)/w:netrw_fpl - let newcolstart = w:netrw_bannercnt + fpc - let newcolend = newcolstart + fpc - 1 -" call Decho("bannercnt=".w:netrw_bannercnt." fpl=".w:netrw_fpl." fpc=".fpc." newcol[".newcolstart.",".newcolend."]",'~'.expand("<slnum>")) - if !has('nvim') && has("clipboard") && g:netrw_clipboard -" call Decho("(s:NetrwWideListing) save @* and @+",'~'.expand("<slnum>")) - sil! let keepregstar = @* - sil! let keepregplus = @+ - endif - while line("$") >= newcolstart - if newcolend > line("$") | let newcolend= line("$") | endif - let newcolqty= newcolend - newcolstart - exe newcolstart - " COMBAK: both of the visual-mode using lines below are problematic vis-a-vis @* - if newcolqty == 0 - exe "sil! NetrwKeepj norm! 0\<c-v>$h\"ax".w:netrw_bannercnt."G$\"ap" - else - exe "sil! NetrwKeepj norm! 0\<c-v>".newcolqty.'j$h"ax'.w:netrw_bannercnt.'G$"ap' - endif - exe "sil! NetrwKeepj ".newcolstart.','.newcolend.'d _' - exe 'sil! NetrwKeepj '.w:netrw_bannercnt - endwhile - if !has('nvim') && has("clipboard") -" call Decho("(s:NetrwWideListing) restore @* and @+",'~'.expand("<slnum>")) - if @* != keepregstar | sil! let @* = keepregstar | endif - if @+ != keepregplus | sil! let @+ = keepregplus | endif - endif - exe "sil! NetrwKeepj ".w:netrw_bannercnt.',$s/\s\+$//e' - NetrwKeepj call histdel("/",-1) - exe 'nno <buffer> <silent> w :call search(''^.\\|\s\s\zs\S'',''W'')'."\<cr>" - exe 'nno <buffer> <silent> b :call search(''^.\\|\s\s\zs\S'',''bW'')'."\<cr>" -" call Decho("NetrwWideListing) setl noma nomod ro",'~'.expand("<slnum>")) - exe "setl ".g:netrw_bufsettings - call s:RestoreRegister(dict) -" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) -" call Dret("NetrwWideListing") - return - else - if hasmapto("w","n") - sil! nunmap <buffer> w - endif - if hasmapto("b","n") - sil! nunmap <buffer> b - endif - endif -endfun - -" --------------------------------------------------------------------- -" s:PerformListing: {{{2 -fun! s:PerformListing(islocal) -" call Dfunc("s:PerformListing(islocal=".a:islocal.")") -" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol()." line($)=".line("$"),'~'.expand("<slnum>")) -" call Decho("settings: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo. " (enter)"." ei<".&ei.">",'~'.expand("<slnum>")) - sil! NetrwKeepj %d _ -" call DechoBuf(bufnr("%")) - - " set up syntax highlighting {{{3 -" call Decho("--set up syntax highlighting (ie. setl ft=netrw)",'~'.expand("<slnum>")) - sil! setl ft=netrw - - NetrwKeepj call s:NetrwOptionsSafe(a:islocal) - setl noro ma -" call Decho("setl noro ma bh=".&bh,'~'.expand("<slnum>")) - -" if exists("g:netrw_silent") && g:netrw_silent == 0 && &ch >= 1 " Decho -" call Decho("Processing your browsing request...",'~'.expand("<slnum>")) -" endif " Decho - -" call Decho('w:netrw_liststyle='.(exists("w:netrw_liststyle")? w:netrw_liststyle : 'n/a'),'~'.expand("<slnum>")) - if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict") - " force a refresh for tree listings -" call Decho("force refresh for treelisting: clear buffer<".expand("%")."> with :%d",'~'.expand("<slnum>")) - sil! NetrwKeepj %d _ - endif - - " save current directory on directory history list - NetrwKeepj call s:NetrwBookHistHandler(3,b:netrw_curdir) - - " Set up the banner {{{3 - if g:netrw_banner -" call Decho("--set up banner",'~'.expand("<slnum>")) - NetrwKeepj call setline(1,'" ============================================================================') - if exists("g:netrw_pchk") - " this undocumented option allows pchk to run with different versions of netrw without causing spurious - " failure detections. - NetrwKeepj call setline(2,'" Netrw Directory Listing') - else - NetrwKeepj call setline(2,'" Netrw Directory Listing (netrw '.g:loaded_netrw.')') - endif - if exists("g:netrw_pchk") - let curdir= substitute(b:netrw_curdir,expand("$HOME"),'~','') - else - let curdir= b:netrw_curdir - endif - if exists("g:netrw_bannerbackslash") && g:netrw_bannerbackslash - NetrwKeepj call setline(3,'" '.substitute(curdir,'/','\\','g')) - else - NetrwKeepj call setline(3,'" '.curdir) - endif - let w:netrw_bannercnt= 3 - NetrwKeepj exe "sil! NetrwKeepj ".w:netrw_bannercnt - else -" call Decho("--no banner",'~'.expand("<slnum>")) - NetrwKeepj 1 - let w:netrw_bannercnt= 1 - endif -" call Decho("w:netrw_bannercnt=".w:netrw_bannercnt." win#".winnr(),'~'.expand("<slnum>")) -" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol()." line($)=".line("$"),'~'.expand("<slnum>")) - - " construct sortby string: [name|time|size|exten] [reversed] - let sortby= g:netrw_sort_by - 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" -" call Decho("directories will be sorted by name",'~'.expand("<slnum>")) - " sorted by name (also includes the sorting sequence in the banner) - NetrwKeepj put ='\" Sorted by '.sortby - NetrwKeepj put ='\" Sort sequence: '.g:netrw_sort_sequence - let w:netrw_bannercnt= w:netrw_bannercnt + 2 - else -" call Decho("directories will be sorted by size or time",'~'.expand("<slnum>")) - " sorted by time, size, exten - NetrwKeepj put ='\" Sorted by '.sortby - let w:netrw_bannercnt= w:netrw_bannercnt + 1 - endif - exe "sil! NetrwKeepj ".w:netrw_bannercnt -" else " Decho -" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>")) - endif - - " show copy/move target, if any {{{3 - if g:netrw_banner - if exists("s:netrwmftgt") && exists("s:netrwmftgt_islocal") -" call Decho("--show copy/move target<".s:netrwmftgt.">",'~'.expand("<slnum>")) - NetrwKeepj put ='' - if s:netrwmftgt_islocal - sil! NetrwKeepj call setline(line("."),'" Copy/Move Tgt: '.s:netrwmftgt.' (local)') - else - sil! NetrwKeepj call setline(line("."),'" Copy/Move Tgt: '.s:netrwmftgt.' (remote)') - endif - let w:netrw_bannercnt= w:netrw_bannercnt + 1 - else -" call Decho("s:netrwmftgt does not exist, don't make Copy/Move Tgt",'~'.expand("<slnum>")) - endif - exe "sil! NetrwKeepj ".w:netrw_bannercnt - endif - - " Hiding... -or- Showing... {{{3 - if g:netrw_banner -" call Decho("--handle hiding/showing in banner (g:netrw_hide=".g:netrw_hide." g:netrw_list_hide<".g:netrw_list_hide.">)",'~'.expand("<slnum>")) - if g:netrw_list_hide != "" && g:netrw_hide - if g:netrw_hide == 1 - NetrwKeepj put ='\" Hiding: '.g:netrw_list_hide - else - NetrwKeepj put ='\" Showing: '.g:netrw_list_hide - endif - let w:netrw_bannercnt= w:netrw_bannercnt + 1 - endif - exe "NetrwKeepj ".w:netrw_bannercnt - -" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) - let quickhelp = g:netrw_quickhelp%len(s:QuickHelp) -" call Decho("quickhelp =".quickhelp,'~'.expand("<slnum>")) - NetrwKeepj put ='\" Quick Help: <F1>:help '.s:QuickHelp[quickhelp] -" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) - NetrwKeepj put ='\" ==============================================================================' - let w:netrw_bannercnt= w:netrw_bannercnt + 2 -" else " Decho -" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>")) - endif - - " bannercnt should index the line just after the banner - if g:netrw_banner - let w:netrw_bannercnt= w:netrw_bannercnt + 1 - exe "sil! NetrwKeepj ".w:netrw_bannercnt -" call Decho("--w:netrw_bannercnt=".w:netrw_bannercnt." (should index line just after banner) line($)=".line("$"),'~'.expand("<slnum>")) -" else " Decho -" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>")) - endif - - " get list of files -" call Decho("--Get list of files - islocal=".a:islocal,'~'.expand("<slnum>")) - if a:islocal - NetrwKeepj call s:LocalListing() - else " remote - NetrwKeepj let badresult= s:NetrwRemoteListing() - if badresult -" call Decho("w:netrw_bannercnt=".(exists("w:netrw_bannercnt")? w:netrw_bannercnt : 'n/a')." win#".winnr()." buf#".bufnr("%")."<".bufname("%").">",'~'.expand("<slnum>")) -" call Dret("s:PerformListing : error detected by NetrwRemoteListing") - return - endif - endif - - " manipulate the directory listing (hide, sort) {{{3 - if !exists("w:netrw_bannercnt") - let w:netrw_bannercnt= 0 - endif -" call Decho("--manipulate directory listing (hide, sort)",'~'.expand("<slnum>")) -" call Decho("g:netrw_banner=".g:netrw_banner." w:netrw_bannercnt=".w:netrw_bannercnt." (banner complete)",'~'.expand("<slnum>")) -" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>")) - - if !g:netrw_banner || line("$") >= w:netrw_bannercnt -" call Decho("manipulate directory listing (support hide)",'~'.expand("<slnum>")) -" call Decho("g:netrw_hide=".g:netrw_hide." g:netrw_list_hide<".g:netrw_list_hide.">",'~'.expand("<slnum>")) - if g:netrw_hide && g:netrw_list_hide != "" - NetrwKeepj call s:NetrwListHide() - endif - 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" - " sort by name -" call Decho("sort by name",'~'.expand("<slnum>")) - 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' - " name: sort by name of file - exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$sort'.' '.g:netrw_sort_options - else - " reverse direction sorting - exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$sort!'.' '.g:netrw_sort_options - endif - endif - - " remove priority pattern prefix -" call Decho("remove priority pattern prefix",'~'.expand("<slnum>")) - exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\d\{3}'.g:netrw_sepchr.'//e' - NetrwKeepj call histdel("/",-1) - - elseif g:netrw_sort_by =~# "^ext" - " exten: sort by extension - " The histdel(...,-1) calls remove the last search from the search history -" call Decho("sort by extension",'~'.expand("<slnum>")) - exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g+/+s/^/001'.g:netrw_sepchr.'/' - NetrwKeepj call histdel("/",-1) - exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$v+[./]+s/^/002'.g:netrw_sepchr.'/' - NetrwKeepj call histdel("/",-1) - exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$v+['.g:netrw_sepchr.'/]+s/^\(.*\.\)\(.\{-\}\)$/\2'.g:netrw_sepchr.'&/e' - 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' - " normal direction sorting - exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$sort'.' '.g:netrw_sort_options - else - " reverse direction sorting - exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$sort!'.' '.g:netrw_sort_options - endif - endif - exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^.\{-}'.g:netrw_sepchr.'//e' - NetrwKeepj call histdel("/",-1) - - 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' -" call Decho('exe sil NetrwKeepj '.w:netrw_bannercnt.',$sort','~'.expand("<slnum>")) - exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$sort'.' '.g:netrw_sort_options - else -" call Decho('exe sil NetrwKeepj '.w:netrw_bannercnt.',$sort!','~'.expand("<slnum>")) - exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$sort!'.' '.g:netrw_sort_options - endif -" call Decho("remove leading digits/ (sorting) information from listing",'~'.expand("<slnum>")) - exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\d\{-}\///e' - NetrwKeepj call histdel("/",-1) - endif - endif - - 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 - call histdel("/",-1) - endif - endif - endif -" call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>")) - - " convert to wide/tree listing {{{3 -" call Decho("--modify display if wide/tree listing style",'~'.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. " (internal#1)",'~'.expand("<slnum>")) - NetrwKeepj call s:NetrwWideListing() -" 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. " (internal#2)",'~'.expand("<slnum>")) - NetrwKeepj call s:NetrwTreeListing(b:netrw_curdir) -" 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. " (internal#3)",'~'.expand("<slnum>")) - - " resolve symbolic links if local and (thin or tree) - if a:islocal && (w:netrw_liststyle == s:THINLIST || (exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST)) -" call Decho("--resolve symbolic links if local and thin|tree",'~'.expand("<slnum>")) - sil! keepp g/@$/call s:ShowLink() - endif - - if exists("w:netrw_bannercnt") && (line("$") >= w:netrw_bannercnt || !g:netrw_banner) - " place cursor on the top-left corner of the file listing -" call Decho("--place cursor on top-left corner of file listing",'~'.expand("<slnum>")) - exe 'sil! '.w:netrw_bannercnt - sil! NetrwKeepj norm! 0 -" call Decho(" tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol()." line($)=".line("$"),'~'.expand("<slnum>")) - else -" call Decho("--did NOT place cursor on top-left corner",'~'.expand("<slnum>")) -" call Decho(" w:netrw_bannercnt=".(exists("w:netrw_bannercnt")? w:netrw_bannercnt : 'n/a'),'~'.expand("<slnum>")) -" call Decho(" line($)=".line("$"),'~'.expand("<slnum>")) -" call Decho(" g:netrw_banner=".(exists("g:netrw_banner")? g:netrw_banner : 'n/a'),'~'.expand("<slnum>")) - endif - - " record previous current directory - let w:netrw_prvdir= b:netrw_curdir -" call Decho("--record netrw_prvdir<".w:netrw_prvdir.">",'~'.expand("<slnum>")) - - " save certain window-oriented variables into buffer-oriented variables {{{3 -" call Decho("--save some window-oriented variables into buffer oriented variables",'~'.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. " (internal#4)",'~'.expand("<slnum>")) - NetrwKeepj call s:SetBufWinVars() -" 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. " (internal#5)",'~'.expand("<slnum>")) - NetrwKeepj call s:NetrwOptionsRestore("w:") -" 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. " (internal#6)",'~'.expand("<slnum>")) - - " set display to netrw display settings -" call Decho("--set display to netrw display settings (".g:netrw_bufsettings.")",'~'.expand("<slnum>")) - exe "setl ".g:netrw_bufsettings -" 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. " (internal#7)",'~'.expand("<slnum>")) - if g:netrw_liststyle == s:LONGLIST -" call Decho("exe setl ts=".(g:netrw_maxfilenamelen+1),'~'.expand("<slnum>")) - exe "setl ts=".(g:netrw_maxfilenamelen+1) - endif -" call Decho("PerformListing buffer:",'~'.expand("<slnum>")) -" call DechoBuf(bufnr("%")) - - if exists("s:treecurpos") -" call Decho("s:treecurpos exists; restore posn",'~'.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. " (internal#8)",'~'.expand("<slnum>")) -" call Decho("restoring posn to s:treecurpos<".string(s:treecurpos).">",'~'.expand("<slnum>")) - NetrwKeepj call winrestview(s:treecurpos) - unlet s:treecurpos - endif - -" 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. " (return)",'~'.expand("<slnum>")) -" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol()." line($)=".line("$"),'~'.expand("<slnum>")) -" call Dret("s:PerformListing : curpos<".string(getpos(".")).">") -endfun - -" --------------------------------------------------------------------- -" s:SetupNetrwStatusLine: {{{2 -fun! s:SetupNetrwStatusLine(statline) -" call Dfunc("SetupNetrwStatusLine(statline<".a:statline.">)") - - if !exists("s:netrw_setup_statline") - let s:netrw_setup_statline= 1 -" call Decho("do first-time status line setup",'~'.expand("<slnum>")) - - if !exists("s:netrw_users_stl") - let s:netrw_users_stl= &stl - endif - if !exists("s:netrw_users_ls") - let s:netrw_users_ls= &laststatus - endif - - " set up User9 highlighting as needed - let dict={} - let dict.a=[getreg('a'), getregtype('a')] - redir @a - try - hi User9 - catch /^Vim\%((\a\{3,})\)\=:E411/ - if &bg == "dark" - hi User9 ctermfg=yellow ctermbg=blue guifg=yellow guibg=blue - else - hi User9 ctermbg=yellow ctermfg=blue guibg=yellow guifg=blue - endif - endtry - redir END - call s:RestoreRegister(dict) - endif - - " set up status line (may use User9 highlighting) - " insure that windows have a statusline - " make sure statusline is displayed - let &l:stl=a:statline - setl laststatus=2 -" call Decho("stl=".&stl,'~'.expand("<slnum>")) - redraw - -" call Dret("SetupNetrwStatusLine : stl=".&stl) -endfun - -" ========================================= -" Remote Directory Browsing Support: {{{1 -" ========================================= - -" --------------------------------------------------------------------- -" s:NetrwRemoteFtpCmd: unfortunately, not all ftp servers honor options for ls {{{2 -" This function assumes that a long listing will be received. Size, time, -" and reverse sorts will be requested of the server but not otherwise -" enforced here. -fun! s:NetrwRemoteFtpCmd(path,listcmd) -" call Dfunc("NetrwRemoteFtpCmd(path<".a:path."> listcmd<".a:listcmd.">) w:netrw_method=".(exists("w:netrw_method")? w:netrw_method : (exists("b:netrw_method")? b:netrw_method : "???"))) -" call Decho("line($)=".line("$")." win#".winnr()." w:netrw_bannercnt=".w:netrw_bannercnt,'~'.expand("<slnum>")) - " sanity check: {{{3 - if !exists("w:netrw_method") - if exists("b:netrw_method") - let w:netrw_method= b:netrw_method - else - call netrw#ErrorMsg(2,"(s:NetrwRemoteFtpCmd) internal netrw error",93) -" call Dret("NetrwRemoteFtpCmd") - return - endif - endif - - " WinXX ftp uses unix style input, so set ff to unix " {{{3 - let ffkeep= &ff - setl ma ff=unix noro -" call Decho("setl ma ff=unix noro",'~'.expand("<slnum>")) - - " clear off any older non-banner lines " {{{3 - " note that w:netrw_bannercnt indexes the line after the banner -" call Decho('exe sil! NetrwKeepj '.w:netrw_bannercnt.",$d _ (clear off old non-banner lines)",'~'.expand("<slnum>")) - exe "sil! NetrwKeepj ".w:netrw_bannercnt.",$d _" - - "......................................... - if w:netrw_method == 2 || w:netrw_method == 5 " {{{3 - " ftp + <.netrc>: Method #2 - if a:path != "" - NetrwKeepj put ='cd \"'.a:path.'\"' - endif - if exists("g:netrw_ftpextracmd") - NetrwKeepj put =g:netrw_ftpextracmd -" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) - endif - NetrwKeepj call setline(line("$")+1,a:listcmd) -" exe "NetrwKeepj ".w:netrw_bannercnt.',$g/^./call Decho("ftp#".line(".").": ".getline("."),''~''.expand("<slnum>"))' - if exists("g:netrw_port") && g:netrw_port != "" -" call Decho("exe ".s:netrw_silentxfer.w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1),'~'.expand("<slnum>")) - exe s:netrw_silentxfer." NetrwKeepj ".w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1) - else -" call Decho("exe ".s:netrw_silentxfer.w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1),'~'.expand("<slnum>")) - exe s:netrw_silentxfer." NetrwKeepj ".w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1) - endif - - "......................................... - elseif w:netrw_method == 3 " {{{3 - " ftp + machine,id,passwd,filename: Method #3 - setl ff=unix - if exists("g:netrw_port") && g:netrw_port != "" - NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port - else - NetrwKeepj put ='open '.g:netrw_machine - endif - - " handle userid and password - let host= substitute(g:netrw_machine,'\..*$','','') -" call Decho("host<".host.">",'~'.expand("<slnum>")) - if exists("s:netrw_hup") && exists("s:netrw_hup[host]") - call NetUserPass("ftp:".host) - endif - if exists("g:netrw_uid") && g:netrw_uid != "" - if exists("g:netrw_ftp") && g:netrw_ftp == 1 - NetrwKeepj put =g:netrw_uid - if exists("s:netrw_passwd") && s:netrw_passwd != "" - NetrwKeepj put ='\"'.s:netrw_passwd.'\"' - endif - elseif exists("s:netrw_passwd") - NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"' - endif - endif - - if a:path != "" - NetrwKeepj put ='cd \"'.a:path.'\"' - endif - if exists("g:netrw_ftpextracmd") - NetrwKeepj put =g:netrw_ftpextracmd -" call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) - endif - NetrwKeepj call setline(line("$")+1,a:listcmd) - - " perform ftp: - " -i : turns off interactive prompting from ftp - " -n unix : DON'T use <.netrc>, even though it exists - " -n win32: quit being obnoxious about password - if exists("w:netrw_bannercnt") -" exe w:netrw_bannercnt.',$g/^./call Decho("ftp#".line(".").": ".getline("."),''~''.expand("<slnum>"))' - call s:NetrwExe(s:netrw_silentxfer.w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." ".g:netrw_ftp_options) -" else " Decho -" call Decho("WARNING: w:netrw_bannercnt doesn't exist!",'~'.expand("<slnum>")) -" g/^./call Decho("SKIPPING ftp#".line(".").": ".getline("."),'~'.expand("<slnum>")) - endif - - "......................................... - elseif w:netrw_method == 9 " {{{3 - " sftp username@machine: Method #9 - " s:netrw_sftp_cmd - setl ff=unix - - " restore settings - let &l:ff= ffkeep -" call Dret("NetrwRemoteFtpCmd") - return - - "......................................... - else " {{{3 - NetrwKeepj call netrw#ErrorMsg(s:WARNING,"unable to comply with your request<" . bufname("%") . ">",23) - endif - - " cleanup for Windows " {{{3 - if has("win32") - sil! NetrwKeepj %s/\r$//e - NetrwKeepj call histdel("/",-1) - endif - if a:listcmd == "dir" - " infer directory/link based on the file permission string - sil! NetrwKeepj g/d\%([-r][-w][-x]\)\{3}/NetrwKeepj s@$@/@e - sil! NetrwKeepj g/l\%([-r][-w][-x]\)\{3}/NetrwKeepj s/$/@/e - NetrwKeepj call histdel("/",-1) - NetrwKeepj call histdel("/",-1) - if w:netrw_liststyle == s:THINLIST || w:netrw_liststyle == s:WIDELIST || (exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST) - exe "sil! NetrwKeepj ".w:netrw_bannercnt.',$s/^\%(\S\+\s\+\)\{8}//e' - NetrwKeepj call histdel("/",-1) - endif - endif - - " ftp's listing doesn't seem to include ./ or ../ " {{{3 - if !search('^\.\/$\|\s\.\/$','wn') - exe 'NetrwKeepj '.w:netrw_bannercnt - NetrwKeepj put ='./' - endif - if !search('^\.\.\/$\|\s\.\.\/$','wn') - exe 'NetrwKeepj '.w:netrw_bannercnt - NetrwKeepj put ='../' - endif - - " restore settings " {{{3 - let &l:ff= ffkeep -" call Dret("NetrwRemoteFtpCmd") -endfun - -" --------------------------------------------------------------------- -" s:NetrwRemoteListing: {{{2 -fun! s:NetrwRemoteListing() -" call Dfunc("s:NetrwRemoteListing() b:netrw_curdir<".b:netrw_curdir.">) win#".winnr()) - - if !exists("w:netrw_bannercnt") && exists("s:bannercnt") - let w:netrw_bannercnt= s:bannercnt - endif - if !exists("w:netrw_bannercnt") && exists("b:bannercnt") - let w:netrw_bannercnt= b:bannercnt - endif - - call s:RemotePathAnalysis(b:netrw_curdir) - - " sanity check: - if exists("b:netrw_method") && b:netrw_method =~ '[235]' -" call Decho("b:netrw_method=".b:netrw_method,'~'.expand("<slnum>")) - if !executable("ftp") -" call Decho("ftp is not executable",'~'.expand("<slnum>")) - if !exists("g:netrw_quiet") - call netrw#ErrorMsg(s:ERROR,"this system doesn't support remote directory listing via ftp",18) - endif - call s:NetrwOptionsRestore("w:") -" call Dret("s:NetrwRemoteListing -1") - return -1 - endif - - elseif !exists("g:netrw_list_cmd") || g:netrw_list_cmd == '' -" call Decho("g:netrw_list_cmd<",(exists("g:netrw_list_cmd")? 'n/a' : "-empty-").">",'~'.expand("<slnum>")) - if !exists("g:netrw_quiet") - if g:netrw_list_cmd == "" - NetrwKeepj call netrw#ErrorMsg(s:ERROR,"your g:netrw_list_cmd is empty; perhaps ".g:netrw_ssh_cmd." is not executable on your system",47) - else - NetrwKeepj call netrw#ErrorMsg(s:ERROR,"this system doesn't support remote directory listing via ".g:netrw_list_cmd,19) - endif - endif - - NetrwKeepj call s:NetrwOptionsRestore("w:") -" call Dret("s:NetrwRemoteListing -1") - return -1 - endif " (remote handling sanity check) -" call Decho("passed remote listing sanity checks",'~'.expand("<slnum>")) - - if exists("b:netrw_method") -" call Decho("setting w:netrw_method to b:netrw_method<".b:netrw_method.">",'~'.expand("<slnum>")) - let w:netrw_method= b:netrw_method - endif - - if s:method == "ftp" - " use ftp to get remote file listing {{{3 -" 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' - let listcmd= g:netrw_ftp_timelist_cmd - 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>")) - call s:NetrwRemoteFtpCmd(s:path,listcmd) -" exe "sil! keepalt NetrwKeepj ".w:netrw_bannercnt.',$g/^./call Decho("raw listing: ".getline("."),''~''.expand("<slnum>"))' - - " report on missing file or directory messages - if search('[Nn]o such file or directory\|Failed to change directory') - let mesg= getline(".") - if exists("w:netrw_bannercnt") - setl ma - exe w:netrw_bannercnt.",$d _" - setl noma - endif - NetrwKeepj call s:NetrwOptionsRestore("w:") - call netrw#ErrorMsg(s:WARNING,mesg,96) -" call Dret("s:NetrwRemoteListing : -1") - return -1 - endif - - if w:netrw_liststyle == s:THINLIST || w:netrw_liststyle == s:WIDELIST || (exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST) - " shorten the listing -" call Decho("generate short listing",'~'.expand("<slnum>")) - exe "sil! keepalt NetrwKeepj ".w:netrw_bannercnt - - " cleanup - if g:netrw_ftp_browse_reject != "" - exe "sil! keepalt NetrwKeepj g/".g:netrw_ftp_browse_reject."/NetrwKeepj d" - NetrwKeepj call histdel("/",-1) - endif - sil! NetrwKeepj %s/\r$//e - NetrwKeepj call histdel("/",-1) - - " if there's no ../ listed, then put ../ in - let line1= line(".") - exe "sil! NetrwKeepj ".w:netrw_bannercnt - let line2= search('\.\.\/\%(\s\|$\)','cnW') -" call Decho("search(".'\.\.\/\%(\s\|$\)'."','cnW')=".line2." w:netrw_bannercnt=".w:netrw_bannercnt,'~'.expand("<slnum>")) - if line2 == 0 -" call Decho("netrw is putting ../ into listing",'~'.expand("<slnum>")) - sil! NetrwKeepj put='../' - endif - exe "sil! NetrwKeepj ".line1 - sil! NetrwKeepj norm! 0 - -" call Decho("line1=".line1." line2=".line2." line(.)=".line("."),'~'.expand("<slnum>")) - if search('^\d\{2}-\d\{2}-\d\{2}\s','n') " M$ ftp site cleanup -" call Decho("M$ ftp cleanup",'~'.expand("<slnum>")) - exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\d\{2}-\d\{2}-\d\{2}\s\+\d\+:\d\+[AaPp][Mm]\s\+\%(<DIR>\|\d\+\)\s\+//' - NetrwKeepj call histdel("/",-1) - else " normal ftp cleanup -" call Decho("normal ftp cleanup",'~'.expand("<slnum>")) - exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\(\%(\S\+\s\+\)\{7}\S\+\)\s\+\(\S.*\)$/\2/e' - exe "sil! NetrwKeepj ".w:netrw_bannercnt.',$g/ -> /s# -> .*/$#/#e' - exe "sil! NetrwKeepj ".w:netrw_bannercnt.',$g/ -> /s# -> .*$#/#e' - NetrwKeepj call histdel("/",-1) - NetrwKeepj call histdel("/",-1) - NetrwKeepj call histdel("/",-1) - endif - endif - - else - " use ssh to get remote file listing {{{3 -" call Decho("use ssh to get remote file listing: s:path<".s:path.">",'~'.expand("<slnum>")) - let listcmd= s:MakeSshCmd(g:netrw_list_cmd) -" call Decho("listcmd<".listcmd."> (using g:netrw_list_cmd)",'~'.expand("<slnum>")) - if g:netrw_scp_cmd =~ '^pscp' -" call Decho("1: exe r! ".s:ShellEscape(listcmd.s:path, 1),'~'.expand("<slnum>")) - exe "NetrwKeepj r! ".listcmd.s:ShellEscape(s:path, 1) - " remove rubbish and adjust listing format of 'pscp' to 'ssh ls -FLa' like - sil! NetrwKeepj g/^Listing directory/NetrwKeepj d - sil! NetrwKeepj g/^d[-rwx][-rwx][-rwx]/NetrwKeepj s+$+/+e - sil! NetrwKeepj g/^l[-rwx][-rwx][-rwx]/NetrwKeepj s+$+@+e - NetrwKeepj call histdel("/",-1) - NetrwKeepj call histdel("/",-1) - NetrwKeepj call histdel("/",-1) - if g:netrw_liststyle != s:LONGLIST - sil! NetrwKeepj g/^[dlsp-][-rwx][-rwx][-rwx]/NetrwKeepj s/^.*\s\(\S\+\)$/\1/e - NetrwKeepj call histdel("/",-1) - endif - else - if s:path == "" -" call Decho("2: exe r! ".listcmd,'~'.expand("<slnum>")) - exe "NetrwKeepj keepalt r! ".listcmd - else -" call Decho("3: exe r! ".listcmd.' '.s:ShellEscape(fnameescape(s:path),1),'~'.expand("<slnum>")) - exe "NetrwKeepj keepalt r! ".listcmd.' '.s:ShellEscape(fnameescape(s:path),1) -" call Decho("listcmd<".listcmd."> path<".s:path.">",'~'.expand("<slnum>")) - endif - endif - - " cleanup - if g:netrw_ssh_browse_reject != "" -" call Decho("cleanup: exe sil! g/".g:netrw_ssh_browse_reject."/NetrwKeepj d",'~'.expand("<slnum>")) - exe "sil! g/".g:netrw_ssh_browse_reject."/NetrwKeepj d" - NetrwKeepj call histdel("/",-1) - endif - endif - - if w:netrw_liststyle == s:LONGLIST - " do a long listing; these substitutions need to be done prior to sorting {{{3 -" call Decho("fix long listing:",'~'.expand("<slnum>")) - - if s:method == "ftp" - " cleanup - exe "sil! NetrwKeepj ".w:netrw_bannercnt - while getline('.') =~# g:netrw_ftp_browse_reject - sil! NetrwKeepj d - endwhile - " if there's no ../ listed, then put ../ in - let line1= line(".") - sil! NetrwKeepj 1 - sil! NetrwKeepj call search('^\.\.\/\%(\s\|$\)','W') - let line2= line(".") - if line2 == 0 - if b:netrw_curdir != '/' - exe 'sil! NetrwKeepj '.w:netrw_bannercnt."put='../'" - endif - endif - exe "sil! NetrwKeepj ".line1 - sil! NetrwKeepj norm! 0 - endif - - if search('^\d\{2}-\d\{2}-\d\{2}\s','n') " M$ ftp site cleanup -" call Decho("M$ ftp site listing cleanup",'~'.expand("<slnum>")) - exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\(\d\{2}-\d\{2}-\d\{2}\s\+\d\+:\d\+[AaPp][Mm]\s\+\%(<DIR>\|\d\+\)\s\+\)\(\w.*\)$/\2\t\1/' - elseif exists("w:netrw_bannercnt") && w:netrw_bannercnt <= line("$") -" call Decho("normal ftp site listing cleanup: bannercnt=".w:netrw_bannercnt." line($)=".line("$"),'~'.expand("<slnum>")) - exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$s/ -> .*$//e' - exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$s/^\(\%(\S\+\s\+\)\{7}\S\+\)\s\+\(\S.*\)$/\2 \t\1/e' - exe 'sil NetrwKeepj '.w:netrw_bannercnt - NetrwKeepj call histdel("/",-1) - NetrwKeepj call histdel("/",-1) - NetrwKeepj call histdel("/",-1) - endif - endif - -" if exists("w:netrw_bannercnt") && w:netrw_bannercnt <= line("$") " Decho -" exe "NetrwKeepj ".w:netrw_bannercnt.',$g/^./call Decho("listing: ".getline("."),''~''.expand("<slnum>"))' -" endif " Decho - -" call Dret("s:NetrwRemoteListing 0") - return 0 -endfun - -" --------------------------------------------------------------------- -" s:NetrwRemoteRm: remove/delete a remote file or directory {{{2 -fun! s:NetrwRemoteRm(usrhost,path) range - let svpos= winsaveview() - - let all= 0 - if exists("s:netrwmarkfilelist_{bufnr('%')}") - " remove all marked files - for fname in s:netrwmarkfilelist_{bufnr("%")} - let ok= s:NetrwRemoteRmFile(a:path,fname,all) - if ok =~# 'q\%[uit]' - break - elseif ok =~# 'a\%[ll]' - let all= 1 - endif - endfor - call s:NetrwUnmarkList(bufnr("%"),b:netrw_curdir) - - else - " remove files specified by range - - " preparation for removing multiple files/directories - let keepsol = &l:sol - setl nosol - let ctr = a:firstline - - " remove multiple files and directories - while ctr <= a:lastline - exe "NetrwKeepj ".ctr - let ok= s:NetrwRemoteRmFile(a:path,s:NetrwGetWord(),all) - if ok =~# 'q\%[uit]' - break - elseif ok =~# 'a\%[ll]' - let all= 1 - endif - let ctr= ctr + 1 - endwhile - let &l:sol = keepsol - endif - - " refresh the (remote) directory listing - NetrwKeepj call s:NetrwRefresh(0,s:NetrwBrowseChgDir(0,'./',0)) - NetrwKeepj call winrestview(svpos) -endfun - -" --------------------------------------------------------------------- -" s:NetrwRemoteRmFile: {{{2 -fun! s:NetrwRemoteRmFile(path,rmfile,all) -" call Dfunc("s:NetrwRemoteRmFile(path<".a:path."> rmfile<".a:rmfile.">) all=".a:all) - - let all= a:all - let ok = "" - - if a:rmfile !~ '^"' && (a:rmfile =~ '@$' || a:rmfile !~ '[\/]$') - " attempt to remove file -" call Decho("attempt to remove file (all=".all.")",'~'.expand("<slnum>")) - if !all - echohl Statement -" call Decho("case all=0:",'~'.expand("<slnum>")) - call inputsave() - let ok= input("Confirm deletion of file<".a:rmfile."> ","[{y(es)},n(o),a(ll),q(uit)] ") - call inputrestore() - echohl NONE - if ok == "" - let ok="no" - endif - let ok= substitute(ok,'\[{y(es)},n(o),a(ll),q(uit)]\s*','','e') - if ok =~# 'a\%[ll]' - let all= 1 - endif - endif - - 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>")) - let path= a:path - if path =~ '^\a\{3,}://' - let path= substitute(path,'^\a\{3,}://[^/]\+/','','') - endif - sil! NetrwKeepj .,$d _ - call s:NetrwRemoteFtpCmd(path,"delete ".'"'.a:rmfile.'"') - else -" call Decho("case ssh: g:netrw_rm_cmd<".g:netrw_rm_cmd.">",'~'.expand("<slnum>")) - let netrw_rm_cmd= s:MakeSshCmd(g:netrw_rm_cmd) -" call Decho("netrw_rm_cmd<".netrw_rm_cmd.">",'~'.expand("<slnum>")) - if !exists("b:netrw_curdir") - NetrwKeepj call netrw#ErrorMsg(s:ERROR,"for some reason b:netrw_curdir doesn't exist!",53) - let ok="q" - else - let remotedir= substitute(b:netrw_curdir,'^.\{-}//[^/]\+/\(.*\)$','\1','') -" call Decho("netrw_rm_cmd<".netrw_rm_cmd.">",'~'.expand("<slnum>")) -" call Decho("remotedir<".remotedir.">",'~'.expand("<slnum>")) -" call Decho("rmfile<".a:rmfile.">",'~'.expand("<slnum>")) - if remotedir != "" - let netrw_rm_cmd= netrw_rm_cmd." ".s:ShellEscape(fnameescape(remotedir.a:rmfile)) - else - let netrw_rm_cmd= netrw_rm_cmd." ".s:ShellEscape(fnameescape(a:rmfile)) - endif -" call Decho("call system(".netrw_rm_cmd.")",'~'.expand("<slnum>")) - let ret= system(netrw_rm_cmd) - if v:shell_error != 0 - if exists("b:netrw_curdir") && b:netrw_curdir != getcwd() && !g:netrw_keepdir - call netrw#ErrorMsg(s:ERROR,"remove failed; perhaps due to vim's current directory<".getcwd()."> not matching netrw's (".b:netrw_curdir.") (see :help netrw-cd)",102) - else - call netrw#ErrorMsg(s:WARNING,"cmd<".netrw_rm_cmd."> failed",60) - endif - 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]' -" call Decho("ok==".ok,'~'.expand("<slnum>")) - endif - - else - " attempt to remove directory -" call Decho("attempt to remove directory",'~'.expand("<slnum>")) - if !all - call inputsave() - let ok= input("Confirm deletion of directory<".a:rmfile."> ","[{y(es)},n(o),a(ll),q(uit)] ") - call inputrestore() - if ok == "" - let ok="no" - endif - let ok= substitute(ok,'\[{y(es)},n(o),a(ll),q(uit)]\s*','','e') - if ok =~# 'a\%[ll]' - let all= 1 - endif - endif - - 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 - let rmfile = substitute(a:path.a:rmfile,'/$','','') - let netrw_rmdir_cmd = s:MakeSshCmd(netrw#WinPath(g:netrw_rmdir_cmd)).' '.s:ShellEscape(netrw#WinPath(rmfile)) -" call Decho("attempt to remove dir: system(".netrw_rmdir_cmd.")",'~'.expand("<slnum>")) - let ret= system(netrw_rmdir_cmd) -" call Decho("returned=".ret." errcode=".v:shell_error,'~'.expand("<slnum>")) - - if v:shell_error != 0 -" call Decho("v:shell_error not 0",'~'.expand("<slnum>")) - let netrw_rmf_cmd= s:MakeSshCmd(netrw#WinPath(g:netrw_rmf_cmd)).' '.s:ShellEscape(netrw#WinPath(substitute(rmfile,'[\/]$','','e'))) -" call Decho("2nd attempt to remove dir: system(".netrw_rmf_cmd.")",'~'.expand("<slnum>")) - let ret= system(netrw_rmf_cmd) -" call Decho("returned=".ret." errcode=".v:shell_error,'~'.expand("<slnum>")) - - if v:shell_error != 0 && !exists("g:netrw_quiet") - NetrwKeepj call netrw#ErrorMsg(s:ERROR,"unable to remove directory<".rmfile."> -- is it empty?",22) - endif - endif - endif - - elseif ok =~# 'q\%[uit]' -" call Decho("ok==".ok,'~'.expand("<slnum>")) - endif - endif - -" call Dret("s:NetrwRemoteRmFile ".ok) - return ok -endfun - -" --------------------------------------------------------------------- -" s:NetrwRemoteRename: rename a remote file or directory {{{2 -fun! s:NetrwRemoteRename(usrhost,path) range - - " preparation for removing multiple files/directories - let svpos = winsaveview() -" call Decho("saving posn to svpos<".string(svpos).">",'~'.expand("<slnum>")) - let ctr = a:firstline - let rename_cmd = s:MakeSshCmd(g:netrw_rename_cmd) - - " rename files given by the markfilelist - if exists("s:netrwmarkfilelist_{bufnr('%')}") - for oldname in s:netrwmarkfilelist_{bufnr("%")} - if exists("subfrom") - let newname= substitute(oldname,subfrom,subto,'') - else - call inputsave() - let newname= input("Moving ".oldname." to : ",oldname) - call inputrestore() - if newname =~ '^s/' - let subfrom = substitute(newname,'^s/\([^/]*\)/.*/$','\1','') - let subto = substitute(newname,'^s/[^/]*/\(.*\)/$','\1','') - let newname = substitute(oldname,subfrom,subto,'') - endif - endif - - if exists("w:netrw_method") && (w:netrw_method == 2 || w:netrw_method == 3) - NetrwKeepj call s:NetrwRemoteFtpCmd(a:path,"rename ".oldname." ".newname) - else - let oldname= s:ShellEscape(a:path.oldname) - let newname= s:ShellEscape(a:path.newname) - let ret = system(netrw#WinPath(rename_cmd).' '.oldname.' '.newname) - endif - - endfor - call s:NetrwUnMarkFile(1) - - else - - " attempt to rename files/directories - let keepsol= &l:sol - setl nosol - while ctr <= a:lastline - exe "NetrwKeepj ".ctr - - let oldname= s:NetrwGetWord() - - call inputsave() - let newname= input("Moving ".oldname." to : ",oldname) - call inputrestore() - - if exists("w:netrw_method") && (w:netrw_method == 2 || w:netrw_method == 3) - call s:NetrwRemoteFtpCmd(a:path,"rename ".oldname." ".newname) - else - let oldname= s:ShellEscape(a:path.oldname) - let newname= s:ShellEscape(a:path.newname) - let ret = system(netrw#WinPath(rename_cmd).' '.oldname.' '.newname) - endif - - let ctr= ctr + 1 - endwhile - let &l:sol= keepsol - endif - - " refresh the directory - NetrwKeepj call s:NetrwRefresh(0,s:NetrwBrowseChgDir(0,'./',0)) - NetrwKeepj call winrestview(svpos) -endfun - -" ========================================== -" Local Directory Browsing Support: {{{1 -" ========================================== - -" --------------------------------------------------------------------- -" netrw#FileUrlEdit: handles editing file://* files {{{2 -" Should accept: file://localhost/etc/fstab -" file:///etc/fstab -" file:///c:/WINDOWS/clock.avi -" file:///c|/WINDOWS/clock.avi -" file://localhost/c:/WINDOWS/clock.avi -" file://localhost/c|/WINDOWS/clock.avi -" file://c:/foo.txt -" file:///c:/foo.txt -" and %XX (where X is [0-9a-fA-F] is converted into a character with the given hexadecimal value -fun! netrw#FileUrlEdit(fname) -" call Dfunc("netrw#FileUrlEdit(fname<".a:fname.">)") - let fname = a:fname - if fname =~ '^file://localhost/' -" call Decho('converting file://localhost/ -to- file:///','~'.expand("<slnum>")) - let fname= substitute(fname,'^file://localhost/','file:///','') -" call Decho("fname<".fname.">",'~'.expand("<slnum>")) - endif - if has("win32") - if fname =~ '^file:///\=\a[|:]/' -" call Decho('converting file:///\a|/ -to- file://\a:/','~'.expand("<slnum>")) - let fname = substitute(fname,'^file:///\=\(\a\)[|:]/','file://\1:/','') -" call Decho("fname<".fname.">",'~'.expand("<slnum>")) - endif - endif - let fname2396 = netrw#RFC2396(fname) - let fname2396e= fnameescape(fname2396) - let plainfname= substitute(fname2396,'file://\(.*\)','\1',"") - if has("win32") -" call Decho("windows exception for plainfname",'~'.expand("<slnum>")) - if plainfname =~ '^/\+\a:' -" call Decho('removing leading "/"s','~'.expand("<slnum>")) - let plainfname= substitute(plainfname,'^/\+\(\a:\)','\1','') - endif - endif - -" call Decho("fname2396<".fname2396.">",'~'.expand("<slnum>")) -" call Decho("plainfname<".plainfname.">",'~'.expand("<slnum>")) - exe "sil doau BufReadPre ".fname2396e - exe 'NetrwKeepj keepalt edit '.plainfname - exe 'sil! NetrwKeepj keepalt bdelete '.fnameescape(a:fname) - -" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) -" call Dret("netrw#FileUrlEdit") - exe "sil doau BufReadPost ".fname2396e -endfun - -" --------------------------------------------------------------------- -" netrw#LocalBrowseCheck: {{{2 -fun! netrw#LocalBrowseCheck(dirname) - " This function is called by netrwPlugin.vim's s:LocalBrowseCheck(), s:NetrwRexplore(), - " and by <cr> when atop a listed file/directory (via a buffer-local map) - " - " unfortunate interaction -- split window debugging can't be used here, must use - " D-echoRemOn or D-echoTabOn as the BufEnter event triggers - " another call to LocalBrowseCheck() when attempts to write - " to the DBG buffer are made. - " - " The &ft == "netrw" test was installed because the BufEnter event - " 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("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>")) - " getting E930: Cannot use :redir inside execute -"" call Dredir("ls!","netrw#LocalBrowseCheck") -" call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) -" call Decho("current buffer#".bufnr("%")."<".bufname("%")."> ft=".&ft,'~'.expand("<slnum>")) - - let ykeep= @@ - if isdirectory(s:NetrwFile(a:dirname)) -" call Decho("is-directory ft<".&ft."> b:netrw_curdir<".(exists("b:netrw_curdir")? b:netrw_curdir : " doesn't exist")."> dirname<".a:dirname.">"." line($)=".line("$")." ft<".&ft."> g:netrw_fastbrowse=".g:netrw_fastbrowse,'~'.expand("<slnum>")) - - if &ft != "netrw" || (exists("b:netrw_curdir") && b:netrw_curdir != a:dirname) || g:netrw_fastbrowse <= 1 -" call Decho("case 1 : ft=".&ft,'~'.expand("<slnum>")) -" call Decho("s:rexposn_".bufnr("%")."<".bufname("%")."> ".(exists("s:rexposn_".bufnr("%"))? "exists" : "does not exist"),'~'.expand("<slnum>")) - sil! NetrwKeepj keepalt call s:NetrwBrowse(1,a:dirname) - - elseif &ft == "netrw" && line("$") == 1 -" call Decho("case 2 (ft≡netrw && line($)≡1)",'~'.expand("<slnum>")) - sil! NetrwKeepj keepalt call s:NetrwBrowse(1,a:dirname) - - elseif exists("s:treeforceredraw") -" call Decho("case 3 (treeforceredraw)",'~'.expand("<slnum>")) - 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 - endif - - " The following code wipes out currently unused netrw buffers - " IF g:netrw_fastbrowse is zero (ie. slow browsing selected) - " AND IF the listing style is not a tree listing - if exists("g:netrw_fastbrowse") && g:netrw_fastbrowse == 0 && g:netrw_liststyle != s:TREELIST -" call Decho("wiping out currently unused netrw buffers",'~'.expand("<slnum>")) - let ibuf = 1 - let buflast = bufnr("$") - while ibuf <= buflast - if bufwinnr(ibuf) == -1 && isdirectory(s:NetrwFile(bufname(ibuf))) - exe "sil! keepj keepalt ".ibuf."bw!" - endif - let ibuf= ibuf + 1 - endwhile - endif - let @@= ykeep -" 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>")) - " not a directory, ignore it -" call Dret("netrw#LocalBrowseCheck : not a directory, ignoring it; dirname<".a:dirname.">") -endfun - -" --------------------------------------------------------------------- -" s:LocalBrowseRefresh: this function is called after a user has {{{2 -" performed any shell command. The idea is to cause all local-browsing -" buffers to be refreshed after a user has executed some shell command, -" on the chance that s/he removed/created a file/directory with it. -fun! s:LocalBrowseRefresh() - " determine which buffers currently reside in a tab - if !exists("s:netrw_browselist") - return - endif - if !exists("w:netrw_bannercnt") - return - endif - if !empty(getcmdwintype()) - " cannot move away from cmdline window, see :h E11 - return - endif - if exists("s:netrw_events") && s:netrw_events == 1 - " s:LocalFastBrowser gets called (indirectly) from a - let s:netrw_events= 2 - return - endif - let itab = 1 - let buftablist = [] - let ykeep = @@ - while itab <= tabpagenr("$") - let buftablist = buftablist + tabpagebuflist() - let itab = itab + 1 - sil! tabn - endwhile - " GO through all buffers on netrw_browselist (ie. just local-netrw buffers): - " | refresh any netrw window - " | wipe out any non-displaying netrw buffer - let curwinid = win_getid(winnr()) - let ibl = 0 - for ibuf in s:netrw_browselist - if bufwinnr(ibuf) == -1 && index(buftablist,ibuf) == -1 - " wipe out any non-displaying netrw buffer - " (ibuf not shown in a current window AND - " ibuf not in any tab) - exe "sil! keepj bd ".fnameescape(ibuf) - call remove(s:netrw_browselist,ibl) - continue - elseif index(tabpagebuflist(),ibuf) != -1 - " refresh any netrw buffer - exe bufwinnr(ibuf)."wincmd w" - 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 - endif - if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST - NetrwKeepj call s:NetrwRefreshTreeDict(w:netrw_treetop) - endif - NetrwKeepj call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0)) - endif - let ibl= ibl + 1 - endfor - call win_gotoid(curwinid) - let @@= ykeep -endfun - -" --------------------------------------------------------------------- -" s:LocalFastBrowser: handles setting up/taking down fast browsing for the local browser {{{2 -" -" g:netrw_ Directory Is -" fastbrowse Local Remote -" slow 0 D D D=Deleting a buffer implies it will not be re-used (slow) -" med 1 D H H=Hiding a buffer implies it may be re-used (fast) -" fast 2 H H -" -" Deleting a buffer means that it will be re-loaded when examined, hence "slow". -" Hiding a buffer means that it will be re-used when examined, hence "fast". -" (re-using a buffer may not be as accurate) -" -" s:netrw_events : doesn't exist, s:LocalFastBrowser() will install autocmds with medium-speed or fast browsing -" =1: autocmds installed, but ignore next FocusGained event to avoid initial double-refresh of listing. -" BufEnter may be first event, then a FocusGained event. Ignore the first FocusGained event. -" If :Explore used: it sets s:netrw_events to 2, so no FocusGained events are ignored. -" =2: autocmds installed (doesn't ignore any FocusGained events) -fun! s:LocalFastBrowser() - - " initialize browselist, a list of buffer numbers that the local browser has used - if !exists("s:netrw_browselist") - let s:netrw_browselist= [] - endif - - " append current buffer to fastbrowse list - if empty(s:netrw_browselist) || bufnr("%") > s:netrw_browselist[-1] - call add(s:netrw_browselist,bufnr("%")) - endif - - " enable autocmd events to handle refreshing/removing local browser buffers - " If local browse buffer is currently showing: refresh it - " If local browse buffer is currently hidden : wipe it - " g:netrw_fastbrowse=0 : slow speed, never re-use directory listing - " =1 : medium speed, re-use directory listing for remote only - " =2 : fast speed, always re-use directory listing when possible - if g:netrw_fastbrowse <= 1 && !exists("#ShellCmdPost") && !exists("s:netrw_events") - let s:netrw_events= 1 - augroup AuNetrwEvent - au! - if has("win32") - au ShellCmdPost * call s:LocalBrowseRefresh() - else - au ShellCmdPost,FocusGained * call s:LocalBrowseRefresh() - endif - augroup END - - " user must have changed fastbrowse to its fast setting, so remove - " the associated autocmd events - elseif g:netrw_fastbrowse > 1 && exists("#ShellCmdPost") && exists("s:netrw_events") - unlet s:netrw_events - augroup AuNetrwEvent - au! - augroup END - augroup! AuNetrwEvent - endif -endfun - -fun! s:NetrwLocalListingList(dirname,setmaxfilenamelen) - " get the list of files contained in the current directory - let dirname = a:dirname - let dirnamelen = strlen(dirname) - let filelist = s:NetrwGlob(dirname,"*",0) - let filelist = filelist + s:NetrwGlob(dirname,".*",0) - - if g:netrw_cygwin == 0 && has("win32") - elseif index(filelist,'..') == -1 && dirname !~ '/' - " include ../ in the glob() entry if its missing - let filelist= filelist+[s:ComposePath(dirname,"../")] - endif - - if a:setmaxfilenamelen && get(g:, 'netrw_dynamic_maxfilenamelen', 0) - let filelistcopy = map(deepcopy(filelist),'fnamemodify(v:val, ":t")') - let g:netrw_maxfilenamelen = max(map(filelistcopy,'len(v:val)')) + 1 - endif - - let resultfilelist = [] - for filename in filelist - - if getftype(filename) == "link" - " indicate a symbolic link - let pfile= filename."@" - - elseif getftype(filename) == "socket" - " indicate a socket - let pfile= filename."=" - - elseif getftype(filename) == "fifo" - " indicate a fifo - let pfile= filename."|" - - elseif isdirectory(s:NetrwFile(filename)) - " indicate a directory - let pfile= filename."/" - - elseif exists("b:netrw_curdir") && b:netrw_curdir !~ '^.*://' && !isdirectory(s:NetrwFile(filename)) - if has("win32") - if filename =~ '\.[eE][xX][eE]$' || filename =~ '\.[cC][oO][mM]$' || filename =~ '\.[bB][aA][tT]$' - " indicate an executable - let pfile= filename."*" - else - " normal file - let pfile= filename - endif - elseif executable(filename) - " indicate an executable - let pfile= filename."*" - else - " normal file - let pfile= filename - endif - - else - " normal file - let pfile= filename - endif - - if pfile =~ '//$' - let pfile= substitute(pfile,'//$','/','e') - endif - let pfile= strpart(pfile,dirnamelen) - let pfile= substitute(pfile,'^[/\\]','','e') - - if w:netrw_liststyle == s:LONGLIST - let longfile = printf("%-".g:netrw_maxfilenamelen."S",pfile) - let sz = getfsize(filename) - let szlen = 15 - (strdisplaywidth(longfile) - g:netrw_maxfilenamelen) - let szlen = (szlen > 0) ? szlen : 0 - - if g:netrw_sizestyle =~# "[hH]" - let sz= s:NetrwHumanReadable(sz) - endif - let fsz = printf("%".szlen."S",sz) - let pfile= longfile." ".fsz." ".strftime(g:netrw_timefmt,getftime(filename)) - endif - - if g:netrw_sort_by =~# "^t" - " sort by time (handles time up to 1 quintillion seconds, US) - " Decorate listing by prepending a timestamp/ . Sorting will then be done based on time. - let t = getftime(filename) - let ft = printf("%018d",t) - let ftpfile= ft.'/'.pfile - let resultfilelist += [ftpfile] - - elseif g:netrw_sort_by =~ "^s" - " sort by size (handles file sizes up to 1 quintillion bytes, US) - let sz = getfsize(filename) - let fsz = printf("%018d",sz) - let fszpfile= fsz.'/'.pfile - let resultfilelist += [fszpfile] - - else - " sort by name - let resultfilelist += [pfile] - endif - endfor - - return resultfilelist -endfun - -" --------------------------------------------------------------------- -" s:LocalListing: does the job of "ls" for local directories {{{2 -fun! s:LocalListing() - - let filelist = s:NetrwLocalListingList(b:netrw_curdir, 1) - for filename in filelist - sil! NetrwKeepj put =filename - endfor - - " cleanup any windows mess at end-of-line - sil! NetrwKeepj g/^$/d - sil! NetrwKeepj %s/\r$//e - call histdel("/",-1) - exe "setl ts=".(g:netrw_maxfilenamelen+1) -endfun - -" --------------------------------------------------------------------- -" s:NetrwLocalExecute: uses system() to execute command under cursor ("X" command support) {{{2 -fun! s:NetrwLocalExecute(cmd) -" call Dfunc("s:NetrwLocalExecute(cmd<".a:cmd.">)") - let ykeep= @@ - " sanity check - if !executable(a:cmd) - call netrw#ErrorMsg(s:ERROR,"the file<".a:cmd."> is not executable!",89) - let @@= ykeep -" call Dret("s:NetrwLocalExecute") - return - endif - - let optargs= input(":!".a:cmd,"","file") -" call Decho("optargs<".optargs.">",'~'.expand("<slnum>")) - let result= system(a:cmd.optargs) -" call Decho("result,'~'.expand("<slnum>")) - - " strip any ansi escape sequences off - let result = substitute(result,"\e\\[[0-9;]*m","","g") - - " show user the result(s) - echomsg result - let @@= ykeep - -" call Dret("s:NetrwLocalExecute") -endfun - -" --------------------------------------------------------------------- -" s:NetrwLocalRename: rename a local file or directory {{{2 -fun! s:NetrwLocalRename(path) range - - if !exists("w:netrw_bannercnt") - let w:netrw_bannercnt= b:netrw_bannercnt - endif - - " preparation for removing multiple files/directories - let ykeep = @@ - let ctr = a:firstline - let svpos = winsaveview() - let all = 0 - - " rename files given by the markfilelist - if exists("s:netrwmarkfilelist_{bufnr('%')}") - for oldname in s:netrwmarkfilelist_{bufnr("%")} - if exists("subfrom") - let newname= substitute(oldname,subfrom,subto,'') - else - call inputsave() - let newname= input("Moving ".oldname." to : ",oldname,"file") - call inputrestore() - if newname =~ '' - " two ctrl-x's : ignore all of string preceding the ctrl-x's - let newname = substitute(newname,'^.*','','') - elseif newname =~ '' - " one ctrl-x : ignore portion of string preceding ctrl-x but after last / - let newname = substitute(newname,'[^/]*','','') - endif - if newname =~ '^s/' - let subfrom = substitute(newname,'^s/\([^/]*\)/.*/$','\1','') - let subto = substitute(newname,'^s/[^/]*/\(.*\)/$','\1','') - let newname = substitute(oldname,subfrom,subto,'') - endif - endif - if !all && filereadable(newname) - call inputsave() - let response= input("File<".newname."> already exists; do you want to overwrite it? (y/all/n) ") - call inputrestore() - if response == "all" - let all= 1 - elseif response != "y" && response != "yes" - " refresh the directory - NetrwKeepj call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0)) - NetrwKeepj call winrestview(svpos) - let @@= ykeep - return - endif - endif - call rename(oldname,newname) - endfor - call s:NetrwUnmarkList(bufnr("%"),b:netrw_curdir) - - else - - " attempt to rename files/directories - while ctr <= a:lastline - exe "NetrwKeepj ".ctr - - " sanity checks - if line(".") < w:netrw_bannercnt - let ctr= ctr + 1 - continue - endif - let curword= s:NetrwGetWord() - if curword == "./" || curword == "../" - let ctr= ctr + 1 - continue - endif - - NetrwKeepj norm! 0 - let oldname= s:ComposePath(a:path,curword) - - call inputsave() - let newname= input("Moving ".oldname." to : ",substitute(oldname,'/*$','','e')) - call inputrestore() - - call rename(oldname,newname) - let ctr= ctr + 1 - endwhile - endif - - " refresh the directory - NetrwKeepj call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0)) - NetrwKeepj call winrestview(svpos) - let @@= ykeep -endfun - -" --------------------------------------------------------------------- -" s:NetrwLocalRm: {{{2 -fun! s:NetrwLocalRm(path) range - if !exists("w:netrw_bannercnt") - let w:netrw_bannercnt= b:netrw_bannercnt - endif - - " preparation for removing multiple files/directories - let ykeep = @@ - let ret = 0 - let all = 0 - let svpos = winsaveview() - - if exists("s:netrwmarkfilelist_{bufnr('%')}") - " remove all marked files - for fname in s:netrwmarkfilelist_{bufnr("%")} - let ok= s:NetrwLocalRmFile(a:path,fname,all) - if ok =~# 'q\%[uit]' || ok == "no" - break - elseif ok =~# '^a\%[ll]$' - let all= 1 - endif - endfor - call s:NetrwUnMarkFile(1) - - else - " remove (multiple) files and directories - - let keepsol= &l:sol - setl nosol - let ctr = a:firstline - while ctr <= a:lastline - exe "NetrwKeepj ".ctr - - " sanity checks - if line(".") < w:netrw_bannercnt - let ctr= ctr + 1 - continue - endif - let curword= s:NetrwGetWord() - if curword == "./" || curword == "../" - let ctr= ctr + 1 - continue - endif - let ok= s:NetrwLocalRmFile(a:path,curword,all) - if ok =~# 'q\%[uit]' || ok == "no" - break - elseif ok =~# '^a\%[ll]$' - let all= 1 - endif - let ctr= ctr + 1 - endwhile - let &l:sol= keepsol - endif - - " refresh the directory - if bufname("%") != "NetrwMessage" - NetrwKeepj call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0)) - NetrwKeepj call winrestview(svpos) - endif - let @@= ykeep -endfun - -" --------------------------------------------------------------------- -" s:NetrwLocalRmFile: remove file fname given the path {{{2 -" Give confirmation prompt unless all==1 -fun! s:NetrwLocalRmFile(path,fname,all) -" call Dfunc("s:NetrwLocalRmFile(path<".a:path."> fname<".a:fname."> all=".a:all) - - let all= a:all - let ok = "" - NetrwKeepj norm! 0 - let rmfile= s:NetrwFile(s:ComposePath(a:path,escape(a:fname, '\\'))) -" call Decho("rmfile<".rmfile.">",'~'.expand("<slnum>")) - - if rmfile !~ '^"' && (rmfile =~ '@$' || rmfile !~ '[\/]$') - " attempt to remove file -" call Decho("attempt to remove file<".rmfile.">",'~'.expand("<slnum>")) - if !all - echohl Statement - call inputsave() - let ok= input("Confirm deletion of file <".rmfile."> ","[{y(es)},n(o),a(ll),q(uit)] ") - call inputrestore() - echohl NONE - if ok == "" - let ok="no" - endif -" 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]$' - let all= 1 - endif - endif - - if all || ok =~# '^y\%[es]$' || ok == "" - let ret= s:NetrwDelete(rmfile) -" call Decho("errcode=".v:shell_error." ret=".ret,'~'.expand("<slnum>")) - endif - - else - " attempt to remove directory - if !all - echohl Statement - call inputsave() - let ok= input("Confirm *recursive* deletion of directory <".rmfile."> ","[{y(es)},n(o),a(ll),q(uit)] ") - call inputrestore() - let ok= substitute(ok,'\[{y(es)},n(o),a(ll),q(uit)]\s*','','e') - if ok == "" - let ok="no" - endif - if ok =~# '^a\%[ll]$' - let all= 1 - endif - endif - let rmfile= substitute(rmfile,'[\/]$','','e') - - if all || ok =~# '^y\%[es]$' || ok == "" - if delete(rmfile,"rf") - call netrw#ErrorMsg(s:ERROR,"unable to delete directory <".rmfile.">!",103) - endif - endif - endif - -" call Dret("s:NetrwLocalRmFile ".ok) - return ok -endfun - -" ===================================================================== -" Support Functions: {{{1 - -" --------------------------------------------------------------------- -" 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 -fun! netrw#Access(ilist) - if a:ilist == 0 - if exists("s:netrwmarkfilelist_".bufnr('%')) - return s:netrwmarkfilelist_{bufnr('%')} - else - return "no-list-buf#".bufnr('%') - endif - elseif a:ilist == 1 - return s:netrwmftgt - endif -endfun - -" --------------------------------------------------------------------- -" netrw#Call: allows user-specified mappings to call internal netrw functions {{{2 -fun! netrw#Call(funcname,...) - return call("s:".a:funcname,a:000) -endfun - -" --------------------------------------------------------------------- -" netrw#Expose: allows UserMaps and pchk to look at otherwise script-local variables {{{2 -" I expect this function to be used in -" :PChkAssert netrw#Expose("netrwmarkfilelist") -" for example. -fun! netrw#Expose(varname) -" call Dfunc("netrw#Expose(varname<".a:varname.">)") - if exists("s:".a:varname) - exe "let retval= s:".a:varname -" call Decho("retval=".retval,'~'.expand("<slnum>")) - if exists("g:netrw_pchk") -" call Decho("type(g:netrw_pchk=".g:netrw_pchk.")=".type(retval),'~'.expand("<slnum>")) - if type(retval) == 3 - let retval = copy(retval) - let i = 0 - while i < len(retval) - let retval[i]= substitute(retval[i],expand("$HOME"),'~','') - let i = i + 1 - endwhile - endif -" call Dret("netrw#Expose ".string(retval)),'~'.expand("<slnum>")) - return string(retval) - else -" call Decho("g:netrw_pchk doesn't exist",'~'.expand("<slnum>")) - endif - else -" call Decho("s:".a:varname." doesn't exist",'~'.expand("<slnum>")) - let retval= "n/a" - endif - -" call Dret("netrw#Expose ".string(retval)) - return retval -endfun - -" --------------------------------------------------------------------- -" netrw#Modify: allows UserMaps to set (modify) script-local variables {{{2 -fun! netrw#Modify(varname,newvalue) -" call Dfunc("netrw#Modify(varname<".a:varname.">,newvalue<".string(a:newvalue).">)") - exe "let s:".a:varname."= ".string(a:newvalue) -" call Dret("netrw#Modify") -endfun - -" --------------------------------------------------------------------- -" netrw#RFC2396: converts %xx into characters {{{2 -fun! netrw#RFC2396(fname) -" call Dfunc("netrw#RFC2396(fname<".a:fname.">)") - let fname = escape(substitute(a:fname,'%\(\x\x\)','\=printf("%c","0x".submatch(1))','ge')," \t") -" call Dret("netrw#RFC2396 ".fname) - return fname -endfun - -" --------------------------------------------------------------------- -" netrw#UserMaps: supports user-specified maps {{{2 -" see :help function() -" -" g:Netrw_UserMaps is a List with members such as: -" [[keymap sequence, function reference],...] -" -" The referenced function may return a string, -" refresh : refresh the display -" -other- : this string will be executed -" or it may return a List of strings. -" -" Each keymap-sequence will be set up with a nnoremap -" to invoke netrw#UserMaps(a:islocal). -" Related functions: -" netrw#Expose(varname) -- see s:varname variables -" netrw#Modify(varname,newvalue) -- modify value of s:varname variable -" netrw#Call(funcname,...) -- call internal netrw function with optional arguments -fun! netrw#UserMaps(islocal) -" call Dfunc("netrw#UserMaps(islocal=".a:islocal.")") -" call Decho("g:Netrw_UserMaps ".(exists("g:Netrw_UserMaps")? "exists" : "does NOT exist"),'~'.expand("<slnum>")) - - " set up usermaplist - if exists("g:Netrw_UserMaps") && type(g:Netrw_UserMaps) == 3 -" call Decho("g:Netrw_UserMaps has type 3<List>",'~'.expand("<slnum>")) - for umap in g:Netrw_UserMaps -" call Decho("type(umap[0]<".string(umap[0]).">)=".type(umap[0])." (should be 1=string)",'~'.expand("<slnum>")) -" call Decho("type(umap[1])=".type(umap[1])." (should be 1=string)",'~'.expand("<slnum>")) - " if umap[0] is a string and umap[1] is a string holding a function name - if type(umap[0]) == 1 && type(umap[1]) == 1 -" call Decho("nno <buffer> <silent> ".umap[0]." :call s:UserMaps(".a:islocal.",".string(umap[1]).")<cr>",'~'.expand("<slnum>")) - exe "nno <buffer> <silent> ".umap[0]." :call <SID>UserMaps(".a:islocal.",'".umap[1]."')<cr>" - else - call netrw#ErrorMsg(s:WARNING,"ignoring usermap <".string(umap[0])."> -- not a [string,funcref] entry",99) - endif - endfor - endif -" call Dret("netrw#UserMaps") -endfun - -" --------------------------------------------------------------------- -" netrw#WinPath: tries to insure that the path is windows-acceptable, whether cygwin is used or not {{{2 -fun! netrw#WinPath(path) -" call Dfunc("netrw#WinPath(path<".a:path.">)") - if (!g:netrw_cygwin || &shell !~ '\%(\<bash\>\|\<zsh\>\)\%(\.exe\)\=$') && has("win32") - " remove cygdrive prefix, if present - let path = substitute(a:path,g:netrw_cygdrive.'/\(.\)','\1:','') - " remove trailing slash (Win95) - let path = substitute(path, '\(\\\|/\)$', '', 'g') - " remove escaped spaces - let path = substitute(path, '\ ', ' ', 'g') - " convert slashes to backslashes - let path = substitute(path, '/', '\', 'g') - else - let path= a:path - endif -" call Dret("netrw#WinPath <".path.">") - return path -endfun - -" --------------------------------------------------------------------- -" s:StripTrailingSlash: removes trailing slashes from a path {{{2 -fun! s:StripTrailingSlash(path) - " remove trailing slash - return substitute(a:path, '[/\\]$', '', 'g') -endfun - -" --------------------------------------------------------------------- -" s:NetrwBadd: adds marked files to buffer list or vice versa {{{2 -" cb : bl2mf=0 add marked files to buffer list -" cB : bl2mf=1 use bufferlist to mark files -" (mnemonic: cb = copy (marked files) to buffer list) -fun! s:NetrwBadd(islocal,bl2mf) -" " call Dfunc("s:NetrwBadd(islocal=".a:islocal." mf2bl=".mf2bl.")") - if a:bl2mf - " cB: add buffer list to marked files - redir => bufl - ls - redir END - let bufl = map(split(bufl,"\n"),'substitute(v:val,''^.\{-}"\(.*\)".\{-}$'',''\1'','''')') - for fname in bufl - call s:NetrwMarkFile(a:islocal,fname) - endfor - else - " cb: add marked files to buffer list - for fname in s:netrwmarkfilelist_{bufnr("%")} -" " call Decho("badd ".fname,'~'.expand("<slnum>")) - exe "badd ".fnameescape(fname) - endfor - let curbufnr = bufnr("%") - let curdir = s:NetrwGetCurdir(a:islocal) - call s:NetrwUnmarkList(curbufnr,curdir) " remove markings from local buffer - endif -" call Dret("s:NetrwBadd") -endfun - -" --------------------------------------------------------------------- -" s:ComposePath: Appends a new part to a path taking different systems into consideration {{{2 -fun! s:ComposePath(base,subdir) -" call Dfunc("s:ComposePath(base<".a:base."> subdir<".a:subdir.">)") - - if has("amiga") -" call Decho("amiga",'~'.expand("<slnum>")) - let ec = a:base[s:Strlen(a:base)-1] - if ec != '/' && ec != ':' - let ret = a:base."/" . a:subdir - else - let ret = a:base.a:subdir - endif - - " COMBAK: test on windows with changing to root directory: :e C:/ - elseif a:subdir =~ '^\a:[/\\]\([^/\\]\|$\)' && has("win32") -" call Decho("windows",'~'.expand("<slnum>")) - let ret= a:subdir - - elseif a:base =~ '^\a:[/\\]\([^/\\]\|$\)' && has("win32") -" call Decho("windows",'~'.expand("<slnum>")) - if a:base =~ '[/\\]$' - let ret= a:base.a:subdir - else - let ret= a:base.'/'.a:subdir - endif - - elseif a:base =~ '^\a\{3,}://' -" call Decho("remote linux/macos",'~'.expand("<slnum>")) - let urlbase = substitute(a:base,'^\(\a\+://.\{-}/\)\(.*\)$','\1','') - let curpath = substitute(a:base,'^\(\a\+://.\{-}/\)\(.*\)$','\2','') - if a:subdir == '../' - if curpath =~ '[^/]/[^/]\+/$' - let curpath= substitute(curpath,'[^/]\+/$','','') - else - let curpath="" - endif - let ret= urlbase.curpath - else - let ret= urlbase.curpath.a:subdir - endif -" call Decho("urlbase<".urlbase.">",'~'.expand("<slnum>")) -" call Decho("curpath<".curpath.">",'~'.expand("<slnum>")) -" call Decho("ret<".ret.">",'~'.expand("<slnum>")) - - else -" call Decho("local linux/macos",'~'.expand("<slnum>")) - let ret = substitute(a:base."/".a:subdir,"//","/","g") - if a:base =~ '^//' - " keeping initial '//' for the benefit of network share listing support - let ret= '/'.ret - endif - let ret= simplify(ret) - endif - -" call Dret("s:ComposePath ".ret) - return ret -endfun - -" --------------------------------------------------------------------- -" s:DeleteBookmark: deletes a file/directory from Netrw's bookmark system {{{2 -" Related Functions: s:MakeBookmark() s:NetrwBookHistHandler() s:NetrwBookmark() -fun! s:DeleteBookmark(fname) -" call Dfunc("s:DeleteBookmark(fname<".a:fname.">)") - call s:MergeBookmarks() - - if exists("g:netrw_bookmarklist") - let indx= index(g:netrw_bookmarklist,a:fname) - if indx == -1 - let indx= 0 - while indx < len(g:netrw_bookmarklist) - if g:netrw_bookmarklist[indx] =~ a:fname - call remove(g:netrw_bookmarklist,indx) - let indx= indx - 1 - endif - let indx= indx + 1 - endwhile - else - " remove exact match - call remove(g:netrw_bookmarklist,indx) - endif - endif - -" call Dret("s:DeleteBookmark") -endfun - -" --------------------------------------------------------------------- -" s:FileReadable: o/s independent filereadable {{{2 -fun! s:FileReadable(fname) -" call Dfunc("s:FileReadable(fname<".a:fname.">)") - - if g:netrw_cygwin - let ret= filereadable(s:NetrwFile(substitute(a:fname,g:netrw_cygdrive.'/\(.\)','\1:/',''))) - else - let ret= filereadable(s:NetrwFile(a:fname)) - endif - -" call Dret("s:FileReadable ".ret) - return ret -endfun - -" --------------------------------------------------------------------- -" s:GetTempfile: gets a tempname that'll work for various o/s's {{{2 -" Places correct suffix on end of temporary filename, -" using the suffix provided with fname -fun! s:GetTempfile(fname) -" call Dfunc("s:GetTempfile(fname<".a:fname.">)") - - if !exists("b:netrw_tmpfile") - " get a brand new temporary filename - let tmpfile= tempname() -" call Decho("tmpfile<".tmpfile."> : from tempname()",'~'.expand("<slnum>")) - - let tmpfile= substitute(tmpfile,'\','/','ge') -" call Decho("tmpfile<".tmpfile."> : chgd any \\ -> /",'~'.expand("<slnum>")) - - " sanity check -- does the temporary file's directory exist? - if !isdirectory(s:NetrwFile(substitute(tmpfile,'[^/]\+$','','e'))) -" call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) - NetrwKeepj call netrw#ErrorMsg(s:ERROR,"your <".substitute(tmpfile,'[^/]\+$','','e')."> directory is missing!",2) -" call Dret("s:GetTempfile getcwd<".getcwd().">") - return "" - endif - - " let netrw#NetSource() know about the tmpfile - let s:netrw_tmpfile= tmpfile " used by netrw#NetSource() and netrw#BrowseX() -" call Decho("tmpfile<".tmpfile."> s:netrw_tmpfile<".s:netrw_tmpfile.">",'~'.expand("<slnum>")) - - " o/s dependencies - if g:netrw_cygwin != 0 - let tmpfile = substitute(tmpfile,'^\(\a\):',g:netrw_cygdrive.'/\1','e') - elseif has("win32") - if !exists("+shellslash") || !&ssl - let tmpfile = substitute(tmpfile,'/','\','g') - endif - else - let tmpfile = tmpfile - endif - let b:netrw_tmpfile= tmpfile -" call Decho("o/s dependent fixed tempname<".tmpfile.">",'~'.expand("<slnum>")) - else - " re-use temporary filename - let tmpfile= b:netrw_tmpfile -" call Decho("tmpfile<".tmpfile."> re-using",'~'.expand("<slnum>")) - endif - - " use fname's suffix for the temporary file - if a:fname != "" - if a:fname =~ '\.[^./]\+$' -" call Decho("using fname<".a:fname.">'s suffix",'~'.expand("<slnum>")) - if a:fname =~ '\.tar\.gz$' || a:fname =~ '\.tar\.bz2$' || a:fname =~ '\.tar\.xz$' - let suffix = ".tar".substitute(a:fname,'^.*\(\.[^./]\+\)$','\1','e') - elseif a:fname =~ '.txz$' - let suffix = ".txz".substitute(a:fname,'^.*\(\.[^./]\+\)$','\1','e') - else - let suffix = substitute(a:fname,'^.*\(\.[^./]\+\)$','\1','e') - endif -" call Decho("suffix<".suffix.">",'~'.expand("<slnum>")) - let tmpfile= substitute(tmpfile,'\.tmp$','','e') -" call Decho("chgd tmpfile<".tmpfile."> (removed any .tmp suffix)",'~'.expand("<slnum>")) - let tmpfile .= suffix -" call Decho("chgd tmpfile<".tmpfile."> (added ".suffix." suffix) netrw_fname<".b:netrw_fname.">",'~'.expand("<slnum>")) - let s:netrw_tmpfile= tmpfile " supports netrw#NetSource() - endif - endif - -" 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:GetTempfile <".tmpfile.">") - return tmpfile -endfun - -" --------------------------------------------------------------------- -" s:MakeSshCmd: transforms input command using USEPORT HOSTNAME into {{{2 -" a correct command for use with a system() call -fun! s:MakeSshCmd(sshcmd) -" call Dfunc("s:MakeSshCmd(sshcmd<".a:sshcmd.">) user<".s:user."> machine<".s:machine.">") - if s:user == "" - let sshcmd = substitute(a:sshcmd,'\<HOSTNAME\>',s:machine,'') - else - let sshcmd = substitute(a:sshcmd,'\<HOSTNAME\>',s:user."@".s:machine,'') - endif - if exists("g:netrw_port") && g:netrw_port != "" - let sshcmd= substitute(sshcmd,"USEPORT",g:netrw_sshport.' '.g:netrw_port,'') - elseif exists("s:port") && s:port != "" - let sshcmd= substitute(sshcmd,"USEPORT",g:netrw_sshport.' '.s:port,'') - else - let sshcmd= substitute(sshcmd,"USEPORT ",'','') - endif -" call Dret("s:MakeSshCmd <".sshcmd.">") - return sshcmd -endfun - -" --------------------------------------------------------------------- -" s:MakeBookmark: enters a bookmark into Netrw's bookmark system {{{2 -fun! s:MakeBookmark(fname) -" call Dfunc("s:MakeBookmark(fname<".a:fname.">)") - - if !exists("g:netrw_bookmarklist") - let g:netrw_bookmarklist= [] - endif - - if index(g:netrw_bookmarklist,a:fname) == -1 - " curdir not currently in g:netrw_bookmarklist, so include it - if isdirectory(s:NetrwFile(a:fname)) && a:fname !~ '/$' - call add(g:netrw_bookmarklist,a:fname.'/') - elseif a:fname !~ '/' - call add(g:netrw_bookmarklist,getcwd()."/".a:fname) - else - call add(g:netrw_bookmarklist,a:fname) - endif - call sort(g:netrw_bookmarklist) - endif - -" call Dret("s:MakeBookmark") -endfun - -" --------------------------------------------------------------------- -" s:MergeBookmarks: merge current bookmarks with saved bookmarks {{{2 -fun! s:MergeBookmarks() -" call Dfunc("s:MergeBookmarks() : merge current bookmarks into .netrwbook") - " get bookmarks from .netrwbook file - let savefile= s:NetrwHome()."/.netrwbook" - if filereadable(s:NetrwFile(savefile)) -" call Decho("merge bookmarks (active and file)",'~'.expand("<slnum>")) - NetrwKeepj call s:NetrwBookHistSave() -" call Decho("bookmark delete savefile<".savefile.">",'~'.expand("<slnum>")) - NetrwKeepj call delete(savefile) - endif -" call Dret("s:MergeBookmarks") -endfun - -" --------------------------------------------------------------------- -" s:NetrwBMShow: {{{2 -fun! s:NetrwBMShow() -" call Dfunc("s:NetrwBMShow()") - redir => bmshowraw - menu - redir END - let bmshowlist = split(bmshowraw,'\n') - if bmshowlist != [] - let bmshowfuncs= filter(bmshowlist,'v:val =~# "<SNR>\\d\\+_BMShow()"') - if bmshowfuncs != [] - let bmshowfunc = substitute(bmshowfuncs[0],'^.*:\(call.*BMShow()\).*$','\1','') - if bmshowfunc =~# '^call.*BMShow()' - exe "sil! NetrwKeepj ".bmshowfunc - endif - endif - endif -" call Dret("s:NetrwBMShow : bmshowfunc<".(exists("bmshowfunc")? bmshowfunc : 'n/a').">") -endfun - -" --------------------------------------------------------------------- -" s:NetrwCursor: responsible for setting cursorline/cursorcolumn based upon g:netrw_cursor {{{2 -fun! s:NetrwCursor(editfile) - if !exists("w:netrw_liststyle") - let w:netrw_liststyle= g:netrw_liststyle - endif -" call Dfunc("s:NetrwCursor() ft<".&ft."> liststyle=".w:netrw_liststyle." g:netrw_cursor=".g:netrw_cursor." s:netrw_usercuc=".s:netrw_usercuc." s:netrw_usercul=".s:netrw_usercul) - -" call Decho("(s:NetrwCursor) COMBAK: cuc=".&l:cuc." cul=".&l:cul) - - if &ft != "netrw" - " if the current window isn't a netrw directory listing window, then use user cursorline/column - " settings. Affects when netrw is used to read/write a file using scp/ftp/etc. -" call Decho("case ft!=netrw: use user cul,cuc",'~'.expand("<slnum>")) - - elseif g:netrw_cursor == 8 - if w:netrw_liststyle == s:WIDELIST - setl cursorline - setl cursorcolumn - else - setl cursorline - endif - elseif g:netrw_cursor == 7 - setl cursorline - elseif g:netrw_cursor == 6 - if w:netrw_liststyle == s:WIDELIST - setl cursorline - endif - elseif g:netrw_cursor == 4 - " all styles: cursorline, cursorcolumn -" call Decho("case g:netrw_cursor==4: setl cul cuc",'~'.expand("<slnum>")) - setl cursorline - setl cursorcolumn - - elseif g:netrw_cursor == 3 - " thin-long-tree: cursorline, user's cursorcolumn - " wide : cursorline, cursorcolumn - if w:netrw_liststyle == s:WIDELIST -" call Decho("case g:netrw_cursor==3 and wide: setl cul cuc",'~'.expand("<slnum>")) - setl cursorline - setl cursorcolumn - else -" call Decho("case g:netrw_cursor==3 and not wide: setl cul (use user's cuc)",'~'.expand("<slnum>")) - setl cursorline - endif - - elseif g:netrw_cursor == 2 - " thin-long-tree: cursorline, user's cursorcolumn - " wide : cursorline, user's cursorcolumn -" call Decho("case g:netrw_cursor==2: setl cuc (use user's cul)",'~'.expand("<slnum>")) - setl cursorline - - elseif g:netrw_cursor == 1 - " thin-long-tree: user's cursorline, user's cursorcolumn - " wide : cursorline, user's cursorcolumn - if w:netrw_liststyle == s:WIDELIST -" call Decho("case g:netrw_cursor==2 and wide: setl cul (use user's cuc)",'~'.expand("<slnum>")) - setl cursorline - else -" call Decho("case g:netrw_cursor==2 and not wide: (use user's cul,cuc)",'~'.expand("<slnum>")) - endif - - else - " all styles: user's cursorline, user's cursorcolumn -" call Decho("default: (use user's cul,cuc)",'~'.expand("<slnum>")) - let &l:cursorline = s:netrw_usercul - let &l:cursorcolumn = s:netrw_usercuc - endif - -" call Decho("(s:NetrwCursor) COMBAK: cuc=".&l:cuc." cul=".&l:cul) -" call Dret("s:NetrwCursor : l:cursorline=".&l:cursorline." l:cursorcolumn=".&l:cursorcolumn) -endfun - -" --------------------------------------------------------------------- -" s:RestoreCursorline: restores cursorline/cursorcolumn to original user settings {{{2 -fun! s:RestoreCursorline() -" call Dfunc("s:RestoreCursorline() currently, cul=".&l:cursorline." cuc=".&l:cursorcolumn." win#".winnr()." buf#".bufnr("%")) - if exists("s:netrw_usercul") - let &l:cursorline = s:netrw_usercul - endif - if exists("s:netrw_usercuc") - let &l:cursorcolumn = s:netrw_usercuc - endif -" call Decho("(s:RestoreCursorline) COMBAK: cuc=".&l:cuc." cul=".&l:cul) -" call Dret("s:RestoreCursorline : restored cul=".&l:cursorline." cuc=".&l:cursorcolumn) -endfun - -" s:RestoreRegister: restores all registers given in the dict {{{2 -fun! s:RestoreRegister(dict) - for [key, val] in items(a:dict) - if key == 'unnamed' - let key = '' - endif - call setreg(key, val[0], val[1]) - endfor -endfun - -" --------------------------------------------------------------------- -" s:NetrwDelete: Deletes a file. {{{2 -" Uses Steve Hall's idea to insure that Windows paths stay -" acceptable. No effect on Unix paths. -" Examples of use: let result= s:NetrwDelete(path) -fun! s:NetrwDelete(path) -" call Dfunc("s:NetrwDelete(path<".a:path.">)") - - let path = netrw#WinPath(a:path) - if !g:netrw_cygwin && has("win32") - if exists("+shellslash") - let sskeep= &shellslash - setl noshellslash - let result = delete(path) - let &shellslash = sskeep - else -" call Decho("exe let result= ".a:cmd."('".path."')",'~'.expand("<slnum>")) - let result= delete(path) - endif - else -" call Decho("let result= delete(".path.")",'~'.expand("<slnum>")) - let result= delete(path) - endif - if result < 0 - NetrwKeepj call netrw#ErrorMsg(s:WARNING,"delete(".path.") failed!",71) - endif - -" call Dret("s:NetrwDelete ".result) - return result -endfun - -" --------------------------------------------------------------------- -" s:NetrwBufRemover: removes a buffer that: {{{2s -" has buffer-id > 1 -" is unlisted -" is unnamed -" does not appear in any window -fun! s:NetrwBufRemover(bufid) -" call Dfunc("s:NetrwBufRemover(".a:bufid.")") -" call Decho("buf#".a:bufid." ".((a:bufid > 1)? ">" : "≯")." must be >1 for removal","~".expand("<slnum>")) -" call Decho("buf#".a:bufid." is ".(buflisted(a:bufid)? "listed" : "unlisted"),"~".expand("<slnum>")) -" call Decho("buf#".a:bufid." has name <".bufname(a:bufid).">","~".expand("<slnum>")) -" call Decho("buf#".a:bufid." has winid#".bufwinid(a:bufid),"~".expand("<slnum>")) - - if a:bufid > 1 && !buflisted(a:bufid) && bufloaded(a:bufid) && bufname(a:bufid) == "" && bufwinid(a:bufid) == -1 -" call Decho("(s:NetrwBufRemover) removing buffer#".a:bufid,"~".expand("<slnum>")) - exe "sil! bd! ".a:bufid - endif - -" call Dret("s:NetrwBufRemover") -endfun - -" --------------------------------------------------------------------- -" s:NetrwEnew: opens a new buffer, passes netrw buffer variables through {{{2 -fun! s:NetrwEnew(...) -" call Dfunc("s:NetrwEnew() a:0=".a:0." win#".winnr()." winnr($)=".winnr("$")." bufnr($)=".bufnr("$")." expand(%)<".expand("%").">") -" call Decho("curdir<".((a:0>0)? a:1 : "")."> buf#".bufnr("%")."<".bufname("%").">",'~'.expand("<slnum>")) - - " Clean out the last buffer: - " Check if the last buffer has # > 1, is unlisted, is unnamed, and does not appear in a window - " If so, delete it. - call s:NetrwBufRemover(bufnr("$")) - - " grab a function-local-variable copy of buffer variables -" call Decho("make function-local copy of netrw variables",'~'.expand("<slnum>")) - if exists("b:netrw_bannercnt") |let netrw_bannercnt = b:netrw_bannercnt |endif - if exists("b:netrw_browser_active") |let netrw_browser_active = b:netrw_browser_active |endif - if exists("b:netrw_cpf") |let netrw_cpf = b:netrw_cpf |endif - if exists("b:netrw_curdir") |let netrw_curdir = b:netrw_curdir |endif - if exists("b:netrw_explore_bufnr") |let netrw_explore_bufnr = b:netrw_explore_bufnr |endif - if exists("b:netrw_explore_indx") |let netrw_explore_indx = b:netrw_explore_indx |endif - if exists("b:netrw_explore_line") |let netrw_explore_line = b:netrw_explore_line |endif - if exists("b:netrw_explore_list") |let netrw_explore_list = b:netrw_explore_list |endif - if exists("b:netrw_explore_listlen")|let netrw_explore_listlen = b:netrw_explore_listlen|endif - if exists("b:netrw_explore_mtchcnt")|let netrw_explore_mtchcnt = b:netrw_explore_mtchcnt|endif - if exists("b:netrw_fname") |let netrw_fname = b:netrw_fname |endif - if exists("b:netrw_lastfile") |let netrw_lastfile = b:netrw_lastfile |endif - if exists("b:netrw_liststyle") |let netrw_liststyle = b:netrw_liststyle |endif - if exists("b:netrw_method") |let netrw_method = b:netrw_method |endif - if exists("b:netrw_option") |let netrw_option = b:netrw_option |endif - if exists("b:netrw_prvdir") |let netrw_prvdir = b:netrw_prvdir |endif - - NetrwKeepj call s:NetrwOptionsRestore("w:") -" call Decho("generate a buffer with NetrwKeepj enew!",'~'.expand("<slnum>")) - " when tree listing uses file TreeListing... a new buffer is made. - " Want the old buffer to be unlisted. - " COMBAK: this causes a problem, see P43 -" setl nobl - let netrw_keepdiff= &l:diff - call s:NetrwEditFile("enew!","","") - let &l:diff= netrw_keepdiff -" call Decho("bufnr($)=".bufnr("$")."<".bufname(bufnr("$"))."> winnr($)=".winnr("$"),'~'.expand("<slnum>")) - NetrwKeepj call s:NetrwOptionsSave("w:") - - " copy function-local-variables to buffer variable equivalents -" call Decho("copy function-local variables back to buffer netrw variables",'~'.expand("<slnum>")) - if exists("netrw_bannercnt") |let b:netrw_bannercnt = netrw_bannercnt |endif - if exists("netrw_browser_active") |let b:netrw_browser_active = netrw_browser_active |endif - if exists("netrw_cpf") |let b:netrw_cpf = netrw_cpf |endif - if exists("netrw_curdir") |let b:netrw_curdir = netrw_curdir |endif - if exists("netrw_explore_bufnr") |let b:netrw_explore_bufnr = netrw_explore_bufnr |endif - if exists("netrw_explore_indx") |let b:netrw_explore_indx = netrw_explore_indx |endif - if exists("netrw_explore_line") |let b:netrw_explore_line = netrw_explore_line |endif - if exists("netrw_explore_list") |let b:netrw_explore_list = netrw_explore_list |endif - if exists("netrw_explore_listlen")|let b:netrw_explore_listlen = netrw_explore_listlen|endif - if exists("netrw_explore_mtchcnt")|let b:netrw_explore_mtchcnt = netrw_explore_mtchcnt|endif - if exists("netrw_fname") |let b:netrw_fname = netrw_fname |endif - if exists("netrw_lastfile") |let b:netrw_lastfile = netrw_lastfile |endif - if exists("netrw_liststyle") |let b:netrw_liststyle = netrw_liststyle |endif - if exists("netrw_method") |let b:netrw_method = netrw_method |endif - if exists("netrw_option") |let b:netrw_option = netrw_option |endif - if exists("netrw_prvdir") |let b:netrw_prvdir = netrw_prvdir |endif - - if a:0 > 0 - let b:netrw_curdir= a:1 - if b:netrw_curdir =~ '/$' - if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST - setl nobl - file NetrwTreeListing - setl nobl bt=nowrite bh=hide - nno <silent> <buffer> [ :sil call <SID>TreeListMove('[')<cr> - nno <silent> <buffer> ] :sil call <SID>TreeListMove(']')<cr> - else - call s:NetrwBufRename(b:netrw_curdir) - endif - endif - endif - if v:version >= 700 && has("balloon_eval") && !exists("s:initbeval") && !exists("g:netrw_nobeval") && has("syntax") && exists("g:syntax_on") - let &l:bexpr = "netrw#BalloonHelp()" - endif - -" call Dret("s:NetrwEnew : buf#".bufnr("%")."<".bufname("%")."> expand(%)<".expand("%")."> expand(#)<".expand("#")."> bh=".&bh." win#".winnr()." winnr($)#".winnr("$")) -endfun - -" --------------------------------------------------------------------- -" s:NetrwExe: executes a string using "!" {{{2 -fun! s:NetrwExe(cmd) -" call Dfunc("s:NetrwExe(a:cmd<".a:cmd.">)") - if has("win32") -" call Decho("using win32:",expand("<slnum>")) - let savedShell=[&shell,&shellcmdflag,&shellxquote,&shellxescape,&shellquote,&shellpipe,&shellredir,&shellslash] - set shell& shellcmdflag& shellxquote& shellxescape& - set shellquote& shellpipe& shellredir& shellslash& - try - exe a:cmd - finally - let [&shell,&shellcmdflag,&shellxquote,&shellxescape,&shellquote,&shellpipe,&shellredir,&shellslash] = savedShell - endtry - else -" call Decho("exe ".a:cmd,'~'.expand("<slnum>")) - exe a:cmd - endif - if v:shell_error - call netrw#ErrorMsg(s:WARNING,"shell signalled an error",106) - endif -" call Dret("s:NetrwExe : v:shell_error=".v:shell_error) -endfun - -" --------------------------------------------------------------------- -" s:NetrwInsureWinVars: insure that a netrw buffer has its w: variables in spite of a wincmd v or s {{{2 -fun! s:NetrwInsureWinVars() - if !exists("w:netrw_liststyle") -" call Dfunc("s:NetrwInsureWinVars() win#".winnr()) - let curbuf = bufnr("%") - let curwin = winnr() - let iwin = 1 - while iwin <= winnr("$") - exe iwin."wincmd w" - if winnr() != curwin && bufnr("%") == curbuf && exists("w:netrw_liststyle") - " looks like ctrl-w_s or ctrl-w_v was used to split a netrw buffer - let winvars= w: - break - endif - let iwin= iwin + 1 - endwhile - exe "keepalt ".curwin."wincmd w" - if exists("winvars") -" call Decho("copying w#".iwin." window variables to w#".curwin,'~'.expand("<slnum>")) - for k in keys(winvars) - let w:{k}= winvars[k] - endfor - endif -" call Dret("s:NetrwInsureWinVars win#".winnr()) - endif -endfun - -" --------------------------------------------------------------------- -" s:NetrwLcd: handles changing the (local) directory {{{2 -" Returns: 0=success -" -1=failed -fun! s:NetrwLcd(newdir) -" call Dfunc("s:NetrwLcd(newdir<".a:newdir.">)") -" call Decho("changing local directory",'~'.expand("<slnum>")) - - let err472= 0 - try - exe 'NetrwKeepj sil lcd '.fnameescape(a:newdir) - catch /^Vim\%((\a\+)\)\=:E344/ - " Vim's lcd fails with E344 when attempting to go above the 'root' of a Windows share. - " Therefore, detect if a Windows share is present, and if E344 occurs, just settle at - " 'root' (ie. '\'). The share name may start with either backslashes ('\\Foo') or - " forward slashes ('//Foo'), depending on whether backslashes have been converted to - " forward slashes by earlier code; so check for both. - if has("win32") && !g:netrw_cygwin - if a:newdir =~ '^\\\\\w\+' || a:newdir =~ '^//\w\+' - let dirname = '\' - exe 'NetrwKeepj sil lcd '.fnameescape(dirname) - endif - endif - catch /^Vim\%((\a\+)\)\=:E472/ - let err472= 1 - endtry - - if err472 - call netrw#ErrorMsg(s:ERROR,"unable to change directory to <".a:newdir."> (permissions?)",61) - if exists("w:netrw_prvdir") - let a:newdir= w:netrw_prvdir - else - call s:NetrwOptionsRestore("w:") -" call Decho("setl noma nomod nowrap",'~'.expand("<slnum>")) - exe "setl ".g:netrw_bufsettings -" call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) - let a:newdir= dirname - endif -" call Dret("s:NetrwBrowse -1 : reusing buffer#".(exists("bufnum")? bufnum : 'N/A')."<".dirname."> getcwd<".getcwd().">") - return -1 - endif - -" call Decho("getcwd <".getcwd().">") -" call Decho("b:netrw_curdir<".b:netrw_curdir.">") -" call Dret("s:NetrwLcd 0") - return 0 -endfun - -" ------------------------------------------------------------------------ -" s:NetrwSaveWordPosn: used to keep cursor on same word after refresh, {{{2 -" changed sorting, etc. Also see s:NetrwRestoreWordPosn(). -fun! s:NetrwSaveWordPosn() -" call Dfunc("NetrwSaveWordPosn()") - let s:netrw_saveword= '^'.fnameescape(getline('.')).'$' -" call Dret("NetrwSaveWordPosn : saveword<".s:netrw_saveword.">") -endfun - -" --------------------------------------------------------------------- -" s:NetrwHumanReadable: takes a number and makes it "human readable" {{{2 -" 1000 -> 1K, 1000000 -> 1M, 1000000000 -> 1G -fun! s:NetrwHumanReadable(sz) -" call Dfunc("s:NetrwHumanReadable(sz=".a:sz.") type=".type(a:sz)." style=".g:netrw_sizestyle ) - - if g:netrw_sizestyle == 'h' - if a:sz >= 1000000000 - let sz = printf("%.1f",a:sz/1000000000.0)."g" - elseif a:sz >= 10000000 - let sz = printf("%d",a:sz/1000000)."m" - elseif a:sz >= 1000000 - let sz = printf("%.1f",a:sz/1000000.0)."m" - elseif a:sz >= 10000 - let sz = printf("%d",a:sz/1000)."k" - elseif a:sz >= 1000 - let sz = printf("%.1f",a:sz/1000.0)."k" - else - let sz= a:sz - endif - - elseif g:netrw_sizestyle == 'H' - if a:sz >= 1073741824 - let sz = printf("%.1f",a:sz/1073741824.0)."G" - elseif a:sz >= 10485760 - let sz = printf("%d",a:sz/1048576)."M" - elseif a:sz >= 1048576 - let sz = printf("%.1f",a:sz/1048576.0)."M" - elseif a:sz >= 10240 - let sz = printf("%d",a:sz/1024)."K" - elseif a:sz >= 1024 - let sz = printf("%.1f",a:sz/1024.0)."K" - else - let sz= a:sz - endif - - else - let sz= a:sz - endif - -" call Dret("s:NetrwHumanReadable ".sz) - return sz -endfun - -" --------------------------------------------------------------------- -" s:NetrwRestoreWordPosn: used to keep cursor on same word after refresh, {{{2 -" changed sorting, etc. Also see s:NetrwSaveWordPosn(). -fun! s:NetrwRestoreWordPosn() -" call Dfunc("NetrwRestoreWordPosn()") - sil! call search(s:netrw_saveword,'w') -" call Dret("NetrwRestoreWordPosn") -endfun - -" --------------------------------------------------------------------- -" s:RestoreBufVars: {{{2 -fun! s:RestoreBufVars() -" call Dfunc("s:RestoreBufVars()") - - if exists("s:netrw_curdir") |let b:netrw_curdir = s:netrw_curdir |endif - if exists("s:netrw_lastfile") |let b:netrw_lastfile = s:netrw_lastfile |endif - if exists("s:netrw_method") |let b:netrw_method = s:netrw_method |endif - if exists("s:netrw_fname") |let b:netrw_fname = s:netrw_fname |endif - if exists("s:netrw_machine") |let b:netrw_machine = s:netrw_machine |endif - if exists("s:netrw_browser_active")|let b:netrw_browser_active = s:netrw_browser_active|endif - -" call Dret("s:RestoreBufVars") -endfun - -" --------------------------------------------------------------------- -" s:RemotePathAnalysis: {{{2 -fun! s:RemotePathAnalysis(dirname) -" call Dfunc("s:RemotePathAnalysis(a:dirname<".a:dirname.">)") - - " method :// user @ machine :port /path - let dirpat = '^\(\w\{-}\)://\(\(\w\+\)@\)\=\([^/:#]\+\)\%([:#]\(\d\+\)\)\=/\(.*\)$' - let s:method = substitute(a:dirname,dirpat,'\1','') - let s:user = substitute(a:dirname,dirpat,'\3','') - let s:machine = substitute(a:dirname,dirpat,'\4','') - let s:port = substitute(a:dirname,dirpat,'\5','') - let s:path = substitute(a:dirname,dirpat,'\6','') - let s:fname = substitute(s:path,'^.*/\ze.','','') - if s:machine =~ '@' - let dirpat = '^\(.*\)@\(.\{-}\)$' - let s:user = s:user.'@'.substitute(s:machine,dirpat,'\1','') - let s:machine = substitute(s:machine,dirpat,'\2','') - endif - -" call Decho("set up s:method <".s:method .">",'~'.expand("<slnum>")) -" call Decho("set up s:user <".s:user .">",'~'.expand("<slnum>")) -" call Decho("set up s:machine<".s:machine.">",'~'.expand("<slnum>")) -" call Decho("set up s:port <".s:port.">",'~'.expand("<slnum>")) -" call Decho("set up s:path <".s:path .">",'~'.expand("<slnum>")) -" call Decho("set up s:fname <".s:fname .">",'~'.expand("<slnum>")) - -" call Dret("s:RemotePathAnalysis") -endfun - -" --------------------------------------------------------------------- -" s:RemoteSystem: runs a command on a remote host using ssh {{{2 -" Returns status -" Runs system() on -" [cd REMOTEDIRPATH;] a:cmd -" Note that it doesn't do s:ShellEscape(a:cmd)! -fun! s:RemoteSystem(cmd) -" call Dfunc("s:RemoteSystem(cmd<".a:cmd.">)") - if !executable(g:netrw_ssh_cmd) - NetrwKeepj call netrw#ErrorMsg(s:ERROR,"g:netrw_ssh_cmd<".g:netrw_ssh_cmd."> is not executable!",52) - elseif !exists("b:netrw_curdir") - NetrwKeepj call netrw#ErrorMsg(s:ERROR,"for some reason b:netrw_curdir doesn't exist!",53) - else - let cmd = s:MakeSshCmd(g:netrw_ssh_cmd." USEPORT HOSTNAME") - let remotedir= substitute(b:netrw_curdir,'^.*//[^/]\+/\(.*\)$','\1','') - if remotedir != "" - let cmd= cmd.' cd '.s:ShellEscape(remotedir).";" - else - let cmd= cmd.' ' - endif - let cmd= cmd.a:cmd -" call Decho("call system(".cmd.")",'~'.expand("<slnum>")) - let ret= system(cmd) - endif -" call Dret("s:RemoteSystem ".ret) - return ret -endfun - -" --------------------------------------------------------------------- -" s:RestoreWinVars: (used by Explore() and NetrwSplit()) {{{2 -fun! s:RestoreWinVars() -" call Dfunc("s:RestoreWinVars()") - if exists("s:bannercnt") |let w:netrw_bannercnt = s:bannercnt |unlet s:bannercnt |endif - if exists("s:col") |let w:netrw_col = s:col |unlet s:col |endif - if exists("s:curdir") |let w:netrw_curdir = s:curdir |unlet s:curdir |endif - if exists("s:explore_bufnr") |let w:netrw_explore_bufnr = s:explore_bufnr |unlet s:explore_bufnr |endif - if exists("s:explore_indx") |let w:netrw_explore_indx = s:explore_indx |unlet s:explore_indx |endif - if exists("s:explore_line") |let w:netrw_explore_line = s:explore_line |unlet s:explore_line |endif - if exists("s:explore_listlen")|let w:netrw_explore_listlen = s:explore_listlen|unlet s:explore_listlen|endif - if exists("s:explore_list") |let w:netrw_explore_list = s:explore_list |unlet s:explore_list |endif - if exists("s:explore_mtchcnt")|let w:netrw_explore_mtchcnt = s:explore_mtchcnt|unlet s:explore_mtchcnt|endif - if exists("s:fpl") |let w:netrw_fpl = s:fpl |unlet s:fpl |endif - if exists("s:hline") |let w:netrw_hline = s:hline |unlet s:hline |endif - if exists("s:line") |let w:netrw_line = s:line |unlet s:line |endif - if exists("s:liststyle") |let w:netrw_liststyle = s:liststyle |unlet s:liststyle |endif - if exists("s:method") |let w:netrw_method = s:method |unlet s:method |endif - if exists("s:prvdir") |let w:netrw_prvdir = s:prvdir |unlet s:prvdir |endif - if exists("s:treedict") |let w:netrw_treedict = s:treedict |unlet s:treedict |endif - if exists("s:treetop") |let w:netrw_treetop = s:treetop |unlet s:treetop |endif - if exists("s:winnr") |let w:netrw_winnr = s:winnr |unlet s:winnr |endif -" call Dret("s:RestoreWinVars") -endfun - -" --------------------------------------------------------------------- -" s:Rexplore: implements returning from a buffer to a netrw directory {{{2 -" -" s:SetRexDir() sets up <2-leftmouse> maps (if g:netrw_retmap -" is true) and a command, :Rexplore, which call this function. -" -" s:netrw_posn is set up by s:NetrwBrowseChgDir() -" -" s:rexposn_BUFNR used to save/restore cursor position -fun! s:NetrwRexplore(islocal,dirname) - if exists("s:netrwdrag") - return - endif -" call Dfunc("s:NetrwRexplore() w:netrw_rexlocal=".w:netrw_rexlocal." w:netrw_rexdir<".w:netrw_rexdir."> win#".winnr()) -" call Decho("currently in bufname<".bufname("%").">",'~'.expand("<slnum>")) -" call Decho("ft=".&ft." win#".winnr()." w:netrw_rexfile<".(exists("w:netrw_rexfile")? w:netrw_rexfile : 'n/a').">",'~'.expand("<slnum>")) - - if &ft == "netrw" && exists("w:netrw_rexfile") && w:netrw_rexfile != "" - " a :Rex while in a netrw buffer means: edit the file in w:netrw_rexfile -" call Decho("in netrw buffer, will edit file<".w:netrw_rexfile.">",'~'.expand("<slnum>")) - exe "NetrwKeepj e ".w:netrw_rexfile - unlet w:netrw_rexfile -" call Dret("s:NetrwRexplore returning from netrw to buf#".bufnr("%")."<".bufname("%")."> (ft=".&ft.")") - return -" else " Decho -" call Decho("treating as not-netrw-buffer: ft=".&ft.((&ft == "netrw")? " == netrw" : "!= netrw"),'~'.expand("<slnum>")) -" call Decho("treating as not-netrw-buffer: w:netrw_rexfile<".((exists("w:netrw_rexfile"))? w:netrw_rexfile : 'n/a').">",'~'.expand("<slnum>")) - endif - - " --------------------------- - " :Rex issued while in a file - " --------------------------- - - " record current file so :Rex can return to it from netrw - let w:netrw_rexfile= expand("%") -" call Decho("set w:netrw_rexfile<".w:netrw_rexfile."> (win#".winnr().")",'~'.expand("<slnum>")) - - if !exists("w:netrw_rexlocal") -" call Dret("s:NetrwRexplore w:netrw_rexlocal doesn't exist (".&ft." win#".winnr().")") - return - endif -" 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>")) - if w:netrw_rexlocal - NetrwKeepj call netrw#LocalBrowseCheck(w:netrw_rexdir) - else - NetrwKeepj call s:NetrwBrowse(0,w:netrw_rexdir) - endif - if exists("s:initbeval") - setl beval - endif - if exists("s:rexposn_".bufnr("%")) -" call Decho("restore posn, then unlet s:rexposn_".bufnr('%')."<".bufname("%").">",'~'.expand("<slnum>")) - " restore position in directory listing -" call Decho("restoring posn to s:rexposn_".bufnr('%')."<".string(s:rexposn_{bufnr('%')}).">",'~'.expand("<slnum>")) - NetrwKeepj call winrestview(s:rexposn_{bufnr('%')}) - if exists("s:rexposn_".bufnr('%')) - unlet s:rexposn_{bufnr('%')} - endif - else -" call Decho("s:rexposn_".bufnr('%')."<".bufname("%")."> doesn't exist",'~'.expand("<slnum>")) - endif - - if has("syntax") && exists("g:syntax_on") && g:syntax_on - if exists("s:explore_match") - exe "2match netrwMarkFile /".s:explore_match."/" - endif - endif - -" 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 Dret("s:NetrwRexplore : ft=".&ft) -endfun - -" --------------------------------------------------------------------- -" s:SaveBufVars: save selected b: variables to s: variables {{{2 -" use s:RestoreBufVars() to restore b: variables from s: variables -fun! s:SaveBufVars() -" call Dfunc("s:SaveBufVars() buf#".bufnr("%")) - - if exists("b:netrw_curdir") |let s:netrw_curdir = b:netrw_curdir |endif - if exists("b:netrw_lastfile") |let s:netrw_lastfile = b:netrw_lastfile |endif - if exists("b:netrw_method") |let s:netrw_method = b:netrw_method |endif - if exists("b:netrw_fname") |let s:netrw_fname = b:netrw_fname |endif - if exists("b:netrw_machine") |let s:netrw_machine = b:netrw_machine |endif - if exists("b:netrw_browser_active")|let s:netrw_browser_active = b:netrw_browser_active|endif - -" call Dret("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("%").">") - - if !exists("a:posndict[bufnr('%')]") - let a:posndict[bufnr("%")]= [] - endif -" call Decho("before push: a:posndict[buf#".bufnr("%")."]=".string(a:posndict[bufnr('%')])) - call add(a:posndict[bufnr("%")],winsaveview()) -" call Decho("after push: a:posndict[buf#".bufnr("%")."]=".string(a:posndict[bufnr('%')])) - -" 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 exists("a:posndict") - if has_key(a:posndict,bufnr("%")) -" call Decho("before pop: a:posndict[buf#".bufnr("%")."]=".string(a:posndict[bufnr('%')])) - let posnlen= len(a:posndict[bufnr("%")]) - if posnlen > 0 - let posnlen= posnlen - 1 -" call Decho("restoring posn posndict[".bufnr("%")."][".posnlen."]=".string(a:posndict[bufnr("%")][posnlen]),'~'.expand("<slnum>")) - call winrestview(a:posndict[bufnr("%")][posnlen]) - call remove(a:posndict[bufnr("%")],posnlen) -" call Decho("after pop: a:posndict[buf#".bufnr("%")."]=".string(a:posndict[bufnr('%')])) - endif - endif - endif -" call Dret("s:RestorePosn") -endfun - -" --------------------------------------------------------------------- -" s:SaveWinVars: (used by Explore() and NetrwSplit()) {{{2 -fun! s:SaveWinVars() -" call Dfunc("s:SaveWinVars() win#".winnr()) - if exists("w:netrw_bannercnt") |let s:bannercnt = w:netrw_bannercnt |endif - if exists("w:netrw_col") |let s:col = w:netrw_col |endif - if exists("w:netrw_curdir") |let s:curdir = w:netrw_curdir |endif - if exists("w:netrw_explore_bufnr") |let s:explore_bufnr = w:netrw_explore_bufnr |endif - if exists("w:netrw_explore_indx") |let s:explore_indx = w:netrw_explore_indx |endif - if exists("w:netrw_explore_line") |let s:explore_line = w:netrw_explore_line |endif - if exists("w:netrw_explore_listlen")|let s:explore_listlen = w:netrw_explore_listlen|endif - if exists("w:netrw_explore_list") |let s:explore_list = w:netrw_explore_list |endif - if exists("w:netrw_explore_mtchcnt")|let s:explore_mtchcnt = w:netrw_explore_mtchcnt|endif - if exists("w:netrw_fpl") |let s:fpl = w:netrw_fpl |endif - if exists("w:netrw_hline") |let s:hline = w:netrw_hline |endif - if exists("w:netrw_line") |let s:line = w:netrw_line |endif - if exists("w:netrw_liststyle") |let s:liststyle = w:netrw_liststyle |endif - if exists("w:netrw_method") |let s:method = w:netrw_method |endif - if exists("w:netrw_prvdir") |let s:prvdir = w:netrw_prvdir |endif - if exists("w:netrw_treedict") |let s:treedict = w:netrw_treedict |endif - if exists("w:netrw_treetop") |let s:treetop = w:netrw_treetop |endif - if exists("w:netrw_winnr") |let s:winnr = w:netrw_winnr |endif -" call Dret("s:SaveWinVars") -endfun - -" --------------------------------------------------------------------- -" s:SetBufWinVars: (used by NetrwBrowse() and LocalBrowseCheck()) {{{2 -" To allow separate windows to have their own activities, such as -" Explore **/pattern, several variables have been made window-oriented. -" However, when the user splits a browser window (ex: ctrl-w s), these -" variables are not inherited by the new window. SetBufWinVars() and -" UseBufWinVars() get around that. -fun! s:SetBufWinVars() -" call Dfunc("s:SetBufWinVars() win#".winnr()) - if exists("w:netrw_liststyle") |let b:netrw_liststyle = w:netrw_liststyle |endif - if exists("w:netrw_bannercnt") |let b:netrw_bannercnt = w:netrw_bannercnt |endif - if exists("w:netrw_method") |let b:netrw_method = w:netrw_method |endif - if exists("w:netrw_prvdir") |let b:netrw_prvdir = w:netrw_prvdir |endif - if exists("w:netrw_explore_indx") |let b:netrw_explore_indx = w:netrw_explore_indx |endif - if exists("w:netrw_explore_listlen")|let b:netrw_explore_listlen= w:netrw_explore_listlen|endif - if exists("w:netrw_explore_mtchcnt")|let b:netrw_explore_mtchcnt= w:netrw_explore_mtchcnt|endif - if exists("w:netrw_explore_bufnr") |let b:netrw_explore_bufnr = w:netrw_explore_bufnr |endif - if exists("w:netrw_explore_line") |let b:netrw_explore_line = w:netrw_explore_line |endif - if exists("w:netrw_explore_list") |let b:netrw_explore_list = w:netrw_explore_list |endif -" call Dret("s:SetBufWinVars") -endfun - -" --------------------------------------------------------------------- -" s:SetRexDir: set directory for :Rexplore {{{2 -fun! s:SetRexDir(islocal,dirname) -" call Dfunc("s:SetRexDir(islocal=".a:islocal." dirname<".a:dirname.">) win#".winnr()) - 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,'~'.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 ".string(winsaveview()),'~'.expand("<slnum>")) -" call Dret("s:SetRexDir : win#".winnr()." ".(a:islocal? "local" : "remote")." dir: ".a:dirname) -endfun - -" --------------------------------------------------------------------- -" s:ShowLink: used to modify thin and tree listings to show links {{{2 -fun! s:ShowLink() - if exists("b:netrw_curdir") - norm! $?\a - if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treetop") - let basedir = s:NetrwTreePath(w:netrw_treetop) - else - let basedir = b:netrw_curdir.'/' - endif - let fname = basedir.s:NetrwGetWord() - let resname = resolve(fname) - if resname =~ '^\M'.basedir - let dirlen = strlen(basedir) - let resname = strpart(resname,dirlen) - endif - let modline = getline(".")."\t --> ".resname - setl noro ma - call setline(".",modline) - setl ro noma nomod - endif -endfun - -" --------------------------------------------------------------------- -" s:ShowStyle: {{{2 -fun! s:ShowStyle() - if !exists("w:netrw_liststyle") - let liststyle= g:netrw_liststyle - else - let liststyle= w:netrw_liststyle - endif - if liststyle == s:THINLIST - return s:THINLIST.":thin" - elseif liststyle == s:LONGLIST - return s:LONGLIST.":long" - elseif liststyle == s:WIDELIST - return s:WIDELIST.":wide" - elseif liststyle == s:TREELIST - return s:TREELIST.":tree" - else - return 'n/a' - endif -endfun - -" --------------------------------------------------------------------- -" s:Strlen: this function returns the length of a string, even if its using multi-byte characters. {{{2 -" Solution from Nicolai Weibull, vim docs (:help strlen()), -" Tony Mechelynck, and my own invention. -fun! s:Strlen(x) -" "" call Dfunc("s:Strlen(x<".a:x."> g:Align_xstrlen=".g:Align_xstrlen.")") - - if v:version >= 703 && exists("*strdisplaywidth") - let ret= strdisplaywidth(a:x) - - elseif type(g:Align_xstrlen) == 1 - " allow user to specify a function to compute the string length (ie. let g:Align_xstrlen="mystrlenfunc") - exe "let ret= ".g:Align_xstrlen."('".substitute(a:x,"'","''","g")."')" - - elseif g:Align_xstrlen == 1 - " number of codepoints (Latin a + combining circumflex is two codepoints) - " (comment from TM, solution from NW) - let ret= strlen(substitute(a:x,'.','c','g')) - - elseif g:Align_xstrlen == 2 - " number of spacing codepoints (Latin a + combining circumflex is one spacing - " codepoint; a hard tab is one; wide and narrow CJK are one each; etc.) - " (comment from TM, solution from TM) - let ret=strlen(substitute(a:x, '.\Z', 'x', 'g')) - - elseif g:Align_xstrlen == 3 - " virtual length (counting, for instance, tabs as anything between 1 and - " 'tabstop', wide CJK as 2 rather than 1, Arabic alif as zero when immediately - " preceded by lam, one otherwise, etc.) - " (comment from TM, solution from me) - let modkeep= &l:mod - exe "norm! o\<esc>" - call setline(line("."),a:x) - let ret= virtcol("$") - 1 - d - NetrwKeepj norm! k - let &l:mod= modkeep - - else - " at least give a decent default - let ret= strlen(a:x) - endif -" "" call Dret("s:Strlen ".ret) - return ret -endfun - -" --------------------------------------------------------------------- -" s:ShellEscape: shellescape(), or special windows handling {{{2 -fun! s:ShellEscape(s, ...) - if has('win32') && $SHELL == '' && &shellslash - return printf('"%s"', substitute(a:s, '"', '""', 'g')) - endif - let f = a:0 > 0 ? a:1 : 0 - return shellescape(a:s, f) -endfun - -" --------------------------------------------------------------------- -" s:TreeListMove: supports [[, ]], [], and ][ in tree mode {{{2 -fun! s:TreeListMove(dir) -" call Dfunc("s:TreeListMove(dir<".a:dir.">)") - let curline = getline('.') - let prvline = (line(".") > 1)? getline(line(".")-1) : '' - let nxtline = (line(".") < line("$"))? getline(line(".")+1) : '' - let curindent = substitute(getline('.'),'^\(\%('.s:treedepthstring.'\)*\)[^'.s:treedepthstring.'].\{-}$','\1','e') - let indentm1 = substitute(curindent,'^'.s:treedepthstring,'','') - let treedepthchr = substitute(s:treedepthstring,' ','','g') - let stopline = exists("w:netrw_bannercnt")? w:netrw_bannercnt : 1 -" call Decho("prvline <".prvline."> #".(line(".")-1), '~'.expand("<slnum>")) -" call Decho("curline <".curline."> #".line(".") , '~'.expand("<slnum>")) -" call Decho("nxtline <".nxtline."> #".(line(".")+1), '~'.expand("<slnum>")) -" call Decho("curindent<".curindent.">" , '~'.expand("<slnum>")) -" call Decho("indentm1 <".indentm1.">" , '~'.expand("<slnum>")) - " COMBAK : need to handle when on a directory - " COMBAK : need to handle ]] and ][. In general, needs work!!! - if curline !~ '/$' - if a:dir == '[[' && prvline != '' - NetrwKeepj norm! 0 - let nl = search('^'.indentm1.'\%('.s:treedepthstring.'\)\@!','bWe',stopline) " search backwards -" call Decho("regfile srch back: ".nl,'~'.expand("<slnum>")) - elseif a:dir == '[]' && nxtline != '' - NetrwKeepj norm! 0 -" call Decho('srchpat<'.'^\%('.curindent.'\)\@!'.'>','~'.expand("<slnum>")) - let nl = search('^\%('.curindent.'\)\@!','We') " search forwards - if nl != 0 - NetrwKeepj norm! k - else - NetrwKeepj norm! G - endif -" call Decho("regfile srch fwd: ".nl,'~'.expand("<slnum>")) - endif - endif - -" call Dret("s:TreeListMove") -endfun - -" --------------------------------------------------------------------- -" s:UpdateBuffersMenu: does emenu Buffers.Refresh (but due to locale, the menu item may not be called that) {{{2 -" The Buffers.Refresh menu calls s:BMShow(); unfortunately, that means that that function -" can't be called except via emenu. But due to locale, that menu line may not be called -" Buffers.Refresh; hence, s:NetrwBMShow() utilizes a "cheat" to call that function anyway. -fun! s:UpdateBuffersMenu() -" call Dfunc("s:UpdateBuffersMenu()") - if has("gui") && has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu - try - sil emenu Buffers.Refresh\ menu - catch /^Vim\%((\a\+)\)\=:E/ - let v:errmsg= "" - sil NetrwKeepj call s:NetrwBMShow() - endtry - endif -" call Dret("s:UpdateBuffersMenu") -endfun - -" --------------------------------------------------------------------- -" s:UseBufWinVars: (used by NetrwBrowse() and LocalBrowseCheck() {{{2 -" Matching function to s:SetBufWinVars() -fun! s:UseBufWinVars() -" call Dfunc("s:UseBufWinVars()") - if exists("b:netrw_liststyle") && !exists("w:netrw_liststyle") |let w:netrw_liststyle = b:netrw_liststyle |endif - if exists("b:netrw_bannercnt") && !exists("w:netrw_bannercnt") |let w:netrw_bannercnt = b:netrw_bannercnt |endif - if exists("b:netrw_method") && !exists("w:netrw_method") |let w:netrw_method = b:netrw_method |endif - if exists("b:netrw_prvdir") && !exists("w:netrw_prvdir") |let w:netrw_prvdir = b:netrw_prvdir |endif - if exists("b:netrw_explore_indx") && !exists("w:netrw_explore_indx") |let w:netrw_explore_indx = b:netrw_explore_indx |endif - if exists("b:netrw_explore_listlen") && !exists("w:netrw_explore_listlen")|let w:netrw_explore_listlen = b:netrw_explore_listlen|endif - if exists("b:netrw_explore_mtchcnt") && !exists("w:netrw_explore_mtchcnt")|let w:netrw_explore_mtchcnt = b:netrw_explore_mtchcnt|endif - if exists("b:netrw_explore_bufnr") && !exists("w:netrw_explore_bufnr") |let w:netrw_explore_bufnr = b:netrw_explore_bufnr |endif - if exists("b:netrw_explore_line") && !exists("w:netrw_explore_line") |let w:netrw_explore_line = b:netrw_explore_line |endif - if exists("b:netrw_explore_list") && !exists("w:netrw_explore_list") |let w:netrw_explore_list = b:netrw_explore_list |endif -" call Dret("s:UseBufWinVars") -endfun - -" --------------------------------------------------------------------- -" s:UserMaps: supports user-defined UserMaps {{{2 -" * calls a user-supplied funcref(islocal,curdir) -" * interprets result -" See netrw#UserMaps() -fun! s:UserMaps(islocal,funcname) - if !exists("b:netrw_curdir") - let b:netrw_curdir= getcwd() - endif - let Funcref = function(a:funcname) - let result = Funcref(a:islocal) - - if type(result) == 1 - " if result from user's funcref is a string... - if result == "refresh" - call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) - elseif result != "" - exe result - endif - - elseif type(result) == 3 - " if result from user's funcref is a List... - for action in result - if action == "refresh" - call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) - elseif action != "" - exe action - endif - endfor - endif -endfun - -" ========================== -" Settings Restoration: {{{1 -" ========================== -let &cpo= s:keepcpo -unlet s:keepcpo - -" =============== -" Modelines: {{{1 -" =============== -" vim:ts=8 fdm=marker diff --git a/runtime/autoload/netrwFileHandlers.vim b/runtime/autoload/netrwFileHandlers.vim deleted file mode 100644 index e69de29bb2..0000000000 --- a/runtime/autoload/netrwFileHandlers.vim +++ /dev/null diff --git a/runtime/autoload/netrwSettings.vim b/runtime/autoload/netrwSettings.vim deleted file mode 100644 index 3452602272..0000000000 --- a/runtime/autoload/netrwSettings.vim +++ /dev/null @@ -1,249 +0,0 @@ -" netrwSettings.vim: makes netrw settings simpler -" Date: Nov 15, 2021 -" Maintainer: This runtime file is looking for a new maintainer. -" Former Maintainer: Charles E Campbell -" Version: 18 -" Last Change: -" 2024 May 08 by Vim Project: cleanup legacy Win9X checks -" Copyright: Copyright (C) 1999-2007 Charles E. Campbell {{{1 -" Permission is hereby granted to use and distribute this code, -" with or without modifications, provided that this copyright -" notice is copied with it. Like anything else that's free, -" netrwSettings.vim is provided *as is* and comes with no -" warranty of any kind, either expressed or implied. By using -" this plugin, you agree that in no event will the copyright -" holder be liable for any damages resulting from the use -" of this software. -" -" Mat 4:23 (WEB) Jesus went about in all Galilee, teaching in their {{{1 -" synagogues, preaching the gospel of the kingdom, and healing -" every disease and every sickness among the people. -" Load Once: {{{1 -if exists("g:loaded_netrwSettings") || &cp - finish -endif -let g:loaded_netrwSettings = "v18" -if v:version < 700 - echohl WarningMsg - echo "***warning*** this version of netrwSettings needs vim 7.0" - echohl Normal - finish -endif - -" --------------------------------------------------------------------- -" NetrwSettings: {{{1 -fun! netrwSettings#NetrwSettings() - " this call is here largely just to insure that netrw has been loaded - call netrw#WinPath("") - if !exists("g:loaded_netrw") - echohl WarningMsg | echomsg "***sorry*** netrw needs to be loaded prior to using NetrwSettings" | echohl None - return - endif - - above wincmd s - enew - setlocal noswapfile bh=wipe - set ft=vim - file Netrw\ Settings - - " these variables have the following default effects when they don't - " exist (ie. have not been set by the user in his/her .vimrc) - if !exists("g:netrw_liststyle") - let g:netrw_liststyle= 0 - let g:netrw_list_cmd= "ssh HOSTNAME ls -FLa" - endif - if !exists("g:netrw_silent") - let g:netrw_silent= 0 - endif - if !exists("g:netrw_use_nt_rcp") - let g:netrw_use_nt_rcp= 0 - endif - if !exists("g:netrw_ftp") - let g:netrw_ftp= 0 - endif - if !exists("g:netrw_ignorenetrc") - let g:netrw_ignorenetrc= 0 - endif - - put ='+ ---------------------------------------------' - put ='+ NetrwSettings: by Charles E. Campbell' - put ='+ Press <F1> with cursor atop any line for help' - put ='+ ---------------------------------------------' - let s:netrw_settings_stop= line(".") - - put ='' - put ='+ Netrw Protocol Commands' - put = 'let g:netrw_dav_cmd = '.g:netrw_dav_cmd - put = 'let g:netrw_fetch_cmd = '.g:netrw_fetch_cmd - put = 'let g:netrw_ftp_cmd = '.g:netrw_ftp_cmd - put = 'let g:netrw_http_cmd = '.g:netrw_http_cmd - put = 'let g:netrw_rcp_cmd = '.g:netrw_rcp_cmd - put = 'let g:netrw_rsync_cmd = '.g:netrw_rsync_cmd - put = 'let g:netrw_scp_cmd = '.g:netrw_scp_cmd - put = 'let g:netrw_sftp_cmd = '.g:netrw_sftp_cmd - put = 'let g:netrw_ssh_cmd = '.g:netrw_ssh_cmd - let s:netrw_protocol_stop= line(".") - put = '' - - put ='+Netrw Transfer Control' - put = 'let g:netrw_cygwin = '.g:netrw_cygwin - put = 'let g:netrw_ftp = '.g:netrw_ftp - put = 'let g:netrw_ftpmode = '.g:netrw_ftpmode - put = 'let g:netrw_ignorenetrc = '.g:netrw_ignorenetrc - put = 'let g:netrw_sshport = '.g:netrw_sshport - put = 'let g:netrw_silent = '.g:netrw_silent - put = 'let g:netrw_use_nt_rcp = '.g:netrw_use_nt_rcp - let s:netrw_xfer_stop= line(".") - put ='' - put ='+ Netrw Messages' - put ='let g:netrw_use_errorwindow = '.g:netrw_use_errorwindow - - put = '' - put ='+ Netrw Browser Control' - if exists("g:netrw_altfile") - put = 'let g:netrw_altfile = '.g:netrw_altfile - else - put = 'let g:netrw_altfile = 0' - endif - put = 'let g:netrw_alto = '.g:netrw_alto - put = 'let g:netrw_altv = '.g:netrw_altv - put = 'let g:netrw_banner = '.g:netrw_banner - if exists("g:netrw_bannerbackslash") - put = 'let g:netrw_bannerbackslash = '.g:netrw_bannerbackslash - else - put = '\" let g:netrw_bannerbackslash = (not defined)' - endif - put = 'let g:netrw_browse_split = '.g:netrw_browse_split - if exists("g:netrw_browsex_viewer") - put = 'let g:netrw_browsex_viewer = '.g:netrw_browsex_viewer - else - put = '\" let g:netrw_browsex_viewer = (not defined)' - endif - put = 'let g:netrw_compress = '.g:netrw_compress - if exists("g:Netrw_corehandler") - put = 'let g:Netrw_corehandler = '.g:Netrw_corehandler - else - put = '\" let g:Netrw_corehandler = (not defined)' - endif - put = 'let g:netrw_ctags = '.g:netrw_ctags - put = 'let g:netrw_cursor = '.g:netrw_cursor - let decompressline= line("$") - put = 'let g:netrw_decompress = '.string(g:netrw_decompress) - if exists("g:netrw_dynamic_maxfilenamelen") - put = 'let g:netrw_dynamic_maxfilenamelen='.g:netrw_dynamic_maxfilenamelen - else - put = '\" let g:netrw_dynamic_maxfilenamelen= (not defined)' - endif - put = 'let g:netrw_dirhistmax = '.g:netrw_dirhistmax - put = 'let g:netrw_errorlvl = '.g:netrw_errorlvl - put = 'let g:netrw_fastbrowse = '.g:netrw_fastbrowse - let fnameescline= line("$") - put = 'let g:netrw_fname_escape = '.string(g:netrw_fname_escape) - put = 'let g:netrw_ftp_browse_reject = '.g:netrw_ftp_browse_reject - put = 'let g:netrw_ftp_list_cmd = '.g:netrw_ftp_list_cmd - put = 'let g:netrw_ftp_sizelist_cmd = '.g:netrw_ftp_sizelist_cmd - put = 'let g:netrw_ftp_timelist_cmd = '.g:netrw_ftp_timelist_cmd - let globescline= line("$") - put = 'let g:netrw_glob_escape = '.string(g:netrw_glob_escape) - put = 'let g:netrw_hide = '.g:netrw_hide - if exists("g:netrw_home") - put = 'let g:netrw_home = '.g:netrw_home - else - put = '\" let g:netrw_home = (not defined)' - endif - put = 'let g:netrw_keepdir = '.g:netrw_keepdir - put = 'let g:netrw_list_cmd = '.g:netrw_list_cmd - put = 'let g:netrw_list_hide = '.g:netrw_list_hide - put = 'let g:netrw_liststyle = '.g:netrw_liststyle - put = 'let g:netrw_localcopycmd = '.g:netrw_localcopycmd - put = 'let g:netrw_localcopycmdopt = '.g:netrw_localcopycmdopt - put = 'let g:netrw_localmkdir = '.g:netrw_localmkdir - put = 'let g:netrw_localmkdiropt = '.g:netrw_localmkdiropt - put = 'let g:netrw_localmovecmd = '.g:netrw_localmovecmd - put = 'let g:netrw_localmovecmdopt = '.g:netrw_localmovecmdopt - put = 'let g:netrw_maxfilenamelen = '.g:netrw_maxfilenamelen - put = 'let g:netrw_menu = '.g:netrw_menu - put = 'let g:netrw_mousemaps = '.g:netrw_mousemaps - put = 'let g:netrw_mkdir_cmd = '.g:netrw_mkdir_cmd - if exists("g:netrw_nobeval") - put = 'let g:netrw_nobeval = '.g:netrw_nobeval - else - put = '\" let g:netrw_nobeval = (not defined)' - endif - put = 'let g:netrw_remote_mkdir = '.g:netrw_remote_mkdir - put = 'let g:netrw_preview = '.g:netrw_preview - put = 'let g:netrw_rename_cmd = '.g:netrw_rename_cmd - put = 'let g:netrw_retmap = '.g:netrw_retmap - put = 'let g:netrw_rm_cmd = '.g:netrw_rm_cmd - put = 'let g:netrw_rmdir_cmd = '.g:netrw_rmdir_cmd - put = 'let g:netrw_rmf_cmd = '.g:netrw_rmf_cmd - put = 'let g:netrw_sort_by = '.g:netrw_sort_by - put = 'let g:netrw_sort_direction = '.g:netrw_sort_direction - put = 'let g:netrw_sort_options = '.g:netrw_sort_options - put = 'let g:netrw_sort_sequence = '.g:netrw_sort_sequence - put = 'let g:netrw_servername = '.g:netrw_servername - put = 'let g:netrw_special_syntax = '.g:netrw_special_syntax - put = 'let g:netrw_ssh_browse_reject = '.g:netrw_ssh_browse_reject - put = 'let g:netrw_ssh_cmd = '.g:netrw_ssh_cmd - put = 'let g:netrw_scpport = '.g:netrw_scpport - put = 'let g:netrw_sepchr = '.g:netrw_sepchr - put = 'let g:netrw_sshport = '.g:netrw_sshport - put = 'let g:netrw_timefmt = '.g:netrw_timefmt - let tmpfileescline= line("$") - put ='let g:netrw_tmpfile_escape...' - put = 'let g:netrw_use_noswf = '.g:netrw_use_noswf - put = 'let g:netrw_xstrlen = '.g:netrw_xstrlen - put = 'let g:netrw_winsize = '.g:netrw_winsize - - put ='' - put ='+ For help, place cursor on line and press <F1>' - - 1d - silent %s/^+/"/e - res 99 - silent %s/= \([^0-9].*\)$/= '\1'/e - silent %s/= $/= ''/e - 1 - - call setline(decompressline,"let g:netrw_decompress = ".substitute(string(g:netrw_decompress),"^'\\(.*\\)'$",'\1','')) - call setline(fnameescline, "let g:netrw_fname_escape = '".escape(g:netrw_fname_escape,"'")."'") - call setline(globescline, "let g:netrw_glob_escape = '".escape(g:netrw_glob_escape,"'")."'") - call setline(tmpfileescline,"let g:netrw_tmpfile_escape = '".escape(g:netrw_tmpfile_escape,"'")."'") - - set nomod - - nmap <buffer> <silent> <F1> :call NetrwSettingHelp()<cr> - nnoremap <buffer> <silent> <leftmouse> <leftmouse>:call NetrwSettingHelp()<cr> - let tmpfile= tempname() - exe 'au BufWriteCmd Netrw\ Settings silent w! '.tmpfile.'|so '.tmpfile.'|call delete("'.tmpfile.'")|set nomod' -endfun - -" --------------------------------------------------------------------- -" NetrwSettingHelp: {{{2 -fun! NetrwSettingHelp() -" call Dfunc("NetrwSettingHelp()") - let curline = getline(".") - if curline =~ '=' - let varhelp = substitute(curline,'^\s*let ','','e') - let varhelp = substitute(varhelp,'\s*=.*$','','e') -" call Decho("trying help ".varhelp) - try - exe "he ".varhelp - catch /^Vim\%((\a\+)\)\=:E149/ - echo "***sorry*** no help available for <".varhelp.">" - endtry - elseif line(".") < s:netrw_settings_stop - he netrw-settings - elseif line(".") < s:netrw_protocol_stop - he netrw-externapp - elseif line(".") < s:netrw_xfer_stop - he netrw-variables - else - he netrw-browse-var - endif -" call Dret("NetrwSettingHelp") -endfun - -" --------------------------------------------------------------------- -" Modelines: {{{1 -" vim:ts=8 fdm=marker diff --git a/runtime/autoload/provider/clipboard.vim b/runtime/autoload/provider/clipboard.vim index 58d3d4550f..0bfd82f61d 100644 --- a/runtime/autoload/provider/clipboard.vim +++ b/runtime/autoload/provider/clipboard.vim @@ -158,7 +158,7 @@ function! provider#clipboard#Executable() abort let s:copy['*'] = s:copy['+'] let s:paste['*'] = s:paste['+'] return 'termux-clipboard' - elseif !empty($TMUX) && executable('tmux') + elseif executable('tmux') && (!empty($TMUX) || 0 == jobwait([jobstart(['tmux', 'list-buffers'])], 2000)[0]) let tmux_v = v:lua.vim.version.parse(system(['tmux', '-V'])) if !empty(tmux_v) && !v:lua.vim.version.lt(tmux_v, [3,2,0]) let s:copy['+'] = ['tmux', 'load-buffer', '-w', '-'] @@ -169,6 +169,14 @@ function! provider#clipboard#Executable() abort let s:copy['*'] = s:copy['+'] let s:paste['*'] = s:paste['+'] return 'tmux' + elseif get(get(g:, 'termfeatures', {}), 'osc52') && &clipboard ==# '' + " Don't use OSC 52 when 'clipboard' is set. It can be slow and cause a lot + " of user prompts. Users can opt-in to it by setting g:clipboard manually. + let s:copy['+'] = v:lua.require'vim.ui.clipboard.osc52'.copy('+') + let s:copy['*'] = v:lua.require'vim.ui.clipboard.osc52'.copy('*') + let s:paste['+'] = v:lua.require'vim.ui.clipboard.osc52'.paste('+') + let s:paste['*'] = v:lua.require'vim.ui.clipboard.osc52'.paste('*') + return 'OSC 52' endif let s:err = 'clipboard: No clipboard tool. :help clipboard' diff --git a/runtime/autoload/spotbugs.vim b/runtime/autoload/spotbugs.vim new file mode 100644 index 0000000000..6fd822d68e --- /dev/null +++ b/runtime/autoload/spotbugs.vim @@ -0,0 +1,354 @@ +" Default pre- and post-compiler actions and commands for SpotBugs +" Maintainers: @konfekt and @zzzyxwvut +" Last Change: 2024 Dec 08 + +let s:save_cpo = &cpo +set cpo&vim + +" Look for the setting of "g:spotbugs#state" in "ftplugin/java.vim". +let s:state = get(g:, 'spotbugs#state', {}) +let s:commands = get(s:state, 'commands', {}) +let s:compiler = get(s:state, 'compiler', '') +let s:readable = filereadable($VIMRUNTIME . '/compiler/' . s:compiler . '.vim') + +if has_key(s:commands, 'DefaultPreCompilerCommand') + let g:SpotBugsPreCompilerCommand = s:commands.DefaultPreCompilerCommand +else + + function! s:DefaultPreCompilerCommand(arguments) abort + execute 'make ' . a:arguments + cc + endfunction + + let g:SpotBugsPreCompilerCommand = function('s:DefaultPreCompilerCommand') +endif + +if has_key(s:commands, 'DefaultPreCompilerTestCommand') + let g:SpotBugsPreCompilerTestCommand = s:commands.DefaultPreCompilerTestCommand +else + + function! s:DefaultPreCompilerTestCommand(arguments) abort + execute 'make ' . a:arguments + cc + endfunction + + let g:SpotBugsPreCompilerTestCommand = function('s:DefaultPreCompilerTestCommand') +endif + +if has_key(s:commands, 'DefaultPostCompilerCommand') + let g:SpotBugsPostCompilerCommand = s:commands.DefaultPostCompilerCommand +else + + function! s:DefaultPostCompilerCommand(arguments) abort + execute 'make ' . a:arguments + endfunction + + let g:SpotBugsPostCompilerCommand = function('s:DefaultPostCompilerCommand') +endif + +if v:version > 900 || has('nvim') + + function! spotbugs#DeleteClassFiles() abort + if !exists('b:spotbugs_class_files') + return + endif + + for pathname in b:spotbugs_class_files + let classname = pathname =~# "^'.\\+\\.class'$" + \ ? eval(pathname) + \ : pathname + + if classname =~# '\.class$' && filereadable(classname) + " Since v9.0.0795. + let octad = readblob(classname, 0, 8) + + " Test the magic number and the major version number (45 for v1.0). + " Since v9.0.2027. + if len(octad) == 8 && octad[0 : 3] == 0zcafe.babe && + " Nvim: no << operator + "\ or((octad[6] << 8), octad[7]) >= 45 + \ or((octad[6] * 256), octad[7]) >= 45 + echomsg printf('Deleting %s: %d', classname, delete(classname)) + endif + endif + endfor + + let b:spotbugs_class_files = [] + endfunction + +else + + function! s:DeleteClassFilesWithNewLineCodes(classname) abort + " The distribution of "0a"s in class file versions 2560 and 2570: + " + " 0zca.fe.ba.be.00.00.0a.00 0zca.fe.ba.be.00.00.0a.0a + " 0zca.fe.ba.be.00.0a.0a.00 0zca.fe.ba.be.00.0a.0a.0a + " 0zca.fe.ba.be.0a.00.0a.00 0zca.fe.ba.be.0a.00.0a.0a + " 0zca.fe.ba.be.0a.0a.0a.00 0zca.fe.ba.be.0a.0a.0a.0a + let numbers = [0, 0, 0, 0, 0, 0, 0, 0] + let offset = 0 + let lines = readfile(a:classname, 'b', 4) + + " Track NL byte counts to handle files of less than 8 bytes. + let nl_cnt = len(lines) + " Track non-NL byte counts for "0zca.fe.ba.be.0a.0a.0a.0a". + let non_nl_cnt = 0 + + for line in lines + for idx in range(strlen(line)) + " Remap NLs to Nuls. + let numbers[offset] = (line[idx] == "\n") ? 0 : char2nr(line[idx]) % 256 + let non_nl_cnt += 1 + let offset += 1 + + if offset > 7 + break + endif + endfor + + let nl_cnt -= 1 + + if offset > 7 || (nl_cnt < 1 && non_nl_cnt > 4) + break + endif + + " Reclaim NLs. + let numbers[offset] = 10 + let offset += 1 + + if offset > 7 + break + endif + endfor + + " Test the magic number and the major version number (45 for v1.0). + if offset > 7 && numbers[0] == 0xca && numbers[1] == 0xfe && + \ numbers[2] == 0xba && numbers[3] == 0xbe && + \ (numbers[6] * 256 + numbers[7]) >= 45 + echomsg printf('Deleting %s: %d', a:classname, delete(a:classname)) + endif + endfunction + + function! spotbugs#DeleteClassFiles() abort + if !exists('b:spotbugs_class_files') + return + endif + + let encoding = &encoding + + try + set encoding=latin1 + + for pathname in b:spotbugs_class_files + let classname = pathname =~# "^'.\\+\\.class'$" + \ ? eval(pathname) + \ : pathname + + if classname =~# '\.class$' && filereadable(classname) + let line = get(readfile(classname, 'b', 1), 0, '') + let length = strlen(line) + + " Test the magic number and the major version number (45 for v1.0). + if length > 3 && line[0 : 3] == "\xca\xfe\xba\xbe" + if length > 7 && ((line[6] == "\n" ? 0 : char2nr(line[6]) % 256) * 256 + + \ (line[7] == "\n" ? 0 : char2nr(line[7]) % 256)) >= 45 + echomsg printf('Deleting %s: %d', classname, delete(classname)) + else + call s:DeleteClassFilesWithNewLineCodes(classname) + endif + endif + endif + endfor + finally + let &encoding = encoding + endtry + + let b:spotbugs_class_files = [] + endfunction + +endif + +function! spotbugs#DefaultPostCompilerAction() abort + " Since v7.4.191. + call call(g:SpotBugsPostCompilerCommand, ['%:S']) +endfunction + +if s:readable && s:compiler ==# 'maven' && executable('mvn') + + function! spotbugs#DefaultPreCompilerAction() abort + call spotbugs#DeleteClassFiles() + compiler maven + call call(g:SpotBugsPreCompilerCommand, ['compile']) + endfunction + + function! spotbugs#DefaultPreCompilerTestAction() abort + call spotbugs#DeleteClassFiles() + compiler maven + call call(g:SpotBugsPreCompilerTestCommand, ['test-compile']) + endfunction + + function! spotbugs#DefaultProperties() abort + return { + \ 'PreCompilerAction': + \ function('spotbugs#DefaultPreCompilerAction'), + \ 'PreCompilerTestAction': + \ function('spotbugs#DefaultPreCompilerTestAction'), + \ 'PostCompilerAction': + \ function('spotbugs#DefaultPostCompilerAction'), + \ 'sourceDirPath': ['src/main/java'], + \ 'classDirPath': ['target/classes'], + \ 'testSourceDirPath': ['src/test/java'], + \ 'testClassDirPath': ['target/test-classes'], + \ } + endfunction + + unlet s:readable s:compiler +elseif s:readable && s:compiler ==# 'ant' && executable('ant') + + function! spotbugs#DefaultPreCompilerAction() abort + call spotbugs#DeleteClassFiles() + compiler ant + call call(g:SpotBugsPreCompilerCommand, ['compile']) + endfunction + + function! spotbugs#DefaultPreCompilerTestAction() abort + call spotbugs#DeleteClassFiles() + compiler ant + call call(g:SpotBugsPreCompilerTestCommand, ['compile-test']) + endfunction + + function! spotbugs#DefaultProperties() abort + return { + \ 'PreCompilerAction': + \ function('spotbugs#DefaultPreCompilerAction'), + \ 'PreCompilerTestAction': + \ function('spotbugs#DefaultPreCompilerTestAction'), + \ 'PostCompilerAction': + \ function('spotbugs#DefaultPostCompilerAction'), + \ 'sourceDirPath': ['src'], + \ 'classDirPath': ['build/classes'], + \ 'testSourceDirPath': ['test'], + \ 'testClassDirPath': ['build/test/classes'], + \ } + endfunction + + unlet s:readable s:compiler +elseif s:readable && s:compiler ==# 'javac' && executable('javac') + let s:filename = tempname() + + function! spotbugs#DefaultPreCompilerAction() abort + call spotbugs#DeleteClassFiles() + compiler javac + + if get(b:, 'javac_makeprg_params', get(g:, 'javac_makeprg_params', '')) =~ '\s@\S' + " Only read options and filenames from @options [@sources ...] and do + " not update these files when filelists change. + call call(g:SpotBugsPreCompilerCommand, ['']) + else + " Collect filenames so that Javac can figure out what to compile. + let filelist = [] + + for arg_num in range(argc(-1)) + let arg_name = argv(arg_num) + + if arg_name =~# '\.java\=$' + call add(filelist, fnamemodify(arg_name, ':p:S')) + endif + endfor + + for buf_num in range(1, bufnr('$')) + if !buflisted(buf_num) + continue + endif + + let buf_name = bufname(buf_num) + + if buf_name =~# '\.java\=$' + let buf_name = fnamemodify(buf_name, ':p:S') + + if index(filelist, buf_name) < 0 + call add(filelist, buf_name) + endif + endif + endfor + + noautocmd call writefile(filelist, s:filename) + call call(g:SpotBugsPreCompilerCommand, [shellescape('@' . s:filename)]) + endif + endfunction + + function! spotbugs#DefaultPreCompilerTestAction() abort + call spotbugs#DefaultPreCompilerAction() + endfunction + + function! spotbugs#DefaultProperties() abort + return { + \ 'PreCompilerAction': + \ function('spotbugs#DefaultPreCompilerAction'), + \ 'PostCompilerAction': + \ function('spotbugs#DefaultPostCompilerAction'), + \ } + endfunction + + unlet s:readable s:compiler g:SpotBugsPreCompilerTestCommand + delfunction! s:DefaultPreCompilerTestCommand +else + + function! spotbugs#DefaultPreCompilerAction() abort + echomsg printf('Not supported: "%s"', s:compiler) + endfunction + + function! spotbugs#DefaultPreCompilerTestAction() abort + call spotbugs#DefaultPreCompilerAction() + endfunction + + function! spotbugs#DefaultProperties() abort + return {} + endfunction + + " XXX: Keep "s:compiler" around for "spotbugs#DefaultPreCompilerAction()", + " "s:DefaultPostCompilerCommand" -- "spotbugs#DefaultPostCompilerAction()". + unlet s:readable g:SpotBugsPreCompilerCommand g:SpotBugsPreCompilerTestCommand + delfunction! s:DefaultPreCompilerCommand + delfunction! s:DefaultPreCompilerTestCommand +endif + +function! s:DefineBufferAutocmd(event, ...) abort + if !exists('#java_spotbugs#User') + return 1 + endif + + for l:event in insert(copy(a:000), a:event) + if l:event != 'User' + execute printf('silent! autocmd! java_spotbugs %s <buffer>', l:event) + execute printf('autocmd java_spotbugs %s <buffer> doautocmd User', l:event) + endif + endfor + + return 0 +endfunction + +function! s:RemoveBufferAutocmd(event, ...) abort + if !exists('#java_spotbugs') + return 1 + endif + + for l:event in insert(copy(a:000), a:event) + if l:event != 'User' + execute printf('silent! autocmd! java_spotbugs %s <buffer>', l:event) + endif + endfor + + return 0 +endfunction + +" Documented in ":help compiler-spotbugs". +command! -bar -nargs=+ -complete=event SpotBugsDefineBufferAutocmd + \ call s:DefineBufferAutocmd(<f-args>) +command! -bar -nargs=+ -complete=event SpotBugsRemoveBufferAutocmd + \ call s:RemoveBufferAutocmd(<f-args>) + +let &cpo = s:save_cpo +unlet s:commands s:state s:save_cpo + +" vim: set foldmethod=syntax shiftwidth=2 expandtab: diff --git a/runtime/autoload/typst.vim b/runtime/autoload/typst.vim index 6d097dd922..362da3f45e 100644 --- a/runtime/autoload/typst.vim +++ b/runtime/autoload/typst.vim @@ -1,6 +1,7 @@ " Language: Typst -" Maintainer: Gregory Anders -" Last Change: 2024 Nov 02 +" Previous Maintainer: Gregory Anders +" Maintainer: Luca Saccarola <github.e41mv@aleeas.com> +" Last Change: 2024 Dec 09 " Based on: https://github.com/kaarmu/typst.vim function! typst#indentexpr() abort diff --git a/runtime/colors/blue.vim b/runtime/colors/blue.vim index 9ee878e103..7a17fc38b7 100644 --- a/runtime/colors/blue.vim +++ b/runtime/colors/blue.vim @@ -4,7 +4,7 @@ " Maintainer: Original maintainer Steven Vertigan <steven@vertigan.wattle.id.au> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2024 Aug 15 +" Last Change: 2025 Jan 07 " Generated by Colortemplate v2.2.3 @@ -82,6 +82,7 @@ hi Type guifg=#ffa500 guibg=NONE gui=bold cterm=NONE hi Underlined guifg=NONE guibg=NONE gui=underline ctermfg=NONE ctermbg=NONE cterm=underline hi Label guifg=#ffd700 guibg=NONE gui=NONE cterm=NONE hi! link Terminal Normal +hi! link PopupSelected PmenuSel hi! link Debug Special hi! link Added String hi! link Removed WarningMsg @@ -194,6 +195,7 @@ if s:t_Co >= 256 hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline hi Label ctermfg=220 ctermbg=NONE cterm=NONE hi! link Terminal Normal + hi! link PopupSelected PmenuSel hi! link Debug Special hi! link Added String hi! link Removed WarningMsg @@ -309,6 +311,7 @@ if s:t_Co >= 16 hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline hi Label ctermfg=yellow ctermbg=NONE cterm=NONE hi! link Terminal Normal + hi! link PopupSelected PmenuSel hi! link Debug Special hi! link Added String hi! link Removed WarningMsg @@ -423,6 +426,7 @@ if s:t_Co >= 8 hi Underlined ctermfg=NONE ctermbg=NONE cterm=underline hi Label ctermfg=yellow ctermbg=NONE cterm=NONE hi! link Terminal Normal + hi! link PopupSelected PmenuSel hi! link Debug Special hi! link Added String hi! link Removed WarningMsg diff --git a/runtime/colors/darkblue.vim b/runtime/colors/darkblue.vim index 9451e397e5..90dc304791 100644 --- a/runtime/colors/darkblue.vim +++ b/runtime/colors/darkblue.vim @@ -4,7 +4,7 @@ " Maintainer: Original author Bohdan Vlasyuk <bohdan@vstu.edu.ua> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2024 Aug 15 +" Last Change: 2025 Jan 07 " Generated by Colortemplate v2.2.3 @@ -24,6 +24,7 @@ if (has('termguicolors') && &termguicolors) || has('gui_running') endfor endif hi! link Terminal Normal +hi! link PopupSelected PmenuSel hi! link CursorColumn CursorLine hi! link CursorIM Cursor hi! link EndOfBuffer NonText @@ -134,6 +135,7 @@ hi DiffDelete guifg=#ffffff guibg=#af5faf gui=NONE cterm=NONE if s:t_Co >= 256 hi! link Terminal Normal + hi! link PopupSelected PmenuSel hi! link CursorColumn CursorLine hi! link CursorIM Cursor hi! link EndOfBuffer NonText diff --git a/runtime/colors/delek.vim b/runtime/colors/delek.vim index 35d4934f5c..d29980d685 100644 --- a/runtime/colors/delek.vim +++ b/runtime/colors/delek.vim @@ -4,7 +4,7 @@ " Maintainer: Original maintainer David Schweikert <david@schweikert.ch> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2024 Aug 15 +" Last Change: 2025 Jan 07 " Generated by Colortemplate v2.2.3 @@ -24,6 +24,7 @@ if (has('termguicolors') && &termguicolors) || has('gui_running') endfor endif hi! link Terminal Normal +hi! link PopupSelected PmenuSel hi! link LineNrAbove LineNr hi! link LineNrBelow LineNr hi! link CurSearch Search @@ -100,6 +101,7 @@ hi DiffDelete guifg=#ffffff guibg=#af5faf gui=NONE cterm=NONE if s:t_Co >= 256 hi! link Terminal Normal + hi! link PopupSelected PmenuSel hi! link LineNrAbove LineNr hi! link LineNrBelow LineNr hi! link CurSearch Search diff --git a/runtime/colors/desert.vim b/runtime/colors/desert.vim index 07ef937ab6..b5f404a14e 100644 --- a/runtime/colors/desert.vim +++ b/runtime/colors/desert.vim @@ -4,7 +4,7 @@ " Maintainer: Original maintainer Hans Fugal <hans@fugal.net> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2024 Aug 15 +" Last Change: 2025 Jan 07 " Generated by Colortemplate v2.2.3 @@ -32,6 +32,7 @@ hi! link CursorLineSign CursorLine hi! link EndOfBuffer NonText hi! link MessageWindow Pmenu hi! link PopupNotification Todo +hi! link PopupSelected PmenuSel hi Normal guifg=#ffffff guibg=#333333 gui=NONE cterm=NONE hi StatusLine guifg=#333333 guibg=#c2bfa5 gui=NONE cterm=NONE hi StatusLineNC guifg=#7f7f8c guibg=#c2bfa5 gui=NONE cterm=NONE @@ -108,6 +109,7 @@ if s:t_Co >= 256 hi! link EndOfBuffer NonText hi! link MessageWindow Pmenu hi! link PopupNotification Todo + hi! link PopupSelected PmenuSel hi Normal ctermfg=231 ctermbg=236 cterm=NONE hi StatusLine ctermfg=236 ctermbg=144 cterm=NONE hi StatusLineNC ctermfg=242 ctermbg=144 cterm=NONE diff --git a/runtime/colors/evening.vim b/runtime/colors/evening.vim index d5e081337a..2f6859ad10 100644 --- a/runtime/colors/evening.vim +++ b/runtime/colors/evening.vim @@ -4,7 +4,7 @@ " Maintainer: Original maintainer Steven Vertigan <steven@vertigan.wattle.id.au> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2024 Aug 15 +" Last Change: 2025 Jan 07 " Generated by Colortemplate v2.2.3 @@ -24,6 +24,7 @@ if (has('termguicolors') && &termguicolors) || has('gui_running') endfor endif hi! link VertSplit StatusLineNC +hi! link PopupSelected PmenuSel hi! link StatusLineTerm StatusLine hi! link StatusLineTermNC StatusLineNC hi! link TabLineFill TabLine @@ -134,6 +135,7 @@ hi DiffDelete guifg=#ffffff guibg=#af5faf gui=NONE cterm=NONE if s:t_Co >= 256 hi! link VertSplit StatusLineNC + hi! link PopupSelected PmenuSel hi! link StatusLineTerm StatusLine hi! link StatusLineTermNC StatusLineNC hi! link TabLineFill TabLine @@ -247,6 +249,7 @@ endif if s:t_Co >= 16 hi! link VertSplit StatusLineNC + hi! link PopupSelected PmenuSel hi! link StatusLineTerm StatusLine hi! link StatusLineTermNC StatusLineNC hi! link TabLineFill TabLine diff --git a/runtime/colors/habamax.vim b/runtime/colors/habamax.vim index 4b4c95e050..e4113d80bb 100644 --- a/runtime/colors/habamax.vim +++ b/runtime/colors/habamax.vim @@ -4,7 +4,7 @@ " Maintainer: Maxim Kim <habamax@gmail.com> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2024 Aug 15 +" Last Change: 2025 Jan 07 " Generated by Colortemplate v2.2.3 @@ -27,6 +27,7 @@ hi! link Terminal Normal hi! link StatuslineTerm Statusline hi! link StatuslineTermNC StatuslineNC hi! link MessageWindow Pmenu +hi! link PopupSelected PmenuSel hi! link javaScriptFunction Statement hi! link javaScriptIdentifier Statement hi! link sqlKeyword Statement @@ -122,6 +123,7 @@ if s:t_Co >= 256 hi! link StatuslineTerm Statusline hi! link StatuslineTermNC StatuslineNC hi! link MessageWindow Pmenu + hi! link PopupSelected PmenuSel hi! link javaScriptFunction Statement hi! link javaScriptIdentifier Statement hi! link sqlKeyword Statement diff --git a/runtime/colors/industry.vim b/runtime/colors/industry.vim index 6e66a4a791..edb86188a8 100644 --- a/runtime/colors/industry.vim +++ b/runtime/colors/industry.vim @@ -4,7 +4,7 @@ " Maintainer: Original maintainer Shian Lee. " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2024 Aug 15 +" Last Change: 2025 Jan 07 " Generated by Colortemplate v2.2.3 @@ -86,6 +86,7 @@ hi Conceal guifg=#6c6c6c guibg=NONE gui=NONE cterm=NONE hi Ignore guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE hi Title guifg=#ff00ff guibg=NONE gui=bold cterm=bold hi! link Terminal Normal +hi! link PopupSelected PmenuSel hi! link LineNrAbove LineNr hi! link LineNrBelow LineNr hi! link CurSearch Search @@ -162,6 +163,7 @@ if s:t_Co >= 256 hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE hi Title ctermfg=201 ctermbg=NONE cterm=bold hi! link Terminal Normal + hi! link PopupSelected PmenuSel hi! link LineNrAbove LineNr hi! link LineNrBelow LineNr hi! link CurSearch Search @@ -241,6 +243,7 @@ if s:t_Co >= 16 hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE hi Title ctermfg=magenta ctermbg=NONE cterm=bold hi! link Terminal Normal + hi! link PopupSelected PmenuSel hi! link LineNrAbove LineNr hi! link LineNrBelow LineNr hi! link CurSearch Search diff --git a/runtime/colors/lunaperche.vim b/runtime/colors/lunaperche.vim index 75d25e2a23..464b7af00e 100644 --- a/runtime/colors/lunaperche.vim +++ b/runtime/colors/lunaperche.vim @@ -4,7 +4,7 @@ " Maintainer: Maxim Kim <habamax@gmail.com> " Website: https://www.github.com/vim/colorschemes " License: Vim License (see `:help license`) -" Last Change: 2024 Aug 15 +" Last Change: 2025 Jan 07 " Generated by Colortemplate v2.2.3 @@ -93,6 +93,7 @@ hi! link LineNrAbove LineNr hi! link LineNrBelow LineNr hi! link MessageWindow PMenu hi! link PopupNotification Todo +hi! link PopupSelected PmenuSel if &background ==# 'dark' if (has('termguicolors') && &termguicolors) || has('gui_running') let g:terminal_ansi_colors = ['#000000', '#af5f5f', '#5faf5f', '#af875f', '#5f87af', '#d787d7', '#5fafaf', '#c6c6c6', '#767676', '#ff5f5f', '#5fd75f', '#ffd787', '#5fafff', '#ff87ff', '#5fd7d7', '#ffffff'] @@ -369,6 +370,7 @@ if s:t_Co >= 256 hi! link LineNrBelow LineNr hi! link MessageWindow PMenu hi! link PopupNotification Todo + hi! link PopupSelected PmenuSel if &background ==# 'dark' hi Normal ctermfg=251 ctermbg=16 cterm=NONE hi Statusline ctermfg=251 ctermbg=16 cterm=bold,reverse diff --git a/runtime/colors/morning.vim b/runtime/colors/morning.vim index 82a3d6d97d..463d009e88 100644 --- a/runtime/colors/morning.vim +++ b/runtime/colors/morning.vim @@ -4,7 +4,7 @@ " Maintainer: Original maintainer Bram Moolenaar <Bram@vim.org> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2024 Aug 15 +" Last Change: 2025 Jan 07 " Generated by Colortemplate v2.2.3 @@ -33,6 +33,7 @@ hi! link StatuslineTerm Statusline hi! link StatuslineTermNC StatuslineNC hi! link MessageWindow Pmenu hi! link PopupNotification Todo +hi! link PopupSelected PmenuSel hi Normal guifg=#000000 guibg=#e4e4e4 gui=NONE cterm=NONE hi EndOfBuffer guifg=#0000ff guibg=#cccccc gui=bold cterm=bold hi Folded guifg=#00008b guibg=#d3d3d3 gui=NONE cterm=NONE @@ -107,6 +108,7 @@ if s:t_Co >= 256 hi! link StatuslineTermNC StatuslineNC hi! link MessageWindow Pmenu hi! link PopupNotification Todo + hi! link PopupSelected PmenuSel hi Normal ctermfg=16 ctermbg=254 cterm=NONE hi EndOfBuffer ctermfg=21 ctermbg=252 cterm=bold hi Folded ctermfg=18 ctermbg=252 cterm=NONE diff --git a/runtime/colors/murphy.vim b/runtime/colors/murphy.vim index f38c8259dd..f00bdbd608 100644 --- a/runtime/colors/murphy.vim +++ b/runtime/colors/murphy.vim @@ -4,7 +4,7 @@ " Maintainer: Original maintainer Ron Aaron <ron@ronware.org>. " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2024 Aug 15 +" Last Change: 2025 Jan 07 " Generated by Colortemplate v2.2.3 @@ -33,6 +33,7 @@ hi! link StatusLineTerm StatusLine hi! link StatusLineTermNC StatusLineNC hi! link MessageWindow Pmenu hi! link PopupNotification Todo +hi! link PopupSelected PmenuSel hi! link Added Constant hi Normal guifg=#87ff87 guibg=#000000 gui=NONE cterm=NONE hi EndOfBuffer guifg=#0000ff guibg=#000000 gui=NONE cterm=NONE @@ -108,6 +109,7 @@ if s:t_Co >= 256 hi! link StatusLineTermNC StatusLineNC hi! link MessageWindow Pmenu hi! link PopupNotification Todo + hi! link PopupSelected PmenuSel hi! link Added Constant hi Normal ctermfg=120 ctermbg=16 cterm=NONE hi EndOfBuffer ctermfg=21 ctermbg=16 cterm=NONE diff --git a/runtime/colors/pablo.vim b/runtime/colors/pablo.vim index de585adfe2..202f505136 100644 --- a/runtime/colors/pablo.vim +++ b/runtime/colors/pablo.vim @@ -3,7 +3,7 @@ " Maintainer: Original maintainerRon Aaron <ron@ronware.org> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2024 Aug 15 +" Last Change: 2025 Jan 07 " Generated by Colortemplate v2.2.3 @@ -30,6 +30,7 @@ hi! link CursorLineFold CursorLine hi! link CursorLineSign CursorLine hi! link MessageWindow Pmenu hi! link PopupNotification Todo +hi! link PopupSelected PmenuSel hi Normal guifg=#ffffff guibg=#000000 gui=NONE cterm=NONE hi Comment guifg=#808080 guibg=NONE gui=NONE cterm=NONE hi Constant guifg=#00ffff guibg=NONE gui=NONE cterm=NONE @@ -105,6 +106,7 @@ if s:t_Co >= 256 hi! link CursorLineSign CursorLine hi! link MessageWindow Pmenu hi! link PopupNotification Todo + hi! link PopupSelected PmenuSel hi Normal ctermfg=231 ctermbg=16 cterm=NONE hi Comment ctermfg=244 ctermbg=NONE cterm=NONE hi Constant ctermfg=51 ctermbg=NONE cterm=NONE diff --git a/runtime/colors/peachpuff.vim b/runtime/colors/peachpuff.vim index 91c98119b3..508346a7ce 100644 --- a/runtime/colors/peachpuff.vim +++ b/runtime/colors/peachpuff.vim @@ -4,7 +4,7 @@ " Maintainer: Original maintainer David Ne\v{c}as (Yeti) <yeti@physics.muni.cz> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2024 Aug 15 +" Last Change: 2025 Jan 07 " Generated by Colortemplate v2.2.3 @@ -31,6 +31,7 @@ hi! link CursorLineFold CursorLine hi! link CursorLineSign CursorLine hi! link MessageWindow Pmenu hi! link PopupNotification Todo +hi! link PopupSelected PmenuSel hi Normal guifg=#000000 guibg=#ffdab9 gui=NONE cterm=NONE hi Folded guifg=#000000 guibg=#e3c1a5 gui=NONE cterm=NONE hi CursorLine guifg=NONE guibg=#f5c195 gui=NONE cterm=NONE @@ -105,6 +106,7 @@ if s:t_Co >= 256 hi! link CursorLineSign CursorLine hi! link MessageWindow Pmenu hi! link PopupNotification Todo + hi! link PopupSelected PmenuSel hi Normal ctermfg=16 ctermbg=223 cterm=NONE hi Folded ctermfg=16 ctermbg=252 cterm=NONE hi CursorLine ctermfg=NONE ctermbg=180 cterm=NONE diff --git a/runtime/colors/quiet.vim b/runtime/colors/quiet.vim index bcf2eced16..38349d76e8 100644 --- a/runtime/colors/quiet.vim +++ b/runtime/colors/quiet.vim @@ -4,7 +4,7 @@ " Maintainer: Maxence Weynans <neutaaaaan@gmail.com> " Website: https://github.com/vim/colorschemes " License: Vim License (see `:help license`)` -" Last Change: 2024 Aug 05 +" Last Change: 2025 Jan 07 " Generated by Colortemplate v2.2.3 @@ -22,6 +22,7 @@ hi! link StatusLineTerm StatusLine hi! link StatusLineTermNC StatusLineNC hi! link MessageWindow Pmenu hi! link PopupNotification Todo +hi! link PopupSelected PmenuSel hi! link Boolean Constant hi! link Character Constant hi! link Conditional Statement diff --git a/runtime/colors/retrobox.vim b/runtime/colors/retrobox.vim index a89bf0557e..f34fc99dc5 100644 --- a/runtime/colors/retrobox.vim +++ b/runtime/colors/retrobox.vim @@ -4,7 +4,7 @@ " Maintainer: Maxim Kim <habamax@gmail.com>, ported from gruvbox8 of Lifepillar <lifepillar@lifepillar.me> " Website: https://www.github.com/vim/colorschemes " License: Vim License (see `:help license`) -" Last Change: 2024 Aug 15 +" Last Change: 2025 Jan 07 " Generated by Colortemplate v2.2.3 @@ -22,6 +22,7 @@ hi! link Tag Special hi! link lCursor Cursor hi! link MessageWindow PMenu hi! link PopupNotification Todo +hi! link PopupSelected PmenuSel hi! link CurSearch IncSearch hi! link Terminal Normal diff --git a/runtime/colors/shine.vim b/runtime/colors/shine.vim index f3697c9ad6..39e6dae956 100644 --- a/runtime/colors/shine.vim +++ b/runtime/colors/shine.vim @@ -4,7 +4,7 @@ " Maintainer: Original maintainer is Yasuhiro Matsumoto <mattn@mail.goo.ne.jp> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2024 Aug 15 +" Last Change: 2025 Jan 07 " Generated by Colortemplate v2.2.3 @@ -35,6 +35,7 @@ hi! link Tag Special hi! link Operator Statement hi! link MessageWindow Pmenu hi! link PopupNotification Todo +hi! link PopupSelected PmenuSel hi Normal guifg=#000000 guibg=#ffffff gui=NONE cterm=NONE hi Folded guifg=#00008b guibg=#dadada gui=NONE cterm=NONE hi CursorLine guifg=NONE guibg=#dadada gui=NONE cterm=NONE @@ -115,6 +116,7 @@ if s:t_Co >= 256 hi! link Operator Statement hi! link MessageWindow Pmenu hi! link PopupNotification Todo + hi! link PopupSelected PmenuSel hi Normal ctermfg=16 ctermbg=231 cterm=NONE hi Folded ctermfg=18 ctermbg=253 cterm=NONE hi CursorLine ctermfg=NONE ctermbg=253 cterm=NONE diff --git a/runtime/colors/slate.vim b/runtime/colors/slate.vim index c9ce78946b..319ab9e0aa 100644 --- a/runtime/colors/slate.vim +++ b/runtime/colors/slate.vim @@ -4,7 +4,7 @@ " Maintainer: Original maintainer Ralph Amissah <ralph@amissah.com> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2024 Aug 15 +" Last Change: 2025 Jan 07 " Generated by Colortemplate v2.2.3 @@ -31,6 +31,7 @@ hi! link CursorLineFold CursorLine hi! link CursorLineSign CursorLine hi! link MessageWindow Pmenu hi! link PopupNotification Todo +hi! link PopupSelected PmenuSel hi Normal guifg=#ffffff guibg=#262626 gui=NONE cterm=NONE hi EndOfBuffer guifg=#5f87d7 guibg=NONE gui=NONE cterm=NONE hi StatusLine guifg=#000000 guibg=#afaf87 gui=NONE cterm=NONE @@ -110,6 +111,7 @@ if s:t_Co >= 256 hi! link CursorLineSign CursorLine hi! link MessageWindow Pmenu hi! link PopupNotification Todo + hi! link PopupSelected PmenuSel hi Normal ctermfg=231 ctermbg=235 cterm=NONE hi EndOfBuffer ctermfg=68 ctermbg=NONE cterm=NONE hi StatusLine ctermfg=16 ctermbg=144 cterm=NONE diff --git a/runtime/colors/sorbet.vim b/runtime/colors/sorbet.vim index bd4fb7baf7..25c27bf635 100644 --- a/runtime/colors/sorbet.vim +++ b/runtime/colors/sorbet.vim @@ -4,7 +4,7 @@ " Maintainer: Maxence Weynans <neutaaaaan@gmail.com> " Website: https://github.com/vim/colorschemes " License: Vim License (see `:help license`)` -" Last Change: 2024 Aug 05 +" Last Change: 2025 Jan 07 " Generated by Colortemplate v2.2.3 @@ -21,6 +21,7 @@ hi! link StatusLineTerm StatusLine hi! link StatusLineTermNC StatusLineNC hi! link MessageWindow Pmenu hi! link PopupNotification Todo +hi! link PopupSelected PmenuSel hi! link Boolean Constant hi! link Character Constant hi! link Conditional Statement diff --git a/runtime/colors/torte.vim b/runtime/colors/torte.vim index 7271188f0d..0709263f7c 100644 --- a/runtime/colors/torte.vim +++ b/runtime/colors/torte.vim @@ -4,7 +4,7 @@ " Maintainer: Original maintainer Thorsten Maerz <info@netztorte.de> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2024 Aug 15 +" Last Change: 2025 Jan 07 " Generated by Colortemplate v2.2.3 @@ -33,6 +33,7 @@ hi! link StatusLineTerm StatusLine hi! link StatusLineTermNC StatusLineNC hi! link MessageWindow Pmenu hi! link PopupNotification Todo +hi! link PopupSelected PmenuSel hi Normal guifg=#cccccc guibg=#000000 gui=NONE cterm=NONE hi Comment guifg=#80a0ff guibg=NONE gui=NONE cterm=NONE hi Constant guifg=#ffa0a0 guibg=NONE gui=NONE cterm=NONE @@ -108,6 +109,7 @@ if s:t_Co >= 256 hi! link StatusLineTermNC StatusLineNC hi! link MessageWindow Pmenu hi! link PopupNotification Todo + hi! link PopupSelected PmenuSel hi Normal ctermfg=251 ctermbg=16 cterm=NONE hi Comment ctermfg=111 ctermbg=NONE cterm=NONE hi Constant ctermfg=217 ctermbg=NONE cterm=NONE diff --git a/runtime/colors/unokai.vim b/runtime/colors/unokai.vim new file mode 100644 index 0000000000..168eda1483 --- /dev/null +++ b/runtime/colors/unokai.vim @@ -0,0 +1,522 @@ +" Name: unokai +" Description: Color scheme similar to Monokai originally created by Wimer Hazenberg for TextMate +" Author: k-37 <60838818+k-37@users.noreply.github.com> +" Maintainer: k-37 <60838818+k-37@users.noreply.github.com> +" Website: https://github.com/vim/colorschemes +" License: Vim License (see `:help license`) +" Last Change: 2024 Dec 15 + +" Generated by Colortemplate v2.2.3 + +set background=dark + +" hi clear +source $VIMRUNTIME/colors/vim.lua " Nvim: revert to Vim default color scheme +let g:colors_name = 'unokai' + +let s:t_Co = &t_Co + +if (has('termguicolors') && &termguicolors) || has('gui_running') + let g:terminal_ansi_colors = ['#282923', '#c61e5c', '#81af24', '#fd971f', '#51aebe', '#ae81ff', '#80beb5', '#bababa', '#74705d', '#f92672', '#a6e22e', '#e6db74', '#66d9ef', '#fd5ff0', '#a1efe4', '#f8f8f2'] + " Nvim uses g:terminal_color_{0-15} instead + for i in range(g:terminal_ansi_colors->len()) + let g:terminal_color_{i} = g:terminal_ansi_colors[i] + endfor +endif +hi! link CursorLineFold FoldColumn +hi! link CursorLineSign SignColumn +hi! link MessageWindow Pmenu +hi! link PopupNotification Todo +hi! link PopupSelected PmenuSel +hi! link StatusLineTerm StatusLine +hi! link StatusLineTermNC StatusLineNC +hi! link Terminal Normal +hi! link Delimiter PreProc +hi! link Operator PreProc +hi! link StorageClass PreProc +hi! link Structure PreProc +hi! link Define Identifier +hi! link Label String +hi! link markdownCode Comment +hi! link markdownCodeBlock markdownCode +hi! link markdownCodeDelimiter markdownCode +hi Normal guifg=#f8f8f2 guibg=#282923 gui=NONE cterm=NONE +hi StatusLine guifg=#282923 guibg=#bababa gui=NONE cterm=NONE +hi StatusLineNC guifg=#282923 guibg=#74705d gui=NONE cterm=NONE +hi VertSplit guifg=#74705d guibg=#74705d gui=NONE cterm=NONE +hi TabLine guifg=#282923 guibg=#74705d gui=NONE cterm=NONE +hi TabLineFill guifg=#282923 guibg=#74705d gui=NONE cterm=NONE +hi TabLineSel guifg=#282923 guibg=#bababa gui=bold cterm=bold +hi ToolbarLine guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE +hi ToolbarButton guifg=#74705d guibg=#f8f8f2 gui=bold,reverse cterm=bold,reverse +hi QuickFixLine guifg=#282923 guibg=#51aebe gui=NONE cterm=NONE +hi CursorLineNr guifg=#dadada guibg=NONE gui=bold cterm=bold +hi LineNr guifg=#8a8a8a guibg=NONE gui=NONE cterm=NONE +hi LineNrAbove guifg=#8a8a8a guibg=NONE gui=NONE cterm=NONE +hi LineNrBelow guifg=#8a8a8a guibg=NONE gui=NONE cterm=NONE +hi NonText guifg=#8a8a8a guibg=NONE gui=NONE cterm=NONE +hi EndOfBuffer guifg=#8a8a8a guibg=NONE gui=NONE cterm=NONE +hi SpecialKey guifg=#8a8a8a guibg=NONE gui=NONE cterm=NONE +hi FoldColumn guifg=#8a8a8a guibg=NONE gui=NONE cterm=NONE +hi Visual guifg=#a1efe4 guibg=#282923 gui=reverse cterm=reverse +hi VisualNOS guifg=#282923 guibg=#80beb5 gui=NONE cterm=NONE +hi Pmenu guifg=NONE guibg=#585858 gui=NONE cterm=NONE +hi PmenuThumb guifg=NONE guibg=#74705d gui=NONE cterm=NONE +hi PmenuSbar guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE +hi PmenuSel guifg=NONE guibg=#8a8a8a gui=NONE cterm=NONE +hi PmenuKind guifg=#80beb5 guibg=#585858 gui=NONE cterm=NONE +hi PmenuKindSel guifg=#80beb5 guibg=#8a8a8a gui=NONE cterm=NONE +hi PmenuExtra guifg=#bababa guibg=#585858 gui=NONE cterm=NONE +hi PmenuExtraSel guifg=#bababa guibg=#8a8a8a gui=NONE cterm=NONE +hi PmenuMatch guifg=#ffaf5f guibg=#585858 gui=NONE cterm=NONE +hi PmenuMatchSel guifg=#ffaf5f guibg=#8a8a8a gui=NONE cterm=NONE +hi SignColumn guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE +hi Error guifg=#f92672 guibg=#000000 gui=reverse cterm=reverse +hi ErrorMsg guifg=#f92672 guibg=#000000 gui=reverse cterm=reverse +hi ModeMsg guifg=NONE guibg=NONE gui=bold ctermfg=NONE ctermbg=NONE cterm=bold +hi MoreMsg guifg=#81af24 guibg=NONE gui=NONE cterm=NONE +hi Question guifg=#e6db74 guibg=NONE gui=NONE cterm=NONE +hi WarningMsg guifg=#f92672 guibg=NONE gui=NONE cterm=NONE +hi Todo guifg=#dadada guibg=NONE gui=bold cterm=bold +hi MatchParen guifg=#fd971f guibg=NONE gui=bold cterm=bold +hi Search guifg=#66d9ef guibg=#282923 gui=reverse cterm=reverse +hi IncSearch guifg=#ffaf5f guibg=#282923 gui=reverse cterm=reverse +hi CurSearch guifg=#ffaf5f guibg=#282923 gui=reverse cterm=reverse +hi WildMenu guifg=#282923 guibg=#e6db74 gui=bold cterm=bold +hi debugPC guifg=#282923 guibg=#51aebe gui=NONE cterm=NONE +hi debugBreakpoint guifg=#282923 guibg=#f92672 gui=NONE cterm=NONE +hi Cursor guifg=#000000 guibg=#dadada gui=NONE cterm=NONE +hi lCursor guifg=#282923 guibg=#5fff00 gui=NONE cterm=NONE +hi CursorLine guifg=NONE guibg=#3a392f gui=NONE cterm=NONE +hi CursorColumn guifg=NONE guibg=#3a392f gui=NONE cterm=NONE +hi Folded guifg=#bababa guibg=#414141 gui=NONE cterm=NONE +hi ColorColumn guifg=NONE guibg=#585858 gui=NONE cterm=NONE +hi SpellBad guifg=NONE guibg=NONE guisp=#d75f5f gui=undercurl ctermfg=NONE ctermbg=NONE cterm=underline +hi SpellCap guifg=NONE guibg=NONE guisp=#ffaf5f gui=undercurl ctermfg=NONE ctermbg=NONE cterm=underline +hi SpellLocal guifg=NONE guibg=NONE guisp=#5fd75f gui=undercurl ctermfg=NONE ctermbg=NONE cterm=underline +hi SpellRare guifg=NONE guibg=NONE guisp=#fd5ff0 gui=undercurl ctermfg=NONE ctermbg=NONE cterm=underline +hi Constant guifg=#ae81ff guibg=NONE gui=NONE cterm=NONE +hi Type guifg=#fd971f guibg=NONE gui=bold cterm=bold +hi Character guifg=#a6e22e guibg=NONE gui=NONE cterm=NONE +hi Comment guifg=#74705d guibg=NONE gui=NONE cterm=NONE +hi String guifg=#e6db74 guibg=NONE gui=NONE cterm=NONE +hi Function guifg=#a6e22e guibg=NONE gui=NONE cterm=NONE +hi Identifier guifg=#66d9ef guibg=NONE gui=NONE cterm=NONE +hi PreProc guifg=#f92672 guibg=NONE gui=NONE cterm=NONE +hi Special guifg=#80beb5 guibg=NONE gui=NONE cterm=NONE +hi Statement guifg=#f92672 guibg=NONE gui=bold cterm=bold +hi Underlined guifg=#66d9ef guibg=NONE gui=underline cterm=underline +hi Title guifg=NONE guibg=NONE gui=bold ctermfg=NONE ctermbg=NONE cterm=bold +hi Debug guifg=#80beb5 guibg=NONE gui=NONE cterm=NONE +hi Ignore guifg=NONE guibg=NONE gui=NONE ctermfg=NONE ctermbg=NONE cterm=NONE +hi Directory guifg=#a1efe4 guibg=NONE gui=bold cterm=bold +hi Conceal guifg=#8a8a8a guibg=NONE gui=NONE cterm=NONE +hi DiffAdd guifg=#5faf5f guibg=NONE gui=reverse cterm=reverse +hi DiffChange guifg=#5f87af guibg=NONE gui=reverse cterm=reverse +hi DiffText guifg=#af87af guibg=NONE gui=reverse cterm=reverse +hi DiffDelete guifg=#af5f5f guibg=NONE gui=reverse cterm=reverse +hi Added guifg=#5fd75f guibg=NONE gui=NONE cterm=NONE +hi Changed guifg=#ffaf5f guibg=NONE gui=NONE cterm=NONE +hi Removed guifg=#d75f5f guibg=NONE gui=NONE cterm=NONE +hi htmlBold guifg=#f8f8f2 guibg=NONE gui=bold cterm=bold +hi htmlItalic guifg=#f8f8f2 guibg=NONE gui=italic cterm=italic +hi markdownHeadingDelimiter guifg=#f8f8f2 guibg=NONE gui=NONE cterm=NONE +hi markdownH1Delimiter guifg=#f92672 guibg=NONE gui=NONE cterm=NONE +hi markdownH2Delimiter guifg=#e6db74 guibg=NONE gui=NONE cterm=NONE +hi markdownH4Delimiter guifg=#66d9ef guibg=NONE gui=NONE cterm=NONE +hi markdownH6Delimiter guifg=#a6e22e guibg=NONE gui=NONE cterm=NONE +hi markdownH3Delimiter guifg=#fd971f guibg=NONE gui=NONE cterm=NONE +hi markdownH5Delimiter guifg=#51aebe guibg=NONE gui=NONE cterm=NONE + +if s:t_Co >= 256 + hi! link CursorLineFold FoldColumn + hi! link CursorLineSign SignColumn + hi! link MessageWindow Pmenu + hi! link PopupNotification Todo + hi! link PopupSelected PmenuSel + hi! link StatusLineTerm StatusLine + hi! link StatusLineTermNC StatusLineNC + hi! link Terminal Normal + hi! link Delimiter PreProc + hi! link Operator PreProc + hi! link StorageClass PreProc + hi! link Structure PreProc + hi! link Define Identifier + hi! link Label String + hi! link markdownCode Comment + hi! link markdownCodeBlock markdownCode + hi! link markdownCodeDelimiter markdownCode + hi Normal ctermfg=255 ctermbg=235 cterm=NONE + hi StatusLine ctermfg=235 ctermbg=250 cterm=NONE + hi StatusLineNC ctermfg=235 ctermbg=244 cterm=NONE + hi VertSplit ctermfg=244 ctermbg=244 cterm=NONE + hi TabLine ctermfg=235 ctermbg=244 cterm=NONE + hi TabLineFill ctermfg=235 ctermbg=244 cterm=NONE + hi TabLineSel ctermfg=235 ctermbg=250 cterm=bold + hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE + hi ToolbarButton ctermfg=244 ctermbg=255 cterm=bold,reverse + hi QuickFixLine ctermfg=235 ctermbg=141 cterm=NONE + hi CursorLineNr ctermfg=253 ctermbg=NONE cterm=bold + hi LineNr ctermfg=245 ctermbg=NONE cterm=NONE + hi LineNrAbove ctermfg=245 ctermbg=NONE cterm=NONE + hi LineNrBelow ctermfg=245 ctermbg=NONE cterm=NONE + hi NonText ctermfg=245 ctermbg=NONE cterm=NONE + hi EndOfBuffer ctermfg=245 ctermbg=NONE cterm=NONE + hi SpecialKey ctermfg=245 ctermbg=NONE cterm=NONE + hi FoldColumn ctermfg=245 ctermbg=NONE cterm=NONE + hi Visual ctermfg=116 ctermbg=235 cterm=reverse + hi VisualNOS ctermfg=235 ctermbg=73 cterm=NONE + hi Pmenu ctermfg=NONE ctermbg=240 cterm=NONE + hi PmenuThumb ctermfg=NONE ctermbg=244 cterm=NONE + hi PmenuSbar ctermfg=NONE ctermbg=NONE cterm=NONE + hi PmenuSel ctermfg=NONE ctermbg=245 cterm=NONE + hi PmenuKind ctermfg=73 ctermbg=240 cterm=NONE + hi PmenuKindSel ctermfg=73 ctermbg=245 cterm=NONE + hi PmenuExtra ctermfg=250 ctermbg=240 cterm=NONE + hi PmenuExtraSel ctermfg=250 ctermbg=245 cterm=NONE + hi PmenuMatch ctermfg=215 ctermbg=240 cterm=NONE + hi PmenuMatchSel ctermfg=215 ctermbg=245 cterm=NONE + hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE + hi Error ctermfg=197 ctermbg=16 cterm=reverse + hi ErrorMsg ctermfg=197 ctermbg=16 cterm=reverse + hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=bold + hi MoreMsg ctermfg=106 ctermbg=NONE cterm=NONE + hi Question ctermfg=185 ctermbg=NONE cterm=NONE + hi WarningMsg ctermfg=197 ctermbg=NONE cterm=NONE + hi Todo ctermfg=253 ctermbg=NONE cterm=bold + hi MatchParen ctermfg=208 ctermbg=NONE cterm=bold + hi Search ctermfg=81 ctermbg=235 cterm=reverse + hi IncSearch ctermfg=215 ctermbg=235 cterm=reverse + hi CurSearch ctermfg=215 ctermbg=235 cterm=reverse + hi WildMenu ctermfg=235 ctermbg=185 cterm=bold + hi debugPC ctermfg=235 ctermbg=73 cterm=NONE + hi debugBreakpoint ctermfg=235 ctermbg=197 cterm=NONE + hi CursorLine ctermfg=NONE ctermbg=237 cterm=NONE + hi CursorColumn ctermfg=NONE ctermbg=237 cterm=NONE + hi Folded ctermfg=250 ctermbg=238 cterm=NONE + hi ColorColumn ctermfg=NONE ctermbg=240 cterm=NONE + hi SpellBad ctermfg=167 ctermbg=NONE cterm=underline + hi SpellCap ctermfg=215 ctermbg=NONE cterm=underline + hi SpellLocal ctermfg=77 ctermbg=NONE cterm=underline + hi SpellRare ctermfg=207 ctermbg=NONE cterm=underline + hi Constant ctermfg=141 ctermbg=NONE cterm=NONE + hi Type ctermfg=208 ctermbg=NONE cterm=bold + hi Character ctermfg=112 ctermbg=NONE cterm=NONE + hi Comment ctermfg=244 ctermbg=NONE cterm=NONE + hi String ctermfg=185 ctermbg=NONE cterm=NONE + hi Function ctermfg=112 ctermbg=NONE cterm=NONE + hi Identifier ctermfg=81 ctermbg=NONE cterm=NONE + hi PreProc ctermfg=197 ctermbg=NONE cterm=NONE + hi Special ctermfg=73 ctermbg=NONE cterm=NONE + hi Statement ctermfg=197 ctermbg=NONE cterm=bold + hi Underlined ctermfg=81 ctermbg=NONE cterm=underline + hi Title ctermfg=NONE ctermbg=NONE cterm=bold + hi Debug ctermfg=73 ctermbg=NONE cterm=NONE + hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE + hi Directory ctermfg=116 ctermbg=NONE cterm=bold + hi Conceal ctermfg=245 ctermbg=NONE cterm=NONE + hi DiffAdd ctermfg=71 ctermbg=NONE cterm=reverse + hi DiffChange ctermfg=67 ctermbg=NONE cterm=reverse + hi DiffText ctermfg=139 ctermbg=NONE cterm=reverse + hi DiffDelete ctermfg=131 ctermbg=NONE cterm=reverse + hi Added ctermfg=77 ctermbg=NONE cterm=NONE + hi Changed ctermfg=215 ctermbg=NONE cterm=NONE + hi Removed ctermfg=167 ctermbg=NONE cterm=NONE + hi htmlBold ctermfg=255 ctermbg=NONE cterm=bold + hi htmlItalic ctermfg=255 ctermbg=NONE cterm=underline + hi markdownHeadingDelimiter ctermfg=255 ctermbg=NONE cterm=NONE + hi markdownH1Delimiter ctermfg=197 ctermbg=NONE cterm=NONE + hi markdownH2Delimiter ctermfg=185 ctermbg=NONE cterm=NONE + hi markdownH4Delimiter ctermfg=81 ctermbg=NONE cterm=NONE + hi markdownH6Delimiter ctermfg=112 ctermbg=NONE cterm=NONE + hi markdownH3Delimiter ctermfg=208 ctermbg=NONE cterm=NONE + hi markdownH5Delimiter ctermfg=73 ctermbg=NONE cterm=NONE + unlet s:t_Co + finish +endif + +if s:t_Co >= 16 + hi Normal ctermfg=white ctermbg=black cterm=NONE + hi StatusLine ctermfg=black ctermbg=gray cterm=NONE + hi StatusLineNC ctermfg=black ctermbg=darkgray cterm=NONE + hi VertSplit ctermfg=darkgray ctermbg=darkgray cterm=NONE + hi TabLine ctermfg=black ctermbg=darkgray cterm=NONE + hi TabLineFill ctermfg=black ctermbg=darkgray cterm=NONE + hi TabLineSel ctermfg=black ctermbg=gray cterm=bold + hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE + hi ToolbarButton ctermfg=darkgray ctermbg=white cterm=bold,reverse + hi QuickFixLine ctermfg=black ctermbg=darkmagenta cterm=NONE + hi CursorLineNr ctermfg=white ctermbg=NONE cterm=bold + hi LineNr ctermfg=darkgrey ctermbg=NONE cterm=NONE + hi LineNrAbove ctermfg=darkgrey ctermbg=NONE cterm=NONE + hi LineNrBelow ctermfg=darkgrey ctermbg=NONE cterm=NONE + hi NonText ctermfg=darkgrey ctermbg=NONE cterm=NONE + hi EndOfBuffer ctermfg=darkgrey ctermbg=NONE cterm=NONE + hi SpecialKey ctermfg=darkgrey ctermbg=NONE cterm=NONE + hi FoldColumn ctermfg=darkgrey ctermbg=NONE cterm=NONE + hi Visual ctermfg=cyan ctermbg=black cterm=reverse + hi VisualNOS ctermfg=black ctermbg=darkcyan cterm=NONE + hi Pmenu ctermfg=black ctermbg=gray cterm=NONE + hi PmenuThumb ctermfg=gray ctermbg=black cterm=NONE + hi PmenuSbar ctermfg=NONE ctermbg=gray cterm=NONE + hi PmenuSel ctermfg=black ctermbg=darkyellow cterm=NONE + hi PmenuKind ctermfg=darkred ctermbg=gray cterm=NONE + hi PmenuKindSel ctermfg=darkred ctermbg=darkyellow cterm=NONE + hi PmenuExtra ctermfg=darkgray ctermbg=gray cterm=NONE + hi PmenuExtraSel ctermfg=black ctermbg=darkyellow cterm=NONE + hi PmenuMatch ctermfg=black ctermbg=gray cterm=bold + hi PmenuMatchSel ctermfg=black ctermbg=darkyellow cterm=bold + hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE + hi Error ctermfg=red ctermbg=black cterm=reverse + hi ErrorMsg ctermfg=red ctermbg=black cterm=reverse + hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=bold + hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=NONE + hi Question ctermfg=yellow ctermbg=NONE cterm=NONE + hi WarningMsg ctermfg=red ctermbg=NONE cterm=NONE + hi Todo ctermfg=white ctermbg=NONE cterm=bold + hi MatchParen ctermfg=darkyellow ctermbg=NONE cterm=bold + hi Search ctermfg=blue ctermbg=black cterm=reverse + hi IncSearch ctermfg=red ctermbg=black cterm=reverse + hi CurSearch ctermfg=red ctermbg=black cterm=reverse + hi WildMenu ctermfg=black ctermbg=yellow cterm=bold + hi debugPC ctermfg=black ctermbg=darkblue cterm=NONE + hi debugBreakpoint ctermfg=black ctermbg=red cterm=NONE + hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline + hi CursorColumn ctermfg=black ctermbg=darkyellow cterm=NONE + hi Folded ctermfg=black ctermbg=darkyellow cterm=NONE + hi ColorColumn ctermfg=black ctermbg=darkyellow cterm=NONE + hi SpellBad ctermfg=darkred ctermbg=NONE cterm=underline + hi SpellCap ctermfg=darkyellow ctermbg=NONE cterm=underline + hi SpellLocal ctermfg=darkgreen ctermbg=NONE cterm=underline + hi SpellRare ctermfg=magenta ctermbg=NONE cterm=underline + hi Constant ctermfg=darkmagenta ctermbg=NONE cterm=NONE + hi Type ctermfg=darkyellow ctermbg=NONE cterm=bold + hi Character ctermfg=green ctermbg=NONE cterm=NONE + hi Comment ctermfg=darkgray ctermbg=NONE cterm=NONE + hi String ctermfg=yellow ctermbg=NONE cterm=NONE + hi Function ctermfg=green ctermbg=NONE cterm=NONE + hi Identifier ctermfg=blue ctermbg=NONE cterm=NONE + hi PreProc ctermfg=red ctermbg=NONE cterm=NONE + hi Special ctermfg=darkcyan ctermbg=NONE cterm=NONE + hi Statement ctermfg=red ctermbg=NONE cterm=bold + hi Underlined ctermfg=blue ctermbg=NONE cterm=underline + hi Title ctermfg=NONE ctermbg=NONE cterm=bold + hi Debug ctermfg=darkcyan ctermbg=NONE cterm=NONE + hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE + hi Directory ctermfg=cyan ctermbg=NONE cterm=bold + hi Conceal ctermfg=darkgrey ctermbg=NONE cterm=NONE + hi DiffAdd ctermfg=darkgreen ctermbg=NONE cterm=reverse + hi DiffChange ctermfg=darkblue ctermbg=NONE cterm=reverse + hi DiffText ctermfg=darkmagenta ctermbg=NONE cterm=reverse + hi DiffDelete ctermfg=darkred ctermbg=NONE cterm=reverse + hi Added ctermfg=darkgreen ctermbg=NONE cterm=NONE + hi Changed ctermfg=darkyellow ctermbg=NONE cterm=NONE + hi Removed ctermfg=darkred ctermbg=NONE cterm=NONE + hi htmlBold ctermfg=white ctermbg=NONE cterm=bold + hi htmlItalic ctermfg=white ctermbg=NONE cterm=underline + hi markdownHeadingDelimiter ctermfg=white ctermbg=NONE cterm=NONE + hi markdownH1Delimiter ctermfg=red ctermbg=NONE cterm=NONE + hi markdownH2Delimiter ctermfg=yellow ctermbg=NONE cterm=NONE + hi markdownH4Delimiter ctermfg=blue ctermbg=NONE cterm=NONE + hi markdownH6Delimiter ctermfg=green ctermbg=NONE cterm=NONE + hi markdownH3Delimiter ctermfg=darkyellow ctermbg=NONE cterm=NONE + hi markdownH5Delimiter ctermfg=darkblue ctermbg=NONE cterm=NONE + unlet s:t_Co + finish +endif + +if s:t_Co >= 8 + hi Normal ctermfg=gray ctermbg=black cterm=NONE + hi StatusLine ctermfg=gray ctermbg=black cterm=bold,reverse + hi StatusLineNC ctermfg=gray ctermbg=black cterm=reverse + hi VertSplit ctermfg=gray ctermbg=gray cterm=NONE + hi TabLine ctermfg=black ctermbg=gray cterm=NONE + hi TabLineFill ctermfg=gray ctermbg=gray cterm=NONE + hi TabLineSel ctermfg=black ctermbg=gray cterm=bold + hi ToolbarLine ctermfg=NONE ctermbg=NONE cterm=NONE + hi ToolbarButton ctermfg=gray ctermbg=black cterm=reverse + hi QuickFixLine ctermfg=black ctermbg=darkyellow cterm=NONE + hi CursorLineNr ctermfg=darkyellow ctermbg=NONE cterm=bold + hi LineNr ctermfg=gray ctermbg=NONE cterm=bold + hi LineNrAbove ctermfg=gray ctermbg=NONE cterm=bold + hi LineNrBelow ctermfg=gray ctermbg=NONE cterm=bold + hi NonText ctermfg=gray ctermbg=NONE cterm=bold + hi EndOfBuffer ctermfg=gray ctermbg=NONE cterm=bold + hi SpecialKey ctermfg=gray ctermbg=NONE cterm=bold + hi FoldColumn ctermfg=gray ctermbg=NONE cterm=bold + hi Visual ctermfg=black ctermbg=darkcyan cterm=NONE + hi VisualNOS ctermfg=black ctermbg=darkcyan cterm=NONE + hi Pmenu ctermfg=black ctermbg=gray cterm=NONE + hi PmenuThumb ctermfg=gray ctermbg=black cterm=NONE + hi PmenuSbar ctermfg=NONE ctermbg=gray cterm=NONE + hi PmenuSel ctermfg=black ctermbg=darkyellow cterm=NONE + hi PmenuKind ctermfg=darkred ctermbg=gray cterm=NONE + hi PmenuKindSel ctermfg=darkred ctermbg=darkyellow cterm=NONE + hi PmenuExtra ctermfg=black ctermbg=gray cterm=NONE + hi PmenuExtraSel ctermfg=black ctermbg=darkyellow cterm=NONE + hi PmenuMatch ctermfg=black ctermbg=gray cterm=bold + hi PmenuMatchSel ctermfg=black ctermbg=darkyellow cterm=bold + hi SignColumn ctermfg=NONE ctermbg=NONE cterm=NONE + hi Error ctermfg=darkred ctermbg=gray cterm=bold,reverse + hi ErrorMsg ctermfg=darkred ctermbg=gray cterm=bold,reverse + hi ModeMsg ctermfg=NONE ctermbg=NONE cterm=bold + hi MoreMsg ctermfg=darkgreen ctermbg=NONE cterm=NONE + hi Question ctermfg=darkyellow ctermbg=NONE cterm=NONE + hi WarningMsg ctermfg=darkred ctermbg=NONE cterm=NONE + hi Todo ctermfg=gray ctermbg=NONE cterm=bold + hi MatchParen ctermfg=darkyellow ctermbg=NONE cterm=bold + hi Search ctermfg=black ctermbg=darkblue cterm=NONE + hi IncSearch ctermfg=black ctermbg=darkyellow cterm=NONE + hi CurSearch ctermfg=black ctermbg=darkyellow cterm=NONE + hi WildMenu ctermfg=black ctermbg=darkyellow cterm=NONE + hi debugPC ctermfg=black ctermbg=darkblue cterm=NONE + hi debugBreakpoint ctermfg=black ctermbg=darkcyan cterm=NONE + hi CursorLine ctermfg=NONE ctermbg=NONE cterm=underline + hi CursorColumn ctermfg=black ctermbg=darkyellow cterm=NONE + hi Folded ctermfg=black ctermbg=darkyellow cterm=NONE + hi ColorColumn ctermfg=black ctermbg=darkyellow cterm=NONE + hi SpellBad ctermfg=darkred ctermbg=gray cterm=reverse + hi SpellCap ctermfg=darkblue ctermbg=gray cterm=reverse + hi SpellLocal ctermfg=darkgreen ctermbg=black cterm=reverse + hi SpellRare ctermfg=darkmagenta ctermbg=gray cterm=reverse + hi Constant ctermfg=darkmagenta ctermbg=NONE cterm=NONE + hi Type ctermfg=darkyellow ctermbg=NONE cterm=bold + hi Character ctermfg=darkgreen ctermbg=NONE cterm=NONE + hi Comment ctermfg=gray ctermbg=NONE cterm=bold + hi String ctermfg=darkyellow ctermbg=NONE cterm=bold + hi Function ctermfg=darkgreen ctermbg=NONE cterm=NONE + hi Identifier ctermfg=darkblue ctermbg=NONE cterm=NONE + hi PreProc ctermfg=darkred ctermbg=NONE cterm=NONE + hi Special ctermfg=darkcyan ctermbg=NONE cterm=bold + hi Statement ctermfg=darkred ctermbg=NONE cterm=bold + hi Underlined ctermfg=darkblue ctermbg=NONE cterm=underline + hi Title ctermfg=NONE ctermbg=NONE cterm=bold + hi Debug ctermfg=darkcyan ctermbg=NONE cterm=NONE + hi Ignore ctermfg=NONE ctermbg=NONE cterm=NONE + hi Directory ctermfg=darkcyan ctermbg=NONE cterm=bold + hi Conceal ctermfg=gray ctermbg=NONE cterm=NONE + hi DiffAdd ctermfg=darkgreen ctermbg=NONE cterm=reverse + hi DiffChange ctermfg=darkblue ctermbg=NONE cterm=reverse + hi DiffText ctermfg=darkmagenta ctermbg=NONE cterm=reverse + hi DiffDelete ctermfg=darkred ctermbg=NONE cterm=reverse + hi Added ctermfg=darkgreen ctermbg=NONE cterm=NONE + hi Changed ctermfg=darkyellow ctermbg=NONE cterm=NONE + hi Removed ctermfg=darkred ctermbg=NONE cterm=NONE + hi htmlBold ctermfg=gray ctermbg=NONE cterm=bold + hi htmlItalic ctermfg=gray ctermbg=NONE cterm=underline + hi markdownHeadingDelimiter ctermfg=gray ctermbg=NONE cterm=NONE + hi markdownH1Delimiter ctermfg=darkred ctermbg=NONE cterm=NONE + hi markdownH2Delimiter ctermfg=darkyellow ctermbg=NONE cterm=NONE + hi markdownH4Delimiter ctermfg=darkblue ctermbg=NONE cterm=NONE + hi markdownH6Delimiter ctermfg=darkgreen ctermbg=NONE cterm=NONE + hi markdownH3Delimiter ctermfg=darkyellow ctermbg=NONE cterm=NONE + hi markdownH5Delimiter ctermfg=darkblue ctermbg=NONE cterm=NONE + unlet s:t_Co + finish +endif + +if s:t_Co >= 0 + hi Normal term=NONE + hi ColorColumn term=reverse + hi Conceal term=NONE + hi Cursor term=reverse + hi CursorColumn term=NONE + hi CursorLine term=underline + hi CursorLineNr term=bold + hi DiffAdd term=reverse + hi DiffChange term=NONE + hi DiffDelete term=reverse + hi DiffText term=reverse + hi Directory term=NONE + hi EndOfBuffer term=NONE + hi ErrorMsg term=bold,reverse + hi FoldColumn term=NONE + hi Folded term=NONE + hi IncSearch term=bold,reverse,underline + hi LineNr term=NONE + hi MatchParen term=bold,underline + hi ModeMsg term=bold + hi MoreMsg term=NONE + hi NonText term=NONE + hi Pmenu term=reverse + hi PmenuSbar term=reverse + hi PmenuSel term=bold + hi PmenuThumb term=NONE + hi Question term=standout + hi Search term=reverse + hi SignColumn term=reverse + hi SpecialKey term=bold + hi SpellBad term=underline + hi SpellCap term=underline + hi SpellLocal term=underline + hi SpellRare term=underline + hi StatusLine term=bold,reverse + hi StatusLineNC term=bold,underline + hi TabLine term=bold,underline + hi TabLineFill term=NONE + hi Terminal term=NONE + hi TabLineSel term=bold,reverse + hi Title term=NONE + hi VertSplit term=NONE + hi Visual term=reverse + hi VisualNOS term=NONE + hi WarningMsg term=standout + hi WildMenu term=bold + hi CursorIM term=NONE + hi ToolbarLine term=reverse + hi ToolbarButton term=bold,reverse + hi CurSearch term=reverse + hi CursorLineFold term=underline + hi CursorLineSign term=underline + hi Comment term=bold + hi Constant term=NONE + hi Error term=bold,reverse + hi Identifier term=NONE + hi Ignore term=NONE + hi PreProc term=NONE + hi Special term=NONE + hi Statement term=NONE + hi Todo term=bold,reverse + hi Type term=NONE + hi Underlined term=underline + unlet s:t_Co + finish +endif + +" Background: dark +" Color: color00 #282923 235 black +" Color: color08 #74705d 244 darkgray +" Color: color01 #c61e5c 125 darkred +" Color: color09 #f92672 197 red +" Color: color02 #81af24 106 darkgreen +" Color: color10 #a6e22e 112 green +" Color: color03 #fd971f 208 darkyellow +" Color: color11 #e6db74 185 yellow +" Color: color04 #51aebe 73 darkblue +" Color: color12 #66d9ef 81 blue +" Color: color05 #ae81ff 141 darkmagenta +" Color: color13 #fd5ff0 207 magenta +" Color: color06 #80beb5 73 darkcyan +" Color: color14 #a1efe4 116 cyan +" Color: color07 #bababa 250 gray +" Color: color15 #f8f8f2 255 white +" Color: colorLine #3a392f 237 darkgrey +" Color: colorB #585858 240 darkgrey +" Color: colorF #414141 238 darkgrey +" Color: colorNonT #8a8a8a 245 darkgrey +" Color: colorC #ffaf5f 215 red +" Color: colorlC #5fff00 82 green +" Color: colorV #1f3f5f 109 cyan +" Color: colorMP #fd971f 208 darkyellow +" Color: diffAdd #5faf5f 71 darkgreen +" Color: diffDelete #af5f5f 131 darkred +" Color: diffChange #5f87af 67 darkblue +" Color: diffText #af87af 139 darkmagenta +" Color: black #000000 16 black +" Color: white #dadada 253 white +" Color: Added #5fd75f 77 darkgreen +" Color: Changed #ffaf5f 215 darkyellow +" Color: Removed #d75f5f 167 darkred +" Term colors: color00 color01 color02 color03 color04 color05 color06 color07 +" Term colors: color08 color09 color10 color11 color12 color13 color14 color15 +" vim: et ts=8 sw=2 sts=2 diff --git a/runtime/colors/vim.lua b/runtime/colors/vim.lua index 5b9309ab38..dd59e5075d 100644 --- a/runtime/colors/vim.lua +++ b/runtime/colors/vim.lua @@ -60,6 +60,7 @@ hi('PmenuMatch', { link = 'Pmenu' }) hi('PmenuMatchSel', { link = 'PmenuSel' }) hi('PmenuExtra', { link = 'Pmenu' }) hi('PmenuExtraSel', { link = 'PmenuSel' }) +hi('ComplMatchIns', {}) hi('Substitute', { link = 'Search' }) hi('Whitespace', { link = 'NonText' }) hi('MsgSeparator', { link = 'StatusLine' }) diff --git a/runtime/colors/wildcharm.vim b/runtime/colors/wildcharm.vim index 47ca5a1408..00ebc16529 100644 --- a/runtime/colors/wildcharm.vim +++ b/runtime/colors/wildcharm.vim @@ -4,7 +4,7 @@ " Maintainer: Maxim Kim <habamax@gmail.com> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2024 Aug 15 +" Last Change: 2025 Jan 07 " Generated by Colortemplate v2.2.3 @@ -21,6 +21,7 @@ hi! link LineNrAbove LineNr hi! link LineNrBelow LineNr hi! link MessageWindow PMenu hi! link PopupNotification Todo +hi! link PopupSelected PmenuSel hi! link CurSearch IncSearch if &background ==# 'dark' if (has('termguicolors') && &termguicolors) || has('gui_running') @@ -194,6 +195,7 @@ if s:t_Co >= 256 hi! link LineNrBelow LineNr hi! link MessageWindow PMenu hi! link PopupNotification Todo + hi! link PopupSelected PmenuSel hi! link CurSearch IncSearch if &background ==# 'dark' hi Normal ctermfg=252 ctermbg=16 cterm=NONE diff --git a/runtime/colors/zaibatsu.vim b/runtime/colors/zaibatsu.vim index 90c6104afe..49047617c4 100644 --- a/runtime/colors/zaibatsu.vim +++ b/runtime/colors/zaibatsu.vim @@ -4,7 +4,7 @@ " Maintainer: Romain Lafourcade <romainlafourcade@gmail.com> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2024 Aug 15 +" Last Change: 2025 Jan 07 " Generated by Colortemplate v2.2.3 @@ -96,6 +96,7 @@ hi! link TabLineFill StatusLineNC hi! link TabLineSel StatusLine hi! link Terminal Normal hi! link lCursor Cursor +hi! link PopupSelected PmenuSel hi! link Boolean Constant hi! link Character Constant hi! link Conditional Statement @@ -201,6 +202,7 @@ if s:t_Co >= 256 hi! link TabLineSel StatusLine hi! link Terminal Normal hi! link lCursor Cursor + hi! link PopupSelected PmenuSel hi! link Boolean Constant hi! link Character Constant hi! link Conditional Statement @@ -309,6 +311,7 @@ if s:t_Co >= 16 hi! link TabLineSel StatusLine hi! link Terminal Normal hi! link lCursor Cursor + hi! link PopupSelected PmenuSel hi! link Boolean Constant hi! link Character Constant hi! link Conditional Statement @@ -417,6 +420,7 @@ if s:t_Co >= 8 hi! link TabLineSel StatusLine hi! link Terminal Normal hi! link lCursor Cursor + hi! link PopupSelected PmenuSel hi! link Boolean Constant hi! link Character Constant hi! link Conditional Statement diff --git a/runtime/colors/zellner.vim b/runtime/colors/zellner.vim index 7781ca9ab8..298ec0f700 100644 --- a/runtime/colors/zellner.vim +++ b/runtime/colors/zellner.vim @@ -4,7 +4,7 @@ " Maintainer: Original maintainer Ron Aaron <ron@ronware.org> " Website: https://github.com/vim/colorschemes " License: Same as Vim -" Last Change: 2024 Aug 15 +" Last Change: 2025 Jan 07 " Generated by Colortemplate v2.2.3 @@ -31,6 +31,7 @@ hi! link CursorLineFold CursorLine hi! link CursorLineSign CursorLine hi! link MessageWindow Pmenu hi! link PopupNotification Todo +hi! link PopupSelected PmenuSel hi Normal guifg=#000000 guibg=#ffffff gui=NONE cterm=NONE hi Folded guifg=#00008b guibg=#d3d3d3 gui=NONE cterm=NONE hi CursorLine guifg=NONE guibg=#e5e5e5 gui=NONE cterm=NONE @@ -106,6 +107,7 @@ if s:t_Co >= 256 hi! link CursorLineSign CursorLine hi! link MessageWindow Pmenu hi! link PopupNotification Todo + hi! link PopupSelected PmenuSel hi Normal ctermfg=16 ctermbg=231 cterm=NONE hi Folded ctermfg=18 ctermbg=252 cterm=NONE hi CursorLine ctermfg=NONE ctermbg=254 cterm=NONE diff --git a/runtime/compiler/bash.vim b/runtime/compiler/bash.vim new file mode 100644 index 0000000000..cbd76ae410 --- /dev/null +++ b/runtime/compiler/bash.vim @@ -0,0 +1,12 @@ +" Vim compiler file +" Compiler: Bash Syntax Checker +" Maintainer: @konfekt +" Last Change: 2024 Dec 27 + +if exists("current_compiler") + finish +endif +let current_compiler = "bash" + +CompilerSet makeprg=bash\ -n +CompilerSet errorformat=%f:\ line\ %l:\ %m diff --git a/runtime/compiler/cppcheck.vim b/runtime/compiler/cppcheck.vim index 4df12d1714..033613c091 100644 --- a/runtime/compiler/cppcheck.vim +++ b/runtime/compiler/cppcheck.vim @@ -1,7 +1,7 @@ " vim compiler file " Compiler: cppcheck (C++ static checker) " Maintainer: Vincent B. (twinside@free.fr) -" Last Change: 2024 Nov 08 by @Konfekt +" Last Change: 2024 Nov 19 by @Konfekt if exists("current_compiler") | finish | endif let current_compiler = "cppcheck" @@ -25,7 +25,7 @@ let &l:makeprg = 'cppcheck --quiet' \ (filereadable('compile_commands.json') ? '--project=compile_commands.json' : \ (!empty(glob('*'..s:slash..'compile_commands.json', 1, 1)) ? '--project='..glob('*'..s:slash..'compile_commands.json', 1, 1)[0] : \ (empty(&path) ? '' : '-I')..join(map(filter(split(&path, ','), 'isdirectory(v:val)'),'shellescape(v:val)'), ' -I'))))) -exe 'CompilerSet makeprg='..escape(&l:makeprg, ' "') +exe 'CompilerSet makeprg='..escape(&l:makeprg, ' \|"') CompilerSet errorformat= \%f:%l:%c:\ %tarning:\ %m, diff --git a/runtime/compiler/eslint.vim b/runtime/compiler/eslint.vim index db7a665991..0414817900 100644 --- a/runtime/compiler/eslint.vim +++ b/runtime/compiler/eslint.vim @@ -1,13 +1,12 @@ " Vim compiler file " Compiler: ESLint for JavaScript " Maintainer: Romain Lafourcade <romainlafourcade@gmail.com> -" Last Change: 2020 August 20 -" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) +" Last Change: 2024 Nov 30 if exists("current_compiler") finish endif let current_compiler = "eslint" -CompilerSet makeprg=npx\ eslint\ --format\ compact -CompilerSet errorformat=%f:\ line\ %l\\,\ col\ %c\\,\ %m,%-G%.%# +CompilerSet makeprg=npx\ eslint\ --format\ stylish +CompilerSet errorformat=%-P%f,\%\\s%#%l:%c\ %#\ %trror\ \ %m,\%\\s%#%l:%c\ %#\ %tarning\ \ %m,\%-Q,\%-G%.%#, diff --git a/runtime/compiler/groff.vim b/runtime/compiler/groff.vim index 640146d6a1..3e9ae0488f 100644 --- a/runtime/compiler/groff.vim +++ b/runtime/compiler/groff.vim @@ -1,7 +1,7 @@ " Vim compiler file " Compiler: Groff " Maintainer: Konfekt -" Last Change: 2024 Sep 8 +" Last Change: 2024 Nov 19 " " Expects output file extension, say `:make html` or `:make pdf`. " Supported devices as of Sept 2024 are: (x)html, pdf, ps, dvi, lj4, lbp ... @@ -30,7 +30,7 @@ execute 'CompilerSet makeprg=groff'..escape( \ ' '..s:groff_compiler_lang().. \ ' -K'..get(b:, 'groff_compiler_encoding', get(g:, 'groff_compiler_encoding', 'utf8')).. \ ' '..get(b:, 'groff_compiler_args', get(g:, 'groff_compiler_args', '')).. - \ ' -mom -T$* -- %:S > %:r:S.$*', ' ') + \ ' -mom -T$* -- %:S > %:r:S.$*', ' \|"') " From Gavin Freeborn's https://github.com/Gavinok/vim-troff under Vim License " https://github.com/Gavinok/vim-troff/blob/91017b1423caa80aba541c997909a4f810edd275/compiler/troff.vim#L39 CompilerSet errorformat=%o:<standard\ input>\ (%f):%l:%m, diff --git a/runtime/compiler/javac.vim b/runtime/compiler/javac.vim index 9bd4cdf270..53cd772ed8 100644 --- a/runtime/compiler/javac.vim +++ b/runtime/compiler/javac.vim @@ -1,7 +1,7 @@ " Vim compiler file " Compiler: Java Development Kit Compiler " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2024 Jun 14 +" Last Change: 2024 Nov 19 (enable local javac_makeprg_params) if exists("current_compiler") finish @@ -11,11 +11,7 @@ let current_compiler = "javac" let s:cpo_save = &cpo set cpo&vim -if exists("g:javac_makeprg_params") - execute $'CompilerSet makeprg=javac\ {escape(g:javac_makeprg_params, ' \|"')}' -else - CompilerSet makeprg=javac -endif +execute $'CompilerSet makeprg=javac\ {escape(get(b:, 'javac_makeprg_params', get(g:, 'javac_makeprg_params', '')), ' \|"')}' CompilerSet errorformat=%E%f:%l:\ error:\ %m, \%W%f:%l:\ warning:\ %m, diff --git a/runtime/compiler/maven.vim b/runtime/compiler/maven.vim index ef8d8a6fb2..72e74e301d 100644 --- a/runtime/compiler/maven.vim +++ b/runtime/compiler/maven.vim @@ -14,7 +14,7 @@ if exists("current_compiler") endif let current_compiler = "maven" -CompilerSet makeprg=mvn\ --batch-mode +execute $'CompilerSet makeprg=mvn\ --batch-mode\ {escape(get(b:, 'maven_makeprg_params', get(g:, 'maven_makeprg_params', '')), ' \|"')}' " Error message for POM CompilerSet errorformat=[FATAL]\ Non-parseable\ POM\ %f:\ %m%\\s%\\+@%.%#line\ %l\\,\ column\ %c%.%#, diff --git a/runtime/compiler/mypy.vim b/runtime/compiler/mypy.vim index 891488626a..907b98b777 100644 --- a/runtime/compiler/mypy.vim +++ b/runtime/compiler/mypy.vim @@ -1,7 +1,7 @@ " Vim compiler file " Compiler: Mypy (Python static checker) " Maintainer: @Konfekt -" Last Change: 2024 Nov 07 +" Last Change: 2024 Nov 19 if exists("current_compiler") | finish | endif let current_compiler = "mypy" @@ -12,7 +12,7 @@ set cpo&vim " CompilerSet makeprg=mypy let &l:makeprg = 'mypy --show-column-numbers ' \ ..get(b:, 'mypy_makeprg_params', get(g:, 'mypy_makeprg_params', '--strict --ignore-missing-imports')) -exe 'CompilerSet makeprg='..escape(&l:makeprg, ' "') +exe 'CompilerSet makeprg='..escape(&l:makeprg, ' \|"') CompilerSet errorformat=%f:%l:%c:\ %t%*[^:]:\ %m let &cpo = s:cpo_save diff --git a/runtime/compiler/pandoc.vim b/runtime/compiler/pandoc.vim index 6c15e104c3..5d90a518c9 100644 --- a/runtime/compiler/pandoc.vim +++ b/runtime/compiler/pandoc.vim @@ -1,7 +1,7 @@ " Vim compiler file " Compiler: Pandoc " Maintainer: Konfekt -" Last Change: 2024 Sep 8 +" Last Change: 2024 Nov 19 " " Expects output file extension, say `:make html` or `:make pdf`. " Passes additional arguments to pandoc, say `:make html --self-contained`. @@ -56,7 +56,7 @@ execute 'CompilerSet makeprg=pandoc'..escape( \ ' '..s:PandocLang().. \ ' --from='..s:PandocFiletype(&filetype).. \ ' '..get(b:, 'pandoc_compiler_args', get(g:, 'pandoc_compiler_args', '')).. - \ ' --output %:r:S.$* -- %:S', ' ') + \ ' --output %:r:S.$* -- %:S', ' \|"') CompilerSet errorformat=\"%f\",\ line\ %l:\ %m let &cpo = s:keepcpo diff --git a/runtime/compiler/powershell.vim b/runtime/compiler/powershell.vim index 821fea4085..3d37d7c847 100644 --- a/runtime/compiler/powershell.vim +++ b/runtime/compiler/powershell.vim @@ -3,8 +3,9 @@ " URL: https://github.com/PProvost/vim-ps1 " Contributors: Enno Nagel " Last Change: 2024 Mar 29 -" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) -" 2024 Apr 05 by The Vim Project (avoid leaving behind g:makeprg) +" 2024 Apr 03 by the Vim Project (removed :CompilerSet definition) +" 2024 Apr 05 by the Vim Project (avoid leaving behind g:makeprg) +" 2024 Nov 19 by the Vim Project (properly escape makeprg setting) if exists("current_compiler") finish @@ -49,7 +50,7 @@ let s:makeprg = g:ps1_makeprg_cmd .. ' %:p:S' " + CategoryInfo : ObjectNotFound: (Write-Ouput:String) [], CommandNotFoundException " + FullyQualifiedErrorId : CommandNotFoundException -execute 'CompilerSet makeprg=' .. escape(s:makeprg, ' ') +execute 'CompilerSet makeprg=' .. escape(s:makeprg, ' \|"') " Showing error in context with underlining. CompilerSet errorformat=%+G+%m diff --git a/runtime/compiler/pylint.vim b/runtime/compiler/pylint.vim index 4c9c23e125..96abf315ab 100644 --- a/runtime/compiler/pylint.vim +++ b/runtime/compiler/pylint.vim @@ -2,6 +2,7 @@ " Compiler: Pylint for Python " Maintainer: Daniel Moch <daniel@danielmoch.com> " Last Change: 2024 Nov 07 by The Vim Project (added params variable) +" 2024 Nov 19 by the Vim Project (properly escape makeprg setting) if exists("current_compiler") | finish | endif let current_compiler = "pylint" @@ -13,7 +14,7 @@ set cpo&vim let &l:makeprg = 'pylint ' . \ '--output-format=text --msg-template="{path}:{line}:{column}:{C}: [{symbol}] {msg}" --reports=no ' . \ get(b:, "pylint_makeprg_params", get(g:, "pylint_makeprg_params", '--jobs=0')) -exe 'CompilerSet makeprg='..escape(&l:makeprg, ' "') +exe 'CompilerSet makeprg='..escape(&l:makeprg, ' \|"') CompilerSet errorformat=%A%f:%l:%c:%t:\ %m,%A%f:%l:\ %m,%A%f:(%l):\ %m,%-Z%p^%.%#,%-G%.%# let &cpo = s:cpo_save diff --git a/runtime/compiler/pytest.vim b/runtime/compiler/pytest.vim new file mode 100644 index 0000000000..7fc189932c --- /dev/null +++ b/runtime/compiler/pytest.vim @@ -0,0 +1,103 @@ +" Vim compiler file +" Compiler: Pytest (Python testing framework) +" Maintainer: @Konfekt and @mgedmin +" Last Change: 2024 Nov 28 + +if exists("current_compiler") | finish | endif +let current_compiler = "pytest" + +let s:cpo_save = &cpo +set cpo&vim + +" CompilerSet makeprg=pytest +if has('unix') + execute $'CompilerSet makeprg=/usr/bin/env\ PYTHONWARNINGS=ignore\ pytest\ {escape(get(b:, 'pytest_makeprg_params', get(g:, 'pytest_makeprg_params', '--tb=short --quiet')), ' \|"')}' +elseif has('win32') + execute $'CompilerSet makeprg=set\ PYTHONWARNINGS=ignore\ &&\ pytest\ {escape(get(b:, 'pytest_makeprg_params', get(g:, 'pytest_makeprg_params', '--tb=short --quiet')), ' \|"')}' +else + CompilerSet makeprg=pytest\ --tb=short\ --quiet + execute $'CompilerSet makeprg=pytest\ {escape(get(b:, 'pytest_makeprg_params', get(g:, 'pytest_makeprg_params', '--tb=short --quiet')), ' \|"')}' +endif + +" Pytest syntax errors {{{2 + +" Reset error format so that sourcing .vimrc again and again doesn't grow it +" without bounds +setlocal errorformat& + +" For the record, the default errorformat is this: +" +" %*[^"]"%f"%*\D%l: %m +" "%f"%*\D%l: %m +" %-G%f:%l: (Each undeclared identifier is reported only once +" %-G%f:%l: for each function it appears in.) +" %-GIn file included from %f:%l:%c: +" %-GIn file included from %f:%l:%c\, +" %-GIn file included from %f:%l:%c +" %-GIn file included from %f:%l +" %-G%*[ ]from %f:%l:%c +" %-G%*[ ]from %f:%l: +" %-G%*[ ]from %f:%l\, +" %-G%*[ ]from %f:%l +" %f:%l:%c:%m +" %f(%l):%m +" %f:%l:%m +" "%f"\, line %l%*\D%c%*[^ ] %m +" %D%*\a[%*\d]: Entering directory %*[`']%f' +" %X%*\a[%*\d]: Leaving directory %*[`']%f' +" %D%*\a: Entering directory %*[`']%f' +" %X%*\a: Leaving directory %*[`']%f' +" %DMaking %*\a in %f +" %f|%l| %m +" +" and sometimes it misfires, so let's fix it up a bit +" (TBH I don't even know what compiler produces filename(lineno) so why even +" have it?) +setlocal errorformat-=%f(%l):%m + +" Sometimes Vim gets confused about ISO-8601 timestamps and thinks they're +" filenames; this is a big hammer that ignores anything filename-like on lines +" that start with at least two spaces, possibly preceded by a number and +" optional punctuation +setlocal errorformat^=%+G%\\d%#%.%\\=\ \ %.%# + +" Similar, but when the entire line starts with a date +setlocal errorformat^=%+G\\d\\d\\d\\d-\\d\\d-\\d\\d\ \\d\\d:\\d\\d%.%# + +" make: *** [Makefile:14: target] Error 1 +setlocal errorformat^=%+Gmake:\ ***\ %.%# + +" FAILED tests.py::test_with_params[YYYY-MM-DD:HH:MM:SS] - Exception: bla bla +setlocal errorformat^=%+GFAILED\ %.%# + +" AssertionError: assert ...YYYY-MM-DD:HH:MM:SS... +setlocal errorformat^=%+GAssertionError:\ %.%# + +" --- /path/to/file:before YYYY-MM-DD HH:MM:SS.ssssss +setlocal errorformat^=---%f:%m + +" +++ /path/to/file:before YYYY-MM-DD HH:MM:SS.ssssss +setlocal errorformat^=+++%f:%m + +" Sometimes pytest prepends an 'E' marker at the beginning of a traceback line +setlocal errorformat+=E\ %#File\ \"%f\"\\,\ line\ %l%.%# + +" Python tracebacks (unittest + doctest output) {{{2 + +" This collapses the entire traceback into just the last file+lineno, +" which is convenient when you want to jump to the line that failed (and not +" the top-level entry point), but it makes it impossible to see the full +" traceback, which sucks. +""setlocal errorformat+= +"" \File\ \"%f\"\\,\ line\ %l%.%#, +"" \%C\ %.%#, +"" \%-A\ \ File\ \"unittest%.py\"\\,\ line\ %.%#, +"" \%-A\ \ File\ \"%f\"\\,\ line\ 0%.%#, +"" \%A\ \ File\ \"%f\"\\,\ line\ %l%.%#, +"" \%Z%[%^\ ]%\\@=%m +setlocal errorformat+=File\ \"%f\"\\,\ line\ %l\\,%#%m + +exe 'CompilerSet errorformat='..escape(&l:errorformat, ' \|"') + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/compiler/ruff.vim b/runtime/compiler/ruff.vim index 11a69740d8..318f4fe5cb 100644 --- a/runtime/compiler/ruff.vim +++ b/runtime/compiler/ruff.vim @@ -2,6 +2,7 @@ " Compiler: Ruff (Python linter) " Maintainer: @pbnj-dragon " Last Change: 2024 Nov 07 +" 2024 Nov 19 by the Vim Project (properly escape makeprg setting) if exists("current_compiler") | finish | endif let current_compiler = "ruff" @@ -12,7 +13,7 @@ set cpo&vim " CompilerSet makeprg=ruff let &l:makeprg= 'ruff check --output-format=concise ' \ ..get(b:, 'ruff_makeprg_params', get(g:, 'ruff_makeprg_params', '--preview')) -exe 'CompilerSet makeprg='..escape(&l:makeprg, ' "') +exe 'CompilerSet makeprg='..escape(&l:makeprg, ' \|"') CompilerSet errorformat=%f:%l:%c:\ %m,%f:%l:\ %m,%f:%l:%c\ -\ %m,%f: let &cpo = s:cpo_save diff --git a/runtime/compiler/spotbugs.vim b/runtime/compiler/spotbugs.vim new file mode 100644 index 0000000000..8ed45f8ee0 --- /dev/null +++ b/runtime/compiler/spotbugs.vim @@ -0,0 +1,254 @@ +" Vim compiler file +" Compiler: Spotbugs (Java static checker; needs javac compiled classes) +" Maintainers: @konfekt and @zzzyxwvut +" Last Change: 2024 Dec 20 + +if exists('g:current_compiler') || bufname() !~# '\.java\=$' || wordcount().chars < 9 + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +" Unfortunately Spotbugs does not output absolute paths, so you need to +" pass the directory of the files being checked as `-sourcepath` parameter. +" The regex, auxpath and glob try to include all dependent classes of the +" current buffer. See https://github.com/spotbugs/spotbugs/issues/856 + +" FIXME: When "search()" is used with the "e" flag, it makes no _further_ +" progress after claiming an EOL match (i.e. "\_" or "\n", but not "$"). +" XXX: Omit anonymous class declarations +let s:keywords = '\C\<\%(\.\@1<!class\|@\=interface\|enum\|record\|package\)\%(\s\|$\)' +let s:type_names = '\C\<\%(\.\@1<!class\|@\=interface\|enum\|record\)\s*\(\K\k*\)\>' +" Capture ";" for counting a class file directory (see s:package_dir_heads below) +let s:package_names = '\C\<package\s*\(\K\%(\k*\.\=\)\+;\)' +let s:package = '' + +if has('syntax') && exists('g:syntax_on') && + \ exists('b:current_syntax') && b:current_syntax == 'java' && + \ hlexists('javaClassDecl') && hlexists('javaExternal') + + function! s:GetDeclaredTypeNames() abort + if bufname() =~# '\<\%(module\|package\)-info\.java\=$' + return [expand('%:t:r')] + endif + defer execute('silent! normal! g``') + call cursor(1, 1) + let type_names = [] + let lnum = search(s:keywords, 'eW') + while lnum > 0 + let name_attr = synIDattr(synID(lnum, (col('.') - 1), 0), 'name') + if name_attr ==# 'javaClassDecl' + let tokens = matchlist(getline(lnum)..getline(lnum + 1), s:type_names) + if !empty(tokens) | call add(type_names, tokens[1]) | endif + elseif name_attr ==# 'javaExternal' + let tokens = matchlist(getline(lnum)..getline(lnum + 1), s:package_names) + if !empty(tokens) | let s:package = tokens[1] | endif + endif + let lnum = search(s:keywords, 'eW') + endwhile + return type_names + endfunction + +else + function! s:GetDeclaredTypeNames() abort + if bufname() =~# '\<\%(module\|package\)-info\.java\=$' + return [expand('%:t:r')] + endif + " Undo the unsetting of &hls, see below + if &hls + defer execute('set hls') + endif + " Possibly restore the current values for registers '"' and "y", see below + defer call('setreg', ['"', getreg('"'), getregtype('"')]) + defer call('setreg', ['y', getreg('y'), getregtype('y')]) + defer execute('silent bwipeout') + " Copy buffer contents for modification + silent %y y + new + " Apply ":help scratch-buffer" effects and match "$" in Java (generated) + " type names (see s:type_names) + setlocal iskeyword+=$ buftype=nofile bufhidden=hide noswapfile nohls + 0put y + " Discard text blocks and strings + silent keeppatterns %s/\\\@<!"""\_.\{-}\\\@<!"""\|\\"//ge + silent keeppatterns %s/".*"//ge + " Discard comments + silent keeppatterns %s/\/\/.\+$//ge + silent keeppatterns %s/\/\*\_.\{-}\*\///ge + call cursor(1, 1) + let type_names = [] + let lnum = search(s:keywords, 'eW') + while lnum > 0 + let line = getline(lnum) + if line =~# '\<package\>' + let tokens = matchlist(line..getline(lnum + 1), s:package_names) + if !empty(tokens) | let s:package = tokens[1] | endif + else + let tokens = matchlist(line..getline(lnum + 1), s:type_names) + if !empty(tokens) | call add(type_names, tokens[1]) | endif + endif + let lnum = search(s:keywords, 'eW') + endwhile + return type_names + endfunction +endif + +if has('win32') + + function! s:GlobClassFiles(src_type_name) abort + return glob(a:src_type_name..'$*.class', 1, 1) + endfunction + +else + function! s:GlobClassFiles(src_type_name) abort + return glob(a:src_type_name..'\$*.class', 1, 1) + endfunction +endif + +if exists('b:spotbugs_properties') + " Let "ftplugin/java.vim" merge global entries, if any, in buffer-local + " entries + + function! s:GetProperty(name, default) abort + return get(b:spotbugs_properties, a:name, a:default) + endfunction + +elseif exists('g:spotbugs_properties') + + function! s:GetProperty(name, default) abort + return get(g:spotbugs_properties, a:name, a:default) + endfunction + +else + function! s:GetProperty(dummy, default) abort + return a:default + endfunction +endif + +if (exists('g:spotbugs_properties') || exists('b:spotbugs_properties')) && + \ ((!empty(s:GetProperty('sourceDirPath', [])) && + \ !empty(s:GetProperty('classDirPath', []))) || + \ (!empty(s:GetProperty('testSourceDirPath', [])) && + \ !empty(s:GetProperty('testClassDirPath', [])))) + + function! s:CommonIdxsAndDirs() abort + let src_dir_path = s:GetProperty('sourceDirPath', []) + let bin_dir_path = s:GetProperty('classDirPath', []) + let test_src_dir_path = s:GetProperty('testSourceDirPath', []) + let test_bin_dir_path = s:GetProperty('testClassDirPath', []) + let dir_cnt = min([len(src_dir_path), len(bin_dir_path)]) + let test_dir_cnt = min([len(test_src_dir_path), len(test_bin_dir_path)]) + " Do not break up path pairs with filtering! + return [[range(dir_cnt), + \ src_dir_path[0 : dir_cnt - 1], + \ bin_dir_path[0 : dir_cnt - 1]], + \ [range(test_dir_cnt), + \ test_src_dir_path[0 : test_dir_cnt - 1], + \ test_bin_dir_path[0 : test_dir_cnt - 1]]] + endfunction + + let s:common_idxs_and_dirs = s:CommonIdxsAndDirs() + delfunction s:CommonIdxsAndDirs + + function! s:FindClassFiles(src_type_name) abort + let class_files = [] + " Match pairwise the components of source and class pathnames + for [idxs, src_dirs, bin_dirs] in s:common_idxs_and_dirs + " Do not use "fnamemodify(a:src_type_name, ':p:s?src?bin?')" because + " only the rightmost "src" is looked for + for idx in idxs + let tail_idx = strridx(a:src_type_name, src_dirs[idx]) + " No such directory or no such inner type (i.e. without "$") + if tail_idx < 0 | continue | endif + " Substitute "bin_dirs[idx]" for the rightmost "src_dirs[idx]" + let candidate_type_name = strpart(a:src_type_name, 0, tail_idx).. + \ bin_dirs[idx].. + \ strpart(a:src_type_name, (tail_idx + strlen(src_dirs[idx]))) + for candidate in insert(s:GlobClassFiles(candidate_type_name), + \ candidate_type_name..'.class') + if filereadable(candidate) | call add(class_files, shellescape(candidate)) | endif + endfor + if !empty(class_files) | break | endif + endfor + if !empty(class_files) | break | endif + endfor + return class_files + endfunction + +else + function! s:FindClassFiles(src_type_name) abort + let class_files = [] + for candidate in insert(s:GlobClassFiles(a:src_type_name), + \ a:src_type_name..'.class') + if filereadable(candidate) | call add(class_files, shellescape(candidate)) | endif + endfor + return class_files + endfunction +endif + +if exists('g:spotbugs_alternative_path') && + \ !empty(get(g:spotbugs_alternative_path, 'fromPath', '')) && + \ !empty(get(g:spotbugs_alternative_path, 'toPath', '')) + + " See https://github.com/spotbugs/spotbugs/issues/909 + function! s:ResolveAbsolutePathname() abort + let pathname = expand('%:p') + let head_idx = stridx(pathname, g:spotbugs_alternative_path.toPath) + " No such file: a mismatched path request for a project + if head_idx < 0 | return pathname | endif + " Settle for failure with file readability tests _in s:FindClassFiles()_ + return strpart(pathname, 0, head_idx).. + \ g:spotbugs_alternative_path.fromPath.. + \ strpart(pathname, (head_idx + strlen(g:spotbugs_alternative_path.toPath))) + endfunction + +else + function! s:ResolveAbsolutePathname() abort + return expand('%:p') + endfunction +endif + +function! s:CollectClassFiles() abort + " Possibly obtain a symlinked path for an unsupported directory name + let pathname = s:ResolveAbsolutePathname() + " Get a platform-independent pathname prefix, cf. "expand('%:p:h')..'/'" + let tail_idx = strridx(pathname, expand('%:t')) + let src_pathname = strpart(pathname, 0, tail_idx) + let all_class_files = [] + " Get all type names in the current buffer and let the filename globbing + " discover inner type names from arbitrary type names + for type_name in s:GetDeclaredTypeNames() + call extend(all_class_files, s:FindClassFiles(src_pathname..type_name)) + endfor + return all_class_files +endfunction + +" Expose class files for removal etc. +let b:spotbugs_class_files = s:CollectClassFiles() +let s:package_dir_heads = repeat(':h', (1 + strlen(substitute(s:package, '[^.;]', '', 'g')))) +let s:package_root_dir = fnamemodify(s:ResolveAbsolutePathname(), s:package_dir_heads..':S') +let g:current_compiler = 'spotbugs' +" CompilerSet makeprg=spotbugs +let &l:makeprg = 'spotbugs'..(has('win32') ? '.bat' : '')..' '.. + \ get(b:, 'spotbugs_makeprg_params', get(g:, 'spotbugs_makeprg_params', '-workHard -experimental')).. + \ ' -textui -emacs -auxclasspath '..s:package_root_dir..' -sourcepath '..s:package_root_dir..' '.. + \ join(b:spotbugs_class_files, ' ') +" Emacs expects doubled line numbers +setlocal errorformat=%f:%l:%*[0-9]\ %m,%f:-%*[0-9]:-%*[0-9]\ %m + +" " This compiler is meant to be used for a single buffer only +" exe 'CompilerSet makeprg='..escape(&l:makeprg, ' \|"') +" exe 'CompilerSet errorformat='..escape(&l:errorformat, ' \|"') + +delfunction s:CollectClassFiles +delfunction s:ResolveAbsolutePathname +delfunction s:FindClassFiles +delfunction s:GetProperty +delfunction s:GlobClassFiles +delfunction s:GetDeclaredTypeNames +let &cpo = s:cpo_save +unlet! s:package_root_dir s:package_dir_heads s:common_idxs_and_dirs s:package +unlet! s:package_names s:type_names s:keywords s:cpo_save + +" vim: set foldmethod=syntax shiftwidth=2 expandtab: diff --git a/runtime/compiler/tex.vim b/runtime/compiler/tex.vim index 282b3a0588..bc1623729a 100644 --- a/runtime/compiler/tex.vim +++ b/runtime/compiler/tex.vim @@ -3,8 +3,9 @@ " Maintainer: Artem Chuprina <ran@ran.pp.ru> " Contributors: Enno Nagel " Last Change: 2024 Mar 29 -" 2024 Apr 03 by The Vim Project (removed :CompilerSet definition) -" 2024 Apr 05 by The Vim Project (avoid leaving behind g:makeprg) +" 2024 Apr 03 by the Vim Project (removed :CompilerSet definition) +" 2024 Apr 05 by the Vim Project (avoid leaving behind g:makeprg) +" 2024 Nov 19 by the Vim Project (properly escape makeprg setting) if exists("current_compiler") finish @@ -27,7 +28,7 @@ if exists('b:tex_ignore_makefile') || exists('g:tex_ignore_makefile') || let current_compiler = "latex" endif let s:makeprg=current_compiler .. ' -interaction=nonstopmode' - execute 'CompilerSet makeprg=' .. escape(s:makeprg, ' ') + execute 'CompilerSet makeprg=' .. escape(s:makeprg, ' \|"') else let current_compiler = 'make' endif diff --git a/runtime/compiler/typst.vim b/runtime/compiler/typst.vim index 33e55818e9..13699f4675 100644 --- a/runtime/compiler/typst.vim +++ b/runtime/compiler/typst.vim @@ -1,7 +1,8 @@ " Vim compiler file " Language: Typst -" Maintainer: Gregory Anders -" Last Change: 2024-07-14 +" Previous Maintainer: Gregory Anders +" Maintainer: Luca Saccarola <github.e41mv@aleeas.com> +" Last Change: 2024 Dec 09 " Based on: https://github.com/kaarmu/typst.vim if exists('current_compiler') diff --git a/runtime/doc/api.txt b/runtime/doc/api.txt index c5dabeb551..92f5a261ee 100644 --- a/runtime/doc/api.txt +++ b/runtime/doc/api.txt @@ -406,18 +406,9 @@ Another use case are plugins that show output in an append-only buffer, and want to add highlights to the outputs. Highlight data cannot be preserved on writing and loading a buffer to file, nor in undo/redo cycles. -Highlights are registered using the |nvim_buf_add_highlight()| function. If an -external highlighter plugin wants to add many highlights in a batch, -performance can be improved by calling |nvim_buf_add_highlight()| as an -asynchronous notification, after first (synchronously) requesting a source id. - -|nvim_buf_add_highlight()| adds highlights as |extmarks|. If highlights need to -be tracked or manipulated after adding them, it is better to use -|nvim_buf_set_extmark()| directly, as this function returns the placed |extmark| -id. Thus, instead of >lua - vim.api.nvim_buf_add_highlight(buf, ns_id, hl_group, line, col_start, col_end) -< -use >lua +Highlights are registered using the |nvim_buf_set_extmark()| function, which +adds highlights as |extmarks|. If highlights need to be tracked or manipulated +after adding them, the returned |extmark| id can be used. >lua -- create the highlight through an extmark extid = vim.api.nvim_buf_set_extmark(buf, ns_id, line, col_start, {end_col = col_end, hl_group = hl_group}) @@ -428,32 +419,10 @@ use >lua vim.api.nvim_buf_set_extmark(buf, ns_id, NEW_LINE, col_start, {end_col = col_end, hl_group = NEW_HL_GROUP, id = extid}) < -Example using the Python API client (|pynvim|): ->python - src = vim.new_highlight_source() - buf = vim.current.buffer - for i in range(5): - buf.add_highlight("String",i,0,-1,src_id=src) - # some time later ... - buf.clear_namespace(src) -< -If the highlights don't need to be deleted or updated, just pass -1 as -src_id (this is the default in python). Use |nvim_buf_clear_namespace()| to -clear highlights from a specific source, in a specific line range or the -entire buffer by passing in the line range 0, -1 (the latter is the default in -python as used above). - -Example using the API from Vimscript: >vim - - call nvim_buf_set_lines(0, 0, 0, v:true, ["test text"]) - let src = nvim_buf_add_highlight(0, 0, "String", 1, 0, 4) - call nvim_buf_add_highlight(0, src, "Identifier", 0, 5, -1) - " some time later ... - call nvim_buf_clear_namespace(0, src, 0, -1) - +See also |vim.hl.range()|. ============================================================================== -Floating windows *api-floatwin* +Floating windows *api-floatwin* *floating-windows* Floating windows ("floats") are displayed on top of normal windows. This is useful to implement simple widgets, such as tooltips displayed next to the @@ -654,35 +623,22 @@ nvim_del_var({name}) *nvim_del_var()* • {name} Variable name nvim_echo({chunks}, {history}, {opts}) *nvim_echo()* - Echo a message. + Prints a message given by a list of `[text, hl_group]` "chunks". + + Example: >lua + vim.api.nvim_echo({ { 'chunk1-line1\nchunk1-line2\n' }, { 'chunk2-line1' } }, true, {}) +< Parameters: ~ - • {chunks} A list of `[text, hl_group]` arrays, each representing a - text chunk with specified highlight group name or ID. - `hl_group` element can be omitted for no highlight. + • {chunks} List of `[text, hl_group]` pairs, where each is a `text` + string highlighted by the (optional) name or ID `hl_group`. • {history} if true, add to |message-history|. • {opts} Optional parameters. - • verbose: Message is printed as a result of 'verbose' - option. If Nvim was invoked with -V3log_file, the message - will be redirected to the log_file and suppressed from - direct output. - -nvim_err_write({str}) *nvim_err_write()* - Writes a message to the Vim error buffer. Does not append "\n", the - message is buffered (won't display) until a linefeed is written. - - Parameters: ~ - • {str} Message - -nvim_err_writeln({str}) *nvim_err_writeln()* - Writes a message to the Vim error buffer. Appends "\n", so the buffer is - flushed (and displayed). - - Parameters: ~ - • {str} Message - - See also: ~ - • nvim_err_write() + • err: Treat the message like `:echoerr`. Sets `hl_group` + to |hl-ErrorMsg| by default. + • verbose: Message is controlled by the 'verbose' option. + Nvim invoked with `-V3log` will write the message to the + "log" file instead of standard output. nvim_eval_statusline({str}, {opts}) *nvim_eval_statusline()* Evaluates statusline string. @@ -716,7 +672,10 @@ nvim_eval_statusline({str}, {opts}) *nvim_eval_statusline()* true. Each element of the array is a |Dict| with these keys: • start: (number) Byte index (0-based) of first character that uses the highlight. - • group: (string) Name of highlight group. + • group: (string) Name of highlight group. May be removed in the + future, use `groups` instead. + • groups: (array) Names of stacked highlight groups (highest + priority last). nvim_exec_lua({code}, {args}) *nvim_exec_lua()* Execute Lua code. Parameters (if any) are available as `...` inside the @@ -775,6 +734,8 @@ nvim_get_api_info() *nvim_get_api_info()* nvim_get_chan_info({chan}) *nvim_get_chan_info()* Gets information about a channel. + See |nvim_list_uis()| for an example of how to get channel info. + Parameters: ~ • {chan} channel_id, or 0 for current channel @@ -796,7 +757,7 @@ nvim_get_chan_info({chan}) *nvim_get_chan_info()* present if a pty is used (e.g. for conpty on Windows). • "buffer" (optional) Buffer connected to |terminal| instance. • "client" (optional) Info about the peer (client on the other end of - the RPC channel), which it provided via |nvim_set_client_info()|. + the channel), as set by |nvim_set_client_info()|. nvim_get_color_by_name({name}) *nvim_get_color_by_name()* Returns the 24-bit RGB value of a |nvim_get_color_map()| color name or @@ -1079,6 +1040,12 @@ nvim_list_tabpages() *nvim_list_tabpages()* nvim_list_uis() *nvim_list_uis()* Gets a list of dictionaries representing attached UIs. + Example: The Nvim builtin |TUI| sets its channel info as described in + |startup-tui|. In particular, it sets `client.name` to "nvim-tui". So you + can check if the TUI is running by inspecting the client name of each UI: >lua + vim.print(vim.api.nvim_get_chan_info(vim.api.nvim_list_uis()[1].chan).client.name) +< + Return: ~ Array of UI dictionaries, each with these keys: • "height" Requested height of the UI @@ -1099,22 +1066,11 @@ nvim_load_context({dict}) *nvim_load_context()* Parameters: ~ • {dict} |Context| map. -nvim_notify({msg}, {log_level}, {opts}) *nvim_notify()* - Notify the user with a message - - Relays the call to vim.notify . By default forwards your message in the - echo area but can be overridden to trigger desktop notifications. - - Parameters: ~ - • {msg} Message to display to the user - • {log_level} The log level - • {opts} Reserved for future use. - nvim_open_term({buffer}, {opts}) *nvim_open_term()* Open a terminal instance in a buffer By default (and currently the only option) the terminal will not be - connected to an external process. Instead, input send on the channel will + connected to an external process. Instead, input sent on the channel will be echoed directly by the terminal. This is useful to display ANSI terminal sequences returned as part of a rpc message, or similar. @@ -1125,6 +1081,17 @@ nvim_open_term({buffer}, {opts}) *nvim_open_term()* |nvim_chan_send()| can be called immediately to process sequences in a virtual terminal having the intended size. + Example: this `TermHl` command can be used to display and highlight raw + ANSI termcodes, so you can use Nvim as a "scrollback pager" (for terminals + like kitty): *ansi-colorize* *terminal-scrollback-pager* >lua + vim.api.nvim_create_user_command('TermHl', function() + local b = vim.api.nvim_create_buf(false, true) + local chan = vim.api.nvim_open_term(b, {}) + vim.api.nvim_chan_send(chan, table.concat(vim.api.nvim_buf_get_lines(0, 0, -1, false), '\n')) + vim.api.nvim_win_set_buf(0, b) + end, { desc = 'Highlights ANSI termcodes in curbuf' }) +< + Attributes: ~ not allowed when |textlock| is active @@ -1143,13 +1110,6 @@ nvim_open_term({buffer}, {opts}) *nvim_open_term()* Return: ~ Channel id, or 0 on error -nvim_out_write({str}) *nvim_out_write()* - Writes a message to the Vim output buffer. Does not append "\n", the - message is buffered (won't display) until a linefeed is written. - - Parameters: ~ - • {str} Message - nvim_paste({data}, {crlf}, {phase}) *nvim_paste()* Pastes at cursor (in any mode), and sets "redo" so dot (|.|) will repeat the input. UIs call this to implement "paste", but it's also intended for @@ -1248,25 +1208,23 @@ nvim_select_popupmenu_item({item}, {insert}, {finish}, {opts}) *nvim_set_client_info()* nvim_set_client_info({name}, {version}, {type}, {methods}, {attributes}) - Self-identifies the client. + Self-identifies the client. Sets the `client` object returned by + |nvim_get_chan_info()|. - The client/plugin/application should call this after connecting, to - provide hints about its identity and purpose, for debugging and - orchestration. + Clients should call this just after connecting, to provide hints for + debugging and orchestration. (Note: Something is better than nothing! + Fields are optional, but at least set `name`.) Can be called more than once; the caller should merge old info if appropriate. Example: library first identifies the channel, then a plugin using that library later identifies itself. - Note: ~ - • "Something is better than nothing". You don't need to include all the - fields. - Attributes: ~ |RPC| only Parameters: ~ - • {name} Short name for the connected client + • {name} Client short-name. Sets the `client.name` field of + |nvim_get_chan_info()|. • {version} Dict describing the version, with these (optional) keys: • "major" major version (defaults to 0 if not set, for no release yet) @@ -1636,11 +1594,9 @@ nvim_command({command}) *nvim_command()* On execution error: fails with Vimscript error, updates v:errmsg. - Prefer using |nvim_cmd()| or |nvim_exec2()| over this. To evaluate - multiple lines of Vim script or an Ex command directly, use - |nvim_exec2()|. To construct an Ex command using a structured format and - then execute it, use |nvim_cmd()|. To modify an Ex command before - evaluating it, use |nvim_parse_cmd()| in conjunction with |nvim_cmd()|. + Prefer |nvim_cmd()| or |nvim_exec2()| instead. To modify an Ex command in + a structured way before executing it, modify the result of + |nvim_parse_cmd()| then pass it to |nvim_cmd()|. Parameters: ~ • {command} Ex command string @@ -2161,12 +2117,12 @@ nvim_buf_call({buffer}, {fun}) *nvim_buf_call()* This temporarily switches current buffer to "buffer". If the current window already shows "buffer", the window is not switched. If a window inside the current tabpage (including a float) already shows the buffer, - then one of these windows will be set as current window temporarily. + then one of those windows will be set as current window temporarily. Otherwise a temporary scratch window (called the "autocmd window" for historical reasons) will be used. This is useful e.g. to call Vimscript functions that only work with the - current buffer/window currently, like |termopen()|. + current buffer/window currently, like `jobstart(…, {'term': v:true})`. Attributes: ~ Lua |vim.api| only @@ -2507,42 +2463,6 @@ nvim_buf_set_var({buffer}, {name}, {value}) *nvim_buf_set_var()* ============================================================================== Extmark Functions *api-extmark* - *nvim_buf_add_highlight()* -nvim_buf_add_highlight({buffer}, {ns_id}, {hl_group}, {line}, {col_start}, - {col_end}) - Adds a highlight to buffer. - - Useful for plugins that dynamically generate highlights to a buffer (like - a semantic highlighter or linter). The function adds a single highlight to - a buffer. Unlike |matchaddpos()| highlights follow changes to line - numbering (as lines are inserted/removed above the highlighted line), like - signs and marks do. - - Namespaces are used for batch deletion/updating of a set of highlights. To - create a namespace, use |nvim_create_namespace()| which returns a - namespace id. Pass it in to this function as `ns_id` to add highlights to - the namespace. All highlights in the same namespace can then be cleared - with single call to |nvim_buf_clear_namespace()|. If the highlight never - will be deleted by an API call, pass `ns_id = -1`. - - As a shorthand, `ns_id = 0` can be used to create a new namespace for the - highlight, the allocated id is then returned. If `hl_group` is the empty - string no highlight is added, but a new `ns_id` is still returned. This is - supported for backwards compatibility, new code should use - |nvim_create_namespace()| to create a new empty namespace. - - Parameters: ~ - • {buffer} Buffer handle, or 0 for current buffer - • {ns_id} namespace to use or -1 for ungrouped highlight - • {hl_group} Name of the highlight group to use - • {line} Line to highlight (zero-indexed) - • {col_start} Start of (byte-indexed) column range to highlight - • {col_end} End of (byte-indexed) column range to highlight, or -1 to - highlight to end of line - - Return: ~ - The ns_id that was used - *nvim_buf_clear_namespace()* nvim_buf_clear_namespace({buffer}, {ns_id}, {line_start}, {line_end}) Clears |namespace|d objects (highlights, |extmarks|, virtual text) from a @@ -2676,6 +2596,8 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {opts}) and below highlight groups can be supplied either as a string or as an integer, the latter of which can be obtained using |nvim_get_hl_id_by_name()|. + Multiple highlight groups can be stacked by passing an + array (highest priority last). • hl_eol : when true, for a multiline highlight covering the EOL of a line, continue the highlight for the rest of the screen line (just like for diff and cursorline highlight). @@ -2687,6 +2609,13 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {line}, {col}, {opts}) last). • virt_text_pos : position of virtual text. Possible values: • "eol": right after eol character (default). + • "eol_right_align": display right aligned in the window + unless the virtual text is longer than the space + available. If the virtual text is too long, it is + truncated to fit in the window after the EOL character. + If the line is wrapped, the virtual text is shown after + the end of the line rather than the previous screen + line. • "overlay": display over the specified column, without shifting the underlying text. • "right_align": display right aligned in the window. @@ -2780,7 +2709,7 @@ nvim_create_namespace({name}) *nvim_create_namespace()* Creates a new namespace or gets an existing one. *namespace* Namespaces are used for buffer highlights and virtual text, see - |nvim_buf_add_highlight()| and |nvim_buf_set_extmark()|. + |nvim_buf_set_extmark()|. Namespaces can be named or anonymous. If `name` matches an existing namespace, the associated id is returned. If `name` is an empty string a @@ -2838,8 +2767,8 @@ nvim_set_decoration_provider({ns_id}, {opts}) • on_start: called first on each screen redraw > ["start", tick] < - • on_buf: called for each buffer being redrawn (before window - callbacks) > + • on_buf: called for each buffer being redrawn (once per + edit, before window callbacks) > ["buf", bufnr, tick] < • on_win: called when starting to redraw a specific window. > @@ -3166,11 +3095,13 @@ nvim_open_win({buffer}, {enter}, {config}) *nvim_open_win()* • {config} Map defining the window configuration. Keys: • relative: Sets the window layout to "floating", placed at (row,col) coordinates relative to: - • "editor" The global editor grid + • "cursor" Cursor position in current window. + • "editor" The global editor grid. + • "laststatus" 'laststatus' if present, or last row. + • "mouse" Mouse position. + • "tabline" Tabline if present, or first row. • "win" Window given by the `win` field, or current window. - • "cursor" Cursor position in current window. - • "mouse" Mouse position • win: |window-ID| window to split, or relative window when creating a float (relative="win"). • anchor: Decides which corner of the float to place at @@ -3483,9 +3414,9 @@ nvim_create_autocmd({event}, {opts}) *nvim_create_autocmd()* • event: (string) name of the triggered event |autocmd-events| • group: (number|nil) autocommand group id, if any - • match: (string) expanded value of <amatch> - • buf: (number) expanded value of <abuf> - • file: (string) expanded value of <afile> + • file: (string) <afile> (not expanded to a full path) + • match: (string) <amatch> (expanded to a full path) + • buf: (number) <abuf> • data: (any) arbitrary data passed from |nvim_exec_autocmds()| *event-data* • command (string) optional: Vim command to execute on event. @@ -3580,33 +3511,35 @@ nvim_get_autocmds({opts}) *nvim_get_autocmds()* Parameters: ~ • {opts} Dict with at least one of the following: - • group (string|integer): the autocommand group name or id to - match against. - • event (string|array): event or events to match against + • buffer: (integer) Buffer number or list of buffer numbers + for buffer local autocommands |autocmd-buflocal|. Cannot be + used with {pattern} + • event: (string|table) event or events to match against |autocmd-events|. - • pattern (string|array): pattern or patterns to match against + • id: (integer) Autocommand ID to match. + • group: (string|table) the autocommand group name or id to + match against. + • pattern: (string|table) pattern or patterns to match against |autocmd-pattern|. Cannot be used with {buffer} - • buffer: Buffer number or list of buffer numbers for buffer - local autocommands |autocmd-buflocal|. Cannot be used with - {pattern} Return: ~ Array of autocommands matching the criteria, with each item containing the following fields: - • id (number): the autocommand id (only when defined with the API). - • group (integer): the autocommand group id. - • group_name (string): the autocommand group name. - • desc (string): the autocommand description. - • event (string): the autocommand event. - • command (string): the autocommand command. Note: this will be empty + • buffer: (integer) the buffer number. + • buflocal: (boolean) true if the autocommand is buffer local. + • command: (string) the autocommand command. Note: this will be empty if a callback is set. - • callback (function|string|nil): Lua function or name of a Vim script - function which is executed when this autocommand is triggered. - • once (boolean): whether the autocommand is only run once. - • pattern (string): the autocommand pattern. If the autocommand is + • callback: (function|string|nil): Lua function or name of a Vim + script function which is executed when this autocommand is + triggered. + • desc: (string) the autocommand description. + • event: (string) the autocommand event. + • id: (integer) the autocommand id (only when defined with the API). + • group: (integer) the autocommand group id. + • group_name: (string) the autocommand group name. + • once: (boolean) whether the autocommand is only run once. + • pattern: (string) the autocommand pattern. If the autocommand is buffer local |autocmd-buffer-local|: - • buflocal (boolean): true if the autocommand is buffer local. - • buffer (number): the buffer number. ============================================================================== diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt index 998ef5e9f3..c094281154 100644 --- a/runtime/doc/autocmd.txt +++ b/runtime/doc/autocmd.txt @@ -463,6 +463,10 @@ CompleteDone After Insert mode completion is done. Either |v:completed_item| gives the completed item. Sets these |v:event| keys: + complete_word The word that was + selected, empty if + abandoned complete. + complete_type |complete_info_mode| reason Reason for completion being done. Can be one of: - "accept": completion was diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index ada3b7103c..96574e2899 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -1174,16 +1174,22 @@ complete_info([{what}]) *complete_info()* See |complete_info_mode| for the values. pum_visible |TRUE| if popup menu is visible. See |pumvisible()|. - items List of completion matches. Each item is a - dictionary containing the entries "word", + items List of all completion candidates. Each item + is a dictionary containing the entries "word", "abbr", "menu", "kind", "info" and "user_data". See |complete-items|. + matches Same as "items", but only returns items that + are matching current query. If both "matches" + and "items" are in "what", the returned list + will still be named "items", but each item + will have an additional "match" field. selected Selected item index. First index is zero. Index is -1 if no item is selected (showing typed text only, or the last completion after no item is selected when using the <Up> or <Down> keys) - inserted Inserted string. [NOT IMPLEMENTED YET] + completed Return a dictionary containing the entries of + the currently selected index item. preview_winid Info floating preview window id. preview_bufnr Info floating preview buffer id. @@ -1305,10 +1311,10 @@ copy({expr}) *copy()* Also see |deepcopy()|. Parameters: ~ - • {expr} (`any`) + • {expr} (`T`) Return: ~ - (`any`) + (`T`) cos({expr}) *cos()* Return the cosine of {expr}, measured in radians, as a |Float|. @@ -1407,7 +1413,7 @@ ctxset({context} [, {index}]) *ctxset()* • {index} (`integer?`) Return: ~ - (`any`) + (`integer`) ctxsize() *ctxsize()* Returns the size of the |context-stack|. @@ -1490,11 +1496,11 @@ deepcopy({expr} [, {noref}]) *deepcopy()* *E69 Also see |copy()|. Parameters: ~ - • {expr} (`any`) + • {expr} (`T`) • {noref} (`boolean?`) Return: ~ - (`any`) + (`T`) delete({fname} [, {flags}]) *delete()* Without {flags} or with {flags} empty: Deletes the file by the @@ -1618,7 +1624,7 @@ did_filetype() *did_filetype()* file. Return: ~ - (`any`) + (`integer`) diff_filler({lnum}) *diff_filler()* Returns the number of filler lines above line {lnum}. @@ -1633,7 +1639,7 @@ diff_filler({lnum}) *diff_filler()* • {lnum} (`integer`) Return: ~ - (`any`) + (`integer`) diff_hlID({lnum}, {col}) *diff_hlID()* Returns the highlight ID for diff mode at line {lnum} column @@ -1674,7 +1680,7 @@ digraph_get({chars}) *digraph_get()* *E121 • {chars} (`string`) Return: ~ - (`any`) + (`string`) digraph_getlist([{listall}]) *digraph_getlist()* Return a list of digraphs. If the {listall} argument is given @@ -1695,7 +1701,7 @@ digraph_getlist([{listall}]) *digraph_getlist()* • {listall} (`boolean?`) Return: ~ - (`any`) + (`string[][]`) digraph_set({chars}, {digraph}) *digraph_set()* Add digraph {chars} to the list. {chars} must be a string @@ -1756,7 +1762,7 @@ empty({expr}) *empty()* • {expr} (`any`) Return: ~ - (`any`) + (`integer`) environ() *environ()* Return all of environment variables as dictionary. You can @@ -1783,7 +1789,7 @@ escape({string}, {chars}) *escape()* • {chars} (`string`) Return: ~ - (`any`) + (`string`) eval({string}) *eval()* Evaluate {string} and return the result. Especially useful to @@ -2666,7 +2672,7 @@ foreach({expr1}, {expr2}) *foreach()* • {expr2} (`string|function`) Return: ~ - (`any`) + (`string|table`) fullcommand({name}) *fullcommand()* Get the full command name from a short abbreviated command @@ -2997,10 +3003,10 @@ getbufline({buf}, {lnum} [, {end}]) *getbufline()* Parameters: ~ • {buf} (`integer|string`) • {lnum} (`integer`) - • {end_} (`integer?`) + • {end} (`integer?`) Return: ~ - (`any`) + (`string[]`) getbufoneline({buf}, {lnum}) *getbufoneline()* Just like `getbufline()` but only get one line and return it @@ -3073,14 +3079,16 @@ getchangelist([{buf}]) *getchangelist()* Return: ~ (`table[]`) -getchar([{expr}]) *getchar()* +getchar([{expr} [, {opts}]]) *getchar()* Get a single character from the user or input stream. - If {expr} is omitted, wait until a character is available. + If {expr} is omitted or is -1, wait until a character is + available. If {expr} is 0, only get a character when one is available. Return zero otherwise. If {expr} is 1, only check if a character is available, it is not consumed. Return zero if no character available. - If you prefer always getting a string use |getcharstr()|. + If you prefer always getting a string use |getcharstr()|, or + specify |FALSE| as "number" in {opts}. Without {expr} and when {expr} is 0 a whole character or special key is returned. If it is a single character, the @@ -3090,7 +3098,8 @@ getchar([{expr}]) *getchar()* starting with 0x80 (decimal: 128). This is the same value as the String "\<Key>", e.g., "\<Left>". The returned value is also a String when a modifier (shift, control, alt) was used - that is not included in the character. + that is not included in the character. |keytrans()| can also + be used to convert a returned String into a readable form. When {expr} is 0 and Esc is typed, there will be a short delay while Vim waits to see if this is the start of an escape @@ -3102,6 +3111,32 @@ getchar([{expr}]) *getchar()* Use getcharmod() to obtain any additional modifiers. + The optional argument {opts} is a Dict and supports the + following items: + + cursor A String specifying cursor behavior + when waiting for a character. + "hide": hide the cursor. + "keep": keep current cursor unchanged. + "msg": move cursor to message area. + (default: automagically decide + between "keep" and "msg") + + number If |TRUE|, return a Number when getting + a single character. + If |FALSE|, the return value is always + converted to a String, and an empty + String (instead of 0) is returned when + no character is available. + (default: |TRUE|) + + simplify If |TRUE|, include modifiers in the + character if possible. E.g., return + the same value for CTRL-I and <Tab>. + If |FALSE|, don't include modifiers in + the character. + (default: |TRUE|) + When the user clicks a mouse button, the mouse event will be returned. The position can then be found in |v:mouse_col|, |v:mouse_lnum|, |v:mouse_winid| and |v:mouse_win|. @@ -3139,10 +3174,11 @@ getchar([{expr}]) *getchar()* < Parameters: ~ - • {expr} (`0|1?`) + • {expr} (`-1|0|1?`) + • {opts} (`table?`) Return: ~ - (`integer`) + (`integer|string`) getcharmod() *getcharmod()* The result is a Number which is the state of the modifiers for @@ -3207,19 +3243,12 @@ getcharsearch() *getcharsearch()* (`table`) getcharstr([{expr}]) *getcharstr()* - Get a single character from the user or input stream as a - string. - If {expr} is omitted, wait until a character is available. - If {expr} is 0 or false, only get a character when one is - available. Return an empty string otherwise. - If {expr} is 1 or true, only check if a character is - available, it is not consumed. Return an empty string - if no character is available. - Otherwise this works like |getchar()|, except that a number - result is converted to a string. + The same as |getchar()|, except that this always returns a + String, and "number" isn't allowed in {opts}. Parameters: ~ - • {expr} (`0|1?`) + • {expr} (`-1|0|1?`) + • {opts} (`table?`) Return: ~ (`string`) @@ -3295,7 +3324,7 @@ getcmdscreenpos() *getcmdscreenpos()* |setcmdline()|. Return: ~ - (`any`) + (`integer`) getcmdtype() *getcmdtype()* Return the current command-line type. Possible return values @@ -3635,7 +3664,7 @@ getline({lnum} [, {end}]) *getline()* Parameters: ~ • {lnum} (`integer|string`) - • {end_} (`nil|false?`) + • {end} (`nil|false?`) Return: ~ (`string`) @@ -4177,6 +4206,21 @@ getscriptinfo([{opts}]) *getscriptinfo()* Return: ~ (`vim.fn.getscriptinfo.ret[]`) +getstacktrace() *getstacktrace()* + Returns the current stack trace of Vim scripts. + Stack trace is a |List|, of which each item is a |Dictionary| + with the following items: + funcref The funcref if the stack is at a function, + otherwise this item is omitted. + event The string of the event description if the + stack is at an autocmd event, otherwise this + item is omitted. + lnum The line number in the script on the stack. + filepath The file path of the script on the stack. + + Return: ~ + (`table[]`) + gettabinfo([{tabnr}]) *gettabinfo()* If {tabnr} is not specified, then information about all the tab pages is returned as a |List|. Each List item is a @@ -4299,7 +4343,7 @@ gettext({text}) *gettext()* • {text} (`string`) Return: ~ - (`any`) + (`string`) getwininfo([{winid}]) *getwininfo()* Returns information about windows as a |List| with Dictionaries. @@ -4315,6 +4359,8 @@ getwininfo([{winid}]) *getwininfo()* botline last complete displayed buffer line bufnr number of buffer in the window height window height (excluding winbar) + leftcol first column displayed; only used when + 'wrap' is off loclist 1 if showing a location list quickfix 1 if quickfix or location list window terminal 1 if a terminal window @@ -4465,7 +4511,7 @@ glob2regpat({string}) *glob2regpat()* • {string} (`string`) Return: ~ - (`any`) + (`string`) globpath({path}, {expr} [, {nosuf} [, {list} [, {allinks}]]]) *globpath()* Perform glob() for String {expr} on all directories in {path} @@ -4822,7 +4868,7 @@ iconv({string}, {from}, {to}) *iconv()* • {to} (`string`) Return: ~ - (`any`) + (`string`) id({expr}) *id()* Returns a |String| which is a unique identifier of the @@ -4845,7 +4891,7 @@ id({expr}) *id()* • {expr} (`any`) Return: ~ - (`any`) + (`string`) indent({lnum}) *indent()* The result is a Number, which is indent of line {lnum} in the @@ -4895,7 +4941,7 @@ index({object}, {expr} [, {start} [, {ic}]]) *index()* • {ic} (`boolean?`) Return: ~ - (`any`) + (`integer`) indexof({object}, {expr} [, {opts}]) *indexof()* Returns the index of an item in {object} where {expr} is @@ -4942,7 +4988,7 @@ indexof({object}, {expr} [, {opts}]) *indexof()* • {opts} (`table?`) Return: ~ - (`any`) + (`integer`) input({prompt} [, {text} [, {completion}]]) *input()* @@ -4952,7 +4998,7 @@ input({prompt} [, {text} [, {completion}]]) *input()* • {completion} (`string?`) Return: ~ - (`any`) + (`string`) input({opts}) The result is a String, which is whatever the user typed on @@ -5069,7 +5115,7 @@ input({opts}) • {opts} (`table`) Return: ~ - (`any`) + (`string`) inputlist({textlist}) *inputlist()* {textlist} must be a |List| of strings. This |List| is @@ -5101,7 +5147,7 @@ inputrestore() *inputrestore()* Returns TRUE when there is nothing to restore, FALSE otherwise. Return: ~ - (`any`) + (`integer`) inputsave() *inputsave()* Preserve typeahead (also from mappings) and clear it, so that @@ -5112,7 +5158,7 @@ inputsave() *inputsave()* Returns TRUE when out of memory, FALSE otherwise. Return: ~ - (`any`) + (`integer`) inputsecret({prompt} [, {text}]) *inputsecret()* This function acts much like the |input()| function with but @@ -5130,7 +5176,7 @@ inputsecret({prompt} [, {text}]) *inputsecret()* • {text} (`string?`) Return: ~ - (`any`) + (`string`) insert({object}, {item} [, {idx}]) *insert()* When {object} is a |List| or a |Blob| insert {item} at the start @@ -5181,10 +5227,10 @@ invert({expr}) *invert()* < Parameters: ~ - • {expr} (`number`) + • {expr} (`integer`) Return: ~ - (`any`) + (`integer`) isabsolutepath({path}) *isabsolutepath()* The result is a Number, which is |TRUE| when {path} is an @@ -5279,7 +5325,7 @@ items({dict}) *items()* the index. Parameters: ~ - • {dict} (`any`) + • {dict} (`table`) Return: ~ (`any`) @@ -5307,7 +5353,7 @@ jobresize({job}, {width}, {height}) *jobresize()* (`any`) jobstart({cmd} [, {opts}]) *jobstart()* - Note: Prefer |vim.system()| in Lua (unless using the `pty` option). + Note: Prefer |vim.system()| in Lua (unless using `rpc`, `pty`, or `term`). Spawns {cmd} as a job. If {cmd} is a List it runs directly (no 'shell'). @@ -5315,8 +5361,11 @@ jobstart({cmd} [, {opts}]) *jobstart()* call jobstart(split(&shell) + split(&shellcmdflag) + ['{cmd}']) < (See |shell-unquoting| for details.) - Example: >vim - call jobstart('nvim -h', {'on_stdout':{j,d,e->append(line('.'),d)}}) + Example: start a job and handle its output: >vim + call jobstart(['nvim', '-h'], {'on_stdout':{j,d,e->append(line('.'),d)}}) +< + Example: start a job in a |terminal| connected to the current buffer: >vim + call jobstart(['nvim', '-h'], {'term':v:true}) < Returns |job-id| on success, 0 on invalid arguments (or job table is full), -1 if {cmd}[0] or 'shell' is not executable. @@ -5381,6 +5430,10 @@ jobstart({cmd} [, {opts}]) *jobstart()* stdin: (string) Either "pipe" (default) to connect the job's stdin to a channel or "null" to disconnect stdin. + term: (boolean) Spawns {cmd} in a new pseudo-terminal session + connected to the current (unmodified) buffer. Implies "pty". + Default "height" and "width" are set to the current window + dimensions. |jobstart()|. Defaults $TERM to "xterm-256color". width: (number) Width of the `pty` terminal. {opts} is passed as |self| dictionary to the callback; the @@ -5397,7 +5450,7 @@ jobstart({cmd} [, {opts}]) *jobstart()* • {opts} (`table?`) Return: ~ - (`any`) + (`integer`) jobstop({id}) *jobstop()* Stop |job-id| {id} by sending SIGTERM to the job process. If @@ -5413,7 +5466,7 @@ jobstop({id}) *jobstop()* • {id} (`integer`) Return: ~ - (`any`) + (`integer`) jobwait({jobs} [, {timeout}]) *jobwait()* Waits for jobs and their |on_exit| handlers to complete. @@ -5441,7 +5494,7 @@ jobwait({jobs} [, {timeout}]) *jobwait()* • {timeout} (`integer?`) Return: ~ - (`any`) + (`integer[]`) join({list} [, {sep}]) *join()* Join the items in {list} together into one String. @@ -5459,7 +5512,7 @@ join({list} [, {sep}]) *join()* • {sep} (`string?`) Return: ~ - (`any`) + (`string`) json_decode({expr}) *json_decode()* Convert {expr} from JSON object. Accepts |readfile()|-style @@ -5498,7 +5551,7 @@ json_encode({expr}) *json_encode()* • {expr} (`any`) Return: ~ - (`any`) + (`string`) keys({dict}) *keys()* Return a |List| with all the keys of {dict}. The |List| is in @@ -5508,7 +5561,7 @@ keys({dict}) *keys()* • {dict} (`table`) Return: ~ - (`any`) + (`string[]`) keytrans({string}) *keytrans()* Turn the internal byte representation of keys into a form that @@ -5521,7 +5574,7 @@ keytrans({string}) *keytrans()* • {string} (`string`) Return: ~ - (`any`) + (`string`) len({expr}) *len()* *E701* The result is a Number, which is the length of the argument. @@ -5535,10 +5588,10 @@ len({expr}) *len()* *E70 Otherwise an error is given and returns zero. Parameters: ~ - • {expr} (`any`) + • {expr} (`any[]`) Return: ~ - (`any`) + (`integer`) libcall({libname}, {funcname}, {argument}) *libcall()* *E364* *E368* Call function {funcname} in the run-time library {libname} @@ -5664,7 +5717,7 @@ lispindent({lnum}) *lispindent()* • {lnum} (`integer`) Return: ~ - (`any`) + (`integer`) list2blob({list}) *list2blob()* Return a Blob concatenating all the number values in {list}. @@ -5680,7 +5733,7 @@ list2blob({list}) *list2blob()* • {list} (`any[]`) Return: ~ - (`any`) + (`string`) list2str({list} [, {utf8}]) *list2str()* Convert each number in {list} to a character string can @@ -5703,14 +5756,14 @@ list2str({list} [, {utf8}]) *list2str()* • {utf8} (`boolean?`) Return: ~ - (`any`) + (`string`) localtime() *localtime()* Return the current time, measured as seconds since 1st Jan 1970. See also |strftime()|, |strptime()| and |getftime()|. Return: ~ - (`any`) + (`integer`) log({expr}) *log()* Return the natural logarithm (base e) of {expr} as a |Float|. @@ -5727,7 +5780,7 @@ log({expr}) *log()* • {expr} (`number`) Return: ~ - (`any`) + (`number`) log10({expr}) *log10()* Return the logarithm of Float {expr} to base 10 as a |Float|. @@ -5743,7 +5796,7 @@ log10({expr}) *log10()* • {expr} (`number`) Return: ~ - (`any`) + (`number`) luaeval({expr} [, {expr}]) *luaeval()* Evaluate Lua expression {expr} and return its result converted @@ -6290,7 +6343,7 @@ matchbufline({buf}, {pat}, {lnum}, {end}, [, {dict}]) *matchbufline()* • {buf} (`string|integer`) • {pat} (`string`) • {lnum} (`string|integer`) - • {end_} (`string|integer`) + • {end} (`string|integer`) • {dict} (`table?`) Return: ~ @@ -6565,7 +6618,7 @@ max({expr}) *max()* • {expr} (`any`) Return: ~ - (`any`) + (`number`) menu_get({path} [, {modes}]) *menu_get()* Returns a |List| of |Dictionaries| describing |menus| (defined @@ -6712,7 +6765,7 @@ min({expr}) *min()* • {expr} (`any`) Return: ~ - (`any`) + (`number`) mkdir({name} [, {flags} [, {prot}]]) *mkdir()* *E739* Create directory {name}. @@ -6760,7 +6813,7 @@ mkdir({name} [, {flags} [, {prot}]]) *mkdir()* *E73 • {prot} (`string?`) Return: ~ - (`any`) + (`integer`) mode([{expr}]) *mode()* Return a string that indicates the current mode. @@ -6935,7 +6988,7 @@ nextnonblank({lnum}) *nextnonblank()* • {lnum} (`integer`) Return: ~ - (`any`) + (`integer`) nr2char({expr} [, {utf8}]) *nr2char()* Return a string with a single character, which has the number @@ -6957,7 +7010,7 @@ nr2char({expr} [, {utf8}]) *nr2char()* • {utf8} (`boolean?`) Return: ~ - (`any`) + (`string`) nvim_...({...}) *nvim_...()* *E5555* *eval-api* Call nvim |api| functions. The type checking of arguments will @@ -7014,7 +7067,7 @@ pathshorten({path} [, {len}]) *pathshorten()* • {len} (`integer?`) Return: ~ - (`any`) + (`string`) perleval({expr}) *perleval()* Evaluate |perl| expression {expr} and return its result @@ -7054,7 +7107,7 @@ pow({x}, {y}) *pow()* • {y} (`number`) Return: ~ - (`any`) + (`number`) prevnonblank({lnum}) *prevnonblank()* Return the line number of the first line at or above {lnum} @@ -7069,7 +7122,7 @@ prevnonblank({lnum}) *prevnonblank()* • {lnum} (`integer`) Return: ~ - (`any`) + (`integer`) printf({fmt}, {expr1} ...) *printf()* Return a String with {fmt}, where "%" items are replaced by @@ -7731,11 +7784,11 @@ reduce({object}, {func} [, {initial}]) *reduce()* *E99 Parameters: ~ • {object} (`any`) - • {func} (`function`) + • {func} (`fun(accumulator: T, current: any): any`) • {initial} (`any?`) Return: ~ - (`any`) + (`T`) reg_executing() *reg_executing()* Returns the single letter name of the register being executed. @@ -7784,7 +7837,7 @@ reltime({start}, {end}) Parameters: ~ • {start} (`any?`) - • {end_} (`any?`) + • {end} (`any?`) Return: ~ (`any`) @@ -7845,7 +7898,7 @@ remove({list}, {idx}, {end}) Parameters: ~ • {list} (`any[]`) • {idx} (`integer`) - • {end_} (`integer?`) + • {end} (`integer?`) Return: ~ (`any`) @@ -7867,7 +7920,7 @@ remove({blob}, {idx}, {end}) Parameters: ~ • {blob} (`any`) • {idx} (`integer`) - • {end_} (`integer?`) + • {end} (`integer?`) Return: ~ (`any`) @@ -7899,7 +7952,7 @@ rename({from}, {to}) *rename()* • {to} (`string`) Return: ~ - (`any`) + (`integer`) repeat({expr}, {count}) *repeat()* Repeat {expr} {count} times and return the concatenated @@ -7935,7 +7988,7 @@ resolve({filename}) *resolve()* *E65 • {filename} (`string`) Return: ~ - (`any`) + (`string`) reverse({object}) *reverse()* Reverse the order of items in {object}. {object} can be a @@ -7949,10 +8002,10 @@ reverse({object}) *reverse()* < Parameters: ~ - • {object} (`any`) + • {object} (`T[]`) Return: ~ - (`any`) + (`T[]`) round({expr}) *round()* Round off {expr} to the nearest integral value and return it @@ -7972,7 +8025,7 @@ round({expr}) *round()* • {expr} (`number`) Return: ~ - (`any`) + (`number`) rpcnotify({channel}, {event} [, {args}...]) *rpcnotify()* Sends {event} to {channel} via |RPC| and returns immediately. @@ -7984,10 +8037,10 @@ rpcnotify({channel}, {event} [, {args}...]) *rpcnotify()* Parameters: ~ • {channel} (`integer`) • {event} (`string`) - • {args} (`any?`) + • {...} (`any`) Return: ~ - (`any`) + (`integer`) rpcrequest({channel}, {method} [, {args}...]) *rpcrequest()* Sends a request to {channel} to invoke {method} via @@ -7999,7 +8052,7 @@ rpcrequest({channel}, {method} [, {args}...]) *rpcrequest()* Parameters: ~ • {channel} (`integer`) • {method} (`string`) - • {args} (`any?`) + • {...} (`any`) Return: ~ (`any`) @@ -8031,7 +8084,7 @@ screenattr({row}, {col}) *screenattr()* • {col} (`integer`) Return: ~ - (`any`) + (`integer`) screenchar({row}, {col}) *screenchar()* The result is a Number, which is the character at position @@ -8048,7 +8101,7 @@ screenchar({row}, {col}) *screenchar()* • {col} (`integer`) Return: ~ - (`any`) + (`integer`) screenchars({row}, {col}) *screenchars()* The result is a |List| of Numbers. The first number is the same @@ -8062,7 +8115,7 @@ screenchars({row}, {col}) *screenchars()* • {col} (`integer`) Return: ~ - (`any`) + (`integer[]`) screencol() *screencol()* The result is a Number, which is the current screen column of @@ -8080,7 +8133,7 @@ screencol() *screencol()* < Return: ~ - (`any`) + (`integer[]`) screenpos({winid}, {lnum}, {col}) *screenpos()* The result is a Dict with the screen position of the text @@ -8123,7 +8176,7 @@ screenrow() *screenrow()* Note: Same restrictions as with |screencol()|. Return: ~ - (`any`) + (`integer`) screenstring({row}, {col}) *screenstring()* The result is a String that contains the base character and @@ -8138,7 +8191,7 @@ screenstring({row}, {col}) *screenstring()* • {col} (`integer`) Return: ~ - (`any`) + (`string`) search({pattern} [, {flags} [, {stopline} [, {timeout} [, {skip}]]]]) *search()* Search for regexp pattern {pattern}. The search starts at the @@ -8253,7 +8306,7 @@ search({pattern} [, {flags} [, {stopline} [, {timeout} [, {skip}]]]]) *search()* • {skip} (`string|function?`) Return: ~ - (`any`) + (`integer`) searchcount([{options}]) *searchcount()* Get or update the last search count, like what is displayed @@ -8498,7 +8551,7 @@ searchpair({start}, {middle}, {end} [, {flags} [, {skip} [, {stopline} [, {timeo Parameters: ~ • {start} (`string`) • {middle} (`string`) - • {end_} (`string`) + • {end} (`string`) • {flags} (`string?`) • {skip} (`string|function?`) • {stopline} (`integer?`) @@ -8522,7 +8575,7 @@ searchpairpos({start}, {middle}, {end} [, {flags} [, {skip} [, {stopline} [, {ti Parameters: ~ • {start} (`string`) • {middle} (`string`) - • {end_} (`string`) + • {end} (`string`) • {flags} (`string?`) • {skip} (`string|function?`) • {stopline} (`integer?`) @@ -8565,7 +8618,7 @@ serverlist() *serverlist()* < Return: ~ - (`any`) + (`string[]`) serverstart([{address}]) *serverstart()* Opens a socket or named pipe at {address} and listens for @@ -8605,7 +8658,7 @@ serverstart([{address}]) *serverstart()* • {address} (`string?`) Return: ~ - (`any`) + (`string`) serverstop({address}) *serverstop()* Closes the pipe or socket at {address}. @@ -8617,7 +8670,7 @@ serverstop({address}) *serverstop()* • {address} (`string`) Return: ~ - (`any`) + (`integer`) setbufline({buf}, {lnum}, {text}) *setbufline()* Set line {lnum} to {text} in buffer {buf}. This works like @@ -8650,7 +8703,7 @@ setbufline({buf}, {lnum}, {text}) *setbufline()* • {text} (`string|string[]`) Return: ~ - (`any`) + (`integer`) setbufvar({buf}, {varname}, {val}) *setbufvar()* Set option or local variable {varname} in buffer {buf} to @@ -8770,7 +8823,7 @@ setcmdline({str} [, {pos}]) *setcmdline()* • {pos} (`integer?`) Return: ~ - (`any`) + (`integer`) setcmdpos({pos}) *setcmdpos()* Set the cursor position in the command line to byte position @@ -9102,7 +9155,7 @@ setqflist({list} [, {action} [, {what}]]) *setqflist()* • {what} (`vim.fn.setqflist.what?`) Return: ~ - (`any`) + (`integer`) setreg({regname}, {value} [, {options}]) *setreg()* Set the register {regname} to {value}. @@ -9273,7 +9326,7 @@ sha256({string}) *sha256()* • {string} (`string`) Return: ~ - (`any`) + (`string`) shellescape({string} [, {special}]) *shellescape()* Escape {string} for use as a shell command argument. @@ -9312,7 +9365,7 @@ shellescape({string} [, {special}]) *shellescape()* • {special} (`boolean?`) Return: ~ - (`any`) + (`string`) shiftwidth([{col}]) *shiftwidth()* Returns the effective value of 'shiftwidth'. This is the @@ -9790,7 +9843,7 @@ simplify({filename}) *simplify()* • {filename} (`string`) Return: ~ - (`any`) + (`string`) sin({expr}) *sin()* Return the sine of {expr}, measured in radians, as a |Float|. @@ -9806,7 +9859,7 @@ sin({expr}) *sin()* • {expr} (`number`) Return: ~ - (`any`) + (`number`) sinh({expr}) *sinh()* Return the hyperbolic sine of {expr} as a |Float| in the range @@ -9838,7 +9891,7 @@ slice({expr}, {start} [, {end}]) *slice()* Parameters: ~ • {expr} (`any`) • {start} (`integer`) - • {end_} (`integer?`) + • {end} (`integer?`) Return: ~ (`any`) @@ -9950,12 +10003,12 @@ sort({list} [, {how} [, {dict}]]) *sort()* *E70 < Parameters: ~ - • {list} (`any`) + • {list} (`T[]`) • {how} (`string|function?`) • {dict} (`any?`) Return: ~ - (`any`) + (`T[]`) soundfold({word}) *soundfold()* Return the sound-folded equivalent of {word}. Uses the first @@ -9969,7 +10022,7 @@ soundfold({word}) *soundfold()* • {word} (`string`) Return: ~ - (`any`) + (`string`) spellbadword([{sentence}]) *spellbadword()* Without argument: The result is the badly spelled word under @@ -10028,7 +10081,7 @@ spellsuggest({word} [, {max} [, {capital}]]) *spellsuggest()* • {capital} (`boolean?`) Return: ~ - (`any`) + (`string[]`) split({string} [, {pattern} [, {keepempty}]]) *split()* Make a |List| out of {string}. When {pattern} is omitted or @@ -10061,7 +10114,7 @@ split({string} [, {pattern} [, {keepempty}]]) *split()* • {keepempty} (`boolean?`) Return: ~ - (`any`) + (`string[]`) sqrt({expr}) *sqrt()* Return the non-negative square root of Float {expr} as a @@ -10232,6 +10285,7 @@ str2list({string} [, {utf8}]) *str2list()* and exists only for backwards-compatibility. With UTF-8 composing characters are handled properly: >vim echo str2list("á") " returns [97, 769] +< Parameters: ~ • {string} (`string`) @@ -11165,28 +11219,6 @@ tempname() *tempname()* Return: ~ (`string`) -termopen({cmd} [, {opts}]) *termopen()* - Spawns {cmd} in a new pseudo-terminal session connected - to the current (unmodified) buffer. Parameters and behavior - are the same as |jobstart()| except "pty", "width", "height", - and "TERM" are ignored: "height" and "width" are taken from - the current window. Note that termopen() implies a "pty" arg - to jobstart(), and thus has the implications documented at - |jobstart()|. - - Returns the same values as jobstart(). - - Terminal environment is initialized as in |jobstart-env|, - except $TERM is set to "xterm-256color". Full behavior is - described in |terminal|. - - Parameters: ~ - • {cmd} (`string|string[]`) - • {opts} (`table?`) - - Return: ~ - (`any`) - test_garbagecollect_now() *test_garbagecollect_now()* Like |garbagecollect()|, but executed right away. This must only be called directly to avoid any structure to exist @@ -11646,7 +11678,7 @@ virtcol2col({winid}, {lnum}, {col}) *virtcol2col()* • {col} (`integer`) Return: ~ - (`any`) + (`integer`) visualmode([{expr}]) *visualmode()* The result is a String, which describes the last Visual mode @@ -11670,7 +11702,7 @@ visualmode([{expr}]) *visualmode()* • {expr} (`boolean?`) Return: ~ - (`any`) + (`string`) wait({timeout}, {condition} [, {interval}]) *wait()* Waits until {condition} evaluates to |TRUE|, where {condition} @@ -11812,7 +11844,7 @@ win_id2win({expr}) *win_id2win()* • {expr} (`integer`) Return: ~ - (`any`) + (`integer`) win_move_separator({nr}, {offset}) *win_move_separator()* Move window {nr}'s vertical separator (i.e., the right border) @@ -11989,7 +12021,7 @@ winlayout([{tabnr}]) *winlayout()* • {tabnr} (`integer?`) Return: ~ - (`any`) + (`any[]`) winline() *winline()* The result is a Number, which is the screen line of the cursor @@ -12037,7 +12069,7 @@ winnr([{arg}]) *winnr()* • {arg} (`string|integer?`) Return: ~ - (`any`) + (`integer`) winrestcmd() *winrestcmd()* Returns a sequence of |:resize| commands that should restore @@ -12051,7 +12083,7 @@ winrestcmd() *winrestcmd()* < Return: ~ - (`any`) + (`string`) winrestview({dict}) *winrestview()* Uses the |Dictionary| returned by |winsaveview()| to restore @@ -12123,7 +12155,7 @@ winwidth({nr}) *winwidth()* • {nr} (`integer`) Return: ~ - (`any`) + (`integer`) wordcount() *wordcount()* The result is a dictionary of byte/chars/word statistics for @@ -12213,11 +12245,11 @@ xor({expr}, {expr}) *xor()* < Parameters: ~ - • {expr} (`number`) - • {expr1} (`number`) + • {expr} (`integer`) + • {expr1} (`integer`) Return: ~ - (`any`) + (`integer`) ============================================================================== 2. Matching a pattern in a String *string-match* diff --git a/runtime/doc/change.txt b/runtime/doc/change.txt index 768a449581..9e44f54e6b 100644 --- a/runtime/doc/change.txt +++ b/runtime/doc/change.txt @@ -1443,18 +1443,17 @@ since formatting is highly dependent on the type of file. It makes sense to use an |autoload| script, so the corresponding script is only loaded when actually needed and the script should be called <filetype>format.vim. -For example, the XML filetype plugin distributed with Vim in the $VIMRUNTIME -directory, sets the 'formatexpr' option to: > +For example, the XML filetype plugin distributed with Vim in the +$VIMRUNTIME/ftplugin directory, sets the 'formatexpr' option to: > setlocal formatexpr=xmlformat#Format() That means, you will find the corresponding script, defining the -xmlformat#Format() function, in the directory: -`$VIMRUNTIME/autoload/xmlformat.vim` +xmlformat#Format() function, in the file `$VIMRUNTIME/autoload/xmlformat.vim` Here is an example script that removes trailing whitespace from the selected -text. Put it in your autoload directory, e.g. ~/.vim/autoload/format.vim: > - +text. Put it in your autoload directory, e.g. ~/.vim/autoload/format.vim: +>vim func! format#Format() " only reformat on explicit gq command if mode() != 'n' @@ -1487,7 +1486,7 @@ debugging it helps to set the 'debug' option. *right-justify* There is no command in Vim to right justify text. You can do it with -an external command, like "par" (e.g.: "!}par" to format until the end of the +an external command, like "par" (e.g.: `:.,}!par` to format until the end of the paragraph) or set 'formatprg' to "par". *format-comments* @@ -1553,7 +1552,7 @@ type of comment string. A part consists of: some indent for the start or end part that can be removed. When a string has none of the 'f', 's', 'm' or 'e' flags, Vim assumes the -comment string repeats at the start of each line. The flags field may be +comment string repeats at the start of each line. The {flags} field may be empty. Any blank space in the text before and after the {string} is part of the diff --git a/runtime/doc/channel.txt b/runtime/doc/channel.txt index 7184151cda..c2d220041c 100644 --- a/runtime/doc/channel.txt +++ b/runtime/doc/channel.txt @@ -11,21 +11,20 @@ Nvim asynchronous IO *channel* ============================================================================== 1. Introduction *channel-intro* -Channels are nvim's way of communicating with external processes. +Channels are Nvim's way of communicating with external processes. There are several ways to open a channel: - 1. Through stdin/stdout when `nvim` is started with `--headless`, and a startup - script or --cmd command opens the stdio channel using |stdioopen()|. + 1. Through stdin/stdout when `nvim` is started with `--headless` and a startup + script or `--cmd` command opens the stdio channel using |stdioopen()|. 2. Through stdin, stdout and stderr of a process spawned by |jobstart()|. - 3. Through the PTY master end of a PTY opened with - `jobstart(..., {'pty': v:true})` or |termopen()|. + 3. Through the PTY master end opened with `jobstart(…, {'pty': v:true})`. 4. By connecting to a TCP/IP socket or named pipe with |sockconnect()|. - 5. By another process connecting to a socket listened to by nvim. This only + 5. By another process connecting to a socket listened to by Nvim. This only supports RPC channels, see |rpc-connecting|. Channels support multiple modes or protocols. In the most basic @@ -146,7 +145,7 @@ from the host TTY, or if Nvim is |--headless| it uses default values: >vim :echo system('nvim --headless +"te stty -a" +"sleep 1" +"1,/^$/print" +q') ============================================================================== -3. Communicating using msgpack-rpc *channel-rpc* +3. Communicating with msgpack RPC *channel-rpc* When channels are opened with the `rpc` option set to true, the channel can be used for remote method calls in both directions, see |msgpack-rpc|. Note that diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index 7967e2ce1a..3fc17af185 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -41,15 +41,19 @@ thus you cannot edit beyond that. The command-lines that you enter are remembered in a history table. You can recall them with the up and down cursor keys. There are actually five history tables: + - one for ':' commands - one for search strings - one for expressions - one for input lines, typed for the |input()| function. - one for debug mode commands + These are completely separate. Each history can only be accessed when entering the same type of line. Use the 'history' option to set the number of lines that are remembered. + Notes: + - When you enter a command-line that is exactly the same as an older one, the old one is removed (to avoid repeated commands moving older commands out of the history). @@ -1218,10 +1222,10 @@ Thus you can resize the command-line window, but not others. The |getcmdwintype()| function returns the type of the command-line being edited as described in |cmdwin-char|. -Nvim defines this default CmdWinEnter autocmd in the "nvim_cmdwin" group: > +Nvim defines this default CmdWinEnter autocmd in the "nvim.cmdwin" group: > autocmd CmdWinEnter [:>] syntax sync minlines=1 maxlines=1 < -You can disable this in your config with "autocmd! nvim_cmdwin". |default-autocmds| +You can disable this in your config with "autocmd! nvim.cmdwin". |default-autocmds| AUTOCOMMANDS diff --git a/runtime/doc/backers.txt b/runtime/doc/credits.txt index d0cbd94978..5fe79dfef8 100644 --- a/runtime/doc/backers.txt +++ b/runtime/doc/credits.txt @@ -1,11 +1,118 @@ -*backers.txt* Nvim +*credits.txt* Nvim NVIM REFERENCE MANUAL ============================================================================== -Fundraiser Backers +Credits *credits* + +Most of Vim was written by Bram Moolenaar <Bram@vim.org> |Bram-Moolenaar|. + +Parts of the documentation come from several Vi manuals, written by: + W.N. Joy + Alan P.W. Hewett + Mark Horton + +The Vim editor is based on Stevie and includes (ideas from) other software, +worked on by the people mentioned here. Other people helped by sending me +patches, suggestions and giving feedback about what is good and bad in Vim. + +Vim would never have become what it is now, without the help of these people! + + Ron Aaron Win32 GUI changes + Mohsin Ahmed encryption + Zoltan Arpadffy work on VMS port + Tony Andrews Stevie + Gert van Antwerpen changes for DJGPP on MS-DOS + Berkeley DB(3) ideas for swap file implementation + Keith Bostic Nvi + Walter Briscoe Makefile updates, various patches + Ralf Brown SPAWNO library for MS-DOS + Robert Colon many useful remarks + Marcin Dalecki GTK+ GUI port, toolbar icons, gettext() + Kayhan Demirel sent me news in Uganda + Chris & John Downey xvi (ideas for multi-windows version) + Henk Elbers first VMS port + Daniel Elstner GTK+ 2 port + Eric Fischer Mac port, 'cindent', and other improvements + Benji Fisher Answering lots of user questions + Bill Foster Athena GUI port (later removed) + Google Let Bram work on Vim one day a week + Loic Grenie xvim (ideas for multi windows version) + Sven Guckes Vim promoter and previous WWW page maintainer + Darren Hiebert Exuberant ctags + Jason Hildebrand GTK+ 2 port + Bruce Hunsaker improvements for VMS port + Andy Kahn Cscope support, GTK+ GUI port + Oezguer Kesim Maintainer of Vim Mailing Lists + Axel Kielhorn work on the Macintosh port + Steve Kirkendall Elvis + Roger Knobbe original port to Windows NT + Sergey Laskavy Vim's help from Moscow + Felix von Leitner Previous maintainer of Vim Mailing Lists + David Leonard Port of Python extensions to Unix + Avner Lottem Edit in right-to-left windows + Flemming Madsen X11 client-server, various features and patches + Tony Mechelynck answers many user questions + Paul Moore Python interface extensions, many patches + Katsuhito Nagano Work on multibyte versions + Sung-Hyun Nam Work on multibyte versions + Vince Negri Win32 GUI and generic console enhancements + Steve Oualline Author of the first Vim book |frombook| + Dominique Pelle Valgrind reports and many fixes + A.Politz Many bug reports and some fixes + George V. Reilly Win32 port, Win32 GUI start-off + Stephen Riehm bug collector + Stefan Roemer various patches and help to users + Ralf Schandl IBM OS/390 port + Olaf Seibert DICE and BeBox version, regexp improvements + Mortaza Shiran Farsi patches + Peter da Silva termlib + Paul Slootman OS/2 port + Henry Spencer regular expressions + Dany St-Amant Macintosh port + Tim Thompson Stevie + G. R. (Fred) Walter Stevie + Sven Verdoolaege Perl interface + Robert Webb Command-line completion, GUI versions, and + lots of patches + Ingo Wilken Tcl interface + Mike Williams PostScript printing + Juergen Weigert Lattice version, AUX improvements, Unix and + MS-DOS ports, autoconf + Stefan 'Sec' Zehl Maintainer of vim.org + Yasuhiro Matsumoto many MS-Windows improvements + Ken Takata fixes and features + Kazunobu Kuriyama GTK 3 + Christian Brabandt many fixes, features, user support, etc. + Yegappan Lakshmanan many quickfix features + +I wish to thank all the people that sent me bug reports and suggestions. The +list is too long to mention them all here. Vim would not be the same without +the ideas from all these people: They keep Vim alive! +*love* *peace* *friendship* *gross-national-happiness* + + +Documentation may refer to other versions of Vi: + *Vi* *vi* +Vi "the original". Without further remarks this is the version + of Vi that appeared in Sun OS 4.x. ":version" returns + "Version 3.7, 6/7/85". Source code only available with a license. + *Nvi* +Nvi The "New" Vi. The version of Vi that comes with BSD 4.4 and FreeBSD. + Very good compatibility with the original Vi, with a few extensions. + The version used is 1.79. ":version" returns "Version 1.79 + (10/23/96)". Source code is freely available. + *Elvis* +Elvis Another Vi clone, made by Steve Kirkendall. Very compact but isn't + as flexible as Vim. Source code is freely available. + +Vim Nvim is based on Vim. https://www.vim.org/ + + +============================================================================== +Neovim fundraiser backers *backers.txt* Thank you to everyone who backed the original Neovim Fundraiser. diff --git a/runtime/doc/deprecated.txt b/runtime/doc/deprecated.txt index 5e809ad26c..68258fedb4 100644 --- a/runtime/doc/deprecated.txt +++ b/runtime/doc/deprecated.txt @@ -16,46 +16,57 @@ Deprecated features DEPRECATED IN 0.11 *deprecated-0.11* API -- nvim_subscribe() Plugins must maintain their own "multicast" channels list. -- nvim_unsubscribe() Plugins must maintain their own "multicast" channels list. - -LUA -- vim.region() Use |getregionpos()| instead. -- *vim.highlight* Renamed to |vim.hl|. -- vim.validate(opts: table) Use form 1. See |vim.validate()|. +• nvim_notify() Use |nvim_echo()| or `nvim_exec_lua("vim.notify(...)", ...)` instead. +• nvim_subscribe() Plugins must maintain their own "multicast" channels list. +• nvim_unsubscribe() Plugins must maintain their own "multicast" channels list. +• nvim_out_write() Use |nvim_echo()|. +• nvim_err_write() Use |nvim_echo()| with `{err=true}`. +• nvim_err_writeln() Use |nvim_echo()| with `{err=true}`. +• nvim_buf_add_highlight() Use |vim.hl.range()| or |nvim_buf_set_extmark()| DIAGNOSTICS -- *vim.diagnostic.goto_next()* Use |vim.diagnostic.jump()| with `{count=1, float=true}` instead. -- *vim.diagnostic.goto_prev()* Use |vim.diagnostic.jump()| with `{count=-1, float=true}` instead. -- *vim.diagnostic.get_next_pos()* - Use the "lnum" and "col" fields from the return value of - |vim.diagnostic.get_next()| instead. -- *vim.diagnostic.get_prev_pos()* - Use the "lnum" and "col" fields from the return value of - |vim.diagnostic.get_prev()| instead. -- The "win_id" parameter used by various functions is deprecated in favor of +• *vim.diagnostic.goto_next()* Use |vim.diagnostic.jump()| with `{count=1, float=true}` instead. +• *vim.diagnostic.goto_prev()* Use |vim.diagnostic.jump()| with `{count=-1, float=true}` instead. +• *vim.diagnostic.get_next_pos()* Use the "lnum" and "col" fields from the + return value of |vim.diagnostic.get_next()| instead. +• *vim.diagnostic.get_prev_pos()* Use the "lnum" and "col" fields from the + return value of |vim.diagnostic.get_prev()| instead. +• The "win_id" parameter used by various functions is deprecated in favor of "winid" |winid| -- The "cursor_position" parameter of |vim.diagnostic.JumpOpts| is renamed to - "pos" +• |vim.diagnostic.JumpOpts| renamed its "cursor_position" field to "pos". -TREESITTER -• *TSNode:child_containing_descendant()* Use - |TSNode:child_with_descendant()| instead; it is identical except that it can - return the descendant itself. +HIGHLIGHTS +• *TermCursorNC* Unfocused |terminal| windows do not have a cursor. LSP • *vim.lsp.util.jump_to_location* Use |vim.lsp.util.show_document()| with - `{focus=true}` instead. + `{focus=true}` instead. • *vim.lsp.buf.execute_command* Use |Client:exec_cmd()| instead. • *vim.lsp.buf.completion* Use |vim.lsp.completion.trigger()| instead. • vim.lsp.buf_request_all The `error` key has been renamed to `err` inside the result parameter of the handler. • *vim.lsp.with()* Pass configuration to equivalent - functions in `vim.lsp.buf.*'. -• |vim.lsp.handlers| - No longer support client to server response handlers. Only server to - client requests/notification handlers are supported. + functions in `vim.lsp.buf.*`. +• |vim.lsp.handlers| Does not support client-to-server response handlers. Only + supports server-to-client requests/notification handlers. • *vim.lsp.handlers.signature_help()* Use |vim.lsp.buf.signature_help()| instead. +• `client.request()` Use |Client:request()| instead. +• `client.request_sync()` Use |Client:request_sync()| instead. +• `client.notify()` Use |Client:notify()| instead. +• `client.cancel_request()` Use |Client:cancel_request()| instead. +• `client.stop()` Use |Client:stop()| instead. +• `client.is_stopped()` Use |Client:is_stopped()| instead. +• `client.supports_method()` Use |Client:supports_method()| instead. +• `client.on_attach()` Use |Client:on_attach()| instead. +• `vim.lsp.start_client()` Use |vim.lsp.start()| instead. + +LUA +• vim.region() Use |getregionpos()| instead. +• *vim.highlight* Renamed to |vim.hl|. +• vim.validate(opts: table) Use form 1. See |vim.validate()|. + +VIMSCRIPT +• *termopen()* Use |jobstart() with `{term: v:true}`. ------------------------------------------------------------------------------ DEPRECATED IN 0.10 *deprecated-0.10* @@ -117,198 +128,198 @@ TREESITTER DEPRECATED IN 0.9 *deprecated-0.9* API -- *nvim_get_hl_by_name()* Use |nvim_get_hl()| instead. -- *nvim_get_hl_by_id()* Use |nvim_get_hl()| instead. +• *nvim_get_hl_by_name()* Use |nvim_get_hl()| instead. +• *nvim_get_hl_by_id()* Use |nvim_get_hl()| instead. TREESITTER -- *vim.treesitter.language.require_language()* Use |vim.treesitter.language.add()| instead. -- *vim.treesitter.get_node_at_pos()* Use |vim.treesitter.get_node()| instead. -- *vim.treesitter.get_node_at_cursor()* Use |vim.treesitter.get_node()| +• *vim.treesitter.language.require_language()* Use |vim.treesitter.language.add()| instead. +• *vim.treesitter.get_node_at_pos()* Use |vim.treesitter.get_node()| instead. +• *vim.treesitter.get_node_at_cursor()* Use |vim.treesitter.get_node()| and |TSNode:type()| instead. • The following top level Treesitter functions have been moved: - - *vim.treesitter.inspect_language()* -> |vim.treesitter.language.inspect()| - - *vim.treesitter.get_query_files()* -> |vim.treesitter.query.get_files()| - - *vim.treesitter.set_query()* -> |vim.treesitter.query.set()| - - *vim.treesitter.query.set_query()* -> |vim.treesitter.query.set()| - - *vim.treesitter.get_query()* -> |vim.treesitter.query.get()| - - *vim.treesitter.query.get_query()* -> |vim.treesitter.query.get()| - - *vim.treesitter.parse_query()* -> |vim.treesitter.query.parse()| - - *vim.treesitter.query.parse_query()* -> |vim.treesitter.query.parse()| - - *vim.treesitter.add_predicate()* -> |vim.treesitter.query.add_predicate()| - - *vim.treesitter.add_directive()* -> |vim.treesitter.query.add_directive()| - - *vim.treesitter.list_predicates()* -> |vim.treesitter.query.list_predicates()| - - *vim.treesitter.list_directives()* -> |vim.treesitter.query.list_directives()| - - *vim.treesitter.query.get_range()* -> |vim.treesitter.get_range()| - - *vim.treesitter.query.get_node_text()* -> |vim.treesitter.get_node_text()| + • *vim.treesitter.inspect_language()* -> |vim.treesitter.language.inspect()| + • *vim.treesitter.get_query_files()* -> |vim.treesitter.query.get_files()| + • *vim.treesitter.set_query()* -> |vim.treesitter.query.set()| + • *vim.treesitter.query.set_query()* -> |vim.treesitter.query.set()| + • *vim.treesitter.get_query()* -> |vim.treesitter.query.get()| + • *vim.treesitter.query.get_query()* -> |vim.treesitter.query.get()| + • *vim.treesitter.parse_query()* -> |vim.treesitter.query.parse()| + • *vim.treesitter.query.parse_query()* -> |vim.treesitter.query.parse()| + • *vim.treesitter.add_predicate()* -> |vim.treesitter.query.add_predicate()| + • *vim.treesitter.add_directive()* -> |vim.treesitter.query.add_directive()| + • *vim.treesitter.list_predicates()* -> |vim.treesitter.query.list_predicates()| + • *vim.treesitter.list_directives()* -> |vim.treesitter.query.list_directives()| + • *vim.treesitter.query.get_range()* -> |vim.treesitter.get_range()| + • *vim.treesitter.query.get_node_text()* -> |vim.treesitter.get_node_text()| LUA - - *nvim_exec()* Use |nvim_exec2()| instead. - - *vim.pretty_print()* Use |vim.print()| instead. + • *nvim_exec()* Use |nvim_exec2()| instead. + • *vim.pretty_print()* Use |vim.print()| instead. ------------------------------------------------------------------------------ DEPRECATED IN 0.8 OR EARLIER API -- *nvim_buf_clear_highlight()* Use |nvim_buf_clear_namespace()| instead. -- *nvim_buf_set_virtual_text()* Use |nvim_buf_set_extmark()| instead. -- *nvim_command_output()* Use |nvim_exec2()| instead. -- *nvim_execute_lua()* Use |nvim_exec_lua()| instead. -- *nvim_get_option_info()* Use |nvim_get_option_info2()| instead. +• *nvim_buf_clear_highlight()* Use |nvim_buf_clear_namespace()| instead. +• *nvim_buf_set_virtual_text()* Use |nvim_buf_set_extmark()| instead. +• *nvim_command_output()* Use |nvim_exec2()| instead. +• *nvim_execute_lua()* Use |nvim_exec_lua()| instead. +• *nvim_get_option_info()* Use |nvim_get_option_info2()| instead. COMMANDS -- *:rv* *:rviminfo* Deprecated alias to |:rshada| command. -- *:wv* *:wviminfo* Deprecated alias to |:wshada| command. +• *:rv* *:rviminfo* Deprecated alias to |:rshada| command. +• *:wv* *:wviminfo* Deprecated alias to |:wshada| command. ENVIRONMENT VARIABLES -- *$NVIM_LISTEN_ADDRESS* - - Deprecated way to: - - set the server name (use |--listen| or |serverstart()| instead) - - get the server name (use |v:servername| instead) - - detect a parent Nvim (use |$NVIM| instead) - - Ignored if --listen is given. - - Unset by |terminal| and |jobstart()| unless explicitly given by the "env" +• *$NVIM_LISTEN_ADDRESS* + • Deprecated way to: + • set the server name (use |--listen| or |serverstart()| instead) + • get the server name (use |v:servername| instead) + • detect a parent Nvim (use |$NVIM| instead) + • Ignored if --listen is given. + • Unset by |terminal| and |jobstart()| unless explicitly given by the "env" option. Example: >vim call jobstart(['foo'], { 'env': { 'NVIM_LISTEN_ADDRESS': v:servername } }) < EVENTS -- *BufCreate* Use |BufAdd| instead. -- *EncodingChanged* Never fired; 'encoding' is always "utf-8". -- *FileEncoding* Never fired; equivalent to |EncodingChanged|. -- *GUIEnter* Never fired; use |UIEnter| instead. -- *GUIFailed* Never fired. +• *BufCreate* Use |BufAdd| instead. +• *EncodingChanged* Never fired; 'encoding' is always "utf-8". +• *FileEncoding* Never fired; equivalent to |EncodingChanged|. +• *GUIEnter* Never fired; use |UIEnter| instead. +• *GUIFailed* Never fired. KEYCODES -- *<MouseDown>* Use <ScrollWheelUp> instead. -- *<MouseUp>* Use <ScrollWheelDown> instead. - -FUNCTIONS -- *buffer_exists()* Obsolete name for |bufexists()|. -- *buffer_name()* Obsolete name for |bufname()|. -- *buffer_number()* Obsolete name for |bufnr()|. -- *file_readable()* Obsolete name for |filereadable()|. -- *highlight_exists()* Obsolete name for |hlexists()|. -- *highlightID()* Obsolete name for |hlID()|. -- *inputdialog()* Use |input()| instead. -- *jobclose()* Obsolete name for |chanclose()| -- *jobsend()* Obsolete name for |chansend()| -- *last_buffer_nr()* Obsolete name for bufnr("$"). -- *rpcstart()* Use |jobstart()| with `{'rpc': v:true}` instead. -- *rpcstop()* Use |jobstop()| instead to stop any job, or - `chanclose(id, "rpc")` to close RPC communication - without stopping the job. Use chanclose(id) to close - any socket. +• *<MouseDown>* Use <ScrollWheelUp> instead. +• *<MouseUp>* Use <ScrollWheelDown> instead. HIGHLIGHTS -- *hl-VertSplit* Use |hl-WinSeparator| instead. +• *hl-VertSplit* Use |hl-WinSeparator| instead. LSP DIAGNOSTICS For each of the functions below, use the corresponding function in |vim.diagnostic| instead (unless otherwise noted). For example, use |vim.diagnostic.get()| instead of |vim.lsp.diagnostic.get()|. -- *vim.lsp.diagnostic.clear()* Use |vim.diagnostic.hide()| instead. -- *vim.lsp.diagnostic.disable()* Use |vim.diagnostic.enable()| instead. -- *vim.lsp.diagnostic.display()* Use |vim.diagnostic.show()| instead. -- *vim.lsp.diagnostic.enable()* -- *vim.lsp.diagnostic.get()* -- *vim.lsp.diagnostic.get_all()* Use |vim.diagnostic.get()| instead. -- *vim.lsp.diagnostic.get_count()* Use |vim.diagnostic.count()| instead. -- *vim.lsp.diagnostic.get_line_diagnostics()* Use |vim.diagnostic.get()| instead. -- *vim.lsp.diagnostic.get_next()* -- *vim.lsp.diagnostic.get_next_pos()* -- *vim.lsp.diagnostic.get_prev()* -- *vim.lsp.diagnostic.get_prev_pos()* -- *vim.lsp.diagnostic.get_virtual_text_chunks_for_line()* No replacement. Use +• *vim.lsp.diagnostic.clear()* Use |vim.diagnostic.hide()| instead. +• *vim.lsp.diagnostic.disable()* Use |vim.diagnostic.enable()| instead. +• *vim.lsp.diagnostic.display()* Use |vim.diagnostic.show()| instead. +• *vim.lsp.diagnostic.enable()* +• *vim.lsp.diagnostic.get()* +• *vim.lsp.diagnostic.get_all()* Use |vim.diagnostic.get()| instead. +• *vim.lsp.diagnostic.get_count()* Use |vim.diagnostic.count()| instead. +• *vim.lsp.diagnostic.get_line_diagnostics()* Use |vim.diagnostic.get()| instead. +• *vim.lsp.diagnostic.get_next()* +• *vim.lsp.diagnostic.get_next_pos()* +• *vim.lsp.diagnostic.get_prev()* +• *vim.lsp.diagnostic.get_prev_pos()* +• *vim.lsp.diagnostic.get_virtual_text_chunks_for_line()* No replacement. Use options provided by |vim.diagnostic.config()| to customize virtual text. -- *vim.lsp.diagnostic.goto_next()* -- *vim.lsp.diagnostic.goto_prev()* -- *vim.lsp.diagnostic.redraw()* Use |vim.diagnostic.show()| instead. -- *vim.lsp.diagnostic.reset()* -- *vim.lsp.diagnostic.save()* Use |vim.diagnostic.set()| instead. -- *vim.lsp.diagnostic.set_loclist()* Use |vim.diagnostic.setloclist()| instead. -- *vim.lsp.diagnostic.set_qflist()* Use |vim.diagnostic.setqflist()| instead. -- *vim.lsp.diagnostic.show_line_diagnostics()* Use |vim.diagnostic.open_float()| instead. -- *vim.lsp.diagnostic.show_position_diagnostics()* Use |vim.diagnostic.open_float()| instead. +• *vim.lsp.diagnostic.goto_next()* +• *vim.lsp.diagnostic.goto_prev()* +• *vim.lsp.diagnostic.redraw()* Use |vim.diagnostic.show()| instead. +• *vim.lsp.diagnostic.reset()* +• *vim.lsp.diagnostic.save()* Use |vim.diagnostic.set()| instead. +• *vim.lsp.diagnostic.set_loclist()* Use |vim.diagnostic.setloclist()| instead. +• *vim.lsp.diagnostic.set_qflist()* Use |vim.diagnostic.setqflist()| instead. +• *vim.lsp.diagnostic.show_line_diagnostics()* Use |vim.diagnostic.open_float()| instead. +• *vim.lsp.diagnostic.show_position_diagnostics()* Use |vim.diagnostic.open_float()| instead. The following are deprecated without replacement. These functions are moved internally and are no longer exposed as part of the API. Instead, use |vim.diagnostic.config()| and |vim.diagnostic.show()|. -- *vim.lsp.diagnostic.set_signs()* -- *vim.lsp.diagnostic.set_underline()* -- *vim.lsp.diagnostic.set_virtual_text()* +• *vim.lsp.diagnostic.set_signs()* +• *vim.lsp.diagnostic.set_underline()* +• *vim.lsp.diagnostic.set_virtual_text()* LSP FUNCTIONS -- *vim.lsp.buf.server_ready()* +• *vim.lsp.buf.server_ready()* Use |LspAttach| instead, depending on your use-case. "Server ready" is not part of the LSP spec, so the Nvim LSP client cannot meaningfully implement it. "Ready" is ambiguous because: - - Language servers may finish analyzing the workspace, but edits can always + • Language servers may finish analyzing the workspace, but edits can always re-trigger analysis/builds. - - Language servers can serve some requests even while processing changes. -- *vim.lsp.buf.range_code_action()* Use |vim.lsp.buf.code_action()| with + • Language servers can serve some requests even while processing changes. +• *vim.lsp.buf.range_code_action()* Use |vim.lsp.buf.code_action()| with the `range` parameter. -- *vim.lsp.util.diagnostics_to_items()* Use |vim.diagnostic.toqflist()| instead. -- *vim.lsp.util.set_qflist()* Use |setqflist()| instead. -- *vim.lsp.util.set_loclist()* Use |setloclist()| instead. -- *vim.lsp.buf_get_clients()* Use |vim.lsp.get_clients()| with +• *vim.lsp.util.diagnostics_to_items()* Use |vim.diagnostic.toqflist()| instead. +• *vim.lsp.util.set_qflist()* Use |setqflist()| instead. +• *vim.lsp.util.set_loclist()* Use |setloclist()| instead. +• *vim.lsp.buf_get_clients()* Use |vim.lsp.get_clients()| with {buffer=bufnr} instead. -- *vim.lsp.buf.formatting()* Use |vim.lsp.buf.format()| with +• *vim.lsp.buf.formatting()* Use |vim.lsp.buf.format()| with {async=true} instead. -- *vim.lsp.buf.formatting_sync()* Use |vim.lsp.buf.format()| with +• *vim.lsp.buf.formatting_sync()* Use |vim.lsp.buf.format()| with {async=false} instead. -- *vim.lsp.buf.range_formatting()* Use |vim.lsp.formatexpr()| +• *vim.lsp.buf.range_formatting()* Use |vim.lsp.formatexpr()| or |vim.lsp.buf.format()| instead. LUA -- vim.register_keystroke_callback() Use |vim.on_key()| instead. +• vim.register_keystroke_callback() Use |vim.on_key()| instead. NORMAL COMMANDS -- *]f* *[f* Same as "gf". +• *]f* *[f* Same as "gf". OPTIONS -- *cpo-<* *:menu-<special>* *:menu-special* *:map-<special>* *:map-special* +• *cpo-<* *:menu-<special>* *:menu-special* *:map-<special>* *:map-special* `<>` notation is always enabled. -- *'fe'* 'fenc'+'enc' before Vim 6.0; no longer used. -- *'highlight'* *'hl'* Names of builtin |highlight-groups| cannot be changed. -- *'langnoremap'* Deprecated alias to 'nolangremap'. -- 'sessionoptions' Flags "unix", "slash" are ignored and always enabled. -- *'vi'* -- 'viewoptions' Flags "unix", "slash" are ignored and always enabled. -- *'viminfo'* Deprecated alias to 'shada' option. -- *'viminfofile'* Deprecated alias to 'shadafile' option. -- *'paste'* *'nopaste'* Just Paste It.™ The 'paste' option is obsolete: +• *'fe'* 'fenc'+'enc' before Vim 6.0; no longer used. +• *'highlight'* *'hl'* Names of builtin |highlight-groups| cannot be changed. +• *'langnoremap'* Deprecated alias to 'nolangremap'. +• 'sessionoptions' Flags "unix", "slash" are ignored and always enabled. +• *'vi'* +• 'viewoptions' Flags "unix", "slash" are ignored and always enabled. +• *'viminfo'* Deprecated alias to 'shada' option. +• *'viminfofile'* Deprecated alias to 'shadafile' option. +• *'paste'* *'nopaste'* Just Paste It.™ The 'paste' option is obsolete: |paste| is handled automatically when you paste text using your terminal's or GUI's paste feature (CTRL-SHIFT-v, CMD-v (macOS), middle-click, …). Enables "paste mode": - - Disables mappings in Insert, Cmdline mode. - - Disables abbreviations. - - Resets 'autoindent' 'expandtab' 'revins' 'ruler' + • Disables mappings in Insert, Cmdline mode. + • Disables abbreviations. + • Resets 'autoindent' 'expandtab' 'revins' 'ruler' 'showmatch' 'smartindent' 'smarttab' 'softtabstop' 'textwidth' 'wrapmargin'. - - Treats 'formatoptions' as empty. - - Disables the effect of these options: - - 'cindent' - - 'indentexpr' - - 'lisp' + • Treats 'formatoptions' as empty. + • Disables the effect of these options: + • 'cindent' + • 'indentexpr' + • 'lisp' UI EXTENSIONS -- *ui-wildmenu* Use |ui-cmdline| with |ui-popupmenu| instead. Enabled +• *ui-wildmenu* Use |ui-cmdline| with |ui-popupmenu| instead. Enabled by the `ext_wildmenu` |ui-option|. Emits these events: - - `["wildmenu_show", items]` - - `["wildmenu_select", selected]` - - `["wildmenu_hide"]` -- *term_background* Unused. The terminal background color is now detected + • `["wildmenu_show", items]` + • `["wildmenu_select", selected]` + • `["wildmenu_hide"]` +• *term_background* Unused. The terminal background color is now detected by the Nvim core directly instead of the TUI. VARIABLES -- *b:terminal_job_pid* Use `jobpid(&channel)` instead. -- *b:terminal_job_id* Use `&channel` instead. To access in non-current buffer: - - Lua: `vim.bo[bufnr].channel` - - Vimscript: `getbufvar(bufnr, '&channel')` +• *b:terminal_job_pid* Use `jobpid(&channel)` instead. +• *b:terminal_job_id* Use `&channel` instead. To access in non-current buffer: + • Lua: `vim.bo[bufnr].channel` + • Vimscript: `getbufvar(bufnr, '&channel')` + +VIMSCRIPT +• *buffer_exists()* Obsolete name for |bufexists()|. +• *buffer_name()* Obsolete name for |bufname()|. +• *buffer_number()* Obsolete name for |bufnr()|. +• *file_readable()* Obsolete name for |filereadable()|. +• *highlight_exists()* Obsolete name for |hlexists()|. +• *highlightID()* Obsolete name for |hlID()|. +• *inputdialog()* Use |input()| instead. +• *jobclose()* Obsolete name for |chanclose()| +• *jobsend()* Obsolete name for |chansend()| +• *last_buffer_nr()* Obsolete name for bufnr("$"). +• *rpcstart()* Use |jobstart()| with `{'rpc': v:true}` instead. +• *rpcstop()* Use |jobstop()| instead to stop any job, or + `chanclose(id, "rpc")` to close RPC communication + without stopping the job. Use chanclose(id) to close + any socket. vim:noet:tw=78:ts=8:ft=help:norl: diff --git a/runtime/doc/dev_arch.txt b/runtime/doc/dev_arch.txt index 1cb3b9ad67..2be6221117 100644 --- a/runtime/doc/dev_arch.txt +++ b/runtime/doc/dev_arch.txt @@ -46,11 +46,11 @@ Remember to bump NVIM_API_LEVEL if it wasn't already during this development cycle. Other references: -* |msgpack-rpc| -* |ui| -* https://github.com/neovim/neovim/pull/3246 -* https://github.com/neovim/neovim/pull/18375 -* https://github.com/neovim/neovim/pull/21605 +- |msgpack-rpc| +- |ui| +- https://github.com/neovim/neovim/pull/3246 +- https://github.com/neovim/neovim/pull/18375 +- https://github.com/neovim/neovim/pull/21605 diff --git a/runtime/doc/dev_vimpatch.txt b/runtime/doc/dev_vimpatch.txt index 5119613b55..76be24878a 100644 --- a/runtime/doc/dev_vimpatch.txt +++ b/runtime/doc/dev_vimpatch.txt @@ -188,6 +188,7 @@ information. vim_strcat strncat xstrlcat VIM_ISWHITE ascii_iswhite IS_WHITE_OR_NUL ascii_iswhite_or_nul + IS_WHITE_NL_OR_NUL ascii_iswhite_nl_or_nul vim_isalpha mb_isalpha vim_isNormalIDc ascii_isident vim_islower vim_isupper mb_islower mb_isupper diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt index a61c569a67..d3170f114f 100644 --- a/runtime/doc/develop.txt +++ b/runtime/doc/develop.txt @@ -300,7 +300,7 @@ vim.paste in runtime/lua/vim/_editor.lua like this: > --- @returns false if client should cancel the paste. -LUA STDLIB DESIGN GUIDELINES *dev-lua* +STDLIB DESIGN GUIDELINES *dev-lua* See also |dev-naming|. @@ -337,7 +337,7 @@ preference): way. Advantage is that propagation happens for free and it's harder to accidentally swallow errors. (E.g. using `uv_handle/pipe:write()` without checking return values is common.) -4. `on_error` parameter +4. `on_error` callback - For async and "visitors" traversing a graph, where many errors may be collected while work continues. 5. `vim.notify` (sometimes with optional `opts.silent` (async, visitors ^)) @@ -376,6 +376,10 @@ Where possible, these patterns apply to _both_ Lua and the API: - See |vim.lsp.inlay_hint.enable()| and |vim.lsp.inlay_hint.is_enabled()| for a reference implementation of these "best practices". - NOTE: open questions: https://github.com/neovim/neovim/issues/28603 +- Transformation functions should also have a filter functionality when + appropriate. That is, when the function returns a nil value it "filters" its + input, otherwise the transformed value is used. + - Example: |vim.diagnostic.config.format()| API DESIGN GUIDELINES *dev-api* @@ -434,7 +438,9 @@ Use existing common {verb} names (actions) if possible: - eval: Evaluates an expression - exec: Executes code, may return a result - fmt: Formats - - get: Gets things (often by a query) + - get: Gets things. Two variants (overloads): + 1. `get<T>(id: int): T` returns one item. + 2. `get<T>(filter: dict): T[]` returns a list. - inspect: Presents a high-level, often interactive, view - is_enabled: Checks if functionality is enabled. - open: Opens something (a buffer, window, …) @@ -506,6 +512,15 @@ be a parameter (typically manifest as mutually-exclusive buf/win/… flags like - Example: `nvim_buf_del_mark` acts on a `Buffer` object (the first parameter) and uses the "del" {verb}. + *dev-namespace-name* +Use namespace names like `nvim.foo.bar`: > + vim.api.nvim_create_namespace('nvim.lsp.codelens') +< + + *dev-augroup-name* +Use autocommand group names like `nvim.foo.bar`: > + vim.api.nvim_create_augroup('nvim.treesitter.dev') +< INTERFACE PATTERNS *dev-api-patterns* diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt index 9ccc3102b6..5e1e04ce56 100644 --- a/runtime/doc/diagnostic.txt +++ b/runtime/doc/diagnostic.txt @@ -93,8 +93,12 @@ The {opts} table passed to a handler is the full set of configuration options values in the table are already resolved (i.e. if a user specifies a function for a config option, the function has already been evaluated). -Nvim provides these handlers by default: "virtual_text", "signs", and -"underline". +If a diagnostic handler is configured with a "severity" key then the list of +diagnostics passed to that handler will be filtered using the value of that +key (see example below). + +Nvim provides these handlers by default: "virtual_text", "virtual_lines", +"signs", and "underline". *diagnostic-handlers-example* The example below creates a new handler that notifies the user of diagnostics @@ -119,6 +123,9 @@ with |vim.notify()|: >lua vim.diagnostic.config({ ["my/notify"] = { log_level = vim.log.levels.INFO + + -- This handler will only receive "error" diagnostics. + severity = vim.diagnostic.severity.ERROR, } }) < @@ -163,6 +170,43 @@ show a sign for the highest severity diagnostic on a given line: >lua } < + *diagnostic-toggle-virtual-lines-example* +Diagnostic handlers can also be toggled. For example, you might want to toggle +the `virtual_lines` handler with the following keymap: >lua + + vim.keymap.set('n', 'gK', function() + local new_config = not vim.diagnostic.config().virtual_lines + vim.diagnostic.config({ virtual_lines = new_config }) + end, { desc = 'Toggle diagnostic virtual_lines' }) +< + + *diagnostic-loclist-example* +Whenever the |location-list| is opened, the following `show` handler will show +the most recent diagnostics: >lua + + vim.diagnostic.handlers.loclist = { + show = function(_, _, _, opts) + -- Generally don't want it to open on every update + opts.loclist.open = opts.loclist.open or false + local winid = vim.api.nvim_get_current_win() + vim.diagnostic.setloclist(opts.loclist) + vim.api.nvim_set_current_win(winid) + end + } +< + +The handler accepts the same options as |vim.diagnostic.setloclist()| and can be +configured using |vim.diagnostic.config()|: >lua + + -- Open the location list on every diagnostic change (warnings/errors only). + vim.diagnostic.config({ + loclist = { + open = true, + severity = { min = vim.diagnostic.severity.WARN }, + } + }) +< + ============================================================================== HIGHLIGHTS *diagnostic-highlights* @@ -430,11 +474,13 @@ Lua module: vim.diagnostic *diagnostic-api* Fields: ~ • {underline}? (`boolean|vim.diagnostic.Opts.Underline|fun(namespace: integer, bufnr:integer): vim.diagnostic.Opts.Underline`, default: `true`) Use underline for diagnostics. - • {virtual_text}? (`boolean|vim.diagnostic.Opts.VirtualText|fun(namespace: integer, bufnr:integer): vim.diagnostic.Opts.VirtualText`, default: `true`) + • {virtual_text}? (`boolean|vim.diagnostic.Opts.VirtualText|fun(namespace: integer, bufnr:integer): vim.diagnostic.Opts.VirtualText`, default: `false`) Use virtual text for diagnostics. If multiple diagnostics are set for a namespace, one prefix per diagnostic + the last diagnostic message are shown. + • {virtual_lines}? (`boolean|vim.diagnostic.Opts.VirtualLines|fun(namespace: integer, bufnr:integer): vim.diagnostic.Opts.VirtualLines`, default: `false`) + Use virtual lines for diagnostics. • {signs}? (`boolean|vim.diagnostic.Opts.Signs|fun(namespace: integer, bufnr:integer): vim.diagnostic.Opts.Signs`, default: `true`) Use signs for diagnostics |diagnostic-signs|. • {float}? (`boolean|vim.diagnostic.Opts.Float|fun(namespace: integer, bufnr:integer): vim.diagnostic.Opts.Float`) @@ -486,11 +532,13 @@ Lua module: vim.diagnostic *diagnostic-api* the buffer. Otherwise, any truthy value means to always show the diagnostic source. Overrides the setting from |vim.diagnostic.config()|. - • {format}? (`fun(diagnostic:vim.Diagnostic): string`) A + • {format}? (`fun(diagnostic:vim.Diagnostic): string?`) A function that takes a diagnostic as input and - returns a string. The return value is the text used - to display the diagnostic. Overrides the setting - from |vim.diagnostic.config()|. + returns a string or nil. If the return value is nil, + the diagnostic is not displayed by the handler. Else + the output text is used to display the diagnostic. + Overrides the setting from + |vim.diagnostic.config()|. • {prefix}? (`string|table|(fun(diagnostic:vim.Diagnostic,i:integer,total:integer): string, string)`) Prefix each diagnostic in the floating window: • If a `function`, {i} is the index of the @@ -556,12 +604,25 @@ Lua module: vim.diagnostic *diagnostic-api* diagnostics matching the given severity |diagnostic-severity|. +*vim.diagnostic.Opts.VirtualLines* + + Fields: ~ + • {current_line}? (`boolean`, default: `false`) Only show diagnostics + for the current line. + • {format}? (`fun(diagnostic:vim.Diagnostic): string?`) A + function that takes a diagnostic as input and returns + a string or nil. If the return value is nil, the + diagnostic is not displayed by the handler. Else the + output text is used to display the diagnostic. + *vim.diagnostic.Opts.VirtualText* Fields: ~ • {severity}? (`vim.diagnostic.SeverityFilter`) Only show virtual text for diagnostics matching the given severity |diagnostic-severity| + • {current_line}? (`boolean`) Only show diagnostics for the + current line. (default `false`) • {source}? (`boolean|"if_many"`) Include the diagnostic source in virtual text. Use `'if_many'` to only show sources if there is more than one @@ -579,9 +640,9 @@ Lua module: vim.diagnostic *diagnostic-api* • {suffix}? (`string|(fun(diagnostic:vim.Diagnostic): string)`) Append diagnostic message with suffix. This can be used to render an LSP diagnostic error code. - • {format}? (`fun(diagnostic:vim.Diagnostic): string`) The - return value is the text used to display the - diagnostic. Example: >lua + • {format}? (`fun(diagnostic:vim.Diagnostic): string?`) If + not nil, the return value is the text used to + display the diagnostic. Example: >lua function(diagnostic) if diagnostic.severity == vim.diagnostic.severity.ERROR then return string.format("E: %s", diagnostic.message) @@ -589,11 +650,14 @@ Lua module: vim.diagnostic *diagnostic-api* return diagnostic.message end < + + If the return value is nil, the diagnostic is + not displayed by the handler. • {hl_mode}? (`'replace'|'combine'|'blend'`) See |nvim_buf_set_extmark()|. • {virt_text}? (`[string,any][]`) See |nvim_buf_set_extmark()|. - • {virt_text_pos}? (`'eol'|'overlay'|'right_align'|'inline'`) See - |nvim_buf_set_extmark()|. + • {virt_text_pos}? (`'eol'|'eol_right_align'|'inline'|'overlay'|'right_align'`) + See |nvim_buf_set_extmark()|. • {virt_text_win_col}? (`integer`) See |nvim_buf_set_extmark()|. • {virt_text_hide}? (`boolean`) See |nvim_buf_set_extmark()|. @@ -847,7 +911,8 @@ setqflist({opts}) *vim.diagnostic.setqflist()* • {open}? (`boolean`, default: `true`) Open quickfix list after setting. • {title}? (`string`) Title of quickfix list. Defaults to - "Diagnostics". + "Diagnostics". If there's already a quickfix list with this + title, it's updated. If not, a new quickfix list is created. • {severity}? (`vim.diagnostic.SeverityFilter`) See |diagnostic-severity|. diff --git a/runtime/doc/digraph.txt b/runtime/doc/digraph.txt index 1e91e5e4b8..13df8ad4ac 100644 --- a/runtime/doc/digraph.txt +++ b/runtime/doc/digraph.txt @@ -119,8 +119,8 @@ see them. On most systems Vim uses the same digraphs. They work for the Unicode and ISO-8859-1 character sets. These default digraphs are taken from the RFC1345 -mnemonics. To make it easy to remember the mnemonic, the second character has -a standard meaning: +mnemonics (with some additions). To make it easy to remember the mnemonic, +the second character has a standard meaning: char name char meaning ~ Exclamation mark ! Grave @@ -1064,6 +1064,7 @@ the 1', 2' and 3' digraphs. ≅ ?= 2245 8773 APPROXIMATELY EQUAL TO ≈ ?2 2248 8776 ALMOST EQUAL TO ≌ =? 224C 8780 ALL EQUAL TO + ≐ .= 2250 8784 APPROACHES THE LIMIT ≓ HI 2253 8787 IMAGE OF OR APPROXIMATELY EQUAL TO ≠ != 2260 8800 NOT EQUAL TO ≡ =3 2261 8801 IDENTICAL TO diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index e0c45503cc..60238bc90d 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2848,7 +2848,8 @@ in the variable |v:exception|: > : echo "Number thrown. Value is" v:exception You may also be interested where an exception was thrown. This is stored in -|v:throwpoint|. Note that "v:exception" and "v:throwpoint" are valid for the +|v:throwpoint|. And you can obtain the stack trace from |v:stacktrace|. +Note that "v:exception", "v:stacktrace" and "v:throwpoint" are valid for the exception most recently caught as long it is not finished. Example: > diff --git a/runtime/doc/filetype.txt b/runtime/doc/filetype.txt index 19c018bc11..cc520484b3 100644 --- a/runtime/doc/filetype.txt +++ b/runtime/doc/filetype.txt @@ -597,7 +597,7 @@ To disable this behavior, set the following variable in your vimrc: > let g:gdscript_recommended_style = 0 -GIT COMMIT *ft-gitcommit-plugin* +GIT COMMIT *ft-gitcommit-plugin* One command, :DiffGitCached, is provided to show a diff of the current commit in the preview window. It is equivalent to calling "git diff --cached" plus @@ -778,7 +778,7 @@ An alternative to using `MANPAGER` in shell can be redefined `man`, for example: nvim "+hide Man $1" } -MARKDOWN *ft-markdown-plugin* +MARKDOWN *ft-markdown-plugin* To enable folding use this: > let g:markdown_folding = 1 @@ -870,7 +870,7 @@ To enable this behavior, set the following variable in your vimrc: > let g:rst_style = 1 -RNOWEB *ft-rnoweb-plugin* +RNOWEB *ft-rnoweb-plugin* The 'formatexpr' option is set dynamically with different values for R code and for LaTeX code. If you prefer that 'formatexpr' is not set, add to your diff --git a/runtime/doc/fold.txt b/runtime/doc/fold.txt index b844e0ed85..9e61e40814 100644 --- a/runtime/doc/fold.txt +++ b/runtime/doc/fold.txt @@ -82,9 +82,11 @@ The most efficient is to call a function without arguments: > The function must use v:lnum. See |expr-option-function|. These are the conditions with which the expression is evaluated: + - The current buffer and window are set for the line. - The variable "v:lnum" is set to the line number. -- The result is used for the fold level in this way: + +The result of foldexpr then determines the fold level as follows: value meaning ~ 0 the line is not in a fold 1, 2, .. the line is in a fold with this level @@ -99,6 +101,9 @@ These are the conditions with which the expression is evaluated: "<1", "<2", .. a fold with this level ends at this line ">1", ">2", .. a fold with this level starts at this line +The result values "=", "s" and "a" are more expensive, please see +|fold-expr-slow|. + It is not required to mark the start (end) of a fold with ">1" ("<1"), a fold will also start (end) when the fold level is higher (lower) than the fold level of the previous line. @@ -112,14 +117,8 @@ recognized, there is no error message and the fold level will be zero. For debugging the 'debug' option can be set to "msg", the error messages will be visible then. -Note: Since the expression has to be evaluated for every line, this fold -method can be very slow! - -Try to avoid the "=", "a" and "s" return values, since Vim often has to search -backwards for a line for which the fold level is defined. This can be slow. - If the 'foldexpr' expression starts with s: or |<SID>|, then it is replaced -with the script ID (|local-function|). Examples: > +with the script ID (|local-function|). Examples: > set foldexpr=s:MyFoldExpr() set foldexpr=<SID>SomeFoldExpr() < @@ -143,6 +142,37 @@ end in that line. It may happen that folds are not updated properly. You can use |zx| or |zX| to force updating folds. +MINIMIZING COMPUTATIONAL COST *fold-expr-slow* + +Due to its computational cost, this fold method can make Vim unresponsive, +especially when the fold level of all lines have to be initially computed. +Afterwards, after each change, Vim restricts the computation of foldlevels +to those lines whose fold level was affected by it (and reuses the known +foldlevels of all the others). + +The fold expression should therefore strive to minimize the number of +dependent lines needed for the computation of a given line: For example, try +to avoid the "=", "a" and "s" return values, because these will require the +evaluation of the fold levels on previous lines until an independent fold +level is found. + +If this proves difficult, the next best thing could be to cache all fold +levels in a buffer-local variable (b:foldlevels) that is only updated on +|b:changedtick|: +>vim + func MyFoldFunc() + if b:lasttick == b:changedtick + return b:foldlevels[v:lnum - 1] + endif + let b:lasttick = b:changedtick + let b:foldlevels = [] + " compute foldlevels ... + return b:foldlevels[v:lnum - 1] + enddef + set foldexpr=s:MyFoldFunc() +< +In above example further speedup was gained by using a function without +arguments (that must still use v:lnum). See |expr-option-function|. SYNTAX *fold-syntax* @@ -379,8 +409,8 @@ zX Undo manually opened and closed folds: re-apply 'foldlevel'. Also forces recomputing folds, like |zx|. *zm* -zm Fold more: Subtract |v:count1| from 'foldlevel'. If 'foldlevel' was - already zero nothing happens. +zm Fold more: Subtract |v:count1| from 'foldlevel'. If + 'foldlevel' was already zero nothing happens. 'foldenable' will be set. *zM* @@ -444,7 +474,7 @@ zk Move upwards to the end of the previous fold. A closed fold EXECUTING COMMANDS ON FOLDS ~ -:[range]foldd[oopen] {cmd} *:foldd* *:folddo* *:folddoopen* +:[range]foldd[oopen] {cmd} *:foldd* *:folddo* *:folddoopen* Execute {cmd} on all lines that are not in a closed fold. When [range] is given, only these lines are used. Each time {cmd} is executed the cursor is positioned on the @@ -532,7 +562,7 @@ When there is room after the text, it is filled with the character specified by 'fillchars'. If the 'foldtext' expression starts with s: or |<SID>|, then it is replaced -with the script ID (|local-function|). Examples: > +with the script ID (|local-function|). Examples: > set foldtext=s:MyFoldText() set foldtext=<SID>SomeFoldText() < diff --git a/runtime/doc/gui.txt b/runtime/doc/gui.txt index 21f1ba8241..ecb4de09bb 100644 --- a/runtime/doc/gui.txt +++ b/runtime/doc/gui.txt @@ -1,10 +1,10 @@ *gui.txt* Nvim - VIM REFERENCE MANUAL by Bram Moolenaar + VIM REFERENCE MANUAL by Bram Moolenaar -Nvim Graphical User Interface *gui* *GUI* +Nvim Graphical User Interface *gui* *GUI* Any client that supports the Nvim |ui-protocol| can be used as a UI for Nvim. And multiple UIs can connect to the same Nvim instance! The terms "UI" and @@ -17,11 +17,16 @@ TUI and GUI (assuming the UI supports the given feature). See |TUI| for notes specific to the terminal UI. Help tags with the "gui-" prefix refer to UI features, whereas help tags with the "ui-" prefix refer to the |ui-protocol|. -Nvim provides a default, builtin UI (the |TUI|), but there are many other -(third-party) GUIs that you can use instead: +============================================================================== +Third-party GUIs *third-party-guis* *vscode* + +Nvim provides a builtin "terminal UI" (|TUI|), but also works with many +(third-party) GUIs which may provide a fresh look or extra features on top of +Nvim. For example, "vscode-neovim" essentially allows you to use VSCode as +a Nvim GUI. -- Firenvim (Nvim in your web browser!) https://github.com/glacambre/firenvim - vscode-neovim (Nvim in VSCode!) https://github.com/vscode-neovim/vscode-neovim +- Firenvim (Nvim in your web browser!) https://github.com/glacambre/firenvim - Neovide https://neovide.dev/ - Goneovim https://github.com/akiyosi/goneovim - Nvy https://github.com/RMichelsen/Nvy @@ -32,71 +37,257 @@ Nvim provides a default, builtin UI (the |TUI|), but there are many other Type |gO| to see the table of contents. ============================================================================== -Starting the GUI *gui-config* *gui-start* +Starting the GUI *gui-config* *gui-start* - *ginit.vim* *gui-init* *gvimrc* *$MYGVIMRC* + *ginit.vim* *gui-init* *gvimrc* *$MYGVIMRC* For GUI-specific configuration Nvim provides the |UIEnter| event. This happens after other |initialization|s, or whenever a UI attaches (multiple UIs can connect to any Nvim instance). Example: this sets "g:gui" to the value of the UI's "rgb" field: > - :autocmd UIEnter * let g:gui = filter(nvim_list_uis(),{k,v-> v.chan==v:event.chan})[0].rgb + :autocmd UIEnter * let g:gui = filter(nvim_list_uis(),{k,v-> v.chan==v:event.chan})[0].rgb < - *:winp* *:winpos* *E188* + *:winp* *:winpos* *E188* :winp[os] - Display current position of the top left corner of the GUI vim - window in pixels. Does not work in all versions. - Also see |getwinpos()|, |getwinposx()| and |getwinposy()|. - -:winp[os] {X} {Y} *E466* - Put the GUI vim window at the given {X} and {Y} coordinates. - The coordinates should specify the position in pixels of the - top left corner of the window. - When the GUI window has not been opened yet, the values are - remembered until the window is opened. The position is - adjusted to make the window fit on the screen (if possible). - - *:wi* *:win* *:winsize* *E465* + Display current position of the top left corner of the GUI vim + window in pixels. Does not work in all versions. + Also see |getwinpos()|, |getwinposx()| and |getwinposy()|. + +:winp[os] {X} {Y} *E466* + Put the GUI vim window at the given {X} and {Y} coordinates. + The coordinates should specify the position in pixels of the + top left corner of the window. + When the GUI window has not been opened yet, the values are + remembered until the window is opened. The position is + adjusted to make the window fit on the screen (if possible). + + *:wi* *:win* *:winsize* *E465* :win[size] {width} {height} - Set the window height to {width} by {height} characters. - Obsolete, use ":set lines=11 columns=22". + Set the window height to {width} by {height} characters. + Obsolete, use ":set lines=11 columns=22". ============================================================================== -Scrollbars *gui-scrollbars* +Using the mouse *mouse-using* + + *mouse-mode-table* *mouse-overview* +Overview of what the mouse buttons do, when 'mousemodel' is "extend": + + *<S-LeftMouse>* *<A-RightMouse>* *<S-RightMouse>* *<RightDrag>* + *<RightRelease>* *<LeftDrag>* +Normal Mode: > + event position selection change action + cursor window + --------------------------------------------------------------------------- + <LeftMouse> yes end yes + <C-LeftMouse> yes end yes "CTRL-]" (2) + <S-LeftMouse> yes no change yes "*" (2) + <LeftDrag> yes start or extend (1) no + <LeftRelease> yes start or extend (1) no + <MiddleMouse> yes if not active no put + <MiddleMouse> yes if active no yank and put + <RightMouse> yes start or extend yes + <A-RightMouse> yes start or extend blockw. yes + <S-RightMouse> yes no change yes "#" (2) + <C-RightMouse> no no change no "CTRL-T" + <RightDrag> yes extend no + <RightRelease> yes extend no + +Insert or Replace Mode: > + event position selection change action + cursor window + --------------------------------------------------------------------------- + <LeftMouse> yes (cannot be active) yes + <C-LeftMouse> yes (cannot be active) yes "CTRL-O^]" (2) + <S-LeftMouse> yes (cannot be active) yes "CTRL-O*" (2) + <LeftDrag> yes start or extend (1) no like CTRL-O (1) + <LeftRelease> yes start or extend (1) no like CTRL-O (1) + <MiddleMouse> no (cannot be active) no put register + <RightMouse> yes start or extend yes like CTRL-O + <A-RightMouse> yes start or extend blockw. yes + <S-RightMouse> yes (cannot be active) yes "CTRL-O#" (2) + <C-RightMouse> no (cannot be active) no "CTRL-O CTRL-T" + +In a help window: > + event position selection change action + cursor window + --------------------------------------------------------------------------- + <2-LeftMouse> yes (cannot be active) no "^]" (jump to help tag) + +When 'mousemodel' is "popup", these are different: + + *<A-LeftMouse>* +Normal Mode: > + event position selection change action + cursor window + --------------------------------------------------------------------------- + <S-LeftMouse> yes start or extend (1) no + <A-LeftMouse> yes start/extend blockw no + <RightMouse> no popup menu no + +Insert or Replace Mode: > + event position selection change action + cursor window + --------------------------------------------------------------------------- + <S-LeftMouse> yes start or extend (1) no like CTRL-O (1) + <A-LeftMouse> yes start/extend blockw no + <RightMouse> no popup menu no + +(1) only if mouse pointer moved since press +(2) only if click is in same buffer + +Clicking the left mouse button causes the cursor to be positioned. If the +click is in another window that window is made the active window. When +editing the command-line the cursor can only be positioned on the +command-line. When in Insert mode Vim remains in Insert mode. If 'scrolloff' +is set, and the cursor is positioned within 'scrolloff' lines from the window +border, the text is scrolled. + +A selection can be started by pressing the left mouse button on the first +character, moving the mouse to the last character, then releasing the mouse +button. You will not always see the selection until you release the button, +only in some versions (GUI, Win32) will the dragging be shown immediately. +Note that you can make the text scroll by moving the mouse at least one +character in the first/last line in the window when 'scrolloff' is non-zero. + +In Normal, Visual and Select mode clicking the right mouse button causes the +Visual area to be extended. When 'mousemodel' is "popup", the left button has +to be used while keeping the shift key pressed. When clicking in a window +which is editing another buffer, the Visual or Select mode is stopped. + +In Normal, Visual and Select mode clicking the right mouse button with the alt +key pressed causes the Visual area to become blockwise. When 'mousemodel' is +"popup" the left button has to be used with the alt key. Note that this won't +work on systems where the window manager consumes the mouse events when the +alt key is pressed (it may move the window). + + *double-click* *<2-LeftMouse>* *<3-LeftMouse>* *<4-LeftMouse>* +Double, triple and quadruple clicks are supported. For selecting text, extra +clicks extend the selection: > + + click select + --------------------------------- + double word or % match + triple line + quadruple rectangular block + +Exception: In a :help window, double-click jumps to help for the word that is +clicked on. + +Double-click on a word selects that word. 'iskeyword' is used to specify +which characters are included in a word. Double-click on a character that has +a match selects until that match (like using "v%"). If the match is an +#if/#else/#endif block, the selection becomes linewise. The time for +double-clicking can be set with the 'mousetime' option. + +Example: configure double-click to jump to the tag under the cursor: >vim + :map <2-LeftMouse> :exe "tag " .. expand("<cword>")<CR> + +Dragging the mouse with a double-click (button-down, button-up, button-down +and then drag) will result in whole words to be selected. This continues +until the button is released, at which point the selection is per character +again. + +For scrolling with the mouse see |scroll-mouse-wheel|. + +In Insert mode, when a selection is started, Vim goes into Normal mode +temporarily. When Visual or Select mode ends, it returns to Insert mode. +This is like using CTRL-O in Insert mode. Select mode is used when the +'selectmode' option contains "mouse". + + *X1Mouse* *X1Drag* *X1Release* + *X2Mouse* *X2Drag* *X2Release* + *<MiddleRelease>* *<MiddleDrag>* +Mouse clicks can be mapped using these |keycodes|: > + code mouse button normal action + --------------------------------------------------------------------------- + <LeftMouse> left pressed set cursor position + <LeftDrag> left moved while pressed extend selection + <LeftRelease> left released set selection end + <MiddleMouse> middle pressed paste text at cursor position + <MiddleDrag> middle moved while pressed - + <MiddleRelease> middle released - + <RightMouse> right pressed extend selection + <RightDrag> right moved while pressed extend selection + <RightRelease> right released set selection end + <X1Mouse> X1 button pressed - + <X1Drag> X1 moved while pressed - + <X1Release> X1 button release - + <X2Mouse> X2 button pressed - + <X2Drag> X2 moved while pressed - + <X2Release> X2 button release - + +The X1 and X2 buttons refer to the extra buttons found on some mice (e.g. the +right thumb). + +Examples: >vim + :noremap <MiddleMouse> <LeftMouse><MiddleMouse> +Paste at the position of the middle mouse button click (otherwise the paste +would be done at the cursor position). >vim + + :noremap <LeftRelease> <LeftRelease>y +Immediately yank the selection, when using Visual mode. + +Note the use of ":noremap" instead of "map" to avoid a recursive mapping. +>vim + :map <X1Mouse> <C-O> + :map <X2Mouse> <C-I> +Map the X1 and X2 buttons to go forwards and backwards in the jump list, see +|CTRL-O| and |CTRL-I|. + + *mouse-swap-buttons* +To swap the meaning of the left and right mouse buttons: >vim + :noremap <LeftMouse> <RightMouse> + :noremap <LeftDrag> <RightDrag> + :noremap <LeftRelease> <RightRelease> + :noremap <RightMouse> <LeftMouse> + :noremap <RightDrag> <LeftDrag> + :noremap <RightRelease> <LeftRelease> + :noremap g<LeftMouse> <C-RightMouse> + :noremap g<RightMouse> <C-LeftMouse> + :noremap! <LeftMouse> <RightMouse> + :noremap! <LeftDrag> <RightDrag> + :noremap! <LeftRelease> <RightRelease> + :noremap! <RightMouse> <LeftMouse> + :noremap! <RightDrag> <LeftDrag> + :noremap! <RightRelease> <LeftRelease> +< + +============================================================================== +Scrollbars *gui-scrollbars* There are vertical scrollbars and a horizontal scrollbar. You may configure which ones appear with the 'guioptions' option. The interface looks like this (with `:set guioptions=mlrb`): > - +------------------------------+ ` - | File Edit Help | <- Menu bar (m) ` - +-+--------------------------+-+ ` - |^| |^| ` - |#| Text area. |#| ` - | | | | ` - |v|__________________________|v| ` - Normal status line -> |-+ File.c 5,2 +-| ` + +------------------------------+ ` + | File Edit Help | <- Menu bar (m) ` + +-+--------------------------+-+ ` + |^| |^| ` + |#| Text area. |#| ` + | | | | ` + |v|__________________________|v| ` + Normal status line -> |-+ File.c 5,2 +-| ` between Vim windows |^|""""""""""""""""""""""""""|^| ` - | | | | ` - | | Another file buffer. | | ` - | | | | ` - |#| |#| ` - Left scrollbar (l) -> |#| |#| <- Right ` - |#| |#| scrollbar (r) ` - | | | | ` - |v| |v| ` - +-+--------------------------+-+ ` - | |< #### >| | <- Bottom ` - +-+--------------------------+-+ scrollbar (b) ` + | | | | ` + | | Another file buffer. | | ` + | | | | ` + |#| |#| ` + Left scrollbar (l) -> |#| |#| <- Right ` + |#| |#| scrollbar (r) ` + | | | | ` + |v| |v| ` + +-+--------------------------+-+ ` + | |< #### >| | <- Bottom ` + +-+--------------------------+-+ scrollbar (b) ` < Any of the scrollbar or menu components may be turned off by not putting the appropriate letter in the 'guioptions' string. The bottom scrollbar is only useful when 'nowrap' is set. -VERTICAL SCROLLBARS *gui-vert-scroll* +VERTICAL SCROLLBARS *gui-vert-scroll* Each Vim window has a scrollbar next to it which may be scrolled up and down to move through the text in that buffer. The size of the scrollbar-thumb @@ -115,7 +306,7 @@ is on the left half, the right scrollbar column will contain scrollbars for the rightmost windows. The same happens on the other side. -HORIZONTAL SCROLLBARS *gui-horiz-scroll* +HORIZONTAL SCROLLBARS *gui-horiz-scroll* The horizontal scrollbar (at the bottom of the Vim GUI) may be used to scroll text sideways when the 'wrap' option is turned off. The @@ -131,7 +322,7 @@ include the 'h' flag in 'guioptions'. Then the scrolling is limited by the text of the current cursor line. ============================================================================== -Drag and drop *drag-n-drop* +Drag and drop *drag-n-drop* You can drag and drop one or more files into the Vim window, where they will be opened as if a |:drop| command was used. @@ -150,12 +341,12 @@ names with any Ex command. Special characters (space, tab, double quote and "|"; backslash on non-MS-Windows systems) will be escaped. ============================================================================== -Menus *menus* +Menus *menus* For an introduction see |usr_42.txt| in the user manual. -Using Menus *using-menus* +Using Menus *using-menus* Basically, menus can be used just like mappings. You can define your own menus, as many as you like. @@ -165,45 +356,45 @@ what the key sequence was. For creating menus in a different language, see |:menutrans|. - *menu.vim* + *menu.vim* The default menus are read from the file "$VIMRUNTIME/menu.vim". See |$VIMRUNTIME| for where the path comes from. You can set up your own menus. Starting off with the default set is a good idea. You can add more items, or, if you don't like the defaults at all, start with removing all menus |:unmenu-all|. You can also avoid the default menus being loaded by adding this line to your vimrc file (NOT your gvimrc file!): > - :let did_install_default_menus = 1 + :let did_install_default_menus = 1 If you also want to avoid the Syntax menu: > - :let did_install_syntax_menu = 1 + :let did_install_syntax_menu = 1 The first item in the Syntax menu can be used to show all available filetypes in the menu (which can take a bit of time to load). If you want to have all filetypes already present at startup, add: > - :let do_syntax_sel_menu = 1 + :let do_syntax_sel_menu = 1 Note that the menu.vim is sourced when `:syntax on` or `:filetype on` is executed or after your .vimrc file is sourced. This means that the 'encoding' option and the language of messages (`:language messages`) must be set before that (if you want to change them). - *console-menus* + *console-menus* Although this documentation is in the GUI section, you can actually use menus in console mode too. You will have to load |menu.vim| explicitly then, it is not done by default. You can use the |:emenu| command and command-line completion with 'wildmenu' to access the menu entries almost like a real menu system. To do this, put these commands in your vimrc file: > - :source $VIMRUNTIME/menu.vim - :set wildmenu - :set cpo-=< - :set wcm=<C-Z> - :map <F4> :emenu <C-Z> + :source $VIMRUNTIME/menu.vim + :set wildmenu + :set cpo-=< + :set wcm=<C-Z> + :map <F4> :emenu <C-Z> Pressing <F4> will start the menu. You can now use the cursor keys to select a menu entry. Hit <Enter> to execute it. Hit <Esc> if you want to cancel. -Creating New Menus *creating-menus* +Creating New Menus *creating-menus* - *:me* *:menu* *:noreme* *:noremenu* - *E330* *E327* *E331* *E336* *E333* - *E328* *E329* *E337* *E792* + *:me* *:menu* *:noreme* *:noremenu* + *E330* *E327* *E331* *E336* *E333* + *E328* *E329* *E337* *E792* To create a new menu item, use the ":menu" commands. They are mostly like the ":map" set of commands (see |map-modes|), but the first argument is a menu item name, given as a path of menus and submenus with a '.' between them, @@ -224,15 +415,16 @@ tooltips for menus. See |terminal-input|. Special characters in a menu name: - *menu-shortcut* - & The next character is the shortcut key. Make sure each - shortcut key is only used once in a (sub)menu. If you want to - insert a literal "&" in the menu name use "&&". - *menu-text* - <Tab> Separates the menu name from right-aligned text. This can be - used to show the equivalent typed command. The text "<Tab>" - can be used here for convenience. If you are using a real - tab, don't forget to put a backslash before it! + *menu-shortcut* +- & The next character is the shortcut key. Make sure each shortcut key is + only used once in a (sub)menu. If you want to insert a literal "&" in the + menu name use "&&". + *menu-text* +- <Tab> Separates the menu name from right-aligned text. This can be used to + show the equivalent typed command. The text "<Tab>" can be used here for + convenience. If you are using a real tab, don't forget to put a backslash + before it! + Example: > :amenu &File.&Open<Tab>:e :browse e<CR> @@ -242,99 +434,99 @@ With the shortcut "F" (while keeping the <Alt> key pressed), and then "O", this menu can be used. The second part is shown as "Open :e". The ":e" is right aligned, and the "O" is underlined, to indicate it is the shortcut. - *:am* *:amenu* *:an* *:anoremenu* + *:am* *:amenu* *:an* *:anoremenu* The ":amenu" command can be used to define menu entries for all modes at once, except for Terminal mode. To make the command work correctly, a character is -automatically inserted for some modes: - mode inserted appended ~ - Normal nothing nothing - Visual <C-C> <C-\><C-G> - Insert <C-\><C-O> - Cmdline <C-C> <C-\><C-G> - Op-pending <C-C> <C-\><C-G> - +automatically inserted for some modes: > + mode inserted appended + Normal nothing nothing + Visual <C-C> <C-\><C-G> + Insert <C-\><C-O> + Cmdline <C-C> <C-\><C-G> + Op-pending <C-C> <C-\><C-G> +< Example: > - :amenu File.Next :next^M + :amenu File.Next :next^M is equal to: > - :nmenu File.Next :next^M - :vmenu File.Next ^C:next^M^\^G - :imenu File.Next ^\^O:next^M - :cmenu File.Next ^C:next^M^\^G - :omenu File.Next ^C:next^M^\^G + :nmenu File.Next :next^M + :vmenu File.Next ^C:next^M^\^G + :imenu File.Next ^\^O:next^M + :cmenu File.Next ^C:next^M^\^G + :omenu File.Next ^C:next^M^\^G Careful: In Insert mode this only works for a SINGLE Normal mode command, because of the CTRL-O. If you have two or more commands, you will need to use the ":imenu" command. For inserting text in any mode, you can use the expression register: > - :amenu Insert.foobar "='foobar'<CR>P + :amenu Insert.foobar "='foobar'<CR>P The special text <Cmd> begins a "command menu", it executes the command directly without changing modes. Where you might use ":...<CR>" you can instead use "<Cmd>...<CR>". See |<Cmd>| for more info. Example: > - anoremenu File.Next <Cmd>next<CR> + anoremenu File.Next <Cmd>next<CR> Note that <Esc> in Cmdline mode executes the command, like in a mapping. This is Vi compatible. Use CTRL-C to quit Cmdline mode. - *:nme* *:nmenu* *:nnoreme* *:nnoremenu* *:nunme* *:nunmenu* + *:nme* *:nmenu* *:nnoreme* *:nnoremenu* *:nunme* *:nunmenu* Menu commands starting with "n" work in Normal mode. |mapmode-n| - *:ome* *:omenu* *:onoreme* *:onoremenu* *:ounme* *:ounmenu* + *:ome* *:omenu* *:onoreme* *:onoremenu* *:ounme* *:ounmenu* Menu commands starting with "o" work in Operator-pending mode. |mapmode-o| - *:vme* *:vmenu* *:vnoreme* *:vnoremenu* *:vunme* *:vunmenu* + *:vme* *:vmenu* *:vnoreme* *:vnoremenu* *:vunme* *:vunmenu* Menu commands starting with "v" work in Visual mode. |mapmode-v| - *:xme* *:xmenu* *:xnoreme* *:xnoremenu* *:xunme* *:xunmenu* + *:xme* *:xmenu* *:xnoreme* *:xnoremenu* *:xunme* *:xunmenu* Menu commands starting with "x" work in Visual and Select mode. |mapmode-x| - *:sme* *:smenu* *:snoreme* *:snoremenu* *:sunme* *:sunmenu* + *:sme* *:smenu* *:snoreme* *:snoremenu* *:sunme* *:sunmenu* Menu commands starting with "s" work in Select mode. |mapmode-s| - *:ime* *:imenu* *:inoreme* *:inoremenu* *:iunme* *:iunmenu* + *:ime* *:imenu* *:inoreme* *:inoremenu* *:iunme* *:iunmenu* Menu commands starting with "i" work in Insert mode. |mapmode-i| - *:cme* *:cmenu* *:cnoreme* *:cnoremenu* *:cunme* *:cunmenu* + *:cme* *:cmenu* *:cnoreme* *:cnoremenu* *:cunme* *:cunmenu* Menu commands starting with "c" work in Cmdline mode. |mapmode-c| - *:tlm* *:tlmenu* *:tln* *:tlnoremenu* *:tlu* *:tlunmenu* + *:tlm* *:tlmenu* *:tln* *:tlnoremenu* *:tlu* *:tlunmenu* Menu commands starting with "tl" work in Terminal mode. |mapmode-t| - *:menu-<silent>* *:menu-silent* + *:menu-<silent>* *:menu-silent* To define a menu which will not be echoed on the command line, add "<silent>" as the first argument. Example: > - :menu <silent> Settings.Ignore\ case :set ic<CR> + :menu <silent> Settings.Ignore\ case :set ic<CR> The ":set ic" will not be echoed when using this menu. Messages from the executed command are still given though. To shut them up too, add a ":silent" in the executed command: > - :menu <silent> Search.Header :exe ":silent normal /Header\r"<CR> + :menu <silent> Search.Header :exe ":silent normal /Header\r"<CR> "<silent>" may also appear just after "<script>". - *:menu-<script>* *:menu-script* + *:menu-<script>* *:menu-script* The "to" part of the menu will be inspected for mappings. If you don't want this, use the ":noremenu" command (or the similar one for a specific mode). If you do want to use script-local mappings, add "<script>" as the very first argument to the ":menu" command or just after "<silent>". - *menu-priority* + *menu-priority* You can give a priority to a menu. Menus with a higher priority go more to the right. The priority is given as a number before the ":menu" command. Example: > - :80menu Buffer.next :bn<CR> - -The default menus have these priorities: - File 10 - Edit 20 - Tools 40 - Syntax 50 - Buffers 60 - Window 70 - Help 9999 - + :80menu Buffer.next :bn<CR> + +The default menus have these priorities: > + File 10 + Edit 20 + Tools 40 + Syntax 50 + Buffers 60 + Window 70 + Help 9999 +< When no or zero priority is given, 500 is used. The priority for the PopUp menu is not used. @@ -342,18 +534,18 @@ You can use a priority higher than 9999, to make it go after the Help menu, but that is non-standard and is discouraged. The highest possible priority is about 32000. The lowest is 1. - *sub-menu-priority* + *sub-menu-priority* The same mechanism can be used to position a sub-menu. The priority is then given as a dot-separated list of priorities, before the menu name: > - :menu 80.500 Buffer.next :bn<CR> + :menu 80.500 Buffer.next :bn<CR> Giving the sub-menu priority is only needed when the item is not to be put in a normal position. For example, to put a sub-menu before the other items: > - :menu 80.100 Buffer.first :brew<CR> + :menu 80.100 Buffer.first :brew<CR> Or to put a sub-menu after the other items, and further items with default priority will be put before it: > - :menu 80.900 Buffer.last :blast<CR> + :menu 80.900 Buffer.last :blast<CR> When a number is missing, the default value 500 will be used: > - :menu .900 myMenu.test :echo "text"<CR> + :menu .900 myMenu.test :echo "text"<CR> The menu priority is only used when creating a new menu. When it already existed, e.g., in another mode, the priority will not change. Thus, the priority only needs to be given the first time a menu is used. @@ -363,49 +555,49 @@ menus can be different. This is different from menu-bar menus, which have the same order for all modes. NOTE: sub-menu priorities currently don't work for all versions of the GUI. - *menu-separator* *E332* + *menu-separator* *E332* Menu items can be separated by a special item that inserts some space between items. Depending on the system this is displayed as a line or a dotted line. These items must start with a '-' and end in a '-'. The part in between is used to give it a unique name. Priorities can be used as with normal items. Example: > - :menu Example.item1 :do something - :menu Example.-Sep- : - :menu Example.item2 :do something different + :menu Example.item1 :do something + :menu Example.-Sep- : + :menu Example.item2 :do something different Note that the separator also requires a rhs. It doesn't matter what it is, because the item will never be selected. Use a single colon to keep it simple. - *gui-toolbar* + *gui-toolbar* The default toolbar is setup in menu.vim. The display of the toolbar is controlled by the 'guioptions' letter 'T'. You can thus have menu & toolbar together, or either on its own, or neither. The appearance is controlled by the 'toolbar' option. You can choose between an image, text or both. - *toolbar-icon* + *toolbar-icon* The toolbar is defined as a special menu called ToolBar, which only has one level. Vim interprets the items in this menu as follows: -1) If an "icon=" argument was specified, the file with this name is used. +- 1 If an "icon=" argument was specified, the file with this name is used. The file can either be specified with the full path or with the base name. In the last case it is searched for in the "bitmaps" directory in 'runtimepath', like in point 3. Examples: > - :amenu icon=/usr/local/pixmaps/foo_icon.xpm ToolBar.Foo :echo "Foo"<CR> - :amenu icon=FooIcon ToolBar.Foo :echo "Foo"<CR> + :amenu icon=/usr/local/pixmaps/foo_icon.xpm ToolBar.Foo :echo "Foo"<CR> + :amenu icon=FooIcon ToolBar.Foo :echo "Foo"<CR> < Note that in the first case the extension is included, while in the second case it is omitted. If the file cannot be opened the next points are tried. A space in the file name must be escaped with a backslash. A menu priority must come _after_ the icon argument: > - :amenu icon=foo 1.42 ToolBar.Foo :echo "42!"<CR> -2) An item called 'BuiltIn##', where ## is a number, is taken as number ## of + :amenu icon=foo 1.42 ToolBar.Foo :echo "42!"<CR> +- 2 An item called 'BuiltIn##', where ## is a number, is taken as number ## of the built-in bitmaps available in Vim. Currently there are 31 numbered from 0 to 30 which cover most common editing operations |builtin-tools|. > - :amenu ToolBar.BuiltIn22 :call SearchNext("back")<CR> -3) An item with another name is first searched for in the directory + :amenu ToolBar.BuiltIn22 :call SearchNext("back")<CR> +- 3 An item with another name is first searched for in the directory "bitmaps" in 'runtimepath'. If found, the bitmap file is used as the toolbar button image. Note that the exact filename is OS-specific: For example, under Win32 the command > - :amenu ToolBar.Hello :echo "hello"<CR> + :amenu ToolBar.Hello :echo "hello"<CR> < would find the file 'hello.bmp'. Under X11 it is 'Hello.xpm'. For MS-Windows and the bitmap is scaled to fit the button. For MS-Windows a size of 18 by 18 pixels works best. @@ -413,55 +605,56 @@ level. Vim interprets the items in this menu as follows: The light grey pixels will be changed to the Window frame color and the dark grey pixels to the window shadow color. More colors might also work, depending on your system. -4) If the bitmap is still not found, Vim checks for a match against its list +- 4 If the bitmap is still not found, Vim checks for a match against its list of built-in names. Each built-in button image has a name. So the command > - :amenu ToolBar.Open :e + :amenu ToolBar.Open :e < will show the built-in "open a file" button image if no open.bmp exists. All the built-in names can be seen used in menu.vim. -5) If all else fails, a blank, but functioning, button is displayed. - - *builtin-tools* -nr Name Normal action ~ -00 New open new window -01 Open browse for file to open in current window -02 Save write buffer to file -03 Undo undo last change -04 Redo redo last undone change -05 Cut delete selected text to clipboard -06 Copy copy selected text to clipboard -07 Paste paste text from clipboard -08 Print print current buffer -09 Help open a buffer on Vim's builtin help -10 Find start a search command -11 SaveAll write all modified buffers to file -12 SaveSesn write session file for current situation -13 NewSesn write new session file -14 LoadSesn load session file -15 RunScript browse for file to run as a Vim script -16 Replace prompt for substitute command -17 WinClose close current window -18 WinMax make current window use many lines -19 WinMin make current window use few lines -20 WinSplit split current window -21 Shell start a shell -22 FindPrev search again, backward -23 FindNext search again, forward -24 FindHelp prompt for word to search help for -25 Make run make and jump to first error -26 TagJump jump to tag under the cursor -27 RunCtags build tags for files in current directory -28 WinVSplit split current window vertically -29 WinMaxWidth make current window use many columns -30 WinMinWidth make current window use few columns - - *hidden-menus* *win32-hidden-menus* +- 5 If all else fails, a blank, but functioning, button is displayed. + + *builtin-tools* +> + nr Name Normal action + 00 New open new window + 01 Open browse for file to open in current window + 02 Save write buffer to file + 03 Undo undo last change + 04 Redo redo last undone change + 05 Cut delete selected text to clipboard + 06 Copy copy selected text to clipboard + 07 Paste paste text from clipboard + 08 Print print current buffer + 09 Help open a buffer on Vim's builtin help + 10 Find start a search command + 11 SaveAll write all modified buffers to file + 12 SaveSesn write session file for current situation + 13 NewSesn write new session file + 14 LoadSesn load session file + 15 RunScript browse for file to run as a Vim script + 16 Replace prompt for substitute command + 17 WinClose close current window + 18 WinMax make current window use many lines + 19 WinMin make current window use few lines + 20 WinSplit split current window + 21 Shell start a shell + 22 FindPrev search again, backward + 23 FindNext search again, forward + 24 FindHelp prompt for word to search help for + 25 Make run make and jump to first error + 26 TagJump jump to tag under the cursor + 27 RunCtags build tags for files in current directory + 28 WinVSplit split current window vertically + 29 WinMaxWidth make current window use many columns + 30 WinMinWidth make current window use few columns +< + *hidden-menus* *win32-hidden-menus* In the Win32 GUI, starting a menu name with ']' excludes that menu from the main menu bar. You must then use the |:popup| command to display it. When splitting the window the window toolbar is not copied to the new window. - *popup-menu* + *popup-menu* You can define the special menu "PopUp". This is the menu that is displayed when the right mouse button is pressed, if 'mousemodel' is set to popup or popup_setpos. @@ -483,7 +676,7 @@ The default "PopUp" menu is: >vim anoremenu PopUp.How-to\ disable\ mouse <Cmd>help disable-mouse<CR> < -Showing What Menus Are Mapped To *showing-menus* +Showing What Menus Are Mapped To *showing-menus* To see what an existing menu is mapped to, use just one argument after the menu commands (just like you would with the ":map" commands). If the menu @@ -502,25 +695,25 @@ Note that hitting <Tab> while entering a menu name after a menu command may be used to complete the name of the menu item. -Executing Menus *execute-menus* +Executing Menus *execute-menus* - *:em* *:emenu* *E334* *E335* -:[range]em[enu] {menu} Execute {menu} from the command line. - The default is to execute the Normal mode - menu. If a range is specified, it executes - the Visual mode menu. - If used from <c-o>, it executes the - insert-mode menu Eg: > - :emenu File.Exit + *:em* *:emenu* *E334* *E335* +:[range]em[enu] {menu} Execute {menu} from the command line. + The default is to execute the Normal mode + menu. If a range is specified, it executes + the Visual mode menu. + If used from <c-o>, it executes the + insert-mode menu Eg: > + :emenu File.Exit -:[range]em[enu] {mode} {menu} Like above, but execute the menu for {mode}: - 'n': |:nmenu| Normal mode - 'v': |:vmenu| Visual mode - 's': |:smenu| Select mode - 'o': |:omenu| Operator-pending mode - 't': |:tlmenu| Terminal mode - 'i': |:imenu| Insert mode - 'c': |:cmenu| Cmdline mode +:[range]em[enu] {mode} {menu} Like above, but execute the menu for {mode}: + - 'n': |:nmenu| Normal mode + - 'v': |:vmenu| Visual mode + - 's': |:smenu| Select mode + - 'o': |:omenu| Operator-pending mode + - 't': |:tlmenu| Terminal mode + - 'i': |:imenu| Insert mode + - 'c': |:cmenu| Cmdline mode You can use :emenu to access useful menu items you may have got used to from @@ -531,10 +724,10 @@ When using a range, if the lines match with '<,'>, then the menu is executed using the last visual selection. -Deleting Menus *delete-menus* +Deleting Menus *delete-menus* - *:unme* *:unmenu* - *:aun* *:aunmenu* + *:unme* *:unmenu* + *:aun* *:aunmenu* To delete a menu item or a whole submenu, use the unmenu commands, which are analogous to the unmap commands. Eg: > :unmenu! Edit.Paste @@ -545,26 +738,26 @@ Command-line modes. Note that hitting <Tab> while entering a menu name after an umenu command may be used to complete the name of the menu item for the appropriate mode. -To remove all menus use: *:unmenu-all* > - :unmenu * " remove all menus in Normal and visual mode - :unmenu! * " remove all menus in Insert and Command-line mode - :aunmenu * " remove all menus in all modes, except for Terminal - " mode - :tlunmenu * " remove all menus in Terminal mode +To remove all menus use: *:unmenu-all* > + :unmenu * " remove all menus in Normal and visual mode + :unmenu! * " remove all menus in Insert and Command-line mode + :aunmenu * " remove all menus in all modes, except for Terminal + " mode + :tlunmenu * " remove all menus in Terminal mode If you want to get rid of the menu bar: > - :set guioptions-=m + :set guioptions-=m -Disabling Menus *disable-menus* +Disabling Menus *disable-menus* - *:menu-disable* *:menu-enable* + *:menu-disable* *:menu-enable* If you do not want to remove a menu, but disable it for a moment, this can be done by adding the "enable" or "disable" keyword to a ":menu" command. Examples: > - :menu disable &File.&Open\.\.\. - :amenu enable * - :amenu disable &Tools.* + :menu disable &File.&Open\.\.\. + :amenu enable * + :amenu disable &Tools.* The command applies to the modes as used with all menu commands. Note that characters like "&" need to be included for translated names to be found. @@ -572,36 +765,36 @@ When the argument is "*", all menus are affected. Otherwise the given menu name and all existing submenus below it are affected. -Examples for Menus *menu-examples* +Examples for Menus *menu-examples* Here is an example on how to add menu items with menus! You can add a menu item for the keyword under the cursor. The register "z" is used. > - :nmenu Words.Add\ Var wb"zye:menu! Words.<C-R>z <C-R>z<CR> - :nmenu Words.Remove\ Var wb"zye:unmenu! Words.<C-R>z<CR> - :vmenu Words.Add\ Var "zy:menu! Words.<C-R>z <C-R>z <CR> - :vmenu Words.Remove\ Var "zy:unmenu! Words.<C-R>z<CR> - :imenu Words.Add\ Var <Esc>wb"zye:menu! Words.<C-R>z <C-R>z<CR>a - :imenu Words.Remove\ Var <Esc>wb"zye:unmenu! Words.<C-R>z<CR>a + :nmenu Words.Add\ Var wb"zye:menu! Words.<C-R>z <C-R>z<CR> + :nmenu Words.Remove\ Var wb"zye:unmenu! Words.<C-R>z<CR> + :vmenu Words.Add\ Var "zy:menu! Words.<C-R>z <C-R>z <CR> + :vmenu Words.Remove\ Var "zy:unmenu! Words.<C-R>z<CR> + :imenu Words.Add\ Var <Esc>wb"zye:menu! Words.<C-R>z <C-R>z<CR>a + :imenu Words.Remove\ Var <Esc>wb"zye:unmenu! Words.<C-R>z<CR>a (the rhs is in <> notation, you can copy/paste this text to try out the mappings, or put these lines in your gvimrc; "<C-R>" is CTRL-R, "<CR>" is the <CR> key. |<>|) - *tooltips* *menu-tips* + *tooltips* *menu-tips* Tooltips & Menu tips See section |42.4| in the user manual. - *:tmenu* -:tm[enu] {menupath} {rhs} Define a tip for a menu or tool. (only in - X11 and Win32 GUI) + *:tmenu* +:tm[enu] {menupath} {rhs} Define a tip for a menu or tool. (only in + X11 and Win32 GUI) -:tm[enu] [menupath] List menu tips. (only in X11 and Win32 GUI) +:tm[enu] [menupath] List menu tips. (only in X11 and Win32 GUI) - *:tunmenu* -:tu[nmenu] {menupath} Remove a tip for a menu or tool. - (only in X11 and Win32 GUI) + *:tunmenu* +:tu[nmenu] {menupath} Remove a tip for a menu or tool. + (only in X11 and Win32 GUI) Note: To create menus for terminal mode, use |:tlmenu| instead. @@ -615,11 +808,11 @@ highlight group to change its colors. A "tip" can be defined for each menu item. For example, when defining a menu item like this: > - :amenu MyMenu.Hello :echo "Hello"<CR> + :amenu MyMenu.Hello :echo "Hello"<CR> The tip is defined like this: > - :tmenu MyMenu.Hello Displays a greeting. + :tmenu MyMenu.Hello Displays a greeting. And delete it with: > - :tunmenu MyMenu.Hello + :tunmenu MyMenu.Hello Tooltips are currently only supported for the X11 and Win32 GUI. However, they should appear for the other gui platforms in the not too distant future. @@ -638,24 +831,24 @@ a menu item - you don't need to do a :tunmenu as well. You can cause a menu to popup at the cursor. This behaves similarly to the PopUp menus except that any menu tree can be popped up. - *:popup* *:popu* -:popu[p] {name} Popup the menu {name}. The menu named must - have at least one subentry, but need not - appear on the menu-bar (see |hidden-menus|). + *:popup* *:popu* +:popu[p] {name} Popup the menu {name}. The menu named must + have at least one subentry, but need not + appear on the menu-bar (see |hidden-menus|). -:popu[p]! {name} Like above, but use the position of the mouse - pointer instead of the cursor. +:popu[p]! {name} Like above, but use the position of the mouse + pointer instead of the cursor. Example: > - :popup File + :popup File will make the "File" menu (if there is one) appear at the text cursor (mouse pointer if ! was used). > - :amenu ]Toolbar.Make :make<CR> - :popup ]Toolbar + :amenu ]Toolbar.Make :make<CR> + :popup ]Toolbar This creates a popup menu that doesn't exist on the main menu-bar. Note that a menu that starts with ']' will not be displayed. - vim:tw=78:sw=4:ts=8:noet:ft=help:norl: + vim:tw=78:sw=4:ts=8:et:ft=help:norl: diff --git a/runtime/doc/health.txt b/runtime/doc/health.txt index cb70961b55..3d37b88321 100644 --- a/runtime/doc/health.txt +++ b/runtime/doc/health.txt @@ -21,7 +21,7 @@ To run all healthchecks, use: >vim < Plugin authors are encouraged to write new healthchecks. |health-dev| -Commands *health-commands* +COMMANDS *health-commands* *:che* *:checkhealth* :che[ckhealth] Run all healthchecks. @@ -49,6 +49,23 @@ Commands *health-commands* :checkhealth vim* < +USAGE *health-usage* + +Local mappings in the healthcheck buffer: + +q Closes the window. + +Global configuration: + + *g:health* +g:health Dictionary with the following optional keys: + - `style` (`'float'|nil`) Set to "float" to display :checkhealth in + a floating window instead of the default behavior. + + Example: >lua + vim.g.health = { style = 'float' } + +-------------------------------------------------------------------------------- Create a healthcheck *health-dev* Healthchecks are functions that check the user environment, configuration, or diff --git a/runtime/doc/help.txt b/runtime/doc/help.txt index fd8bfd644f..914dc64c0a 100644 --- a/runtime/doc/help.txt +++ b/runtime/doc/help.txt @@ -150,6 +150,7 @@ LANGUAGE SUPPORT |arabic.txt| Arabic language support and editing |hebrew.txt| Hebrew language support and editing |russian.txt| Russian language support and editing +|vietnamese.txt| Vietnamese language support and editing ------------------------------------------------------------------------------ INTEROP diff --git a/runtime/doc/helphelp.txt b/runtime/doc/helphelp.txt index 46b3ab507d..72d37f6088 100644 --- a/runtime/doc/helphelp.txt +++ b/runtime/doc/helphelp.txt @@ -193,6 +193,7 @@ Jump to specific subjects by using tags. This can be done in two ways: Use CTRL-T or CTRL-O to jump back. Use ":q" to close the help window. +Use `g==` to execute the current Lua/Vimscript code block. If there are several matches for an item you are looking for, this is how you can jump to each one of them: diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index 9ee75ea950..0256707420 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1190,7 +1190,7 @@ tag command action ~ |:breakdel| :breakd[el] delete a debugger breakpoint |:breaklist| :breakl[ist] list debugger breakpoints |:browse| :bro[wse] use file selection dialog -|:bufdo| :bufdo execute command in each listed buffer +|:bufdo| :bufd[o] execute command in each listed buffer |:buffers| :buffers list all files in the buffer list |:bunload| :bun[load] unload a specific buffer |:bwipeout| :bw[ipeout] really delete a buffer @@ -1206,7 +1206,7 @@ tag command action ~ |:cafter| :caf[ter] go to error after current cursor |:call| :cal[l] call a function |:catch| :cat[ch] part of a :try command -|:cbefore| :cbef[ore] go to error before current cursor +|:cbefore| :cbe[fore] go to error before current cursor |:cbelow| :cbel[ow] go to error below current line |:cbottom| :cbo[ttom] scroll to the bottom of the quickfix window |:cbuffer| :cb[uffer] parse error messages and jump to first error @@ -1262,6 +1262,7 @@ tag command action ~ |:delete| :d[elete] delete lines |:debug| :deb[ug] run a command in debugging mode |:debuggreedy| :debugg[reedy] read debug mode commands from normal input +|:defer| :defe[r] call function when current function is done |:delcommand| :delc[ommand] delete user-defined command |:delfunction| :delf[unction] delete a user function |:delmarks| :delm[arks] delete marks @@ -1271,7 +1272,7 @@ tag command action ~ |:diffpatch| :diffp[atch] apply a patch and show differences |:diffput| :diffpu[t] remove differences in other buffer |:diffsplit| :diffs[plit] show differences with another file -|:diffthis| :diffthis make current window a diff window +|:diffthis| :difft[his] make current window a diff window |:digraphs| :dig[raphs] show or enter digraphs |:display| :di[splay] display registers |:djump| :dj[ump] jump to #define @@ -1372,7 +1373,7 @@ tag command action ~ |:last| :la[st] go to the last file in the argument list |:language| :lan[guage] set the language (locale) |:later| :lat[er] go to newer change, redo -|:lbefore| :lbef[ore] go to location before current cursor +|:lbefore| :lbe[fore] go to location before current cursor |:lbelow| :lbel[ow] go to location below current line |:lbottom| :lbo[ttom] scroll to the bottom of the location window |:lbuffer| :lb[uffer] parse locations and jump to first location @@ -1410,7 +1411,7 @@ tag command action ~ |:lockmarks| :loc[kmarks] following command keeps marks where they are |:lockvar| :lockv[ar] lock variables |:lolder| :lol[der] go to older location list -|:lopen| :lope[n] open location window +|:lopen| :lop[en] open location window |:lprevious| :lp[revious] go to previous location |:lpfile| :lpf[ile] go to last location in previous file |:lrewind| :lr[ewind] go to the specified location, default first one @@ -1472,6 +1473,7 @@ tag command action ~ |:ownsyntax| :ow[nsyntax] set new local syntax highlight for this window |:packadd| :pa[ckadd] add a plugin from 'packpath' |:packloadall| :packl[oadall] load all packages under 'packpath' +|:pbuffer| :pb[uffer] edit buffer in the preview window |:pclose| :pc[lose] close preview window |:pedit| :ped[it] edit file in the preview window |:perl| :pe[rl] execute perl command @@ -1569,6 +1571,8 @@ tag command action ~ |:sign| :sig[n] manipulate signs |:silent| :sil[ent] run a command silently |:sleep| :sl[eep] do nothing for a few seconds +|:sleep!| :sl[eep]! do nothing for a few seconds, without the + cursor visible |:slast| :sla[st] split window and go to last file in the argument list |:smagic| :sm[agic] :substitute with 'magic' @@ -1615,7 +1619,7 @@ tag command action ~ |:tNext| :tN[ext] jump to previous matching tag |:tabNext| :tabN[ext] go to previous tab page |:tabclose| :tabc[lose] close current tab page -|:tabdo| :tabdo execute command in each tab page +|:tabdo| :tabd[o] execute command in each tab page |:tabedit| :tabe[dit] edit a file in a new tab page |:tabfind| :tabf[ind] find file in 'path', edit it in a new tab page |:tabfirst| :tabfir[st] go to first tab page @@ -1684,7 +1688,7 @@ tag command action ~ |:vsplit| :vs[plit] split current window vertically |:vunmap| :vu[nmap] like ":unmap" but for Visual+Select mode |:vunmenu| :vunme[nu] remove menu for Visual+Select mode -|:windo| :windo execute command in each window +|:windo| :wind[o] execute command in each window |:write| :w[rite] write to a file |:wNext| :wN[ext] write to a file and go to previous file in argument list diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt index 48fd442b7e..bc0e9ba314 100644 --- a/runtime/doc/insert.txt +++ b/runtime/doc/insert.txt @@ -1189,6 +1189,7 @@ items: |hl-PmenuKind| highlight group, allowing for the customization of ctermfg and guifg properties for the completion kind + match See "matches" in |complete_info()|. All of these except "icase", "equal", "dup" and "empty" must be a string. If an item does not meet these requirements then an error message is given and @@ -2026,7 +2027,7 @@ the cursor is, or below the specified line. To insert text above the first line use the command ":0r {name}". After the ":read" command, the cursor is left on the first non-blank in the -first new line. Unless in Ex mode, then the cursor is left on the last new +first new line. If in Ex mode, then the cursor is left on the last new line (sorry, this is Vi compatible). If a file name is given with ":r", it becomes the alternate file. This can be diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt index d099c29bdb..0c654b8b30 100644 --- a/runtime/doc/intro.txt +++ b/runtime/doc/intro.txt @@ -1,15 +1,15 @@ *intro.txt* Nvim - NVIM REFERENCE MANUAL + NVIM REFERENCE MANUAL -Nvim *ref* *reference* +Nvim *ref* *reference* Type |gO| to see the table of contents. ============================================================================== -Introduction *intro* +Introduction *intro* Vim is a text editor which includes most commands from the Unix program "Vi" and many new ones. @@ -21,52 +21,75 @@ It can be accessed from within Vim with the <Help> or <F1> key and with the is not located in the default place. You can jump to subjects like with tags: Use CTRL-] to jump to a subject under the cursor, use CTRL-T to jump back. - *pronounce* + *pronounce* Vim is pronounced as one word, like Jim. So Nvim is "En-Vim", two syllables. This manual is a reference for all Nvim editor and API features. It is not an -introduction; instead for beginners, there is a hands-on |tutor| and a user -manual |usr_toc.txt|. - - *book* -There are many books on Vi and Vim. We recommend: - - "Practical Vim" by Drew Neil - "Modern Vim" by Drew Neil - https://vimcasts.org/publications/ - -"Practical Vim" is acclaimed for its focus on quickly learning common editing -tasks with Vim. "Modern Vim" explores new features in Nvim and Vim 8. - - "Vim - Vi Improved" by Steve Oualline - -This was the first book dedicated to Vim. Parts of it were included in the -user manual. |frombook| ISBN: 0735710015 -For more information try one of these: - https://iccf-holland.org/click5.html - https://www.vim.org/iccf/click5.html - -============================================================================== -Nvim on the interwebs *internet* - - *www* *distribution* *download* - - Nvim home page: https://neovim.io/ - Downloads: https://github.com/neovim/neovim/releases - Vim FAQ: https://vimhelp.org/vim_faq.txt.html - - - *bugs* *bug-report* -Report bugs and request features here: -https://github.com/neovim/neovim/issues - +introduction; instead for beginners, there is a hands-on |tutor|, |lua-guide|, +and |user-manual|. + +------------------------------------------------------------------------------ +Resources *resources* + + *internet* *www* *distribution* +Nvim home page: + + https://neovim.io/ + + *book* +There are many resources to learn Vi, Vim, and Nvim. We recommend: + +- "Practical Vim" by Drew Neil. Acclaimed for its focus on quickly learning + common editing tasks with Vim. +- "Modern Vim" by Drew Neil. Explores new features in Nvim and Vim 8. +- https://vimcasts.org/publications/ +- "Vim - Vi Improved" by Steve Oualline. This was the first book dedicated to + Vim. Parts of it were included in the Vim user manual. |frombook| ISBN: + 0735710015 +- For more information try one of these: + - https://iccf-holland.org/click5.html + - https://www.vim.org/iccf/click5.html +- Vim FAQ: https://vimhelp.org/vim_faq.txt.html + + *bugs* *bug-report* *feature-request* +Report bugs and request features here: https://github.com/neovim/neovim/issues Be brief, yet complete. Always give a reproducible example and try to find -out which settings or other things trigger the bug. +out which settings or other things trigger the bug. If Nvim crashed, try to +get a backtrace (see |dev-tools-backtrace|). -If Nvim crashes, try to get a backtrace. See |debug.txt|. +============================================================================== +Installing Nvim *install* + + *download* *upgrade* *ubuntu* +To install or upgrade Nvim, you can... +- Download a pre-built archive: + https://github.com/neovim/neovim/releases +- Use your system package manager: + https://github.com/neovim/neovim/blob/master/INSTALL.md#install-from-package +- Build from source: + https://github.com/neovim/neovim/blob/master/INSTALL.md#install-from-source + +------------------------------------------------------------------------------ +Un-installing Nvim *uninstall* + +To uninstall Nvim: +- If you downloaded a pre-built archive or built Nvim from source (e.g. + `make install`), just delete its files, typically located in: > + /usr/local/bin/nvim + /usr/local/share/nvim +< + - To find where Nvim is installed, run these commands: > + :echo v:progpath + :echo $VIMRUNTIME +< +- If you installed via package manager, read your package manager's + documentation. Common examples: + - APT (Debian, Ubuntu, …): `apt-get remove neovim` + - Homebrew (macOS): `brew uninstall neovim` + - Scoop (Windows): `scoop uninstall neovim` ============================================================================== -Sponsor Vim/Nvim development *sponsor* *register* +Sponsor Vim/Nvim development *sponsor* *register* Fixing bugs and adding new features takes a lot of time and effort. To show your appreciation for the work and motivate developers to continue working on @@ -78,121 +101,15 @@ motivation to keep working on Vim! For the most recent information about sponsoring look on the Vim web site: - https://www.vim.org/sponsor/ + https://www.vim.org/sponsor/ Nvim development is funded separately from Vim: - https://neovim.io/#sponsor - -============================================================================== -Credits *credits* - -Most of Vim was written by Bram Moolenaar <Bram@vim.org> |Bram-Moolenaar|. - -Parts of the documentation come from several Vi manuals, written by: - W.N. Joy - Alan P.W. Hewett - Mark Horton - -The Vim editor is based on Stevie and includes (ideas from) other software, -worked on by the people mentioned here. Other people helped by sending me -patches, suggestions and giving feedback about what is good and bad in Vim. - -Vim would never have become what it is now, without the help of these people! - - Ron Aaron Win32 GUI changes - Mohsin Ahmed encryption - Zoltan Arpadffy work on VMS port - Tony Andrews Stevie - Gert van Antwerpen changes for DJGPP on MS-DOS - Berkeley DB(3) ideas for swap file implementation - Keith Bostic Nvi - Walter Briscoe Makefile updates, various patches - Ralf Brown SPAWNO library for MS-DOS - Robert Colon many useful remarks - Marcin Dalecki GTK+ GUI port, toolbar icons, gettext() - Kayhan Demirel sent me news in Uganda - Chris & John Downey xvi (ideas for multi-windows version) - Henk Elbers first VMS port - Daniel Elstner GTK+ 2 port - Eric Fischer Mac port, 'cindent', and other improvements - Benji Fisher Answering lots of user questions - Bill Foster Athena GUI port (later removed) - Google Let Bram work on Vim one day a week - Loic Grenie xvim (ideas for multi windows version) - Sven Guckes Vim promoter and previous WWW page maintainer - Darren Hiebert Exuberant ctags - Jason Hildebrand GTK+ 2 port - Bruce Hunsaker improvements for VMS port - Andy Kahn Cscope support, GTK+ GUI port - Oezguer Kesim Maintainer of Vim Mailing Lists - Axel Kielhorn work on the Macintosh port - Steve Kirkendall Elvis - Roger Knobbe original port to Windows NT - Sergey Laskavy Vim's help from Moscow - Felix von Leitner Previous maintainer of Vim Mailing Lists - David Leonard Port of Python extensions to Unix - Avner Lottem Edit in right-to-left windows - Flemming Madsen X11 client-server, various features and patches - Tony Mechelynck answers many user questions - Paul Moore Python interface extensions, many patches - Katsuhito Nagano Work on multibyte versions - Sung-Hyun Nam Work on multibyte versions - Vince Negri Win32 GUI and generic console enhancements - Steve Oualline Author of the first Vim book |frombook| - Dominique Pelle Valgrind reports and many fixes - A.Politz Many bug reports and some fixes - George V. Reilly Win32 port, Win32 GUI start-off - Stephen Riehm bug collector - Stefan Roemer various patches and help to users - Ralf Schandl IBM OS/390 port - Olaf Seibert DICE and BeBox version, regexp improvements - Mortaza Shiran Farsi patches - Peter da Silva termlib - Paul Slootman OS/2 port - Henry Spencer regular expressions - Dany St-Amant Macintosh port - Tim Thompson Stevie - G. R. (Fred) Walter Stevie - Sven Verdoolaege Perl interface - Robert Webb Command-line completion, GUI versions, and - lots of patches - Ingo Wilken Tcl interface - Mike Williams PostScript printing - Juergen Weigert Lattice version, AUX improvements, Unix and - MS-DOS ports, autoconf - Stefan 'Sec' Zehl Maintainer of vim.org - Yasuhiro Matsumoto many MS-Windows improvements - Ken Takata fixes and features - Kazunobu Kuriyama GTK 3 - Christian Brabandt many fixes, features, user support, etc. - Yegappan Lakshmanan many quickfix features - -I wish to thank all the people that sent me bug reports and suggestions. The -list is too long to mention them all here. Vim would not be the same without -the ideas from all these people: They keep Vim alive! -*love* *peace* *friendship* *gross-national-happiness* - - -Documentation may refer to other versions of Vi: - *Vi* *vi* -Vi "the original". Without further remarks this is the version - of Vi that appeared in Sun OS 4.x. ":version" returns - "Version 3.7, 6/7/85". Source code only available with a license. - *Nvi* -Nvi The "New" Vi. The version of Vi that comes with BSD 4.4 and FreeBSD. - Very good compatibility with the original Vi, with a few extensions. - The version used is 1.79. ":version" returns "Version 1.79 - (10/23/96)". Source code is freely available. - *Elvis* -Elvis Another Vi clone, made by Steve Kirkendall. Very compact but isn't - as flexible as Vim. Source code is freely available. - -Vim Nvim is based on Vim. https://www.vim.org/ + https://neovim.io/#sponsor ============================================================================== -Bram Moolenaar *Bram* *Moolenaar* *Bram-Moolenaar* *brammool* +Bram Moolenaar *Bram* *Moolenaar* *Bram-Moolenaar* *brammool* Nvim is a fork of the Vim ("Vi IMproved") text editor, which was originally developed by Bram Moolenaar. Searching his name within the source code of @@ -201,11 +118,11 @@ On August 3, 2023, he passed away at the age of 62. If Vim or Nvim have been of use to you in your life, please read |Uganda| and consider honoring his memory however you may see fit. -Obituary Articles: https://github.com/vim/vim/discussions/12742 -Say Farewell: https://github.com/vim/vim/discussions/12737 +- Obituary Articles: https://github.com/vim/vim/discussions/12742 +- Say Farewell: https://github.com/vim/vim/discussions/12737 ============================================================================== -Notation *notation* +Notation *notation* When syntax highlighting is used to read this, text that is not typed literally is often highlighted with the Special group. These are items in [], @@ -215,177 +132,177 @@ Note that Vim uses all possible characters in commands. Sometimes the [], {} and <> are part of what you type, the context should make this clear. -[] Characters in square brackets are optional. - - *count* *[count]* -[count] An optional number that may precede the command to multiply - or iterate the command. If no number is given, a count of one - is used, unless otherwise noted. Note that in this manual the - [count] is not mentioned in the description of the command, - but only in the explanation. This was done to make the - commands easier to look up. If the 'showcmd' option is on, - the (partially) entered count is shown at the bottom of the - window. You can use <Del> to erase the last digit (|N<Del>|). - - *[quotex]* -["x] An optional register designation where text can be stored. - See |registers|. The x is a single character between 'a' and - 'z' or 'A' and 'Z' or '"', and in some cases (with the put - command) between '0' and '9', '%', '#', or others. The - uppercase and lowercase letter designate the same register, - but the lowercase letter is used to overwrite the previous - register contents, while the uppercase letter is used to - append to the previous register contents. Without the ""x" or - with """" the stored text is put into the unnamed register. - - *{}* -{} Curly braces denote parts of the command which must appear, - but which can take a number of different values. The - differences between Vim and Vi are also given in curly braces - (this will be clear from the context). - - *{char1-char2}* -{char1-char2} A single character from the range char1 to char2. For - example: {a-z} is a lowercase letter. Multiple ranges may be - concatenated. For example, {a-zA-Z0-9} is any alphanumeric - character. - - *{motion}* *movement* -{motion} A command that moves the cursor. These are explained in - |motion.txt|. Examples: - w to start of next word - b to begin of current word - 4j four lines down - /The<CR> to next occurrence of "The" - This is used after an |operator| command to move over the text - that is to be operated upon. - - If the motion includes a count and the operator also has a - count, the two counts are multiplied. For example: "2d3w" - deletes six words. - - The motion can be backwards, e.g. "db" to delete to the - start of the word. - - The motion can also be a mouse click. The mouse is not - supported in every terminal though. - - The ":omap" command can be used to map characters while an - operator is pending. - - Ex commands can be used to move the cursor. This can be - used to call a function that does some complicated motion. - The motion is always charwise exclusive, no matter - what ":" command is used. This means it's impossible to - include the last character of a line without the line break - (unless 'virtualedit' is set). - If the Ex command changes the text before where the operator - starts or jumps to another buffer the result is - unpredictable. It is possible to change the text further - down. Jumping to another buffer is possible if the current - buffer is not unloaded. - - *{Visual}* -{Visual} A selected text area. It is started with the "v", "V", or - CTRL-V command, then any cursor movement command can be used - to change the end of the selected text. - This is used before an |operator| command to highlight the - text that is to be operated upon. - See |Visual-mode|. - - *<character>* -<character> A special character from the table below, optionally with - modifiers, or a single ASCII character with modifiers. - - *'character'* -'c' A single ASCII character. - - *CTRL-{char}* -CTRL-{char} {char} typed as a control character; that is, typing {char} - while holding the CTRL key down. The case of {char} is - ignored; thus CTRL-A and CTRL-a are equivalent. But in - some terminals and environments, using the SHIFT key will - produce a distinct code (e.g. CTRL-SHIFT-a); in these - environments using the SHIFT key will not trigger commands - such as CTRL-A. - - *'option'* -'option' An option, or parameter, that can be set to a value, is - enclosed in single quotes. See |options|. - - *quotecommandquote* -"command" A reference to a command that you can type is enclosed in - double quotes. -`command` New style command, this distinguishes it from other quoted - text and strings. - - *key-notation* *key-codes* *keycodes* +- [] Characters in square brackets are optional. + + *count* *[count]* +- [count] An optional number that may precede the command to multiply + or iterate the command. If no number is given, a count of one + is used, unless otherwise noted. Note that in this manual the + [count] is not mentioned in the description of the command, + but only in the explanation. This was done to make the + commands easier to look up. If the 'showcmd' option is on, + the (partially) entered count is shown at the bottom of the + window. You can use <Del> to erase the last digit (|N<Del>|). + + *[quotex]* +- ["x] An optional register designation where text can be stored. + See |registers|. The x is a single character between 'a' and + 'z' or 'A' and 'Z' or '"', and in some cases (with the put + command) between '0' and '9', '%', '#', or others. The + uppercase and lowercase letter designate the same register, + but the lowercase letter is used to overwrite the previous + register contents, while the uppercase letter is used to + append to the previous register contents. Without the ""x" or + with """" the stored text is put into the unnamed register. + + *{}* +- {} Curly braces denote parts of the command which must appear, + but which can take a number of different values. The + differences between Vim and Vi are also given in curly braces + (this will be clear from the context). + + *{char1-char2}* +- {char1-char2} A single character from the range char1 to char2. For + example: {a-z} is a lowercase letter. Multiple ranges may be + concatenated. For example, {a-zA-Z0-9} is any alphanumeric + character. + + *{motion}* *movement* +- {motion} A command that moves the cursor. These are explained in + |motion.txt|. + - Examples: + - `w` to start of next word + - `b` to begin of current word + - `4j` four lines down + - `/The<CR>` to next occurrence of "The" + - This is used after an |operator| command to move over the + text that is to be operated upon. + - If the motion includes a count and the operator also has + a count, the two counts are multiplied. For example: + "2d3w" deletes six words. + - The motion can be backwards, e.g. "db" to delete to the + start of the word. + - The motion can also be a mouse click. The mouse is not + supported in every terminal though. + - The ":omap" command can be used to map characters while an + operator is pending. + - Ex commands can be used to move the cursor. This can be + used to call a function that does some complicated motion. + The motion is always charwise exclusive, no matter what + ":" command is used. This means it's impossible to + include the last character of a line without the line + break (unless 'virtualedit' is set). If the Ex command + changes the text before where the operator starts or jumps + to another buffer the result is unpredictable. It is + possible to change the text further down. Jumping to + another buffer is possible if the current buffer is not + unloaded. + + *{Visual}* +- {Visual} A selected text area. It is started with the "v", "V", or + CTRL-V command, then any cursor movement command can be used + to change the end of the selected text. + This is used before an |operator| command to highlight the + text that is to be operated upon. + See |Visual-mode|. + + *<character>* +- <character> A special character from the table below, optionally with + modifiers, or a single ASCII character with modifiers. + + *'character'* +- 'c' A single ASCII character. + + *CTRL-{char}* +- CTRL-{char} {char} typed as a control character; that is, typing {char} + while holding the CTRL key down. The case of {char} is + ignored; thus CTRL-A and CTRL-a are equivalent. But in + some terminals and environments, using the SHIFT key will + produce a distinct code (e.g. CTRL-SHIFT-a); in these + environments using the SHIFT key will not trigger commands + such as CTRL-A. + + *'option'* +- 'option' An option, or parameter, that can be set to a value, is + enclosed in single quotes. See |options|. + + *quotecommandquote* +- "command" A reference to a command that you can type is enclosed in + double quotes. +- `command` New style command, this distinguishes it from other quoted + text and strings. + + *key-notation* *key-codes* *keycodes* These names for keys are used in the documentation. They can also be used with the ":map" command. -notation meaning equivalent decimal value(s) ~ ------------------------------------------------------------------------ ~ -<Nul> zero CTRL-@ 0 (stored as 10) *<Nul>* -<BS> backspace CTRL-H 8 *backspace* -<Tab> tab CTRL-I 9 *tab* *Tab* - *linefeed* -<NL> linefeed CTRL-J 10 (used for <Nul>) -<CR> carriage return CTRL-M 13 *carriage-return* -<Return> same as <CR> *<Return>* -<Enter> same as <CR> *<Enter>* -<Esc> escape CTRL-[ 27 *escape* *<Esc>* -<Space> space 32 *space* -<lt> less-than < 60 *<lt>* -<Bslash> backslash \ 92 *backslash* *<Bslash>* -<Bar> vertical bar | 124 *<Bar>* -<Del> delete 127 -<CSI> command sequence intro ALT-Esc 155 *<CSI>* - -<EOL> end-of-line (can be <CR>, <NL> or <CR><NL>, - depends on system and 'fileformat') *<EOL>* -<Ignore> cancel wait-for-character *<Ignore>* -<NOP> no-op: do nothing (useful in mappings) *<Nop>* - -<Up> cursor-up *cursor-up* *cursor_up* -<Down> cursor-down *cursor-down* *cursor_down* -<Left> cursor-left *cursor-left* *cursor_left* -<Right> cursor-right *cursor-right* *cursor_right* -<S-Up> shift-cursor-up -<S-Down> shift-cursor-down -<S-Left> shift-cursor-left -<S-Right> shift-cursor-right -<C-Left> control-cursor-left -<C-Right> control-cursor-right -<F1> - <F12> function keys 1 to 12 *function_key* *function-key* -<S-F1> - <S-F12> shift-function keys 1 to 12 *<S-F1>* -<Help> help key -<Undo> undo key -<Insert> insert key -<Home> home *home* -<End> end *end* -<PageUp> page-up *page_up* *page-up* -<PageDown> page-down *page_down* *page-down* -<kUp> keypad cursor-up *keypad-cursor-up* -<kDown> keypad cursor-down *keypad-cursor-down* -<kLeft> keypad cursor-left *keypad-cursor-left* -<kRight> keypad cursor-right *keypad-cursor-right* -<kHome> keypad home (upper left) *keypad-home* -<kEnd> keypad end (lower left) *keypad-end* -<kOrigin> keypad origin (middle) *keypad-origin* -<kPageUp> keypad page-up (upper right) *keypad-page-up* -<kPageDown> keypad page-down (lower right) *keypad-page-down* -<kDel> keypad delete *keypad-delete* -<kPlus> keypad + *keypad-plus* -<kMinus> keypad - *keypad-minus* -<kMultiply> keypad * *keypad-multiply* -<kDivide> keypad / *keypad-divide* -<kPoint> keypad . *keypad-point* -<kComma> keypad , *keypad-comma* -<kEqual> keypad = *keypad-equal* -<kEnter> keypad Enter *keypad-enter* -<k0> - <k9> keypad 0 to 9 *keypad-0* *keypad-9* -<S-…> shift-key *shift* *<S-* -<C-…> control-key *control* *ctrl* *<C-* -<M-…> alt-key or meta-key *META* *ALT* *<M-* -<A-…> same as <M-…> *<A-* -<T-…> meta-key when it's not alt *<T-* -<D-…> command-key or "super" key *<D-* ------------------------------------------------------------------------ ~ +notation meaning equivalent decimal value(s) ~ +<Nul> zero CTRL-@ 0 (stored as 10) *<Nul>* +<BS> backspace CTRL-H 8 *backspace* +<Tab> tab CTRL-I 9 *tab* *Tab* + *linefeed* +<NL> linefeed CTRL-J 10 (used for <Nul>) +<CR> carriage return CTRL-M 13 *carriage-return* +<Return> same as <CR> *<Return>* +<Enter> same as <CR> *<Enter>* +<Esc> escape CTRL-[ 27 *escape* *<Esc>* +<Space> space 32 *space* +<lt> less-than < 60 *<lt>* +<Bslash> backslash \ 92 *backslash* *<Bslash>* +<Bar> vertical bar | 124 *<Bar>* +<Del> delete 127 +<CSI> command sequence intro ALT-Esc 155 *<CSI>* + +<EOL> end-of-line (can be <CR>, <NL> or <CR><NL>, + depends on system and 'fileformat') *<EOL>* +<Ignore> cancel wait-for-character *<Ignore>* +<NOP> no-op: do nothing (useful in mappings) *<Nop>* + +<Up> cursor-up *cursor-up* *cursor_up* +<Down> cursor-down *cursor-down* *cursor_down* +<Left> cursor-left *cursor-left* *cursor_left* +<Right> cursor-right *cursor-right* *cursor_right* +<S-Up> shift-cursor-up +<S-Down> shift-cursor-down +<S-Left> shift-cursor-left +<S-Right> shift-cursor-right +<C-Left> control-cursor-left +<C-Right> control-cursor-right +<F1> - <F12> function keys 1 to 12 *function_key* *function-key* +<S-F1> - <S-F12> shift-function keys 1 to 12 *<S-F1>* +<Help> help key +<Undo> undo key +<Insert> insert key +<Home> home *home* +<End> end *end* +<PageUp> page-up *page_up* *page-up* +<PageDown> page-down *page_down* *page-down* +<kUp> keypad cursor-up *keypad-cursor-up* +<kDown> keypad cursor-down *keypad-cursor-down* +<kLeft> keypad cursor-left *keypad-cursor-left* +<kRight> keypad cursor-right *keypad-cursor-right* +<kHome> keypad home (upper left) *keypad-home* +<kEnd> keypad end (lower left) *keypad-end* +<kOrigin> keypad origin (middle) *keypad-origin* +<kPageUp> keypad page-up (upper right) *keypad-page-up* +<kPageDown> keypad page-down (lower right) *keypad-page-down* +<kDel> keypad delete *keypad-delete* +<kPlus> keypad + *keypad-plus* +<kMinus> keypad - *keypad-minus* +<kMultiply> keypad * *keypad-multiply* +<kDivide> keypad / *keypad-divide* +<kPoint> keypad . *keypad-point* +<kComma> keypad , *keypad-comma* +<kEqual> keypad = *keypad-equal* +<kEnter> keypad Enter *keypad-enter* +<k0> - <k9> keypad 0 to 9 *keypad-0* *keypad-9* +<S-…> shift-key *shift* *<S-* +<C-…> control-key *control* *ctrl* *<C-* +<M-…> alt-key or meta-key *META* *ALT* *<M-* +<A-…> same as <M-…> *<A-* +<T-…> meta-key when it's not alt *<T-* +<D-…> command-key or "super" key *<D-* + Note: @@ -400,125 +317,125 @@ Note: - It is possible to notate combined modifiers (e.g. <M-C-T> for CTRL-ALT-T), but your terminal must encode the input for that to work. |tui-input| - *<>* + *<>* Examples are often given in the <> notation. Sometimes this is just to make clear what you need to type, but often it can be typed literally, e.g., with the ":map" command. The rules are: - 1. Printable characters are typed directly, except backslash and "<" - 2. Backslash is represented with "\\", double backslash, or "<Bslash>". - 3. Literal "<" is represented with "\<" or "<lt>". When there is no - confusion possible, "<" can be used directly. - 4. "<key>" means the special key typed (see the table above). Examples: - <Esc> Escape key - <C-G> CTRL-G - <Up> cursor up key - <C-LeftMouse> Control- left mouse click - <S-F11> Shifted function key 11 - <M-a> Meta- a ('a' with bit 8 set) - <M-A> Meta- A ('A' with bit 8 set) +1. Printable characters are typed directly, except backslash and "<" +2. Backslash is represented with "\\", double backslash, or "<Bslash>". +3. Literal "<" is represented with "\<" or "<lt>". When there is no + confusion possible, "<" can be used directly. +4. "<key>" means the special key typed (see the table above). Examples: + - <Esc> Escape key + - <C-G> CTRL-G + - <Up> cursor up key + - <C-LeftMouse> Control- left mouse click + - <S-F11> Shifted function key 11 + - <M-a> Meta- a ('a' with bit 8 set) + - <M-A> Meta- A ('A' with bit 8 set) The <> notation uses <lt> to escape the special meaning of key names. Using a backslash also works, but only when 'cpoptions' does not include the 'B' flag. Examples for mapping CTRL-H to the six characters "<Home>": >vim - :imap <C-H> \<Home> - :imap <C-H> <lt>Home> + :imap <C-H> \<Home> + :imap <C-H> <lt>Home> The first one only works when the 'B' flag is not in 'cpoptions'. The second one always works. To get a literal "<lt>" in a mapping: >vim - :map <C-L> <lt>lt> + :map <C-L> <lt>lt> The notation can be used in a double quoted strings, using "\<" at the start, e.g. "\<C-Space>". This results in a special key code. To convert this back to readable text use `keytrans()`. ============================================================================== -Modes, introduction *vim-modes-intro* *vim-modes* +Modes, introduction *vim-modes-intro* *vim-modes* Vim has seven BASIC modes: - *Normal* *Normal-mode* *command-mode* -Normal mode In Normal mode you can enter all the normal editor - commands. If you start the editor you are in this - mode. This is also known as command mode. - -Visual mode This is like Normal mode, but the movement commands - extend a highlighted area. When a non-movement - command is used, it is executed for the highlighted - area. See |Visual-mode|. - If the 'showmode' option is on "-- VISUAL --" is shown - at the bottom of the window. - -Select mode This looks most like the MS-Windows selection mode. - Typing a printable character deletes the selection - and starts Insert mode. See |Select-mode|. - If the 'showmode' option is on "-- SELECT --" is shown - at the bottom of the window. - -Insert mode In Insert mode the text you type is inserted into the - buffer. See |Insert-mode|. - If the 'showmode' option is on "-- INSERT --" is shown - at the bottom of the window. - -Command-line mode In Command-line mode (also called Cmdline mode) you -Cmdline mode can enter one line of text at the bottom of the - window. This is for the Ex commands, ":", the pattern - search commands, "?" and "/", and the filter command, - "!". |Cmdline-mode| - -Ex mode Like Command-line mode, but after entering a command - you remain in Ex mode. Very limited editing of the - command line. |Ex-mode| - - *Terminal-mode* -Terminal mode In Terminal mode all input (except CTRL-\) is sent to - the process running in the current |terminal| buffer. - If CTRL-\ is pressed, the next key is sent unless it - is CTRL-N (|CTRL-\_CTRL-N|) or CTRL-O (|t_CTRL-\_CTRL-O|). - If the 'showmode' option is on "-- TERMINAL --" is shown - at the bottom of the window. + *Normal* *Normal-mode* *command-mode* +- Normal mode: In Normal mode you can enter all the normal editor + commands. If you start the editor you are in this + mode. This is also known as command mode. + +- Visual mode: This is like Normal mode, but the movement commands + extend a highlighted area. When a non-movement + command is used, it is executed for the highlighted + area. See |Visual-mode|. + If the 'showmode' option is on "-- VISUAL --" is shown + at the bottom of the window. + +- Select mode: This looks most like the MS-Windows selection mode. + Typing a printable character deletes the selection + and starts Insert mode. See |Select-mode|. + If the 'showmode' option is on "-- SELECT --" is shown + at the bottom of the window. + +- Insert mode: In Insert mode the text you type is inserted into the + buffer. See |Insert-mode|. + If the 'showmode' option is on "-- INSERT --" is shown + at the bottom of the window. + +- Cmdline mode: In Command-line mode (also called Cmdline mode) you + can enter one line of text at the bottom of the + window. This is for the Ex commands, ":", the pattern + search commands, "?" and "/", and the filter command, + "!". |Cmdline-mode| + +- Ex mode: Like Command-line mode, but after entering a command + you remain in Ex mode. Very limited editing of the + command line. |Ex-mode| + + *Terminal-mode* +- Terminal mode: In Terminal mode all input (except CTRL-\) is sent to + the process running in the current |terminal| buffer. + If CTRL-\ is pressed, the next key is sent unless it + is CTRL-N (|CTRL-\_CTRL-N|) or CTRL-O (|t_CTRL-\_CTRL-O|). + If the 'showmode' option is on "-- TERMINAL --" is shown + at the bottom of the window. There are six ADDITIONAL modes. These are variants of the BASIC modes: - *Operator-pending* *Operator-pending-mode* -Operator-pending mode This is like Normal mode, but after an operator - command has started, and Vim is waiting for a {motion} - to specify the text that the operator will work on. - -Replace mode Replace mode is a special case of Insert mode. You - can do the same things as in Insert mode, but for - each character you enter, one character of the existing - text is deleted. See |Replace-mode|. - If the 'showmode' option is on "-- REPLACE --" is - shown at the bottom of the window. - -Virtual Replace mode Virtual Replace mode is similar to Replace mode, but - instead of file characters you are replacing screen - real estate. See |Virtual-Replace-mode|. - If the 'showmode' option is on "-- VREPLACE --" is - shown at the bottom of the window. - -Insert Normal mode Entered when CTRL-O is typed in Insert mode (see - |i_CTRL-O|). This is like Normal mode, but after - executing one command Vim returns to Insert mode. - If the 'showmode' option is on "-- (insert) --" is - shown at the bottom of the window. - -Insert Visual mode Entered when starting a Visual selection from Insert - mode, e.g., by using CTRL-O and then "v", "V" or - CTRL-V. When the Visual selection ends, Vim returns - to Insert mode. - If the 'showmode' option is on "-- (insert) VISUAL --" - is shown at the bottom of the window. - -Insert Select mode Entered when starting Select mode from Insert mode. - E.g., by dragging the mouse or <S-Right>. - When the Select mode ends, Vim returns to Insert mode. - If the 'showmode' option is on "-- (insert) SELECT --" - is shown at the bottom of the window. + *Operator-pending* *Operator-pending-mode* +- Operator-pending mode: This is like Normal mode, but after an operator + command has started, and Vim is waiting for a {motion} + to specify the text that the operator will work on. + +- Replace mode: Replace mode is a special case of Insert mode. You + can do the same things as in Insert mode, but for + each character you enter, one character of the existing + text is deleted. See |Replace-mode|. + If the 'showmode' option is on "-- REPLACE --" is + shown at the bottom of the window. + +- Virtual Replace mode: Virtual Replace mode is similar to Replace mode, but + instead of file characters you are replacing screen + real estate. See |Virtual-Replace-mode|. + If the 'showmode' option is on "-- VREPLACE --" is + shown at the bottom of the window. + +- Insert Normal mode: Entered when CTRL-O is typed in Insert mode (see + |i_CTRL-O|). This is like Normal mode, but after + executing one command Vim returns to Insert mode. + If the 'showmode' option is on "-- (insert) --" is + shown at the bottom of the window. + +- Insert Visual mode: Entered when starting a Visual selection from Insert + mode, e.g., by using CTRL-O and then "v", "V" or + CTRL-V. When the Visual selection ends, Vim returns + to Insert mode. + If the 'showmode' option is on "-- (insert) VISUAL --" + is shown at the bottom of the window. + +- Insert Select mode: Entered when starting Select mode from Insert mode. + E.g., by dragging the mouse or <S-Right>. + When the Select mode ends, Vim returns to Insert mode. + If the 'showmode' option is on "-- (insert) SELECT --" + is shown at the bottom of the window. ============================================================================== -Switching from mode to mode *mode-switching* +Switching from mode to mode *mode-switching* If for any reason you do not know which mode you are in, you can always get back to Normal mode by typing <Esc> twice. This doesn't work for Ex mode @@ -528,25 +445,27 @@ hear the bell after you type <Esc>. However, when pressing <Esc> after using CTRL-O in Insert mode you get a beep but you are still in Insert mode, type <Esc> again. - *i_esc* - FROM mode TO mode ~ - Normal Visual Select Insert Replace Cmd-line Ex > - Normal v V ^V *4 *1 R gR : / ? ! Q - Visual *2 ^G c C -- : -- - Select *5 ^O ^G *6 -- -- -- - Insert <Esc> -- -- <Insert> -- -- - Replace <Esc> -- -- <Insert> -- -- - Command-line *3 -- -- :start -- -- - Ex :vi -- -- -- -- -- - --- not possible + *i_esc* + > + FROM mode TO mode + Normal Visual Select Insert Replace Cmd-line Ex > + Normal v V ^V *4 *1 R gR : / ? ! Q + Visual *2 ^G c C -- : -- + Select *5 ^O ^G *6 -- -- -- + Insert <Esc> -- -- <Insert> -- -- + Replace <Esc> -- -- <Insert> -- -- + Command-line *3 -- -- :start -- -- + Ex :vi -- -- -- -- -- + + -- not possible +< -* 1 Go from Normal mode to Insert mode by giving the command "i", "I", "a", +- 1 Go from Normal mode to Insert mode by giving the command "i", "I", "a", "A", "o", "O", "c", "C", "s" or S". -* 2 Go from Visual mode to Normal mode by giving a non-movement command, which +- 2 Go from Visual mode to Normal mode by giving a non-movement command, which causes the command to be executed, or by hitting <Esc> "v", "V" or "CTRL-V" (see |v_v|), which just stops Visual mode without side effects. -* 3 Go from Command-line mode to Normal mode by: +- 3 Go from Command-line mode to Normal mode by: - Hitting <CR> or <NL>, which causes the entered command to be executed. - Deleting the complete line (e.g., with CTRL-U) and giving a final <BS>. - Hitting CTRL-C or <Esc>, which quits the command-line without executing @@ -554,37 +473,37 @@ CTRL-O in Insert mode you get a beep but you are still in Insert mode, type In the last case <Esc> may be the character defined with the 'wildchar' option, in which case it will start command-line completion. You can ignore that and type <Esc> again. -* 4 Go from Normal to Select mode by: +- 4 Go from Normal to Select mode by: - use the mouse to select text while 'selectmode' contains "mouse" - use a non-printable command to move the cursor while keeping the Shift key pressed, and the 'selectmode' option contains "key" - use "v", "V" or "CTRL-V" while 'selectmode' contains "cmd" - use "gh", "gH" or "g CTRL-H" |g_CTRL-H| -* 5 Go from Select mode to Normal mode by using a non-printable command to move +- 5 Go from Select mode to Normal mode by using a non-printable command to move the cursor, without keeping the Shift key pressed. -* 6 Go from Select mode to Insert mode by typing a printable character. The +- 6 Go from Select mode to Insert mode by typing a printable character. The selection is deleted and the character is inserted. - *CTRL-\_CTRL-N* *i_CTRL-\_CTRL-N* *c_CTRL-\_CTRL-N* - *v_CTRL-\_CTRL-N* *t_CTRL-\_CTRL-N* + *CTRL-\_CTRL-N* *i_CTRL-\_CTRL-N* *c_CTRL-\_CTRL-N* + *v_CTRL-\_CTRL-N* *t_CTRL-\_CTRL-N* Additionally the command CTRL-\ CTRL-N or <C-\><C-N> can be used to go to Normal mode from any other mode. This can be used to make sure Vim is in Normal mode, without causing a beep like <Esc> would. However, this does not work in Ex mode. When used after a command that takes an argument, such as |f| or |m|, the timeout set with 'ttimeoutlen' applies. - *CTRL-\_CTRL-G* *i_CTRL-\_CTRL-G* *c_CTRL-\_CTRL-G* *v_CTRL-\_CTRL-G* + *CTRL-\_CTRL-G* *i_CTRL-\_CTRL-G* *c_CTRL-\_CTRL-G* *v_CTRL-\_CTRL-G* CTRL-\ CTRL-G works the same as |CTRL-\_CTRL-N| for backward compatibility. - *gQ* *mode-Ex* *Ex-mode* *Ex* *EX* *E501* -gQ Switch to Ex mode. This is like typing ":" commands - one after another, except: - - You don't have to keep pressing ":". - - The screen doesn't get updated after each command. - Use the `:vi` command (|:visual|) to exit this mode. + *gQ* *mode-Ex* *Ex-mode* *Ex* *EX* *E501* +gQ Switch to Ex mode. This is like typing ":" commands + one after another, except: + - You don't have to keep pressing ":". + - The screen doesn't get updated after each command. + Use the `:vi` command (|:visual|) to exit this mode. ============================================================================== -Window contents *window-contents* +Window contents *window-contents* In Normal mode and Insert/Replace mode the screen window will show the current contents of the buffer: What You See Is What You Get. There are two @@ -601,24 +520,24 @@ Lines longer than the window width will wrap, unless the 'wrap' option is off If the window has room after the last line of the buffer, Vim will show '~' in the first column of the last lines in the window, like this: > - +-----------------------+ - |some line | - |last line | - |~ | - |~ | - +-----------------------+ + +-----------------------+ + |some line | + |last line | + |~ | + |~ | + +-----------------------+ < Thus the '~' lines indicate that the end of the buffer was reached. If the last line in a window doesn't fit, Vim will indicate this with a '@' in the first column of the last lines in the window, like this: > - +-----------------------+ - |first line | - |second line | - |@ | - |@ | - +-----------------------+ + +-----------------------+ + |first line | + |second line | + |@ | + |@ | + +-----------------------+ < Thus the '@' lines indicate that there is a line that doesn't fit in the window. @@ -628,12 +547,12 @@ When the "lastline" flag is present in the 'display' option, you will not see completely, only the part that fits is shown, and the last three characters of the last line are replaced with "@@@", like this: > - +-----------------------+ - |first line | - |second line | - |a very long line that d| - |oesn't fit in the wi@@@| - +-----------------------+ + +-----------------------+ + |first line | + |second line | + |a very long line that d| + |oesn't fit in the wi@@@| + +-----------------------+ < If there is a single line that is too long to fit in the window, this is a special situation. Vim will show only part of the line, around where the @@ -646,7 +565,7 @@ from real characters in the buffer. The 'showbreak' option contains the string to put in front of wrapped lines. - *wrap-off* + *wrap-off* If the 'wrap' option is off, long lines will not wrap. Only the part that fits on the screen is shown. If the cursor is moved to a part of the line that is not shown, the screen is scrolled horizontally. The advantage of @@ -665,8 +584,8 @@ position on the screen. The cursor can only be positioned on the first one. If you set the 'number' option, all lines will be preceded with their number. Tip: If you don't like wrapping lines to mix with the line numbers, -set the 'showbreak' option to eight spaces: - ":set showbreak=\ \ \ \ \ \ \ \ " +set the 'showbreak' option to eight spaces: > + ":set showbreak=\ \ \ \ \ \ \ \ " If you set the 'list' option, <Tab> characters will not be shown as several spaces, but as "^I". A '$' will be placed at the end of the line, so you can @@ -677,19 +596,19 @@ display of the buffer contents is updated as soon as you go back to Command mode. The last line of the window is used for status and other messages. The -status messages will only be used if an option is on: +status messages will only be used if an option is on: > -status message option default Unix default ~ -current mode 'showmode' on on -command characters 'showcmd' on off -cursor position 'ruler' off off + status message option default Unix default + current mode 'showmode' on on + command characters 'showcmd' on off + cursor position 'ruler' off off The current mode is "-- INSERT --" or "-- REPLACE --", see |'showmode'|. The command characters are those that you typed but were not used yet. If you have a slow terminal you can switch off the status messages to speed up editing: > - :set nosc noru nosm + :set nosc noru nosm If there is an error, an error message will be shown for at least one second (in reverse video). @@ -704,70 +623,70 @@ small not a single line will fit in it. Make it at least 40 characters wide to be able to read most messages on the last line. ============================================================================== -Definitions *definitions* *jargon* +Definitions *definitions* *jargon* - buffer Contains lines of text, usually from a file. - screen The whole area that Nvim uses to display things. - window A view on a buffer. There can be multiple windows for - one buffer. - frame Windows are kept in a tree of frames. Each frame - contains a column, row, or window ("leaf" frame). +- buffer: Contains lines of text, usually from a file. +- screen: The whole area that Nvim uses to display things. +- window: A view on a buffer. There can be multiple windows for one buffer. +- frame: Windows are kept in a tree of frames. Each frame contains a column, + row, or window ("leaf" frame). A screen contains one or more windows, separated by status lines and with the command line at the bottom. > - +-------------------------------+ - screen | window 1 | window 2 | - | | | - | | | - |= status line =|= status line =| - | window 3 | - | | - | | - |==== status line ==============| - |command line | - +-------------------------------+ + +-------------------------------+ + screen | window 1 | window 2 | + | | | + | | | + |= status line =|= status line =| + | window 3 | + | | + | | + |==== status line ==============| + |command line | + +-------------------------------+ < The command line is also used for messages. It scrolls up the screen when there is not enough room in the command line. A difference is made between four types of lines: - buffer lines The lines in the buffer. This is the same as the - lines as they are read from/written to a file. They - can be thousands of characters long. - logical lines The buffer lines with folding applied. Buffer lines - in a closed fold are changed to a single logical line: - "+-- 99 lines folded". They can be thousands of - characters long. - window lines The lines displayed in a window: A range of logical - lines with wrapping, line breaks, etc. applied. They - can only be as long as the width of the window allows, - longer lines are wrapped or truncated. - screen lines The lines of the screen that Nvim uses. Consists of - the window lines of all windows, with status lines - and the command line added. They can only be as long - as the width of the screen allows. When the command - line gets longer it wraps and lines are scrolled to - make room. - -buffer lines logical lines window lines screen lines ~ - -1. one 1. one 1. +-- folded 1. +-- folded -2. two 2. +-- folded 2. five 2. five -3. three 3. five 3. six 3. six -4. four 4. six 4. seven 4. seven -5. five 5. seven 5. === status line === -6. six 6. aaa -7. seven 7. bbb - 8. ccc ccc c -1. aaa 1. aaa 1. aaa 9. cc -2. bbb 2. bbb 2. bbb 10. ddd -3. ccc ccc ccc 3. ccc ccc ccc 3. ccc ccc c 11. ~ -4. ddd 4. ddd 4. cc 12. === status line === - 5. ddd 13. (command line) - 6. ~ +- buffer lines: The lines in the buffer. This is the same as the + lines as they are read from/written to a file. They + can be thousands of characters long. +- logical lines: The buffer lines with folding applied. Buffer lines + in a closed fold are changed to a single logical line: + "+-- 99 lines folded". They can be thousands of + characters long. +- window lines: The lines displayed in a window: A range of logical + lines with wrapping, line breaks, etc. applied. They + can only be as long as the width of the window allows, + longer lines are wrapped or truncated. +- screen lines: The lines of the screen that Nvim uses. Consists of + the window lines of all windows, with status lines + and the command line added. They can only be as long + as the width of the screen allows. When the command + line gets longer it wraps and lines are scrolled to + make room. +> + buffer lines logical lines window lines screen lines + ----------------------------------------------------------------------- + 1. one 1. one 1. +-- folded 1. +-- folded + 2. two 2. +-- folded 2. five 2. five + 3. three 3. five 3. six 3. six + 4. four 4. six 4. seven 4. seven + 5. five 5. seven 5. === status line === + 6. six 6. aaa + 7. seven 7. bbb + 8. ccc ccc c + 1. aaa 1. aaa 1. aaa 9. cc + 2. bbb 2. bbb 2. bbb 10. ddd + 3. ccc ccc ccc 3. ccc ccc ccc 3. ccc ccc c 11. ~ + 4. ddd 4. ddd 4. cc 12. === status line === + 5. ddd 13. (command line) + 6. ~ +< API client ~ All external UIs and remote plugins (as opposed to regular Vim plugins) are @@ -775,8 +694,8 @@ All external UIs and remote plugins (as opposed to regular Vim plugins) are to abstract or wrap the RPC API for the convenience of other applications (just like a REST client or SDK such as boto3 for AWS: you can speak AWS REST using an HTTP client like curl, but boto3 wraps that in a convenient python -interface). For example, the Nvim lua-client is an API client: - https://github.com/neovim/lua-client +interface). For example, the Nvim node-client is an API client: + https://github.com/neovim/node-client Host ~ @@ -791,4 +710,4 @@ process and communicates with Nvim via the |api|. ============================================================================== - vim:tw=78:ts=8:noet:ft=help:norl: + vim:tw=78:ts=8:et:sw=4:ft=help:norl: diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index 64bef849fc..25ef7f24cc 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -28,31 +28,110 @@ Follow these steps to get LSP features: upstream installation instructions. You can find language servers here: https://microsoft.github.io/language-server-protocol/implementors/servers/ -2. Use |vim.lsp.start()| to start the LSP server (or attach to an existing - one) when a file is opened. Example: >lua - -- Create an event handler for the FileType autocommand - vim.api.nvim_create_autocmd('FileType', { - -- This handler will fire when the buffer's 'filetype' is "python" - pattern = 'python', - callback = function(args) - vim.lsp.start({ - name = 'my-server-name', - cmd = {'name-of-language-server-executable', '--option', 'arg1', 'arg2'}, - - -- Set the "root directory" to the parent directory of the file in the - -- current buffer (`args.buf`) that contains either a "setup.py" or a - -- "pyproject.toml" file. Files that share a root directory will reuse - -- the connection to the same LSP server. - root_dir = vim.fs.root(args.buf, {'setup.py', 'pyproject.toml'}), - }) - end, - }) +2. Use |vim.lsp.config()| to define a configuration for an LSP client. + Example: >lua + vim.lsp.config['luals'] = { + -- Command and arguments to start the server. + cmd = { 'lua-language-server' }, + + -- Filetypes to automatically attach to. + filetypes = { 'lua' }, + + -- Sets the "root directory" to the parent directory of the file in the + -- current buffer that contains either a ".luarc.json" or a + -- ".luarc.jsonc" file. Files that share a root directory will reuse + -- the connection to the same LSP server. + root_markers = { '.luarc.json', '.luarc.jsonc' }, + + -- Specific settings to send to the server. The schema for this is + -- defined by the server. For example the schema for lua-language-server + -- can be found here https://raw.githubusercontent.com/LuaLS/vscode-lua/master/setting/schema.json + settings = { + Lua = { + runtime = { + version = 'LuaJIT', + } + } + } + } +< +3. Use |vim.lsp.enable()| to enable a configuration. + Example: >lua + vim.lsp.enable('luals') +< +4. Check that the buffer is attached to the server: >vim + :checkhealth vim.lsp < -3. Check that the buffer is attached to the server: >vim - :checkhealth lsp +5. (Optional) Configure keymaps and autocommands to use LSP features. + |lsp-attach| + + *lsp-config* + +Configurations for LSP clients is done via |vim.lsp.config()|. + +When an LSP client starts, it resolves a configuration by merging +configurations, in increasing priority, from the following: + +1. Configuration defined for the `'*'` name. + +2. Configuration from the result of merging all tables returned by + `lsp/<name>.lua` files in 'runtimepath' for a server of name `name`. + +3. Configurations defined anywhere else. + +Note: The merge semantics of configurations follow the behaviour of +|vim.tbl_deep_extend()|. -4. (Optional) Configure keymaps and autocommands to use LSP features. |lsp-config| +Example: +Given: >lua + -- Defined in init.lua + vim.lsp.config('*', { + capabilities = { + textDocument = { + semanticTokens = { + multilineTokenSupport = true, + } + } + }, + root_markers = { '.git' }, + }) + + -- Defined in ../lsp/clangd.lua + return { + cmd = { 'clangd' }, + root_markers = { '.clangd', 'compile_commands.json' }, + filetypes = { 'c', 'cpp' }, + } + + -- Defined in init.lua + vim.lsp.config('clangd', { + filetypes = { 'c' }, + }) +< +Results in the configuration: >lua + { + -- From the clangd configuration in <rtp>/lsp/clangd.lua + cmd = { 'clangd' }, + + -- From the clangd configuration in <rtp>/lsp/clangd.lua + -- Overrides the * configuration in init.lua + root_markers = { '.clangd', 'compile_commands.json' }, + + -- From the clangd configuration in init.lua + -- Overrides the * configuration in init.lua + filetypes = { 'c' }, + + -- From the * configuration in init.lua + capabilities = { + textDocument = { + semanticTokens = { + multilineTokenSupport = true, + } + } + } + } +< *lsp-defaults* When the Nvim LSP client starts it enables diagnostics |vim.diagnostic| (see |vim.diagnostic.config()| to customize). It also sets various default options, @@ -98,7 +177,7 @@ To override or delete any of the above defaults, set or unset the options on end, }) < - *lsp-config* + *lsp-attach* To use other LSP features, set keymaps and other buffer options on |LspAttach|. Not all language servers provide the same capabilities. Use capability checks to ensure you only use features supported by the language @@ -107,16 +186,16 @@ server. Example: >lua vim.api.nvim_create_autocmd('LspAttach', { callback = function(args) local client = vim.lsp.get_client_by_id(args.data.client_id) - if client.supports_method('textDocument/implementation') then + if client:supports_method('textDocument/implementation') then -- Create a keymap for vim.lsp.buf.implementation end - if client.supports_method('textDocument/completion') then + if client:supports_method('textDocument/completion') then -- Enable auto-completion vim.lsp.completion.enable(true, client.id, args.buf, {autotrigger = true}) end - if client.supports_method('textDocument/formatting') then + if client:supports_method('textDocument/formatting') then -- Format the current buffer on save vim.api.nvim_create_autocmd('BufWritePre', { buffer = args.buf, @@ -204,6 +283,7 @@ won't run if your server doesn't support them. - `'textDocument/diagnostic'` - `'textDocument/documentHighlight'` - `'textDocument/documentSymbol'` +- `'textDocument/foldingRange'` - `'textDocument/formatting'` - `'textDocument/hover'` - `'textDocument/implementation'` @@ -264,22 +344,17 @@ Each response handler has this signature: > *lsp-handler-resolution* Handlers can be set by (in increasing priority): -- Setting a field in vim.lsp.handlers. *vim.lsp.handlers* - `vim.lsp.handlers` is a global table that contains the default mapping of - |lsp-method| names to lsp-handlers. - + *vim.lsp.handlers* +- Setting a field in `vim.lsp.handlers`. This global table contains the + default mappings of |lsp-method| names to handlers. (Note: only for + server-to-client requests/notifications, not client-to-server.) Example: >lua - vim.lsp.handlers['textDocument/publishDiagnostics'] = my_custom_diagnostics_handler < - Note: this only applies for requests/notifications made by the - server to the client. - -- The {handlers} parameter of |vim.lsp.start()|. This sets the default - |lsp-handler| for a specific server. - +- Passing a {handlers} parameter to |vim.lsp.start()|. This sets the default + |lsp-handler| for a specific server. (Note: only for server-to-client + requests/notifications, not client-to-server.) Example: >lua - vim.lsp.start { ..., -- Other configuration omitted. handlers = { @@ -287,14 +362,9 @@ Handlers can be set by (in increasing priority): }, } < - Note: this only applies for requests/notifications made by the - server to the client. - -- The {handler} parameter of |vim.lsp.buf_request_all()|. This sets - the |lsp-handler| ONLY for the given request(s). - +- Passing a {handler} parameter to |vim.lsp.buf_request_all()|. This sets the + |lsp-handler| ONLY for the given request(s). Example: >lua - vim.lsp.buf_request_all( 0, 'textDocument/publishDiagnostics', @@ -464,7 +534,7 @@ EVENTS *lsp-events* LspAttach *LspAttach* After an LSP client attaches to a buffer. The |autocmd-pattern| is the name of the buffer. When used from Lua, the client ID is passed to the - callback in the "data" table. See |lsp-config| for an example. + callback in the "data" table. See |lsp-attach| for an example. LspDetach *LspDetach* Just before an LSP client detaches from a buffer. The |autocmd-pattern| @@ -477,7 +547,7 @@ LspDetach *LspDetach* local client = vim.lsp.get_client_by_id(args.data.client_id) -- Remove the autocommand to format the buffer on save, if it exists - if client.supports_method('textDocument/formatting') then + if client:supports_method('textDocument/formatting') then vim.api.nvim_clear_autocmds({ event = 'BufWritePre', buffer = args.buf, @@ -589,6 +659,34 @@ LspTokenUpdate *LspTokenUpdate* ============================================================================== Lua module: vim.lsp *lsp-core* +*vim.lsp.Config* + Extends: |vim.lsp.ClientConfig| + + + Fields: ~ + • {cmd}? (`string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient`) + See `cmd` in |vim.lsp.ClientConfig|. + • {filetypes}? (`string[]`) Filetypes the client will attach to, if + activated by `vim.lsp.enable()`. If not provided, + then the client will attach to all filetypes. + • {root_markers}? (`string[]`) Directory markers (.e.g. '.git/') where + the LSP server will base its workspaceFolders, + rootUri, and rootPath on initialization. Unused if + `root_dir` is provided. + • {root_dir}? (`string|fun(cb:fun(string))`) Directory where the + LSP server will base its workspaceFolders, rootUri, + and rootPath on initialization. If a function, it + accepts a single callback argument which must be + called with the value of root_dir to use. The LSP + server will not be started until the callback is + called. + • {reuse_client}? (`fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean`) + Predicate used to decide if a client should be + re-used. Used on all running clients. The default + implementation re-uses a client if name and root_dir + matches. + + buf_attach_client({bufnr}, {client_id}) *vim.lsp.buf_attach_client()* Implements the `textDocument/did…` notifications required to track a buffer for any language server. @@ -688,7 +786,7 @@ commands *vim.lsp.commands* value is a function which is called if any LSP action (code action, code lenses, ...) triggers the command. - If a LSP response contains a command for which no matching entry is + If an LSP response contains a command for which no matching entry is available in this registry, the command will be executed via the LSP server using `workspace/executeCommand`. @@ -697,6 +795,108 @@ commands *vim.lsp.commands* The second argument is the `ctx` of |lsp-handler| +config({name}, {cfg}) *vim.lsp.config()* + Update the configuration for an LSP client. + + Use name '*' to set default configuration for all clients. + + Can also be table-assigned to redefine the configuration for a client. + + Examples: + • Add a root marker for all clients: >lua + vim.lsp.config('*', { + root_markers = { '.git' }, + }) +< + • Add additional capabilities to all clients: >lua + vim.lsp.config('*', { + capabilities = { + textDocument = { + semanticTokens = { + multilineTokenSupport = true, + } + } + } + }) +< + • (Re-)define the configuration for clangd: >lua + vim.lsp.config.clangd = { + cmd = { + 'clangd', + '--clang-tidy', + '--background-index', + '--offset-encoding=utf-8', + }, + root_markers = { '.clangd', 'compile_commands.json' }, + filetypes = { 'c', 'cpp' }, + } +< + • Get configuration for luals: >lua + local cfg = vim.lsp.config.luals +< + + Parameters: ~ + • {name} (`string`) + • {cfg} (`vim.lsp.Config`) See |vim.lsp.Config|. + +enable({name}, {enable}) *vim.lsp.enable()* + Enable an LSP server to automatically start when opening a buffer. + + Uses configuration defined with `vim.lsp.config`. + + Examples: >lua + vim.lsp.enable('clangd') + + vim.lsp.enable({'luals', 'pyright'}) +< + + Parameters: ~ + • {name} (`string|string[]`) Name(s) of client(s) to enable. + • {enable} (`boolean?`) `true|nil` to enable, `false` to disable. + +foldclose({kind}, {winid}) *vim.lsp.foldclose()* + Close all {kind} of folds in the the window with {winid}. + + To automatically fold imports when opening a file, you can use an autocmd: >lua + vim.api.nvim_create_autocmd('LspNotify', { + callback = function(args) + if args.data.method == 'textDocument/didOpen' then + vim.lsp.foldclose('imports', vim.fn.bufwinid(args.buf)) + end + end, + }) +< + + Parameters: ~ + • {kind} (`lsp.FoldingRangeKind`) Kind to close, one of "comment", + "imports" or "region". + • {winid} (`integer?`) Defaults to the current window. + +foldexpr({lnum}) *vim.lsp.foldexpr()* + Provides an interface between the built-in client and a `foldexpr` + function. + + To use, check for the "textDocument/foldingRange" capability in an + |LspAttach| autocommand. Example: >lua + vim.api.nvim_create_autocmd('LspAttach', { + callback = function(args) + local client = vim.lsp.get_client_by_id(args.data.client_id) + if client:supports_method('textDocument/foldingRange') then + local win = vim.api.nvim_get_current_win() + vim.wo[win][0].foldmethod = 'expr' + vim.wo[win][0].foldexpr = 'v:lua.vim.lsp.foldexpr()' + end + end, + }) +< + + Parameters: ~ + • {lnum} (`integer`) line number + +foldtext() *vim.lsp.foldtext()* + Provides a `foldtext` function that shows the `collapsedText` retrieved, + defaults to the first folded line if `collapsedText` is not provided. + formatexpr({opts}) *vim.lsp.formatexpr()* Provides an interface between the built-in client and a `formatexpr` function. @@ -798,12 +998,11 @@ start({config}, {opts}) *vim.lsp.start()* }) < - See |vim.lsp.start_client()| for all available options. The most important + See |vim.lsp.ClientConfig| for all available options. The most important are: • `name` arbitrary name for the LSP client. Should be unique per language server. - • `cmd` command string[] or function, described at - |vim.lsp.start_client()|. + • `cmd` command string[] or function. • `root_dir` path to the project root. By default this is used to decide if an existing client should be re-used. The example above uses |vim.fs.root()| to detect the root by traversing the file system upwards @@ -825,33 +1024,25 @@ start({config}, {opts}) *vim.lsp.start()* Parameters: ~ • {config} (`vim.lsp.ClientConfig`) Configuration for the server. See |vim.lsp.ClientConfig|. - • {opts} (`table?`) Optional keyword arguments + • {opts} (`table?`) Optional keyword arguments. • {reuse_client}? (`fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean`) Predicate used to decide if a client should be re-used. Used on all running clients. The default implementation - re-uses a client if name and root_dir matches. + re-uses a client if it has the same name and if the given + workspace folders (or root_dir) are all included in the + client's workspace folders. • {bufnr}? (`integer`) Buffer handle to attach to if starting or re-using a client (0 for current). + • {attach}? (`boolean`) Whether to attach the client to a + buffer (default true). If set to `false`, `reuse_client` + and `bufnr` will be ignored. • {silent}? (`boolean`) Suppress error reporting if the LSP server fails to start (default false). Return: ~ (`integer?`) client_id -start_client({config}) *vim.lsp.start_client()* - Starts and initializes a client with the given configuration. - - Parameters: ~ - • {config} (`vim.lsp.ClientConfig`) Configuration for the server. See - |vim.lsp.ClientConfig|. - - Return (multiple): ~ - (`integer?`) client_id |vim.lsp.get_client_by_id()| Note: client may - not be fully initialized. Use `on_init` to do any actions once the - client has been initialized. - (`string?`) Error message, if any - status() *vim.lsp.status()* Consumes the latest progress messages from all clients and formats them as a string. Empty if there are no clients or if no new messages @@ -904,14 +1095,15 @@ Lua module: vim.lsp.client *lsp-client* • {rpc} (`vim.lsp.rpc.PublicClient`) RPC client object, for low level interaction with the client. See |vim.lsp.rpc.start()|. - • {offset_encoding} (`string`) The encoding used for communicating - with the server. You can modify this in the + • {offset_encoding} (`string`) Called "position encoding" in LSP + spec, the encoding used for communicating with + the server. You can modify this in the `config`'s `on_init` method before text is sent to the server. • {handlers} (`table<string,lsp.Handler>`) The handlers used by the client as described in |lsp-handler|. - • {requests} (`table<integer,{ type: string, bufnr: integer, method: string}>`) + • {requests} (`table<integer,{ type: string, bufnr: integer, method: string}?>`) The current pending requests in flight to the server. Entries are key-value pairs with the key being the request id while the value is a @@ -924,11 +1116,13 @@ Lua module: vim.lsp.client *lsp-client* server. • {config} (`vim.lsp.ClientConfig`) copy of the table that was passed by the user to - |vim.lsp.start_client()|. See - |vim.lsp.ClientConfig|. + |vim.lsp.start()|. See |vim.lsp.ClientConfig|. • {server_capabilities} (`lsp.ServerCapabilities?`) Response from the server sent on `initialize` describing the server's capabilities. + • {server_info} (`lsp.ServerInfo?`) Response from the server + sent on `initialize` describing information + about the server. • {progress} (`vim.lsp.Client.Progress`) A ring buffer (|vim.ringbuf()|) containing progress messages sent by the server. See @@ -948,9 +1142,9 @@ Lua module: vim.lsp.client *lsp-client* lenses, ...) triggers the command. Client commands take precedence over the global command registry. - • {settings} (`table`) Map with language server specific - settings. These are returned to the language - server if requested via + • {settings} (`lsp.LSPObject`) Map with language server + specific settings. These are returned to the + language server if requested via `workspace/configuration`. Keys are case-sensitive. • {flags} (`table`) A table with flags for the client. @@ -972,54 +1166,24 @@ Lua module: vim.lsp.client *lsp-client* • {capabilities} (`lsp.ClientCapabilities`) The capabilities provided by the client (editor or tool) • {dynamic_capabilities} (`lsp.DynamicCapabilities`) - • {request} (`fun(method: string, params: table?, handler: lsp.Handler?, bufnr: integer?): boolean, integer?`) - Sends a request to the server. This is a thin - wrapper around {client.rpc.request} with some - additional checking. If {handler} is not - specified and if there's no respective global - handler, then an error will occur. Returns: - {status}, {client_id}?. {status} is a boolean - indicating if the notification was successful. - If it is `false`, then it will always be - `false` (the client has shutdown). If {status} - is `true`, the function returns {request_id} - as the second result. You can use this with - `client.cancel_request(request_id)` to cancel - the request. - • {request_sync} (`fun(method: string, params: table?, timeout_ms: integer?, bufnr: integer): {err: lsp.ResponseError?, result:any}?, string?`) - err # a dict - • {notify} (`fun(method: string, params: table?): boolean`) - Sends a notification to an LSP server. - Returns: a boolean to indicate if the - notification was successful. If it is false, - then it will always be false (the client has - shutdown). - • {cancel_request} (`fun(id: integer): boolean`) Cancels a - request with a given request id. Returns: same - as `notify()`. - • {stop} (`fun(force?: boolean)`) Stops a client, - optionally with force. By default, it will - just ask the server to shutdown without force. - If you request to stop a client which has - previously been requested to shutdown, it will - automatically escalate and force shutdown. - • {on_attach} (`fun(bufnr: integer)`) Runs the on_attach - function from the client's config if it was - defined. Useful for buffer-local setup. - • {supports_method} (`fun(method: string, opts?: {bufnr: integer?}): boolean`) - Checks if a client supports a given method. - Always returns true for unknown off-spec - methods. {opts} is a optional - `{bufnr?: integer}` table. Some language - server capabilities can be file specific. - • {is_stopped} (`fun(): boolean`) Checks whether a client is - stopped. Returns: true if the client is fully - stopped. + • {request} (`fun(self: vim.lsp.Client, method: string, params: table?, handler: lsp.Handler?, bufnr: integer?): boolean, integer?`) + See |Client:request()|. + • {request_sync} (`fun(self: vim.lsp.Client, method: string, params: table, timeout_ms: integer?, bufnr: integer?): {err: lsp.ResponseError?, result:any}?, string?`) + See |Client:request_sync()|. + • {notify} (`fun(self: vim.lsp.Client, method: string, params: table?): boolean`) + See |Client:notify()|. + • {cancel_request} (`fun(self: vim.lsp.Client, id: integer): boolean`) + See |Client:cancel_request()|. + • {stop} (`fun(self: vim.lsp.Client, force: boolean?)`) + See |Client:stop()|. + • {is_stopped} (`fun(self: vim.lsp.Client): boolean`) See + |Client:is_stopped()|. • {exec_cmd} (`fun(self: vim.lsp.Client, command: lsp.Command, context: {bufnr?: integer}?, handler: lsp.Handler?)`) - Execute a lsp command, either via client - command function (if available) or via - workspace/executeCommand (if supported by the - server) + See |Client:exec_cmd()|. + • {on_attach} (`fun(self: vim.lsp.Client, bufnr: integer)`) + See |Client:on_attach()|. + • {supports_method} (`fun(self: vim.lsp.Client, method: string, bufnr: integer?)`) + See |Client:supports_method()|. *vim.lsp.Client.Progress* Extends: |vim.Ringbuf| @@ -1073,28 +1237,30 @@ Lua module: vim.lsp.client *lsp-client* an array. • {handlers}? (`table<string,function>`) Map of language server method names to |lsp-handler| - • {settings}? (`table`) Map with language server specific - settings. See the {settings} in + • {settings}? (`lsp.LSPObject`) Map with language server + specific settings. See the {settings} in |vim.lsp.Client|. • {commands}? (`table<string,fun(command: lsp.Command, ctx: table)>`) Table that maps string of clientside commands to user-defined functions. Commands passed to - start_client take precedence over the global + `start()` take precedence over the global command registry. Each key must be a unique command name, and the value is a function which is called if any LSP action (code action, code lenses, ...) triggers the command. - • {init_options}? (`table`) Values to pass in the initialization - request as `initializationOptions`. See - `initialize` in the LSP spec. + • {init_options}? (`lsp.LSPObject`) Values to pass in the + initialization request as + `initializationOptions`. See `initialize` in the + LSP spec. • {name}? (`string`, default: client-id) Name in log messages. • {get_language_id}? (`fun(bufnr: integer, filetype: string): string`) Language ID as string. Defaults to the buffer filetype. - • {offset_encoding}? (`'utf-8'|'utf-16'|'utf-32'`) The encoding that - the LSP server expects. Client does not verify - this is correct. + • {offset_encoding}? (`'utf-8'|'utf-16'|'utf-32'`) Called "position + encoding" in LSP spec, the encoding that the LSP + server expects. Client does not verify this is + correct. • {on_error}? (`fun(code: integer, err: string)`) Callback invoked when the client operation throws an error. `code` is a number describing the error. @@ -1107,9 +1273,9 @@ Lua module: vim.lsp.client *lsp-client* Callback invoked before the LSP "initialize" phase, where `params` contains the parameters being sent to the server and `config` is the - config that was passed to - |vim.lsp.start_client()|. You can use this to - modify parameters before they are sent. + config that was passed to |vim.lsp.start()|. You + can use this to modify parameters before they + are sent. • {on_init}? (`elem_or_list<fun(client: vim.lsp.Client, initialize_result: lsp.InitializeResult)>`) Callback invoked after LSP "initialize", where `result` is a table of `capabilities` and @@ -1150,6 +1316,18 @@ Lua module: vim.lsp.client *lsp-client* on initialization. +Client:cancel_request({id}) *Client:cancel_request()* + Cancels a request with a given request id. + + Parameters: ~ + • {id} (`integer`) id of request to cancel + + Return: ~ + (`boolean`) status indicating if the notification was successful. + + See also: ~ + • |Client:notify()| + Client:exec_cmd({command}, {context}, {handler}) *Client:exec_cmd()* Execute a lsp command, either via client command function (if available) or via workspace/executeCommand (if supported by the server) @@ -1159,6 +1337,96 @@ Client:exec_cmd({command}, {context}, {handler}) *Client:exec_cmd()* • {context} (`{bufnr?: integer}?`) • {handler} (`lsp.Handler?`) only called if a server command +Client:is_stopped() *Client:is_stopped()* + Checks whether a client is stopped. + + Return: ~ + (`boolean`) true if client is stopped or in the process of being + stopped; false otherwise + +Client:notify({method}, {params}) *Client:notify()* + Sends a notification to an LSP server. + + Parameters: ~ + • {method} (`string`) LSP method name. + • {params} (`table?`) LSP request params. + + Return: ~ + (`boolean`) status indicating if the notification was successful. If + it is false, then the client has shutdown. + +Client:on_attach({bufnr}) *Client:on_attach()* + Runs the on_attach function from the client's config if it was defined. + Useful for buffer-local setup. + + Parameters: ~ + • {bufnr} (`integer`) Buffer number + + *Client:request()* +Client:request({method}, {params}, {handler}, {bufnr}) + Sends a request to the server. + + This is a thin wrapper around {client.rpc.request} with some additional + checks for capabilities and handler availability. + + Parameters: ~ + • {method} (`string`) LSP method name. + • {params} (`table?`) LSP request params. + • {handler} (`lsp.Handler?`) Response |lsp-handler| for this method. + • {bufnr} (`integer?`) (default: 0) Buffer handle, or 0 for current. + + Return (multiple): ~ + (`boolean`) status indicates whether the request was successful. If it + is `false`, then it will always be `false` (the client has shutdown). + (`integer?`) request_id Can be used with |Client:cancel_request()|. + `nil` is request failed. + + See also: ~ + • |vim.lsp.buf_request_all()| + + *Client:request_sync()* +Client:request_sync({method}, {params}, {timeout_ms}, {bufnr}) + Sends a request to the server and synchronously waits for the response. + + This is a wrapper around |Client:request()| + + Parameters: ~ + • {method} (`string`) LSP method name. + • {params} (`table`) LSP request params. + • {timeout_ms} (`integer?`) Maximum time in milliseconds to wait for a + result. Defaults to 1000 + • {bufnr} (`integer?`) (default: 0) Buffer handle, or 0 for + current. + + Return (multiple): ~ + (`{err: lsp.ResponseError?, result:any}?`) `result` and `err` from the + |lsp-handler|. `nil` is the request was unsuccessful + (`string?`) err On timeout, cancel or error, where `err` is a string + describing the failure reason. + + See also: ~ + • |vim.lsp.buf_request_sync()| + +Client:stop({force}) *Client:stop()* + Stops a client, optionally with force. + + By default, it will just request the server to shutdown without force. If + you request to stop a client which has previously been requested to + shutdown, it will automatically escalate and force shutdown. + + Parameters: ~ + • {force} (`boolean?`) + +Client:supports_method({method}, {bufnr}) *Client:supports_method()* + Checks if a client supports a given method. Always returns true for + unknown off-spec methods. + + Note: Some language server capabilities can be file specific. + + Parameters: ~ + • {method} (`string`) + • {bufnr} (`integer?`) + ============================================================================== Lua module: vim.lsp.buf *lsp-buf* @@ -1178,12 +1446,11 @@ Lua module: vim.lsp.buf *lsp-buf* vim.lsp.buf.definition({ on_list = on_list }) vim.lsp.buf.references(nil, { on_list = on_list }) < - - If you prefer loclist instead of qflist: >lua + • {loclist}? (`boolean`) Whether to use the |location-list| or the + |quickfix| list in the default handler. >lua vim.lsp.buf.definition({ loclist = true }) - vim.lsp.buf.references(nil, { loclist = true }) + vim.lsp.buf.references(nil, { loclist = false }) < - • {loclist}? (`boolean`) *vim.lsp.LocationOpts* Extends: |vim.lsp.ListOpts| @@ -1286,7 +1553,7 @@ document_highlight() *vim.lsp.buf.document_highlight()* |hl-LspReferenceWrite| document_symbol({opts}) *vim.lsp.buf.document_symbol()* - Lists all symbols in the current buffer in the quickfix window. + Lists all symbols in the current buffer in the |location-list|. Parameters: ~ • {opts} (`vim.lsp.ListOpts?`) See |vim.lsp.ListOpts|. @@ -1312,7 +1579,7 @@ format({opts}) *vim.lsp.buf.format()* predicate are included. Example: >lua -- Never request typescript-language-server for formatting vim.lsp.buf.format { - filter = function(client) return client.name ~= "tsserver" end + filter = function(client) return client.name ~= "ts_ls" end } < • {async}? (`boolean`, default: false) If true the method @@ -1375,7 +1642,7 @@ references({context}, {opts}) *vim.lsp.buf.references()* window. Parameters: ~ - • {context} (`table?`) Context for the request + • {context} (`lsp.ReferenceContext?`) Context for the request • {opts} (`vim.lsp.ListOpts?`) See |vim.lsp.ListOpts|. See also: ~ @@ -1461,12 +1728,13 @@ get_namespace({client_id}, {is_pull}) client. Defaults to push *vim.lsp.diagnostic.on_diagnostic()* -on_diagnostic({_}, {result}, {ctx}) +on_diagnostic({error}, {result}, {ctx}) |lsp-handler| for the method "textDocument/diagnostic" See |vim.diagnostic.config()| for configuration options. Parameters: ~ + • {error} (`lsp.ResponseError?`) • {result} (`lsp.DocumentDiagnosticReport`) • {ctx} (`lsp.HandlerContext`) @@ -1599,12 +1867,12 @@ get({filter}) *vim.lsp.inlay_hint.get()* local hint = vim.lsp.inlay_hint.get({ bufnr = 0 })[1] -- 0 for current buffer local client = vim.lsp.get_client_by_id(hint.client_id) - local resp = client.request_sync('inlayHint/resolve', hint.inlay_hint, 100, 0) + local resp = client:request_sync('inlayHint/resolve', hint.inlay_hint, 100, 0) local resolved_hint = assert(resp and resp.result, resp.err) vim.lsp.util.apply_text_edits(resolved_hint.textEdits, 0, client.encoding) location = resolved_hint.label[1].location - client.request('textDocument/hover', { + client:request('textDocument/hover', { textDocument = { uri = location.uri }, position = location.range.start, }) @@ -1756,7 +2024,7 @@ Lua module: vim.lsp.util *lsp-util* • {zindex}? (`integer`) override `zindex`, defaults to 50 • {title}? (`string`) • {title_pos}? (`'left'|'center'|'right'`) - • {relative}? (`'mouse'|'cursor'`) (default: `'cursor'`) + • {relative}? (`'mouse'|'cursor'|'editor'`) (default: `'cursor'`) • {anchor_bias}? (`'auto'|'above'|'below'`, default: `'auto'`) - "auto": place window based on which side of the cursor has more lines @@ -1769,7 +2037,7 @@ Lua module: vim.lsp.util *lsp-util* *vim.lsp.util.apply_text_document_edit()* -apply_text_document_edit({text_document_edit}, {index}, {offset_encoding}) +apply_text_document_edit({text_document_edit}, {index}, {position_encoding}) Applies a `TextDocumentEdit`, which is a list of changes to a single document. @@ -1777,30 +2045,30 @@ apply_text_document_edit({text_document_edit}, {index}, {offset_encoding}) • {text_document_edit} (`lsp.TextDocumentEdit`) • {index} (`integer?`) Optional index of the edit, if from a list of edits (or nil, if not from a list) - • {offset_encoding} (`'utf-8'|'utf-16'|'utf-32'?`) + • {position_encoding} (`'utf-8'|'utf-16'|'utf-32'?`) See also: ~ • https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentEdit *vim.lsp.util.apply_text_edits()* -apply_text_edits({text_edits}, {bufnr}, {offset_encoding}) +apply_text_edits({text_edits}, {bufnr}, {position_encoding}) Applies a list of text edits to a buffer. Parameters: ~ - • {text_edits} (`lsp.TextEdit[]`) - • {bufnr} (`integer`) Buffer id - • {offset_encoding} (`'utf-8'|'utf-16'|'utf-32'`) + • {text_edits} (`lsp.TextEdit[]`) + • {bufnr} (`integer`) Buffer id + • {position_encoding} (`'utf-8'|'utf-16'|'utf-32'`) See also: ~ • https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textEdit *vim.lsp.util.apply_workspace_edit()* -apply_workspace_edit({workspace_edit}, {offset_encoding}) +apply_workspace_edit({workspace_edit}, {position_encoding}) Applies a `WorkspaceEdit`. Parameters: ~ - • {workspace_edit} (`lsp.WorkspaceEdit`) - • {offset_encoding} (`'utf-8'|'utf-16'|'utf-32'`) (required) + • {workspace_edit} (`lsp.WorkspaceEdit`) + • {position_encoding} (`'utf-8'|'utf-16'|'utf-32'`) (required) See also: ~ • https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit @@ -1812,13 +2080,13 @@ buf_clear_references({bufnr}) *vim.lsp.util.buf_clear_references()* • {bufnr} (`integer?`) Buffer id *vim.lsp.util.buf_highlight_references()* -buf_highlight_references({bufnr}, {references}, {offset_encoding}) +buf_highlight_references({bufnr}, {references}, {position_encoding}) Shows a list of document highlights for a certain buffer. Parameters: ~ - • {bufnr} (`integer`) Buffer id - • {references} (`lsp.DocumentHighlight[]`) objects to highlight - • {offset_encoding} (`'utf-8'|'utf-16'|'utf-32'`) + • {bufnr} (`integer`) Buffer id + • {references} (`lsp.DocumentHighlight[]`) objects to highlight + • {position_encoding} (`'utf-8'|'utf-16'|'utf-32'`) See also: ~ • https://microsoft.github.io/language-server-protocol/specification/#textDocumentContentChangeEvent @@ -1893,7 +2161,7 @@ get_effective_tabstop({bufnr}) *vim.lsp.util.get_effective_tabstop()* • 'shiftwidth' *vim.lsp.util.locations_to_items()* -locations_to_items({locations}, {offset_encoding}) +locations_to_items({locations}, {position_encoding}) Returns the items with the byte position calculated correctly and in sorted order, for display in quickfix and location lists. @@ -1904,9 +2172,9 @@ locations_to_items({locations}, {offset_encoding}) |setloclist()|. Parameters: ~ - • {locations} (`lsp.Location[]|lsp.LocationLink[]`) - • {offset_encoding} (`'utf-8'|'utf-16'|'utf-32'?`) default to first - client of buffer + • {locations} (`lsp.Location[]|lsp.LocationLink[]`) + • {position_encoding} (`'utf-8'|'utf-16'|'utf-32'?`) default to first + client of buffer Return: ~ (`vim.quickfix.entry[]`) See |setqflist()| for the format @@ -1941,37 +2209,33 @@ make_formatting_params({options}) • https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_formatting *vim.lsp.util.make_given_range_params()* -make_given_range_params({start_pos}, {end_pos}, {bufnr}, {offset_encoding}) +make_given_range_params({start_pos}, {end_pos}, {bufnr}, {position_encoding}) Using the given range in the current buffer, creates an object that is similar to |vim.lsp.util.make_range_params()|. Parameters: ~ - • {start_pos} (`[integer,integer]?`) {row,col} mark-indexed - position. Defaults to the start of the last visual - selection. - • {end_pos} (`[integer,integer]?`) {row,col} mark-indexed - position. Defaults to the end of the last visual - selection. - • {bufnr} (`integer?`) buffer handle or 0 for current, - defaults to current - • {offset_encoding} (`'utf-8'|'utf-16'|'utf-32'?`) defaults to - `offset_encoding` of first client of `bufnr` + • {start_pos} (`[integer,integer]?`) {row,col} mark-indexed + position. Defaults to the start of the last + visual selection. + • {end_pos} (`[integer,integer]?`) {row,col} mark-indexed + position. Defaults to the end of the last visual + selection. + • {bufnr} (`integer?`) buffer handle or 0 for current, + defaults to current + • {position_encoding} (`'utf-8'|'utf-16'|'utf-32'`) Return: ~ - (`table`) { textDocument = { uri = `current_file_uri` }, range = { - start = `start_position`, end = `end_position` } } + (`{ textDocument: { uri: lsp.DocumentUri }, range: lsp.Range }`) *vim.lsp.util.make_position_params()* -make_position_params({window}, {offset_encoding}) +make_position_params({window}, {position_encoding}) Creates a `TextDocumentPositionParams` object for the current buffer and cursor position. Parameters: ~ - • {window} (`integer?`) window handle or 0 for current, - defaults to current - • {offset_encoding} (`'utf-8'|'utf-16'|'utf-32'?`) defaults to - `offset_encoding` of first client of buffer of - `window` + • {window} (`integer?`) window handle or 0 for current, + defaults to current + • {position_encoding} (`'utf-8'|'utf-16'|'utf-32'`) Return: ~ (`lsp.TextDocumentPositionParams`) @@ -1980,22 +2244,19 @@ make_position_params({window}, {offset_encoding}) • https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams *vim.lsp.util.make_range_params()* -make_range_params({window}, {offset_encoding}) +make_range_params({window}, {position_encoding}) Using the current position in the current buffer, creates an object that can be used as a building block for several LSP requests, such as `textDocument/codeAction`, `textDocument/colorPresentation`, `textDocument/rangeFormatting`. Parameters: ~ - • {window} (`integer?`) window handle or 0 for current, - defaults to current - • {offset_encoding} (`"utf-8"|"utf-16"|"utf-32"?`) defaults to - `offset_encoding` of first client of buffer of - `window` + • {window} (`integer?`) window handle or 0 for current, + defaults to current + • {position_encoding} (`"utf-8"|"utf-16"|"utf-32"`) Return: ~ - (`table`) { textDocument = { uri = `current_file_uri` }, range = { - start = `current_position`, end = `current_position` } } + (`{ textDocument: { uri: lsp.DocumentUri }, range: lsp.Range }`) *vim.lsp.util.make_text_document_params()* make_text_document_params({bufnr}) @@ -2074,17 +2335,17 @@ rename({old_fname}, {new_fname}, {opts}) *vim.lsp.util.rename()* • {ignoreIfExists}? (`boolean`) *vim.lsp.util.show_document()* -show_document({location}, {offset_encoding}, {opts}) +show_document({location}, {position_encoding}, {opts}) Shows document and optionally jumps to the location. Parameters: ~ - • {location} (`lsp.Location|lsp.LocationLink`) - • {offset_encoding} (`'utf-8'|'utf-16'|'utf-32'?`) - • {opts} (`table?`) A table with the following fields: - • {reuse_win}? (`boolean`) Jump to existing window - if buffer is already open. - • {focus}? (`boolean`) Whether to focus/jump to - location if possible. (defaults: true) + • {location} (`lsp.Location|lsp.LocationLink`) + • {position_encoding} (`'utf-8'|'utf-16'|'utf-32'?`) + • {opts} (`table?`) A table with the following fields: + • {reuse_win}? (`boolean`) Jump to existing + window if buffer is already open. + • {focus}? (`boolean`) Whether to focus/jump to + location if possible. (defaults: true) Return: ~ (`boolean`) `true` if succeeded @@ -2168,14 +2429,15 @@ should_log({level}) *vim.lsp.log.should_log()* Lua module: vim.lsp.rpc *lsp-rpc* *vim.lsp.rpc.PublicClient* + Client RPC object Fields: ~ - • {request} (`fun(method: string, params: table?, callback: fun(err: lsp.ResponseError?, result: any), notify_reply_callback: fun(message_id: integer)?):boolean,integer?`) - see |vim.lsp.rpc.request()| - • {notify} (`fun(method: string, params: any):boolean`) see + • {request} (`fun(method: string, params: table?, callback: fun(err?: lsp.ResponseError, result: any), notify_reply_callback?: fun(message_id: integer)):boolean,integer?`) + See |vim.lsp.rpc.request()| + • {notify} (`fun(method: string, params: any): boolean`) See |vim.lsp.rpc.notify()| - • {is_closing} (`fun(): boolean`) - • {terminate} (`fun()`) + • {is_closing} (`fun(): boolean`) Indicates if the RPC is closing. + • {terminate} (`fun()`) Terminates the RPC client. connect({host_or_path}, {port}) *vim.lsp.rpc.connect()* @@ -2185,7 +2447,7 @@ connect({host_or_path}, {port}) *vim.lsp.rpc.connect()* • a host and port via TCP Return a function that can be passed to the `cmd` field for - |vim.lsp.start_client()| or |vim.lsp.start()|. + |vim.lsp.start()|. Parameters: ~ • {host_or_path} (`string`) host to connect to or path to a pipe/domain @@ -2276,12 +2538,7 @@ start({cmd}, {dispatchers}, {extra_spawn_params}) *vim.lsp.rpc.start()* See |vim.system()| Return: ~ - (`vim.lsp.rpc.PublicClient`) Client RPC object, with these methods: - • `notify()` |vim.lsp.rpc.notify()| - • `request()` |vim.lsp.rpc.request()| - • `is_closing()` returns a boolean indicating if the RPC is closing. - • `terminate()` terminates the RPC client. See - |vim.lsp.rpc.PublicClient|. + (`vim.lsp.rpc.PublicClient`) See |vim.lsp.rpc.PublicClient|. ============================================================================== diff --git a/runtime/doc/lua-guide.txt b/runtime/doc/lua-guide.txt index b40d5a0791..d0d148f689 100644 --- a/runtime/doc/lua-guide.txt +++ b/runtime/doc/lua-guide.txt @@ -153,7 +153,7 @@ its functions if this succeeds and prints an error message otherwise: if not ok then print("Module had an error") else - mymod.function() + mymod.func() end < In contrast to |:source|, |require()| not only searches through all `lua/` directories diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 243c907180..0eca3286d0 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -17,9 +17,9 @@ get an idea of what lurks beneath: >vim :lua vim.print(package.loaded) Nvim includes a "standard library" |lua-stdlib| for Lua. It complements the -"editor stdlib" (|builtin-functions| and |Ex-commands|) and the |API|, all of -which can be used from Lua code (|lua-vimscript| |vim.api|). Together these -"namespaces" form the Nvim programming interface. +"editor stdlib" (|vimscript-functions| + |Ex-commands|) and the |API|, all of +which can be used from Lua code (|lua-vimscript| |vim.api|). These three +namespaces form the Nvim programming interface. Lua plugins and user config are automatically discovered and loaded, just like Vimscript. See |lua-guide| for practical guidance. @@ -161,7 +161,7 @@ languages like Python and C#. Example: >lua local func_with_opts = function(opts) local will_do_foo = opts.foo local filename = opts.filename - ... + -- ... end func_with_opts { foo = true, filename = "hello.world" } @@ -618,20 +618,20 @@ Example: TCP echo-server *tcp-server* Multithreading *lua-loop-threading* Plugins can perform work in separate (os-level) threads using the threading -APIs in luv, for instance `vim.uv.new_thread`. Note that every thread -gets its own separate Lua interpreter state, with no access to Lua globals -in the main thread. Neither can the state of the editor (buffers, windows, -etc) be directly accessed from threads. +APIs in luv, for instance `vim.uv.new_thread`. Each thread has its own +separate Lua interpreter state, with no access to Lua globals on the main +thread. Neither can the editor state (buffers, windows, etc) be directly +accessed from threads. -A subset of the `vim.*` API is available in threads. This includes: +A subset of the `vim.*` stdlib is available in threads, including: - `vim.uv` with a separate event loop per thread. - `vim.mpack` and `vim.json` (useful for serializing messages between threads) - `require` in threads can use Lua packages from the global |package.path| - `print()` and `vim.inspect` - `vim.diff` -- most utility functions in `vim.*` for working with pure Lua values - like `vim.split`, `vim.tbl_*`, `vim.list_*`, and so on. +- Most utility functions in `vim.*` that work with pure Lua values, like + `vim.split`, `vim.tbl_*`, `vim.list_*`, etc. - `vim.is_thread()` returns true from a non-main thread. @@ -685,6 +685,8 @@ vim.hl.range({bufnr}, {ns}, {higroup}, {start}, {finish}, {opts}) whether the range is end-inclusive • {priority}? (`integer`, default: `vim.hl.priorities.user`) Highlight priority + • {timeout}? (`integer`, default: -1 no timeout) Time in ms + before highlight is cleared ============================================================================== @@ -811,11 +813,14 @@ vim.json.decode({str}, {opts}) *vim.json.decode()* Return: ~ (`any`) -vim.json.encode({obj}) *vim.json.encode()* +vim.json.encode({obj}, {opts}) *vim.json.encode()* Encodes (or "packs") Lua object {obj} as JSON in a Lua string. Parameters: ~ - • {obj} (`any`) + • {obj} (`any`) + • {opts} (`table<string,any>?`) Options table with keys: + • escape_slash: (boolean) (default false) Escape slash + characters "/" in string values. Return: ~ (`string`) @@ -1083,9 +1088,8 @@ vim.ui_attach({ns}, {options}, {callback}) *vim.ui_attach()* |ui-popupmenu| and the sections below for event format for respective events. - Callbacks for `msg_show` events are executed in |api-fast| context unless - Nvim will wait for input, in which case messages should be shown - immediately. + Callbacks for `msg_show` events are executed in |api-fast| context; + showing the message should be scheduled. Excessive errors inside the callback will result in forced detachment. @@ -1297,7 +1301,7 @@ global value of a |global-local| option, see |:setglobal|. A special interface |vim.opt| exists for conveniently interacting with list- -and map-style option from Lua: It allows accessing them as Lua tables and +and map-style options from Lua: It allows accessing them as Lua tables and offers object-oriented method for adding and removing entries. Examples: ~ @@ -1471,10 +1475,8 @@ vim.go *vim.go* < vim.o *vim.o* - Get or set |options|. Like `:set`. Invalid key is an error. - - Note: this works on both buffer-scoped and window-scoped options using the - current buffer and window. + Get or set |options|. Works like `:set`, so buffer/window-scoped options + target the current buffer/window. Invalid key is an error. Example: >lua vim.o.cmdheight = 4 @@ -1505,7 +1507,7 @@ vim.wo[{winid}][{bufnr}] *vim.wo* Lua module: vim *lua-vim* vim.cmd({command}) *vim.cmd()* - Executes Vim script commands. + Executes Vimscript (|Ex-commands|). Note that `vim.cmd` can be indexed with a command name to return a callable function to the command. @@ -1539,9 +1541,9 @@ vim.cmd({command}) *vim.cmd()* Parameters: ~ • {command} (`string|table`) Command(s) to execute. If a string, - executes multiple lines of Vim script at once. In this - case, it is an alias to |nvim_exec2()|, where `opts.output` - is set to false. Thus it works identical to |:source|. If a + executes multiple lines of Vimscript at once. In this case, + it is an alias to |nvim_exec2()|, where `opts.output` is + set to false. Thus it works identical to |:source|. If a table, executes a single command. In this case, it is an alias to |nvim_cmd()| where `opts` is empty. @@ -1805,7 +1807,7 @@ vim.system({cmd}, {opts}, {on_exit}) *vim.system()* -- Runs synchronously: local obj = vim.system({'echo', 'hello'}, { text = true }):wait() - -- { code = 0, signal = 0, stdout = 'hello', stderr = '' } + -- { code = 0, signal = 0, stdout = 'hello\n', stderr = '' } < See |uv.spawn()| for more details. Note: unlike |uv.spawn()|, vim.system @@ -1911,6 +1913,11 @@ vim.show_pos({bufnr}, {row}, {col}, {filter}) *vim.show_pos()* Can also be shown with `:Inspect`. *:Inspect* + Example: To bind this function to the vim-scriptease inspired `zS` in + Normal mode: >lua + vim.keymap.set('n', 'zS', vim.show_pos) +< + Attributes: ~ Since: 0.9.0 @@ -1937,12 +1944,10 @@ vim.show_pos({bufnr}, {row}, {col}, {filter}) *vim.show_pos()* *vim.Ringbuf* Fields: ~ - • {clear} (`fun()`) Clear all items - • {push} (`fun(item: T)`) Adds an item, overriding the oldest item if - the buffer is full. - • {pop} (`fun(): T?`) Removes and returns the first unread item - • {peek} (`fun(): T?`) Returns the first unread item without removing - it + • {clear} (`fun()`) See |Ringbuf:clear()|. + • {push} (`fun(item: T)`) See |Ringbuf:push()|. + • {pop} (`fun(): T?`) See |Ringbuf:pop()|. + • {peek} (`fun(): T?`) See |Ringbuf:peek()|. Ringbuf:clear() *Ringbuf:clear()* @@ -2425,7 +2430,7 @@ vim.validate({name}, {value}, {validator}, {optional}, {message}) function vim.startswith(s, prefix) vim.validate('s', s, 'string') vim.validate('prefix', prefix, 'string') - ... + -- ... end < 2. `vim.validate(spec)` (deprecated) where `spec` is of type @@ -2439,7 +2444,7 @@ vim.validate({name}, {value}, {validator}, {optional}, {message}) age={age, 'number'}, hobbies={hobbies, 'table'}, } - ... + -- ... end < @@ -2471,7 +2476,7 @@ vim.validate({name}, {value}, {validator}, {optional}, {message}) Parameters: ~ • {name} (`string`) Argument name - • {value} (`string`) Argument value + • {value} (`any`) Argument value • {validator} (`vim.validate.Validator`) • (`string|string[]`): Any value that can be returned from |lua-type()| in addition to `'callable'`: @@ -2487,22 +2492,24 @@ vim.validate({name}, {value}, {validator}, {optional}, {message}) ============================================================================== Lua module: vim.loader *vim.loader* -vim.loader.disable() *vim.loader.disable()* +vim.loader.enable({enable}) *vim.loader.enable()* WARNING: This feature is experimental/unstable. - Disables the experimental Lua module loader: - • removes the loaders - • adds the default Nvim loader - -vim.loader.enable() *vim.loader.enable()* - WARNING: This feature is experimental/unstable. + Enables or disables the experimental Lua module loader: - Enables the experimental Lua module loader: - • overrides loadfile + Enable (`enable=true`): + • overrides |loadfile()| • adds the Lua loader using the byte-compilation cache • adds the libs loader • removes the default Nvim loader + Disable (`enable=false`): + • removes the loaders + • adds the default Nvim loader + + Parameters: ~ + • {enable} (`boolean?`) true/nil to enable, false to disable + vim.loader.find({modname}, {opts}) *vim.loader.find()* WARNING: This feature is experimental/unstable. @@ -2926,6 +2933,31 @@ vim.keymap.set({mode}, {lhs}, {rhs}, {opts}) *vim.keymap.set()* ============================================================================== Lua module: vim.fs *vim.fs* + + *vim.fs.exists()* +Use |uv.fs_stat()| to check a file's type, and whether it exists. + +Example: >lua + if vim.uv.fs_stat(file) then + vim.print("file exists") + end +< + + +vim.fs.abspath({path}) *vim.fs.abspath()* + Convert path to an absolute path. A tilde (~) character at the beginning + of the path is expanded to the user's home directory. Does not check if + the path exists, normalize the path, resolve symlinks or hardlinks + (including `.` and `..`), or expand environment variables. If the path is + already absolute, it is returned unchanged. Also converts `\` path + separators to `/`. + + Parameters: ~ + • {path} (`string`) Path + + Return: ~ + (`string`) Absolute path + vim.fs.basename({file}) *vim.fs.basename()* Return the basename of the given path @@ -2953,6 +2985,7 @@ vim.fs.dir({path}, {opts}) *vim.fs.dir()* • skip: (fun(dir_name: string): boolean)|nil Predicate to control traversal. Return false to stop searching the current directory. Only useful when depth > 1 + • follow: boolean|nil Follow symbolic links. (default: true) Return: ~ (`Iterator`) over items in {path}. Each iteration yields two values: @@ -2994,7 +3027,7 @@ vim.fs.find({names}, {opts}) *vim.fs.find()* -- get all files ending with .cpp or .hpp inside lib/ local cpp_hpp = vim.fs.find(function(name, path) - return name:match('.*%.[ch]pp$') and path:match('[/\\\\]lib$') + return name:match('.*%.[ch]pp$') and path:match('[/\\]lib$') end, {limit = math.huge, type = 'file'}) < @@ -3008,8 +3041,10 @@ vim.fs.find({names}, {opts}) *vim.fs.find()* If {names} is a function, it is called for each traversed item with args: • name: base name of the current item - • path: full path of the current item The function should - return `true` if the given item is considered a match. + • path: full path of the current item + + The function should return `true` if the given item is + considered a match. • {opts} (`table`) Optional keyword arguments: • {path}? (`string`) Path to begin searching from. If omitted, the |current-directory| is used. @@ -3023,14 +3058,21 @@ vim.fs.find({names}, {opts}) *vim.fs.find()* • {limit}? (`number`, default: `1`) Stop the search after finding this many matches. Use `math.huge` to place no limit on the number of matches. + • {follow}? (`boolean`, default: `true`) Follow symbolic + links. Return: ~ (`string[]`) Normalized paths |vim.fs.normalize()| of all matching items vim.fs.joinpath({...}) *vim.fs.joinpath()* - Concatenate directories and/or file paths into a single path with - normalization (e.g., `"foo/"` and `"bar"` get joined to `"foo/bar"`) + Concatenates partial paths (one absolute or relative path followed by zero + or more relative paths). Slashes are normalized: redundant slashes are + removed, and (on Windows) backslashes are replaced with forward-slashes. + + Examples: + • "foo/", "/bar" => "foo/bar" + • Windows: "a\foo\", "\bar" => "a/foo/bar" Attributes: ~ Since: 0.10.0 @@ -3113,6 +3155,23 @@ vim.fs.parents({start}) *vim.fs.parents()* (`nil`) (`string?`) +vim.fs.relpath({base}, {target}, {opts}) *vim.fs.relpath()* + Gets `target` path relative to `base`, or `nil` if `base` is not an + ancestor. + + Example: >lua + vim.fs.relpath('/var', '/var/lib') -- 'lib' + vim.fs.relpath('/var', '/usr/bin') -- nil +< + + Parameters: ~ + • {base} (`string`) + • {target} (`string`) + • {opts} (`table?`) Reserved for future use + + Return: ~ + (`string?`) + vim.fs.rm({path}, {opts}) *vim.fs.rm()* WARNING: This feature is experimental/unstable. @@ -3979,6 +4038,7 @@ vim.version.range({spec}) *vim.version.range()* (`table?`) A table with the following fields: • {from} (`vim.Version`) • {to}? (`vim.Version`) + • {has} (`fun(self: vim.VersionRange, version: string|vim.Version)`) See also: ~ • https://github.com/npm/node-semver#ranges diff --git a/runtime/doc/luvref.txt b/runtime/doc/luvref.txt index 2be73f0412..3cbdb53e01 100644 --- a/runtime/doc/luvref.txt +++ b/runtime/doc/luvref.txt @@ -102,6 +102,7 @@ Low-level implementation details and unexposed C functions and types are not documented here except for when they are relevant to behavior seen in the Lua module. +- |luv-constants| — Constants - |luv-error-handling| — Error handling - |luv-version-checking| — Version checking - |uv_loop_t| — Event loop @@ -131,12 +132,103 @@ module. - |luv-metrics-operations| — Metrics operations ============================================================================== +CONSTANTS *luv-constants* + +As a Lua library, luv supports and encourages the use of lowercase strings to +represent options. For example: +>lua + -- signal start with string input + uv.signal_start("sigterm", function(signame) + print(signame) -- string output: "sigterm" + end) +< +However, luv also superficially exposes libuv constants in a Lua table at +`uv.constants` where its keys are uppercase constant names and their associated +values are integers defined internally by libuv. The values from this table may +be supported as function arguments, but their use may not change the output +type. For example: +>lua + -- signal start with integer input + uv.signal_start(uv.constants.SIGTERM, function(signame) + print(signame) -- string output: "sigterm" + end) +< +The uppercase constants defined in `uv.constants` that have associated +lowercase option strings are listed below. + +Address Families ~ + +- `AF_UNIX`: "unix" +- `AF_INET`: "inet" +- `AF_INET6`: "inet6" +- `AF_IPX`: "ipx" +- `AF_NETLINK`: "netlink" +- `AF_X25`: "x25" +- `AF_AX25`: "as25" +- `AF_ATMPVC`: "atmpvc" +- `AF_APPLETALK`: "appletalk" +- `AF_PACKET`: "packet" + +Signals ~ + +- `SIGHUP`: "sighup" +- `SIGINT`: "sigint" +- `SIGQUIT`: "sigquit" +- `SIGILL`: "sigill" +- `SIGTRAP`: "sigtrap" +- `SIGABRT`: "sigabrt" +- `SIGIOT`: "sigiot" +- `SIGBUS`: "sigbus" +- `SIGFPE`: "sigfpe" +- `SIGKILL`: "sigkill" +- `SIGUSR1`: "sigusr1" +- `SIGSEGV`: "sigsegv" +- `SIGUSR2`: "sigusr2" +- `SIGPIPE`: "sigpipe" +- `SIGALRM`: "sigalrm" +- `SIGTERM`: "sigterm" +- `SIGCHLD`: "sigchld" +- `SIGSTKFLT`: "sigstkflt" +- `SIGCONT`: "sigcont" +- `SIGSTOP`: "sigstop" +- `SIGTSTP`: "sigtstp" +- `SIGBREAK`: "sigbreak" +- `SIGTTIN`: "sigttin" +- `SIGTTOU`: "sigttou" +- `SIGURG`: "sigurg" +- `SIGXCPU`: "sigxcpu" +- `SIGXFSZ`: "sigxfsz" +- `SIGVTALRM`: "sigvtalrm" +- `SIGPROF`: "sigprof" +- `SIGWINCH`: "sigwinch" +- `SIGIO`: "sigio" +- `SIGPOLL`: "sigpoll" +- `SIGLOST`: "siglost" +- `SIGPWR`: "sigpwr" +- `SIGSYS`: "sigsys" + +Socket Types ~ + +- `SOCK_STREAM`: "stream" +- `SOCK_DGRAM`: "dgram" +- `SOCK_SEQPACKET`: "seqpacket" +- `SOCK_RAW`: "raw" +- `SOCK_RDM`: "rdm" + +TTY Modes ~ + +- `TTY_MODE_NORMAL`: "normal" +- `TTY_MODE_RAW`: "raw" +- `TTY_MODE_IO`: "io" + +============================================================================== ERROR HANDLING *luv-error-handling* -In libuv, errors are negative numbered constants; however, while those errors -are exposed through `uv.errno`, the functions used to handle them are not -exposed to luv users. Instead, if an internal error is encountered, the luv -function will return to the caller an assertable `nil, err, name` tuple. +In libuv, errors are represented by negative numbered constants. While these +constants are made available in the `uv.errno` table, they are not returned by +luv funtions and the libuv functions used to handle them are not exposed. +Instead, if an internal error is encountered, the failing luv function will +return to the caller an assertable `nil, err, name` tuple: - `nil` idiomatically indicates failure - `err` is a string with the format `{name}: {message}` @@ -153,9 +245,8 @@ success, or sometimes nothing at all. These cases are documented below. `uv.errno` *uv.errno* -A table value which exposes error constants as a map, where the key is the -error name (without the `UV_` prefix) and its value is a negative number. -See Libuv's "Error constants" page for further details. +Below is a list of known error names and error strings. See Libuv's "Error +constants" page for further details. (https://docs.libuv.org/en/v1.x/errors.html#error-constants) - `E2BIG`: argument list too long. @@ -1154,8 +1245,8 @@ Unix Notes: -- Create a new signal handler local signal = uv.new_signal() -- Define a handler function - uv.signal_start(signal, "sigint", function(signal) - print("got " .. signal .. ", shutting down") + uv.signal_start(signal, "sigint", function(signame) + print("got " .. signame .. ", shutting down") os.exit(1) end) < @@ -1167,34 +1258,40 @@ uv.new_signal() *uv.new_signal()* Returns: `uv_signal_t userdata` or `fail` -uv.signal_start({signal}, {signum}, {callback}) *uv.signal_start()* +uv.signal_start({signal}, {signame}, {callback}) *uv.signal_start()* - > method form `signal:start(signum, callback)` + > method form `signal:start(signame, callback)` Parameters: - `signal`: `uv_signal_t userdata` - - `signum`: `integer` or `string` + - `signame`: `string` or `integer` - `callback`: `callable` - - `signum`: `string` + - `signame`: `string` Start the handle with the given callback, watching for the given signal. + See |luv-constants| for supported `signame` input and output + values. + Returns: `0` or `fail` *uv.signal_start_oneshot()* -uv.signal_start_oneshot({signal}, {signum}, {callback}) +uv.signal_start_oneshot({signal}, {signame}, {callback}) - > method form `signal:start_oneshot(signum, callback)` + > method form `signal:start_oneshot(signame, callback)` Parameters: - `signal`: `uv_signal_t userdata` - - `signum`: `integer` or `string` + - `signame`: `string` or `integer` - `callback`: `callable` - - `signum`: `string` + - `signame`: `string` Same functionality as |uv.signal_start()| but the signal handler is reset the moment the signal is received. + See |luv-constants| for supported `signame` input and output + values. + Returns: `0` or `fail` uv.signal_stop({signal}) *uv.signal_stop()* @@ -1349,30 +1446,36 @@ uv.spawn({path}, {options}, {on_exit}) *uv.spawn()* Returns: `uv_process_t userdata`, `integer` -uv.process_kill({process}, {signum}) *uv.process_kill()* +uv.process_kill({process}, {signame}) *uv.process_kill()* - > method form `process:kill(signum)` + > method form `process:kill(signame)` Parameters: - `process`: `uv_process_t userdata` - - `signum`: `integer` or `string` or `nil` (default: `sigterm`) + - `signame`: `string` or `integer` or `nil` (default: `sigterm`) Sends the specified signal to the given process handle. Check the documentation on |uv_signal_t| for signal support, specially on Windows. + See |luv-constants| for supported `signame` input and output + values. + Returns: `0` or `fail` -uv.kill({pid}, {signum}) *uv.kill()* +uv.kill({pid}, {signame}) *uv.kill()* Parameters: - `pid`: `integer` - - `signum`: `integer` or `string` or `nil` (default: `sigterm`) + - `signame`: `string` or `integer` or `nil` (default: `sigterm`) Sends the specified signal to the given PID. Check the documentation on |uv_signal_t| for signal support, specially on Windows. + See |luv-constants| for supported `signame` input and output + values. + Returns: `0` or `fail` uv.process_get_pid({process}) *uv.process_get_pid()* @@ -1638,12 +1741,13 @@ TCP handles are used to represent both TCP streams and servers. uv.new_tcp([{flags}]) *uv.new_tcp()* Parameters: - - `flags`: `string` or `nil` + - `flags`: `string` or `integer` or `nil` Creates and initializes a new |uv_tcp_t|. Returns the Lua - userdata wrapping it. Flags may be a family string: `"unix"`, - `"inet"`, `"inet6"`, `"ipx"`, `"netlink"`, `"x25"`, `"ax25"`, - `"atmpvc"`, `"appletalk"`, or `"packet"`. + userdata wrapping it. + + If set, `flags` must be a valid address family. See + |luv-constants| for supported address family input values. Returns: `uv_tcp_t userdata` or `fail` @@ -1744,6 +1848,8 @@ uv.tcp_getpeername({tcp}) *uv.tcp_getpeername()* Get the address of the peer connected to the handle. + See |luv-constants| for supported address `family` output values. + Returns: `table` or `fail` - `ip` : `string` - `family` : `string` @@ -1758,6 +1864,8 @@ uv.tcp_getsockname({tcp}) *uv.tcp_getsockname()* Get the current address to which the handle is bound. + See |luv-constants| for supported address `family` output values. + Returns: `table` or `fail` - `ip` : `string` - `family` : `string` @@ -1823,8 +1931,7 @@ uv.socketpair([{socktype}, [{protocol}, [{flags1}, [{flags2}]]]]) |uv.tcp_open()|, used with |uv.spawn()|, or for any other purpose. - When specified as a string, `socktype` must be one of - `"stream"`, `"dgram"`, `"raw"`, `"rdm"`, or `"seqpacket"`. + See |luv-constants| for supported `socktype` input values. When `protocol` is set to 0 or nil, it will be automatically chosen based on the socket's domain and type. When `protocol` @@ -2184,17 +2291,11 @@ uv.tty_set_mode({tty}, {mode}) *uv.tty_set_mode()* Parameters: - `tty`: `uv_tty_t userdata` - - `mode`: `integer` + - `mode`: `string` or `integer` Set the TTY using the specified terminal mode. - Parameter `mode` is a C enum with the following values: - - - 0 - UV_TTY_MODE_NORMAL: Initial/normal terminal mode - - 1 - UV_TTY_MODE_RAW: Raw input mode (On Windows, - ENABLE_WINDOW_INPUT is also enabled) - - 2 - UV_TTY_MODE_IO: Binary-safe I/O mode for IPC - (Unix-only) + See |luv-constants| for supported TTY mode input values. Returns: `0` or `fail` @@ -2265,9 +2366,7 @@ uv.new_udp([{flags}]) *uv.new_udp()* Creates and initializes a new |uv_udp_t|. Returns the Lua userdata wrapping it. The actual socket is created lazily. - When specified, `family` must be one of `"unix"`, `"inet"`, - `"inet6"`, `"ipx"`, `"netlink"`, `"x25"`, `"ax25"`, - `"atmpvc"`, `"appletalk"`, or `"packet"`. + See |luv-constants| for supported address `family` input values. When specified, `mmsgs` determines the number of messages able to be received at one time via `recvmmsg(2)` (the allocated @@ -2510,6 +2609,51 @@ uv.udp_try_send({udp}, {data}, {host}, {port}) *uv.udp_try_send()* Returns: `integer` or `fail` +uv.udp_try_send2({udp}, {messages}, {flags}) *uv.udp_try_send2()* + + > method form `udp:try_send2(messages, flags)` + + Parameters: + - `udp`: `uv_udp_t userdata` + - `messages`: `table` + - `[1, 2, 3, ..., n]` : `table` + - `data` : `buffer` + - `addr` : `table` + - `ip` : `string` + - `port` : `integer` + - `flags`: `nil` (see below) + - `port`: `integer` + + Like `uv.udp_try_send()`, but can send multiple datagrams. + Lightweight abstraction around `sendmmsg(2)`, with a + `sendmsg(2)` fallback loop for platforms that do not support + the former. The `udp` handle must be fully initialized, either + from a `uv.udp_bind` call, another call that will bind + automatically (`udp_send`, `udp_try_send`, etc), or from + `uv.udp_connect`. `messages` should be an array-like table, + where `addr` must be specified if the `udp` has not been + connected via `udp_connect`. Otherwise, `addr` must be `nil`. + + `flags` is reserved for future extension and must currently be + `nil` or `0` or `{}`. + + Returns the number of messages sent successfully. An error will only be returned + if the first datagram failed to be sent. + + Returns: `integer` or `fail` + >lua + -- If client:connect(...) was not called + local addr = { ip = "127.0.0.1", port = 1234 } + client:try_send2({ + { data = "Message 1", addr = addr }, + { data = "Message 2", addr = addr }, + }) + -- If client:connect(...) was called + client:try_send2({ + { data = "Message 1" }, + { data = "Message 2" }, + }) +< uv.udp_recv_start({udp}, {callback}) *uv.udp_recv_start()* > method form `udp:recv_start(callback)` @@ -2531,6 +2675,8 @@ uv.udp_recv_start({udp}, {callback}) *uv.udp_recv_start()* been bound with |uv.udp_bind()| it is bound to `0.0.0.0` (the "all interfaces" IPv4 address) and a random port number. + See |luv-constants| for supported address `family` output values. + Returns: `0` or `fail` uv.udp_recv_stop({udp}) *uv.udp_recv_stop()* @@ -2743,7 +2889,8 @@ uv.fs_open({path}, {flags}, {mode} [, {callback}]) *uv.fs_open()* Parameters: - `path`: `string` - `flags`: `string` or `integer` - - `mode`: `integer` + - `mode`: `integer` (octal `chmod(1)` mode, e.g. + `tonumber('644', 8)`) - `callback`: `callable` (async version) or `nil` (sync version) - `err`: `nil` or `string` @@ -2829,7 +2976,8 @@ uv.fs_mkdir({path}, {mode} [, {callback}]) *uv.fs_mkdir()* Parameters: - `path`: `string` - - `mode`: `integer` + - `mode`: `integer` (octal representation of `chmod(1)` mode, + e.g. `tonumber('755', 8)`) - `callback`: `callable` (async version) or `nil` (sync version) - `err`: `nil` or `string` @@ -3078,7 +3226,9 @@ uv.fs_access({path}, {mode} [, {callback}]) *uv.fs_access()* Parameters: - `path`: `string` - - `mode`: `integer` + - `mode`: `integer` `string` (a combination of the `'r'`, + `'w'` and `'x'` characters denoting the symbolic mode as per + `chmod(1)`) - `callback`: `callable` (async version) or `nil` (sync version) - `err`: `nil` or `string` @@ -3097,7 +3247,8 @@ uv.fs_chmod({path}, {mode} [, {callback}]) *uv.fs_chmod()* Parameters: - `path`: `string` - - `mode`: `integer` + - `mode`: `integer` (octal representation of `chmod(1)` mode, + e.g. `tonumber('644', 8)`) - `callback`: `callable` (async version) or `nil` (sync version) - `err`: `nil` or `string` @@ -3480,14 +3631,17 @@ uv.getaddrinfo({host}, {service} [, {hints} [, {callback}]]) *uv.getaddrinfo()* Equivalent to `getaddrinfo(3)`. Either `node` or `service` may be `nil` but not both. - Valid hint strings for the keys that take a string: - - `family`: `"unix"`, `"inet"`, `"inet6"`, `"ipx"`, - `"netlink"`, `"x25"`, `"ax25"`, `"atmpvc"`, `"appletalk"`, - or `"packet"` - - `socktype`: `"stream"`, `"dgram"`, `"raw"`, `"rdm"`, or - `"seqpacket"` - - `protocol`: will be looked up using the `getprotobyname(3)` - function (examples: `"ip"`, `"icmp"`, `"tcp"`, `"udp"`, etc) + See |luv-constants| for supported address `family` input and + output values. + + See |luv-constants| for supported `socktype` input and + output values. + + When `protocol` is set to `0` or `nil`, it will be + automatically chosen based on the socket's domain and type. + When `protocol` is specified as a string, it will be looked up + using the `getprotobyname(3)` function. Examples: `"ip"`, + `"icmp"`, `"tcp"`, `"udp"`, etc. Returns (sync version): `table` or `fail` - `[1, 2, 3, ..., n]` : `table` @@ -3515,9 +3669,7 @@ uv.getnameinfo({address} [, {callback}]) *uv.getnameinfo()* Equivalent to `getnameinfo(3)`. - When specified, `family` must be one of `"unix"`, `"inet"`, - `"inet6"`, `"ipx"`, `"netlink"`, `"x25"`, `"ax25"`, - `"atmpvc"`, `"appletalk"`, or `"packet"`. + See |luv-constants| for supported address `family` input values. Returns (sync version): `string, string` or `fail` @@ -3526,7 +3678,7 @@ uv.getnameinfo({address} [, {callback}]) *uv.getnameinfo()* ============================================================================== THREADING AND SYNCHRONIZATION UTILITIES *luv-threading-and-synchronization-utilities* -Libuv provides cross-platform implementations for multiple threading an +Libuv provides cross-platform implementations for multiple threading and synchronization primitives. The API largely follows the pthreads API. uv.new_thread([{options}, ] {entry}, {...}) *uv.new_thread()* @@ -3683,6 +3835,43 @@ uv.thread_join({thread}) *uv.thread_join()* Returns: `boolean` or `fail` +uv.thread_detach({thread}) *uv.thread_detach()* + + > method form `thread:detach()` + + Parameters: + - `thread`: `luv_thread_t userdata` + + Detaches a thread. Detached threads automatically release + their resources upon termination, eliminating the need for the + application to call `uv.thread_join`. + + Returns: `boolean` or `fail` + +uv.thread_setname({name}) *uv.thread_setname()* + + Parameters: + - `name`: `string` + + Sets the name of the current thread. Different platforms + define different limits on the max number of characters a + thread name can be: Linux, IBM i (16), macOS (64), Windows + (32767), and NetBSD (32), etc. The name will be truncated if + `name` is larger than the limit of the platform. + + Returns: `0` or `fail` + +uv.thread_getname({thread}) *uv.thread_getname()* + + > method form `thread:getname()` + + Parameters: + - `thread`: `luv_thread_t userdata` + + Gets the name of the thread specified by `thread`. + + Returns: `string` or `fail` + uv.sleep({msec}) *uv.sleep()* Parameters: @@ -3796,6 +3985,36 @@ uv.getrusage() *uv.getrusage()* - `nvcsw` : `integer` (voluntary context switches) - `nivcsw` : `integer` (involuntary context switches) +uv.getrusage_thread() *uv.getrusage_thread()* + Gets the resource usage measures for the calling thread. + + Note: Not supported on all platforms. May return `ENOTSUP`. + + On macOS and Windows not all fields are set (the unsupported + fields are filled with zeroes). + + Returns: `table` or `fail` + - `utime` : `table` (user CPU time used) + - `sec` : `integer` + - `usec` : `integer` + - `stime` : `table` (system CPU time used) + - `sec` : `integer` + - `usec` : `integer` + - `maxrss` : `integer` (maximum resident set size) + - `ixrss` : `integer` (integral shared memory size) + - `idrss` : `integer` (integral unshared data size) + - `isrss` : `integer` (integral unshared stack size) + - `minflt` : `integer` (page reclaims (soft page faults)) + - `majflt` : `integer` (page faults (hard page faults)) + - `nswap` : `integer` (swaps) + - `inblock` : `integer` (block input operations) + - `oublock` : `integer` (block output operations) + - `msgsnd` : `integer` (IPC messages sent) + - `msgrcv` : `integer` (IPC messages received) + - `nsignals` : `integer` (signals received) + - `nvcsw` : `integer` (voluntary context switches) + - `nivcsw` : `integer` (involuntary context switches) + uv.available_parallelism() *uv.available_parallelism()* Returns an estimate of the default amount of parallelism a @@ -3968,6 +4187,8 @@ uv.interface_addresses() *uv.interface_addresses()* information where fields are `ip`, `family`, `netmask`, `internal`, and `mac`. + See |luv-constants| for supported address `family` output values. + Returns: `table` - `[name(s)]` : `table` - `ip` : `string` @@ -4202,6 +4423,67 @@ uv.metrics_info() *uv.metrics_info()* - `events_waiting` : `integer` ============================================================================== +STRING MANIPULATION FUNCTIONS *luv-string-manipulation* + +These string utilities are needed internally for dealing with Windows, and are +exported to allow clients to work uniformly with this data when the libuv API +is not complete. + +Notes: +1. New in luv version 1.49.0. +2. See the WTF-8 spec (https://simonsapin.github.io/wtf-8/) for information + about WTF-8. +3. Luv uses Lua-style strings, which means that all inputs and return values + (UTF-8 or UTF-16 strings) do not include a NUL terminator. + +uv.utf16_length_as_wtf8({utf16}) *uv.utf16_length_as_wtf8()* + + Get the length (in bytes) of a UTF-16 (or UCS-2) string + `utf16` value after converting it to WTF-8. The endianness of + the UTF-16 (or UCS-2) string is assumed to be the same as the + native endianness of the platform. + + Parameters: + - `utf16`: `string` + + Returns: `integer` + +uv.utf16_to_wtf8({utf16}) *uv.utf16_to_wtf8()* + + Convert UTF-16 (or UCS-2) string `utf16` to UTF-8 string. The + endianness of the UTF-16 (or UCS-2) string is assumed to be + the same as the native endianness of the platform. + + Parameters: + - `utf16`: `string` + + Returns: `string` + +uv.wtf8_length_as_utf16({wtf16}) *uv.wtf8_length_as_utf16()* + + Get the length (in UTF-16 code units) of a WTF-8 `wtf8` value + after converting it to UTF-16 (or UCS-2). + + Note: The number of bytes needed for a UTF-16 (or UCS-2) + string is `<number of code units> * 2`. + + Parameters: + - `wtf8`: `string` + + Returns: `integer` + +uv.wtf8_to_utf16({wtf16}) *uv.wtf8_to_utf16()* + + Convert WTF-8 string in `wtf8` to UTF-16 (or UCS-2) string. + The endianness of the UTF-16 (or UCS-2) string is assumed to + be the same as the native endianness of the platform. + + Parameters: + - `wtf8`: `string` + + Returns: `string` + +============================================================================== CREDITS *luv-credits* This document is a reformatted version of the LUV documentation, up-to-date @@ -4211,4 +4493,4 @@ https://github.com/luvit/luv/commit/dcd1a1cad5b05634a7691402d6ca2f214fb4ae76. Based on https://github.com/nanotee/luv-vimdocs with kind permission. -vim:tw=78:ts=8:ft=help:norl: +vim:tw=78:ts=8:sw=2:et:ft=help:norl: diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index 11048aee30..52c04333fc 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -12,7 +12,7 @@ manual. Type |gO| to see the table of contents. ============================================================================== -1. Key mapping *key-mapping* *mapping* *macro* +1. Key mapping *keybind* *key-mapping* *mapping* Key mapping is used to change the meaning of typed keys. The most common use is to define a sequence of commands for a function key. Example: > @@ -1545,7 +1545,7 @@ Your command preview routine must implement this protocol: 3. Add required highlights to the target buffers. If preview buffer is provided, add required highlights to the preview buffer as well. All highlights must be added to the preview namespace which is provided as an - argument to the preview callback (see |nvim_buf_add_highlight()| and + argument to the preview callback (see |vim.hl.range()| and |nvim_buf_set_extmark()| for help on how to add highlights to a namespace). 4. Return an integer (0, 1, 2) which controls how Nvim behaves as follows: 0: No preview is shown. @@ -1574,13 +1574,12 @@ supports incremental command preview: if start_idx then -- Highlight the match - vim.api.nvim_buf_add_highlight( + vim.hl.range( buf, preview_ns, 'Substitute', - line1 + i - 2, - start_idx - 1, - end_idx + {line1 + i - 2, start_idx - 1}, + {line1 + i - 2, end_idx}, ) -- Add lines and set highlights in the preview buffer @@ -1595,13 +1594,12 @@ supports incremental command preview: false, { prefix .. line } ) - vim.api.nvim_buf_add_highlight( + vim.hl.range( preview_buf, preview_ns, 'Substitute', - preview_buf_line, - #prefix + start_idx - 1, - #prefix + end_idx + {preview_buf_line, #prefix + start_idx - 1}, + {preview_buf_line, #prefix + end_idx}, ) preview_buf_line = preview_buf_line + 1 end diff --git a/runtime/doc/message.txt b/runtime/doc/message.txt index ac151811c2..d6f5aeb354 100644 --- a/runtime/doc/message.txt +++ b/runtime/doc/message.txt @@ -27,7 +27,7 @@ depends on the 'shortmess' option. Clear messages, keeping only the {count} most recent ones. -The number of remembered messages is fixed at 200. +The number of remembered messages is determined by the 'messagesopt' option. *g<* The "g<" command can be used to see the last page of previous command output. @@ -789,6 +789,7 @@ If you accidentally hit <Enter> or <Space> and you want to see the displayed text then use |g<|. This only works when 'more' is set. To reduce the number of hit-enter prompts: +- Set 'messagesopt'. - Set 'cmdheight' to 2 or higher. - Add flags to 'shortmess'. - Reset 'showcmd' and/or 'ruler'. diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt index 48e13d795e..284be09121 100644 --- a/runtime/doc/motion.txt +++ b/runtime/doc/motion.txt @@ -115,6 +115,12 @@ This cannot be repeated: > endif<CR> Note that when using ":" any motion becomes charwise exclusive. + *inclusive-motion-selection-exclusive* +When 'selection' is "exclusive", |Visual| mode is active and an inclusive +motion has been used, the cursor position will be adjusted by another +character to the right, so that the Visual selection includes the expected +text and can be acted upon. + *forced-motion* FORCING A MOTION TO BE LINEWISE, CHARWISE OR BLOCKWISE diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index d19df84023..12fac28db8 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -11,16 +11,36 @@ For changes in the previous release, see |news-0.10|. Type |gO| to see the table of contents. ============================================================================== -BREAKING CHANGES IN HEAD *news-breaking-dev* +BREAKING CHANGES IN HEAD OR EXPERIMENTAL *news-breaking-dev* ====== Remove this section before release. ====== The following changes to UNRELEASED features were made during the development cycle (Nvim HEAD, the "master" branch). +EXPERIMENTS + +• Removed `vim.loader.disable()`. Use `vim.loader.enable(false)` instead. + +LSP + +• `lsp/` runtimepath files should return a table instead of calling + |vim.lsp.config()| (or assigning to `vim.lsp.config`). See |lsp-config| +• `vim.lsp.buf.document_symbol()` uses the |location-list| by default. Use + `vim.lsp.buf.document_symbol({ loclist = false })` to use the |quickfix| + list. + + OPTIONS • 'jumpoptions' flag "unload" has been renamed to "clean". +• The `msghistory` option has been removed in favor of 'messagesopt'. + +TREESITTER + +• *TSNode:child_containing_descendant()* has been removed in the tree-sitter + library and is no longer available; use |TSNode:child_with_descendant()| + instead. ============================================================================== BREAKING CHANGES *news-breaking* @@ -29,7 +49,6 @@ These changes may require adaptations in your config or plugins. API -• Improved API "meta" docstrings and :help documentation. • `vim.rpcnotify(0)` and `rpcnotify(0)` broadcast to ALL channels. Previously they would "multicast" only to subscribed channels (controlled by `nvim_subscribe()`). Plugins and clients that want "multicast" behavior must @@ -45,19 +64,24 @@ API • Renamed `nvim__id_dictionary` (unsupported/experimental API) to `nvim__id_dict`. +BUILD + +On Windows, only building with the UCRT runtime is supported. + DEFAULTS -• |]d-default| and |[d-default| accept a count. -• |[D-default| and |]D-default| jump to the first and last diagnostic in the - current buffer, respectively. +• 'number', 'relativenumber', 'signcolumn', and 'foldcolumn' are disabled in + |terminal| buffers. See |terminal-config| for an example of changing these defaults. DIAGNOSTICS -• |vim.diagnostic.config()| accepts a "jump" table to specify defaults for - |vim.diagnostic.jump()|. • The "underline" diagnostics handler sorts diagnostics by severity when using the "severity_sort" option. - +• Diagnostics are filtered by severity before being passed to a diagnostic + handler |diagnostic-handlers|. +• The "virtual_text" handler is disabled by default. Enable with >lua + vim.diagnostic.config({ virtual_text = true }) +< EDITOR • The order in which signs are placed was changed. Higher priority signs will @@ -74,14 +98,24 @@ EVENTS • |vim.ui_attach()| callbacks for |ui-messages| `msg_show` events are executed in |api-fast| context. +• New/enhanced arguments in these existing UI events: + • `cmdline_hide`: `abort` argument indicating if the cmdline was aborted. + • `cmdline_show`: + • Prompts that were previously emitted as `msg_show` events, are now routed + through `cmdline_show`. + • `hl_id` argument to highlight the prompt text. + • `msg_show`: + • `history` argument indicating if the message was added to the history. + • new message kinds: "bufwrite", "completion", "list_cmd", "lua_print", + "search_cmd", "shell_out/err/ret", "undo", "verbose", wildlist". + +HIGHLIGHTS + +• |TermCursorNC| is removed and no longer supported. Unfocused terminals no + longer have a cursor. LSP -• Improved rendering of LSP hover docs. |K-lsp-default| -• |vim.lsp.completion.enable()| gained the `convert` callback which enables - customizing the transformation of an LSP CompletionItem to |complete-items|. -• |vim.lsp.diagnostic.from()| can be used to convert a list of - |vim.Diagnostic| objects into their LSP diagnostic representation. • |vim.lsp.buf.references()|, |vim.lsp.buf.declaration()|, |vim.lsp.buf.definition()|, |vim.lsp.buf.type_definition()|, |vim.lsp.buf.implementation()| and |vim.lsp.buf.hover()| now support merging the results of multiple clients @@ -95,14 +129,15 @@ LSP Instead use: >lua vim.diagnostic.config(config, vim.lsp.diagnostic.get_namespace(client_id)) < +• |vim.lsp.util.make_position_params()|, |vim.lsp.util.make_range_params()| + and |vim.lsp.util.make_given_range_params()| now require the `position_encoding` + parameter. LUA • API functions now consistently return an empty dictionary as |vim.empty_dict()|. Earlier, a |lua-special-tbl| was sometimes used. -• Command-line completions for: `vim.g`, `vim.t`, `vim.w`, `vim.b`, `vim.v`, - `vim.o`, `vim.wo`, `vim.bo`, `vim.opt`, `vim.opt_local`, `vim.opt_global`, - and `vim.fn`. +• |vim.json.encode()| no longer escapes forward slashes "/" by default OPTIONS @@ -134,13 +169,17 @@ TREESITTER if no languages are explicitly registered. • |vim.treesitter.language.add()| returns `true` if a parser was loaded successfully and `nil,errmsg` otherwise instead of throwing an error. -• New |TSNode:child_with_descendant()|, which is nearly identical to - |TSNode:child_containing_descendant()| except that it can return the - descendant itself. +• |vim.treesitter.get_parser()| and |vim.treesitter.start()| no longer parse + the tree before returning. Scripts must call |LanguageTree:parse()| explicitly. >lua + local p = vim.treesitter.get_parser(0, 'c') + p:parse() +< TUI -• TODO +• OSC 52 is used as a fallback clipboard provider when no other + |clipboard-tool| is found, even when not using SSH |clipboard-osc52|. To + disable OSC 52 queries, set the "osc52" key of |g:termfeatures| to false. VIMSCRIPT @@ -155,7 +194,15 @@ The following new features were added. API +• Improved API "meta" docstrings and :help documentation. • |nvim__ns_set()| can set properties for a namespace +• |nvim_echo()| `err` field to print error messages and `chunks` accepts + highlight group IDs. +• |nvim_open_win()| `relative` field can be set to "laststatus" and "tabline". +• |nvim_buf_set_extmark()| `hl_group` field can be an array of layered groups +• |vim.hl.range()| now has a optional `timeout` field which allows for a timed highlight +• |nvim_buf_set_extmark()| virt_text_pos accepts `eol_right_align` to + allow for right aligned text that truncates before covering up buffer text. DEFAULTS @@ -173,6 +220,9 @@ DEFAULTS on a URL. • Mouse |popup-menu| includes a "Go to definition" item when LSP is active in the buffer. + • |]d-default| and |[d-default| accept a count. + • |[D-default| and |]D-default| jump to the first and last diagnostic in the + current buffer, respectively. • Mappings inspired by Tim Pope's vim-unimpaired: • |[q|, |]q|, |[Q|, |]Q|, |[CTRL-Q|, |]CTRL-Q| navigate through the |quickfix| list • |[l|, |]l|, |[L|, |]L|, |[CTRL-L|, |]CTRL-L| navigate through the |location-list| @@ -187,14 +237,25 @@ DEFAULTS • `<S-Tab>` in Insert and Select mode maps to `vim.snippet.jump({ direction = -1 })` when a snippet is active and jumpable backwards. +DIAGNOSTICS + +• |vim.diagnostic.config()| accepts a "jump" table to specify defaults for + |vim.diagnostic.jump()|. +• A "virtual_lines" diagnostic handler was added to render diagnostics using + virtual lines below the respective code. +• The "virtual_text" diagnostic handler accepts a `current_line` option to + only show virtual text at the cursor's line. + EDITOR +• Use |g==| in :help docs to execute Lua and Vimscript code examples. • Improved |paste| handling for redo (dot-repeat) and macros (|recording|): • Redoing a large paste is significantly faster and ignores 'autoindent'. • Replaying a macro with |@| also replays pasted text. • On Windows, filename arguments on the command-line prefixed with "~\" or "~/" are now expanded to the user's profile directory, not a relative path to a literal "~" directory. +• |hl-ComplMatchIns| shows matched text of the currently inserted completion. • |hl-PmenuMatch| and |hl-PmenuMatchSel| show matched text in completion popup. EVENTS @@ -205,6 +266,12 @@ EVENTS LSP +• Improved rendering of LSP hover docs. |K-lsp-default| +• |vim.lsp.completion.enable()| gained the `convert` callback which enables + customizing the transformation of an LSP CompletionItem to |complete-items|. +• |vim.lsp.diagnostic.from()| can be used to convert a list of + |vim.Diagnostic| objects into their LSP diagnostic representation. +• `:checkhealth vim.lsp` displays the server version (if available). • Completion side effects (including snippet expansion, execution of commands and application of additional text edits) is now built-in. • |vim.lsp.util.locations_to_items()| sets `end_col` and `end_lnum` fields. @@ -218,32 +285,61 @@ LSP • The client now supports `'utf-8'` and `'utf-32'` position encodings. • |vim.lsp.buf.hover()| now highlights hover ranges using the |hl-LspReferenceTarget| highlight group. +• Functions in |vim.lsp.Client| can now be called as methods. +• Implemented LSP folding: |vim.lsp.foldexpr()| + https://microsoft.github.io/language-server-protocol/specification/#textDocument_foldingRange +• |vim.lsp.config()| has been added to define default configurations for + servers. In addition, configurations can be specified in `lsp/<name>.lua`. +• |vim.lsp.enable()| has been added to enable servers. LUA +• Command-line completions for: `vim.g`, `vim.t`, `vim.w`, `vim.b`, `vim.v`, + `vim.o`, `vim.wo`, `vim.bo`, `vim.opt`, `vim.opt_local`, `vim.opt_global`, + and `vim.fn`. • |vim.fs.rm()| can delete files and directories. • |vim.validate()| now has a new signature which uses less tables, - is more peformant and easier to read. + is more performant and easier to read. • |vim.str_byteindex()| and |vim.str_utfindex()| gained overload signatures supporting two new parameters, `encoding` and `strict_indexing`. +• |vim.json.encode()| has an option to enable forward slash escaping +• |vim.fs.abspath()| converts paths to absolute paths. +• |vim.fs.relpath()| gets relative path compared to base path. +• |vim.fs.dir()| and |vim.fs.find()| now follow symbolic links by default, + the behavior can be turn off using the new `follow` option. OPTIONS • 'completeopt' flag "fuzzy" enables |fuzzy-matching| during |ins-completion|. -• 'msghistory' controls maximum number of messages to remember. +• 'completeopt' flag "preinsert" highlights text to be inserted. +• 'messagesopt' configures |:messages| and |hit-enter| prompt. • 'tabclose' controls which tab page to focus when closing a tab page. PERFORMANCE -• TODO +• Significantly reduced redraw time for long lines with treesitter + highlighting. +• LSP diagnostics and inlay hints are de-duplicated (new requests cancel + inflight requests). This greatly improves performance with slow LSP servers. +• 10x speedup for |vim.treesitter.foldexpr()| (when no parser exists for the + buffer). +• Strong |treesitter-query| caching makes repeat |vim.treesitter.query.get()| + and |vim.treesitter.query.parse()| calls significantly faster for large + queries. +• Treesitter highlighting is now asynchronous. To force synchronous parsing, + use `vim.g._ts_force_sync_parsing = true`. +• Treesitter folding is now calculated asynchronously. PLUGINS • EditorConfig • spelling_language property is now supported. +• 'inccommand' incremental preview can run on 'nomodifiable' buffers and + restores their 'modifiable' state STARTUP +• |-es| ("script mode") disables shada by default. • Nvim will fail if the |--listen| or |$NVIM_LISTEN_ADDRESS| address is invalid, instead of silently skipping an invalid address. @@ -258,12 +354,36 @@ TERMINAL 'scrollback' are not reflown. • The |terminal| now supports OSC 8 escape sequences and will display hyperlinks in supporting host terminals. +• The |terminal| now uses the actual cursor, rather than a "virtual" cursor. + This means that escape codes sent by applications running in a terminal + buffer can change the cursor shape and visibility. However, it also + means that the |TermCursorNC| highlight group is no longer supported: an + unfocused terminal window will have no cursor at all (so there is nothing to + highlight). +• |jobstart()| gained the "term" flag. +• The |terminal| will send theme update notifications when 'background' is + changed and DEC mode 2031 is enabled. +• The |terminal| has experimental support for the Kitty keyboard protocol + (sometimes called "CSI u" key encoding). Only the "Disambiguate escape + codes" mode is currently supported. TREESITTER • |LanguageTree:node_for_range()| gets anonymous and named nodes for a range • |vim.treesitter.get_node()| now takes an option `include_anonymous`, default false, which allows it to return anonymous nodes as well as named nodes. +• |treesitter-directive-trim!| can trim all whitespace (not just empty lines) + from both sides of a node. +• |vim.treesitter.get_captures_at_pos()| now returns the `id` of each capture +• New |TSNode:child_with_descendant()|, which efficiently gets the node's + child that contains a given node as descendant. +• |LanguageTree:parse()| optionally supports asynchronous invocation, which is + activated by passing the `on_parse` callback parameter. +• |vim.treesitter.query.set()| can now inherit and/or extend runtime file + queries in addition to overriding. +• |LanguageTree:is_valid()| now accepts a range parameter to narrow the scope + of the validity check. +• |:InspectTree| now shows which nodes are missing. TUI @@ -272,6 +392,8 @@ TUI :lua =vim.api.nvim_get_chan_info(vim.api.nvim_list_uis()[1].chan) • |log| messages written by the builtin UI client (TUI, |--remote-ui|) are now prefixed with "ui" instead of "?". +• The TUI will re-query the terminal's background color when a theme update + notification is received and Nvim will update 'background' accordingly. UI @@ -279,7 +401,7 @@ UI which controls the tool used to open the given path or URL. If you want to globally set this, you can override vim.ui.open using the same approach described at |vim.paste()|. -- `vim.ui.open()` now supports +• `vim.ui.open()` now supports [lemonade](https://github.com/lemonade-command/lemonade) as an option for opening urls/files. This is handy if you are in an ssh connection and use `lemonade`. @@ -287,8 +409,16 @@ UI |hl-PmenuSel| and |hl-PmenuMatch| both inherit from |hl-Pmenu|, and |hl-PmenuMatchSel| inherits highlights from both |hl-PmenuSel| and |hl-PmenuMatch|. - +• |vim.diagnostic.setqflist()| updates an existing quickfix list with the + given title if found • |ui-messages| content chunks now also contain the highlight group ID. +• |:checkhealth| can display in a floating window, controlled by the + |g:health| variable. + +VIMSCRIPT + +• |getchar()| and |getcharstr()| have optional {opts} |Dict| argument to control: + cursor behavior, return type, and whether to simplify the returned key. ============================================================================== CHANGED FEATURES *news-changed* @@ -309,9 +439,14 @@ These existing features changed their behavior. more emoji characters than before, including those encoded with multiple emoji codepoints combined with ZWJ (zero width joiner) codepoints. -• Text in the 'statusline', 'tabline', and 'winbar' now inherits highlights - from the respective |hl-StatusLine|, |hl-TabLine|, and |hl-WinBar| highlight - groups. + This also applies to :terminal output, where width of cells will be calculated + using the upgraded implementation. + +• Custom highlights in 'rulerformat', 'statuscolumn', 'statusline', 'tabline', + 'winbar', and the sign/number column are stacked with their respective + highlight groups, as opposed to |hl-Normal|. + This is also reflected in the `highlights` from |nvim_eval_statusline()|, + with a new `groups` field containing an array of stacked highlight groups. • |vim.on_key()| callbacks won't be invoked recursively when a callback itself consumes input. @@ -320,6 +455,11 @@ These existing features changed their behavior. current window, and it no longer throws |E444| when there is only one window on the screen. Global variable `vim.g.pager` is removed. +• Default 'titlestring' is now implemented with 'statusline' "%" format items. + This means the default, empty value is essentially an alias to: + `%t%(\ %M%)%(\ \(%{expand(\"%:~:h\")}\)%)%a\ -\ Nvim`. This is only an + implementation simplification, not a behavior change. + ============================================================================== REMOVED FEATURES *news-removed* diff --git a/runtime/doc/nvim.txt b/runtime/doc/nvim.txt index 86e344c654..8593511dbc 100644 --- a/runtime/doc/nvim.txt +++ b/runtime/doc/nvim.txt @@ -6,21 +6,23 @@ Nvim *nvim* *neovim* *nvim-intro* -Nvim is based on Vim by Bram Moolenaar. +Nvim is based on Vim by Bram Moolenaar. Nvim is emphatically a fork of Vim, +not a clone: compatibility with Vim (especially editor and Vimscript features, +except |Vim9script|) is maintained where possible. See |vim-differences| for +the complete reference. -If you already use Vim see |nvim-from-vim| for a quickstart. -If you are new to Vim, try the 30-minute tutorial: >vim +If you already use Vim, see |nvim-from-vim| for a quickstart. If you just +installed Nvim and have never used it before, watch this 10-minute +video: https://youtu.be/TQn2hJeHQbM . - :Tutor<Enter> - -Nvim is emphatically a fork of Vim, not a clone: compatibility with Vim -(especially editor and Vimscript features) is maintained where possible. See -|vim-differences| for the complete reference of differences from Vim. +To learn how to use Vim in 30 minutes, try the tutorial: >vim + :Tutor<Enter> +< Type |gO| to see the table of contents. ============================================================================== -Transitioning from Vim *nvim-from-vim* +Transitioning from Vim *nvim-from-vim* 1. To start the transition, create your |init.vim| (user config) file: >vim @@ -71,4 +73,20 @@ the same Nvim configuration on all of your machines, by creating source ~/.config/nvim/init.vim ============================================================================== +What next? *nvim-quickstart* + +If you are just trying out Nvim for a few minutes, and want to see the +extremes of what it can do, try one of these popular "extension packs" or +"distributions" (Note: Nvim is not affiliated with these projects, and does +not support them): + +- *kickstart* https://github.com/nvim-lua/kickstart.nvim +- *lazyvim* https://www.lazyvim.org/ +- *nvchad* https://nvchad.com/ + +However, in general, we recommend (eventually) taking time to learn Nvim from +its stock configuration, and incrementally setting options and adding plugins +to your |config| as you find an explicit need to do so. + +============================================================================== vim:tw=78:ts=8:et:ft=help:norl: diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 97d5082e9e..b35049343d 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -1559,9 +1559,9 @@ A jump table for the options with a short description can be found at |Q_op|. a match from the menu. Only works in combination with "menu" or "menuone". No effect if "longest" is present. - noselect Do not select a match in the menu, force the user to - select one from the menu. Only works in combination with - "menu" or "menuone". + noselect Same as "noinsert", except that no menu item is + pre-selected. If both "noinsert" and "noselect" are + present, "noselect" has precedence. fuzzy Enable |fuzzy-matching| for completion candidates. This allows for more flexible and intuitive matching, where @@ -1571,6 +1571,16 @@ A jump table for the options with a short description can be found at |Q_op|. list of alternatives, but not how the candidates are collected (using different completion types). + nosort Disable sorting of completion candidates based on fuzzy + scores when "fuzzy" is enabled. Candidates will appear + in their original order. + + preinsert + Preinsert the portion of the first candidate word that is + not part of the current completion leader and using the + |hl-ComplMatchIns| highlight group. Does not work when + "fuzzy" is also included. + *'completeslash'* *'csl'* 'completeslash' 'csl' string (default "") local to buffer @@ -2027,11 +2037,20 @@ A jump table for the options with a short description can be found at |Q_op|. Option settings for diff mode. It can consist of the following items. All are optional. Items must be separated by a comma. - filler Show filler lines, to keep the text - synchronized with a window that has inserted - lines at the same position. Mostly useful - when windows are side-by-side and 'scrollbind' - is set. + algorithm:{text} Use the specified diff algorithm with the + internal diff engine. Currently supported + algorithms are: + myers the default algorithm + minimal spend extra time to generate the + smallest possible diff + patience patience diff algorithm + histogram histogram diff algorithm + + closeoff When a window is closed where 'diff' is set + and there is only one window remaining in the + same tab page with 'diff' set, execute + `:diffoff` in that window. This undoes a + `:diffsplit` command. context:{n} Use a context of {n} lines between a change and a fold that contains unchanged lines. @@ -2042,6 +2061,23 @@ A jump table for the options with a short description can be found at |Q_op|. value (999999) to disable folding completely. See |fold-diff|. + filler Show filler lines, to keep the text + synchronized with a window that has inserted + lines at the same position. Mostly useful + when windows are side-by-side and 'scrollbind' + is set. + + foldcolumn:{n} Set the 'foldcolumn' option to {n} when + starting diff mode. Without this 2 is used. + + followwrap Follow the 'wrap' option and leave as it is. + + horizontal Start diff mode with horizontal splits (unless + explicitly specified otherwise). + + hiddenoff Do not use diff mode for a buffer when it + becomes hidden. + iblank Ignore changes where lines are all blank. Adds the "-B" flag to the "diff" command if 'diffexpr' is empty. Check the documentation @@ -2055,6 +2091,17 @@ A jump table for the options with a short description can be found at |Q_op|. are considered the same. Adds the "-i" flag to the "diff" command if 'diffexpr' is empty. + indent-heuristic + Use the indent heuristic for the internal + diff library. + + internal Use the internal diff library. This is + ignored when 'diffexpr' is set. *E960* + When running out of memory when writing a + buffer this item will be ignored for diffs + involving that buffer. Set the 'verbose' + option to see when this happens. + iwhite Ignore changes in amount of white space. Adds the "-b" flag to the "diff" command if 'diffexpr' is empty. Check the documentation @@ -2074,56 +2121,19 @@ A jump table for the options with a short description can be found at |Q_op|. of the "diff" command for what this does exactly. - horizontal Start diff mode with horizontal splits (unless - explicitly specified otherwise). + linematch:{n} Align and mark changes between the most + similar lines between the buffers. When the + total number of lines in the diff hunk exceeds + {n}, the lines will not be aligned because for + very large diff hunks there will be a + noticeable lag. A reasonable setting is + "linematch:60", as this will enable alignment + for a 2 buffer diff hunk of 30 lines each, + or a 3 buffer diff hunk of 20 lines each. vertical Start diff mode with vertical splits (unless explicitly specified otherwise). - closeoff When a window is closed where 'diff' is set - and there is only one window remaining in the - same tab page with 'diff' set, execute - `:diffoff` in that window. This undoes a - `:diffsplit` command. - - hiddenoff Do not use diff mode for a buffer when it - becomes hidden. - - foldcolumn:{n} Set the 'foldcolumn' option to {n} when - starting diff mode. Without this 2 is used. - - followwrap Follow the 'wrap' option and leave as it is. - - internal Use the internal diff library. This is - ignored when 'diffexpr' is set. *E960* - When running out of memory when writing a - buffer this item will be ignored for diffs - involving that buffer. Set the 'verbose' - option to see when this happens. - - indent-heuristic - Use the indent heuristic for the internal - diff library. - - linematch:{n} Enable a second stage diff on each generated - hunk in order to align lines. When the total - number of lines in a hunk exceeds {n}, the - second stage diff will not be performed as - very large hunks can cause noticeable lag. A - recommended setting is "linematch:60", as this - will enable alignment for a 2 buffer diff with - hunks of up to 30 lines each, or a 3 buffer - diff with hunks of up to 20 lines each. - - algorithm:{text} Use the specified diff algorithm with the - internal diff engine. Currently supported - algorithms are: - myers the default algorithm - minimal spend extra time to generate the - smallest possible diff - patience patience diff algorithm - histogram histogram diff algorithm - Examples: >vim set diffopt=internal,filler,context:4 set diffopt= @@ -2977,7 +2987,7 @@ A jump table for the options with a short description can be found at |Q_op|. An |OptionSet| autocmd can be used to set it up to match automatically. *'guicursor'* *'gcr'* *E545* *E546* *E548* *E549* -'guicursor' 'gcr' string (default "n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20") +'guicursor' 'gcr' string (default "n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20,t:block-blinkon500-blinkoff500-TermCursor") global Configures the cursor style for each mode. Works in the GUI and many terminals. See |tui-cursor-shape|. @@ -3005,6 +3015,7 @@ A jump table for the options with a short description can be found at |Q_op|. ci Command-line Insert mode cr Command-line Replace mode sm showmatch in Insert mode + t Terminal mode a all modes The argument-list is a dash separated list of these arguments: hor{N} horizontal bar, {N} percent of the character height @@ -3021,7 +3032,8 @@ A jump table for the options with a short description can be found at |Q_op|. cursor is not shown. Times are in msec. When one of the numbers is zero, there is no blinking. E.g.: >vim set guicursor=n:blinkon0 -< - Default is "blinkon0" for each mode. +< + Default is "blinkon0" for each mode. {group-name} Highlight group that decides the color and font of the cursor. @@ -3197,7 +3209,7 @@ A jump table for the options with a short description can be found at |Q_op|. global A history of ":" commands, and a history of previous search patterns is remembered. This option decides how many entries may be stored in - each of these histories (see |cmdline-editing| and 'msghistory' for + each of these histories (see |cmdline-editing| and 'messagesopt' for the number of messages to remember). The maximum value is 10000. @@ -4045,6 +4057,28 @@ A jump table for the options with a short description can be found at |Q_op|. generated from a list of items, e.g., the Buffers menu. Changing this option has no direct effect, the menu must be refreshed first. + *'messagesopt'* *'mopt'* +'messagesopt' 'mopt' string (default "hit-enter,history:500") + global + Option settings for outputting messages. It can consist of the + following items. Items must be separated by a comma. + + hit-enter Use a |hit-enter| prompt when the message is longer than + 'cmdheight' size. + + wait:{n} Instead of using a |hit-enter| prompt, simply wait for + {n} milliseconds so that the user has a chance to read + the message. The maximum value of {n} is 10000. Use + 0 to disable the wait (but then the user may miss an + important message). + This item is ignored when "hit-enter" is present, but + required when "hit-enter" is not present. + + history:{n} Determines how many entries are remembered in the + |:messages| history. The maximum value is 10000. + Setting it to zero clears the message history. + This item must always be present. + *'mkspellmem'* *'msm'* 'mkspellmem' 'msm' string (default "460000,2000,500") global @@ -4290,12 +4324,6 @@ A jump table for the options with a short description can be found at |Q_op|. Defines the maximum time in msec between two mouse clicks for the second click to be recognized as a multi click. - *'msghistory'* *'mhi'* -'msghistory' 'mhi' number (default 500) - global - Determines how many entries are remembered in the |:messages| history. - The maximum value is 10000. - *'nrformats'* *'nf'* 'nrformats' 'nf' string (default "bin,hex") local to buffer @@ -4639,8 +4667,8 @@ A jump table for the options with a short description can be found at |Q_op|. 'redrawtime' 'rdt' number (default 2000) global Time in milliseconds for redrawing the display. Applies to - 'hlsearch', 'inccommand', |:match| highlighting and syntax - highlighting. + 'hlsearch', 'inccommand', |:match| highlighting, syntax highlighting, + and async |LanguageTree:parse()|. When redrawing takes more than this many milliseconds no further matches will be highlighted. For syntax highlighting the time applies per window. When over the @@ -4794,6 +4822,7 @@ A jump table for the options with a short description can be found at |Q_op|. indent/ indent scripts |indent-expression| keymap/ key mapping files |mbyte-keymap| lang/ menu translations |:menutrans| + lsp/ LSP client configurations |lsp-config| lua/ |Lua| plugins menu.vim GUI menus |menu.vim| pack/ packages |:packadd| @@ -4965,6 +4994,8 @@ A jump table for the options with a short description can be found at |Q_op|. selection. When "old" is used and 'virtualedit' allows the cursor to move past the end of line the line break still isn't included. + When "exclusive" is used, cursor position in visual mode will be + adjusted for inclusive motions |inclusive-motion-selection-exclusive|. Note that when "exclusive" is used and selecting from the end backwards, you cannot include the last character of a line, when starting in Normal mode and 'virtualedit' empty. @@ -5916,6 +5947,7 @@ A jump table for the options with a short description can be found at |Q_op|. All fields except the {item} are optional. A single percent sign can be given as "%%". + *stl-%!* When the option starts with "%!" then it is used as an expression, evaluated and the result is used as the option value. Example: >vim set statusline=%!MyStatusLine() @@ -6590,6 +6622,10 @@ A jump table for the options with a short description can be found at |Q_op|. expanded according to the rules used for 'statusline'. If it contains an invalid '%' format, the value is used as-is and no error or warning will be given when the value is set. + + The default behaviour is equivalent to: >vim + set titlestring=%t%(\ %M%)%(\ \(%{expand(\"%:~:h\")}\)%)%a\ -\ Nvim +< This option cannot be set in a modeline when 'modelineexpr' is off. Example: >vim diff --git a/runtime/doc/pattern.txt b/runtime/doc/pattern.txt index 7f0938be05..be913e941e 100644 --- a/runtime/doc/pattern.txt +++ b/runtime/doc/pattern.txt @@ -1485,6 +1485,7 @@ criteria: - Matches at a camel case character (e.g. Case in CamelCase) - Matches after a path separator or a hyphen. - The number of unmatched characters in a string. + - A full/exact match is preferred. The matching string with the highest score is returned first. For example, when you search for the "get pat" string using fuzzy matching, it diff --git a/runtime/doc/pi_tar.txt b/runtime/doc/pi_tar.txt index c8570044e5..96b26d92e7 100644 --- a/runtime/doc/pi_tar.txt +++ b/runtime/doc/pi_tar.txt @@ -1,4 +1,4 @@ -*pi_tar.txt* For Vim version 8.2. Last change: 2020 Jan 07 +*pi_tar.txt* Nvim +====================+ | Tar File Interface | diff --git a/runtime/doc/provider.txt b/runtime/doc/provider.txt index f1b0daee76..69ae0f20d1 100644 --- a/runtime/doc/provider.txt +++ b/runtime/doc/provider.txt @@ -193,7 +193,7 @@ registers. Nvim looks for these clipboard tools, in order of priority: - xclip (if $DISPLAY is set) - lemonade (for SSH) https://github.com/pocke/lemonade - doitclient (for SSH) https://www.chiark.greenend.org.uk/~sgtatham/doit/ - - win32yank (Windows) + - *win32yank* (Windows) - putclip, getclip (Windows) https://cygwin.com/packages/summary/cygutils.html - clip, powershell (Windows) https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/clip - termux (via termux-clipboard-set, termux-clipboard-set) @@ -259,23 +259,21 @@ For Windows WSL, try this g:clipboard definition: *clipboard-osc52* Nvim bundles a clipboard provider that allows copying to the system clipboard using OSC 52. OSC 52 is an Operating System Command control sequence that -writes the copied text to the terminal emulator. If the terminal emulator -supports OSC 52 then it will write the copied text into the system clipboard. - -Nvim will attempt to automatically determine if the host terminal emulator -supports the OSC 52 sequence and enable the OSC 52 clipboard provider if it -does as long as all of the following are true: - - • Nvim is running in the |TUI| - • |g:clipboard| is unset - • 'clipboard' is not set to "unnamed" or "unnamedplus" - • $SSH_TTY is set - -If any of the above conditions are not met then the OSC 52 clipboard provider -will not be used by default and Nvim will fall back to discovering a -|clipboard-tool| through the usual process. - -To force Nvim to use the OSC 52 provider you can use the following +causes the terminal emulator to write to or read from the system clipboard. + +When Nvim is running in the |TUI|, it will automatically attempt to determine if +the host terminal emulator supports OSC 52. If it does, then Nvim will use OSC +52 for copying and pasting if no other |clipboard-tool| is found and when +'clipboard' is unset. + + *g:termfeatures* +To disable the automatic detection, set the "osc52" key of |g:termfeatures| to +|v:false| in the |config| file. Example: >lua + local termfeatures = vim.g.termfeatures or {} + termfeatures.osc52 = false + vim.g.termfeatures = termfeatures +< +To force Nvim to always use the OSC 52 provider you can use the following |g:clipboard| definition: >lua vim.g.clipboard = { diff --git a/runtime/doc/quickfix.txt b/runtime/doc/quickfix.txt index b3399b2766..7aeb494437 100644 --- a/runtime/doc/quickfix.txt +++ b/runtime/doc/quickfix.txt @@ -538,9 +538,9 @@ EXECUTE A COMMAND IN ALL THE BUFFERS IN QUICKFIX OR LOCATION LIST: < Otherwise it works the same as `:ldo`. FILTERING A QUICKFIX OR LOCATION LIST: - *cfilter-plugin* *:Cfilter* *:Lfilter* + *cfilter-plugin* *:Cfilter* *:Lfilter* *package-cfilter* If you have too many entries in a quickfix list, you can use the cfilter -plugin to reduce the number of entries. Load the plugin with: > +plugin to reduce the number of entries. Load the plugin with: >vim packadd cfilter @@ -1317,9 +1317,235 @@ g:compiler_gcc_ignore_unmatched_lines JAVAC *compiler-javac* Commonly used compiler options can be added to 'makeprg' by setting the -g:javac_makeprg_params variable. For example: > +b/g:javac_makeprg_params variable. For example: > + let g:javac_makeprg_params = "-Xlint:all -encoding utf-8" -< + +MAVEN *compiler-maven* + +Commonly used compiler options can be added to 'makeprg' by setting the +b/g:maven_makeprg_params variable. For example: > + + let g:maven_makeprg_params = "-DskipTests -U -X" + +SPOTBUGS *compiler-spotbugs* + +SpotBugs is a static analysis tool that can be used to find bugs in Java. +It scans the Java bytecode of all classes in the currently open buffer. +(Therefore, `:compiler! spotbugs` is not supported.) + +Commonly used compiler options can be added to 'makeprg' by setting the +"b:" or "g:spotbugs_makeprg_params" variable. For example: >vim + + let b:spotbugs_makeprg_params = "-longBugCodes -effort:max -low" + +The global default is "-workHard -experimental". + +By default, the class files are searched in the directory where the source +files are placed. However, typical Java projects use distinct directories +for source files and class files. To make both known to SpotBugs, assign +their paths (distinct and relative to their common root directory) to the +following properties (using the example of a common Maven project): >vim + + let g:spotbugs_properties = { + \ 'sourceDirPath': ['src/main/java'], + \ 'classDirPath': ['target/classes'], + \ 'testSourceDirPath': ['src/test/java'], + \ 'testClassDirPath': ['target/test-classes'], + \ } + +Note that source and class path entries are expected to come in pairs: define +both "sourceDirPath" and "classDirPath" when you are considering at least one, +and apply the same logic to "testSourceDirPath" and "testClassDirPath". +Note that values for the path keys describe only for SpotBugs where to look +for files; refer to the documentation for particular compiler plugins for more +information. + +The default pre- and post-compiler actions are provided for Ant, Maven, and +Javac compiler plugins and can be selected by assigning the name of a compiler +plugin (`ant`, `maven`, or `javac`) to the "compiler" key: >vim + + let g:spotbugs_properties = { + \ 'compiler': 'maven', + \ } + +This single setting is essentially equivalent to all the settings below, with +the exception made for the "PreCompilerAction" and "PreCompilerTestAction" +values: their listed |Funcref|s will obtain no-op implementations whereas the +implicit Funcrefs of the "compiler" key will obtain the requested defaults if +available. >vim + + let g:spotbugs_properties = { + \ 'PreCompilerAction': + \ function('spotbugs#DefaultPreCompilerAction'), + \ 'PreCompilerTestAction': + \ function('spotbugs#DefaultPreCompilerTestAction'), + \ 'PostCompilerAction': + \ function('spotbugs#DefaultPostCompilerAction'), + \ 'sourceDirPath': ['src/main/java'], + \ 'classDirPath': ['target/classes'], + \ 'testSourceDirPath': ['src/test/java'], + \ 'testClassDirPath': ['target/test-classes'], + \ } + +With default actions, the compiler of choice will attempt to rebuild the class +files for the buffer (and possibly for the whole project) as soon as a Java +syntax file is loaded; then, `spotbugs` will attempt to analyze the quality of +the compilation unit of the buffer. + +Vim commands proficient in 'makeprg' [0] can be composed with default actions. +Begin by considering which of the supported keys, "DefaultPreCompilerCommand", +"DefaultPreCompilerTestCommand", or "DefaultPostCompilerCommand", you need to +write an implementation for, observing that each of these keys corresponds to +a particular "*Action" key. Follow it by defining a new function that always +declares an only parameter of type string and puts to use a command equivalent +of |:make|, and assigning its |Funcref| to the selected key. For example: +>vim + function! GenericPostCompilerCommand(arguments) abort + execute 'make ' . a:arguments + endfunction + + let g:spotbugs_properties = { + \ 'DefaultPostCompilerCommand': + \ function('GenericPostCompilerCommand'), + \ } + +When "PostCompilerAction" is available, "PostCompilerActionExecutor" is also +supported. Its value must be a Funcref pointing to a function that always +declares a single parameter of type string and decides whether |:execute| can +be dispatched on its argument, containing a pending post-compiler action, +after ascertaining the current status of |:cc| (or |:ll|): >vim + + function! GenericPostCompilerActionExecutor(action) abort + try + cc + catch /\<E42:/ + execute a:action + endtry + endfunction + +Complementary, some or all of the available "Pre*Action"s (or "*Pre*Command"s) +may run `:doautocmd java_spotbugs_post User` in their implementations before +|:make| (or its equivalent) to define a once-only |ShellCmdPost| `:autocmd` +that will arrange for "PostCompilerActionExecutor" to be invoked; and then run +`:doautocmd java_spotbugs_post ShellCmdPost` to consume this event: >vim + + function! GenericPreCompilerCommand(arguments) abort + if !exists('g:spotbugs_compilation_done') + doautocmd java_spotbugs_post User + execute 'make ' . a:arguments + " only run doautocmd when :make was synchronous + " see note below + doautocmd java_spotbugs_post ShellCmdPost " XXX: (a) + let g:spotbugs_compilation_done = 1 + else + cc + endif + endfunction + + function! GenericPreCompilerTestCommand(arguments) abort + if !exists('g:spotbugs_test_compilation_done') + doautocmd java_spotbugs_post User + execute 'make ' . a:arguments + " only run doautocmd when :make was synchronous + " see note below + doautocmd java_spotbugs_post ShellCmdPost " XXX: (b) + let g:spotbugs_test_compilation_done = 1 + else + cc + endif + endfunction + + let g:spotbugs_properties = { + \ 'compiler': 'maven', + \ 'DefaultPreCompilerCommand': + \ function('GenericPreCompilerCommand'), + \ 'DefaultPreCompilerTestCommand': + \ function('GenericPreCompilerTestCommand'), + \ 'PostCompilerActionExecutor': + \ function('GenericPostCompilerActionExecutor'), + \ } + +If a command equivalent of `:make` is capable of asynchronous execution and +consuming `ShellCmdPost` events, `:doautocmd java_spotbugs_post ShellCmdPost` +must be removed from such "*Action" (or "*Command") implementations (i.e. the +lines `(a)` and `(b)` in the listed examples) to retain a sequential order for +non-blocking execution, and any notification (see below) must be suppressed. +A `ShellCmdPost` `:autocmd` can be associated with any |:augroup| by assigning +its name to the "augroupForPostCompilerAction" key. + +When default actions are not suited to a desired workflow, proceed by writing +arbitrary functions yourself and matching their Funcrefs to the supported +keys: "PreCompilerAction", "PreCompilerTestAction", and "PostCompilerAction". + +The next example re-implements the default pre-compiler actions for a Maven +project and requests other default Maven settings with the "compiler" entry: +>vim + function! MavenPreCompilerAction() abort + call spotbugs#DeleteClassFiles() + compiler maven + make compile + cc + endfunction + + function! MavenPreCompilerTestAction() abort + call spotbugs#DeleteClassFiles() + compiler maven + make test-compile + cc + endfunction + + let g:spotbugs_properties = { + \ 'compiler': 'maven', + \ 'PreCompilerAction': + \ function('MavenPreCompilerAction'), + \ 'PreCompilerTestAction': + \ function('MavenPreCompilerTestAction'), + \ } + +Note that all entered custom settings will take precedence over the matching +default settings in "g:spotbugs_properties". +Note that it is necessary to notify the plugin of the result of a pre-compiler +action before further work can be undertaken. Using |:cc| after |:make| (or +|:ll| after |:lmake|) as the last command of an action is the supported means +of such communication. + +Two commands, "SpotBugsRemoveBufferAutocmd" and "SpotBugsDefineBufferAutocmd", +are provided to toggle actions for buffer-local autocommands. For example, to +also run actions on any |BufWritePost| and |Signal| event, add these lines to +`~/.config/nvim/after/ftplugin/java.vim`: >vim + + if exists(':SpotBugsDefineBufferAutocmd') == 2 + SpotBugsDefineBufferAutocmd BufWritePost Signal + endif + +Otherwise, you can turn to `:doautocmd java_spotbugs User` at any time. + +The "g:spotbugs_properties" variable is consulted by the Java filetype plugin +(|ft-java-plugin|) to arrange for the described automation, and, therefore, it +must be defined before |FileType| events can take place for the buffers loaded +with Java source files. It could, for example, be set in a project-local +|vimrc| loaded by [1]. + +Both "g:spotbugs_properties" and "b:spotbugs_properties" are recognized and +must be modifiable (|:unlockvar|). The "*Command" entries are always treated +as global functions to be shared among all Java buffers. + +The SpotBugs Java library and, by extension, its distributed shell scripts do +not support in the `-textui` mode listed pathnames with directory filenames +that contain blank characters [2]. To work around this limitation, consider +making a symbolic link to such a directory from a directory that does not have +blank characters in its name and passing this information to SpotBugs: >vim + + let g:spotbugs_alternative_path = { + \ 'fromPath': 'path/to/dir_without_blanks', + \ 'toPath': 'path/to/dir with blanks', + \ } + +[0] https://github.com/Konfekt/vim-compilers +[1] https://github.com/MarcWeber/vim-addon-local-vimrc +[2] https://github.com/spotbugs/spotbugs/issues/909 + GNU MAKE *compiler-make* Since the default make program is "make", the compiler plugin for make, @@ -1409,6 +1635,13 @@ Useful values for the 'makeprg' options therefore are: setlocal makeprg=./alltests.py " Run a testsuite setlocal makeprg=python\ %:S " Run a single testcase +PYTEST COMPILER *compiler-pytest* +Commonly used compiler options can be added to 'makeprg' by setting the +b/g:pytest_makeprg_params variable. For example: > + + let b:pytest_makeprg_params = "--verbose --no-summary --disable-warnings" + +The global default is "--tb=short --quiet"; Python warnings are suppressed. TEX COMPILER *compiler-tex* diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt index abeefb980e..e65caa72ed 100644 --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -111,7 +111,7 @@ To abort this type CTRL-C twice. ============================================================================== Complex repeats *complex-repeat* - *q* *recording* + *q* *recording* *macro* q{0-9a-zA-Z"} Record typed characters into register {0-9a-zA-Z"} (uppercase to append). The 'q' command is disabled while executing a register, and it doesn't work inside diff --git a/runtime/doc/sign.txt b/runtime/doc/sign.txt index 9d74f1f376..9895b606fd 100644 --- a/runtime/doc/sign.txt +++ b/runtime/doc/sign.txt @@ -10,7 +10,7 @@ Sign Support Features *sign-support* Type |gO| to see the table of contents. ============================================================================== -1. Introduction *sign-intro* *signs* +1. Introduction *sign-intro* *signs* *gutter* When a debugger or other IDE tool is driving an editor it needs to be able to give specific highlights which quickly tell the user useful information diff --git a/runtime/doc/starting.txt b/runtime/doc/starting.txt index 3b0fa2b371..d8d5e42397 100644 --- a/runtime/doc/starting.txt +++ b/runtime/doc/starting.txt @@ -164,8 +164,7 @@ argument. you can overwrite a file by adding an exclamation mark to the Ex command, as in ":w!". The 'readonly' option can be reset with ":set noro" (see the options chapter, |options|). - Subsequent edits will not be done in readonly mode. Calling - the executable "view" has the same effect as the -R argument. + Subsequent edits will not be done in readonly mode. The 'updatecount' option will be set to 10000, meaning that the swap file will not be updated automatically very often. See |-M| for disallowing modifications. @@ -207,12 +206,12 @@ argument. :print :set With |:verbose| or 'verbose', other commands display on stderr: > - nvim -es +":verbose echo 'foo'" - nvim -V1 -es +foo - -< User |config| is skipped unless |-u| was given. - Swap file is skipped (like |-n|). - User |shada| is loaded (unless "-i NONE" is given). + nvim -es +"verbose echo 'foo'" + nvim -V1 -es +"echo 'foo'" +< + Skips user |config| unless |-u| was given. + Disables |shada| unless |-i| was given. + Disables swapfile (like |-n|). *-l* -l {script} [args] @@ -226,7 +225,8 @@ argument. arguments. The {script} name is stored at `_G.arg[0]`. Sets 'verbose' to 1 (like "-V1"), so Lua `print()` writes to - output. + output, as well as other message-emitting functions like + |:echo|. If {script} prints messages and doesn't cause Nvim to exit, Nvim ensures output ends with a newline. @@ -235,6 +235,11 @@ argument. nvim +q -l foo.lua < This loads Lua module "bar" before executing "foo.lua": > nvim +"lua require('bar')" -l foo.lua +< *lua-shebang* + You can set the "shebang" of the script so that Nvim executes + the script when called with "./" from a shell (remember to + "chmod u+x"): > + #!/usr/bin/env -S nvim -l < Skips user |config| unless |-u| was given. Disables plugins unless 'loadplugins' was set. @@ -243,7 +248,7 @@ argument. *-ll* -ll {script} [args] - Execute a Lua script, similarly to |-l|, but the editor is not + Executes a Lua script, similarly to |-l|, but the editor is not initialized. This gives a Lua environment similar to a worker thread. See |lua-loop-threading|. @@ -283,21 +288,18 @@ argument. command from a script. |debug-mode| *-n* --n No |swap-file| will be used. Recovery after a crash will be - impossible. Handy if you want to view or edit a file on a - very slow medium (e.g., a floppy). - Can also be done with ":set updatecount=0". You can switch it - on again by setting the 'updatecount' option to some value, - e.g., ":set uc=100". - 'updatecount' is set to 0 AFTER executing commands from a - vimrc file, but before the GUI initializations. Thus it - overrides a setting for 'updatecount' in a vimrc file, but not - in a gvimrc file. See |startup|. - When you want to reduce accesses to the disk (e.g., for a - laptop), don't use "-n", but set 'updatetime' and - 'updatecount' to very big numbers, and type ":preserve" when - you want to save your work. This way you keep the possibility - for crash recovery. +-n Disables |swap-file| by setting 'updatecount' to 0 (after + executing any |vimrc|). Recovery after a crash will be + impossible. Improves performance when working with a file on + a very slow medium (usb drive, network share). + + Enable it again by setting 'updatecount' to some value, e.g. + ":set updatecount=100". + + To reduce accesses to the disk, don't use "-n", but set + 'updatetime' and 'updatecount' to very big numbers, and type + ":preserve" when you want to save your work. This way you + keep the possibility for crash recovery. *-o* -o[N] Open N windows, split horizontally. If [N] is not given, diff --git a/runtime/doc/support.txt b/runtime/doc/support.txt index a2776fca0d..103fb90b6d 100644 --- a/runtime/doc/support.txt +++ b/runtime/doc/support.txt @@ -12,9 +12,10 @@ Support *support* Supported platforms *supported-platforms* `System` `Tier` `Versions` `Tested versions` -Linux 1 >= 2.6.32, glibc >= 2.12 Ubuntu 24.04 -macOS (Intel) 1 >= 11 macOS 13 -macOS (M1) 1 >= 11 macOS 15 +Linux (x86_64) 1 >= 2.6.32, glibc >= 2.12 Ubuntu 24.04 +Linux (arm64) 1 >= 2.6.32, glibc >= 2.12 Ubuntu 24.04 +macOS (x86_64) 1 >= 11 macOS 13 +macOS (arm64) 1 >= 11 macOS 15 Windows 64-bit 1 >= Windows 10 Version 1809 Windows Server 2022 FreeBSD 1 >= 10 FreeBSD 14 OpenBSD 2 >= 7 diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 75a855bbdd..75d6d85183 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -414,12 +414,15 @@ There are many types of assembly languages that all use the same file name extensions. Therefore you will have to select the type yourself, or add a line in the assembly file that Vim will recognize. Currently these syntax files are included: - asm GNU assembly (the default) + asm GNU assembly (usually have .s or .S extension and were + already built using C compiler such as GCC or CLANG) asm68k Motorola 680x0 assembly asmh8300 Hitachi H-8300 version of GNU assembly ia64 Intel Itanium 64 fasm Flat assembly (https://flatassembler.net) - masm Microsoft assembly (probably works for any 80x86) + masm Microsoft assembly (.masm files are compiled with + Microsoft's Macro Assembler. This is only supported + for x86, x86_64, ARM and AARCH64 CPU families) nasm Netwide assembly tasm Turbo Assembly (with opcodes 80x86 up to Pentium, and MMX) @@ -590,6 +593,7 @@ Variable Highlight ~ *c_no_cformat* don't highlight %-formats in strings *c_no_c99* don't highlight C99 standard items *c_no_c11* don't highlight C11 standard items +*c_no_c23* don't highlight C23 standard items *c_no_bsd* don't highlight BSD specific types *c_functions* highlight function calls and definitions *c_function_pointers* highlight function pointers definitions @@ -1741,6 +1745,16 @@ To disable numbers having their own color add the following to your vimrc: > If you want quotes to have different highlighting than strings > let g:jq_quote_highlight = 1 +KCONFIG *ft-kconfig-syntax* + +Kconfig syntax highlighting language. For syntax syncing, you can configure +the following variable (default: 50): > + + let kconfig_minlines = 50 + +To configure a bit more (heavier) highlighting, set the following variable: > + + let kconfig_syntax_heavy = 1 LACE *lace.vim* *ft-lace-syntax* @@ -5159,8 +5173,6 @@ EndOfBuffer Filler lines (~) after the end of the buffer. By default, this is highlighted like |hl-NonText|. *hl-TermCursor* TermCursor Cursor in a focused terminal. - *hl-TermCursorNC* -TermCursorNC Cursor in an unfocused terminal. *hl-ErrorMsg* ErrorMsg Error messages on the command line. *hl-WinSeparator* @@ -5242,6 +5254,8 @@ PmenuMatch Popup menu: Matched text in normal item. Combined with *hl-PmenuMatchSel* PmenuMatchSel Popup menu: Matched text in selected item. Combined with |hl-PmenuMatch| and |hl-PmenuSel|. + *hl-ComplMatchIns* +ComplMatchIns Matched text of the currently inserted completion. *hl-Question* Question |hit-enter| prompt and yes/no questions. *hl-QuickFixLine* diff --git a/runtime/doc/terminal.txt b/runtime/doc/terminal.txt index ed9659d6e7..0ab7151728 100644 --- a/runtime/doc/terminal.txt +++ b/runtime/doc/terminal.txt @@ -26,7 +26,7 @@ Start *terminal-start* There are several ways to create a terminal buffer: - Run the |:terminal| command. -- Call the |nvim_open_term()| or |termopen()| function. +- Call |nvim_open_term()| or `jobstart(…, {'term': v:true})`. - Edit a "term://" buffer. Examples: >vim :edit term://bash :vsplit term://top @@ -101,13 +101,17 @@ Configuration *terminal-config* Options: 'modified', 'scrollback' Events: |TermOpen|, |TermEnter|, |TermLeave|, |TermClose| -Highlight groups: |hl-TermCursor|, |hl-TermCursorNC| +Highlight groups: |hl-TermCursor| Terminal sets local defaults for some options, which may differ from your global configuration. - 'list' is disabled - 'wrap' is disabled +- 'number' is disabled +- 'relativenumber' is disabled +- 'signcolumn' is set to "no" +- 'foldcolumn' is set to "0" You can change the defaults with a TermOpen autocommand: >vim au TermOpen * setlocal list @@ -161,8 +165,8 @@ directory indicated in the request. >lua end }) -To try it out, select the above code and source it with `:'<,'>lua`, then run -this command in a :terminal buffer: > +To try it out, select the above code and source it with `:'<,'>lua` (or +`g==`), then run this command in a :terminal buffer: > printf "\033]7;file://./foo/bar\033\\" @@ -203,7 +207,7 @@ Use |jobwait()| to check if the terminal job has finished: >vim let running = jobwait([&channel], 0)[0] == -1 < ============================================================================== -:Termdebug plugin *terminal-debug* +:Termdebug plugin *terminal-debug* *terminal-debugger* *package-termdebug* The Terminal debugging plugin can be used to debug a program with gdb and view the source code in a Vim window. Since this is completely contained inside diff --git a/runtime/doc/treesitter.txt b/runtime/doc/treesitter.txt index 5fc6429f7a..b04f13add5 100644 --- a/runtime/doc/treesitter.txt +++ b/runtime/doc/treesitter.txt @@ -70,7 +70,7 @@ adds arbitrary metadata and conditional data to a match. Queries are written in a lisp-like language documented in https://tree-sitter.github.io/tree-sitter/using-parsers#query-syntax -Note: The predicates listed there page differ from those Nvim supports. See +Note: The predicates listed there differ from those Nvim supports. See |treesitter-predicates| for a complete list of predicates supported by Nvim. Nvim looks for queries as `*.scm` files in a `queries` directory under @@ -81,8 +81,7 @@ that user config takes precedence over plugins, which take precedence over queries bundled with Nvim). If a query should extend other queries instead of replacing them, use |treesitter-query-modeline-extends|. -See |lua-treesitter-query| for the list of available methods for working with -treesitter queries from Lua. +The Lua interface is described at |lua-treesitter-query|. TREESITTER QUERY PREDICATES *treesitter-predicates* @@ -158,12 +157,12 @@ The following predicates are built in: (field_identifier) @method)) @_parent (#has-parent? @_parent template_method function_declarator)) < - *lua-treesitter-not-predicate* + *treesitter-predicate-not* Each predicate has a `not-` prefixed predicate that is just the negation of the predicate. - *lua-treesitter-all-predicate* - *lua-treesitter-any-predicate* + *treesitter-predicate-all* + *treesitter-predicate-any* Queries can use quantifiers to capture multiple nodes. When a capture contains multiple nodes, predicates match only if ALL nodes contained by the capture match the predicate. Some predicates (`eq?`, `match?`, `lua-match?`, @@ -245,15 +244,32 @@ The following directives are built in: (#gsub! @_node ".*%.(.*)" "%1") < `trim!` *treesitter-directive-trim!* - Trim blank lines from the end of the node. This will set a new - `metadata[capture_id].range`. + Trims whitespace from the node. Sets a new + `metadata[capture_id].range`. Takes a capture ID and, optionally, four + integers to customize trimming behavior (`1` meaning trim, `0` meaning + don't trim). When only given a capture ID, trims blank lines (lines + that contain only whitespace, or are empty) from the end of the node + (for backwards compatibility). Can trim all whitespace from both sides + of the node if parameters are given. + Examples: >query + ; only trim blank lines from the end of the node + ; (equivalent to (#trim! @fold 0 0 1 0)) + (#trim! @fold) + + ; trim blank lines from both sides of the node + (#trim! @fold 1 0 1 0) + + ; trim all whitespace around the node + (#trim! @fold 1 1 1 1) +< Parameters: ~ {capture_id} + {trim_start_linewise} + {trim_start_charwise} + {trim_end_linewise} (default `1` if only given {capture_id}) + {trim_end_charwise} - Example: >query - (#trim! @fold) -< Further directives can be added via |vim.treesitter.query.add_directive()|. Use |vim.treesitter.query.list_directives()| to list all available directives. @@ -481,7 +497,7 @@ convention, nodes to be concealed are captured as `@conceal`, but any capture can be used. For example, the following query can be used to hide code block delimiters in Markdown: >query - (fenced_code_block_delimiter @conceal (#set! conceal "")) + ((fenced_code_block_delimiter) @conceal (#set! conceal "")) < It is also possible to replace a node with a single character, which (unlike legacy syntax) can be given a custom highlight. For example, the following @@ -492,6 +508,13 @@ still highlighted the same as other operators: >query < Conceals specified in this way respect 'conceallevel'. +Note that although you can use any string for `conceal`, only the first +character will be used: >query + + ; identifiers will be concealed with 'f'. + ((identifier) @conceal (#set! conceal "foo")) +< + *treesitter-highlight-priority* Treesitter uses |nvim_buf_set_extmark()| to set highlights with a default priority of 100. This enables plugins to set a highlighting priority lower or @@ -556,6 +579,10 @@ associated with patterns: • `injection.parent` - indicates that the captured node’s text should be parsed with the same language as the node's parent LanguageTree. +Injection queries are currently run over the entire buffer, which can be slow +for large buffers. To disable injections for, e.g., `c`, just place an +empty `queries/c/injections.scm` file in your 'runtimepath'. + ============================================================================== VIM.TREESITTER *lua-treesitter* @@ -816,7 +843,13 @@ TSNode:range({include_bytes}) *TSNode:range()* • end byte (if {include_bytes} is `true`) Parameters: ~ - • {include_bytes} (`boolean?`) + • {include_bytes} (`false?`) + + Return (multiple): ~ + (`integer`) + (`integer`) + (`integer`) + (`integer`) TSNode:sexpr() *TSNode:sexpr()* Get an S-expression representing the node as a string. @@ -885,8 +918,8 @@ get_captures_at_pos({bufnr}, {row}, {col}) Returns a list of highlight captures at the given position Each capture is represented by a table containing the capture name as a - string as well as a table of metadata (`priority`, `conceal`, ...; empty - if none are defined). + string, the capture's language, a table of metadata (`priority`, + `conceal`, ...; empty if none are defined), and the id of the capture. Parameters: ~ • {bufnr} (`integer`) Buffer number (0 for current buffer) @@ -894,7 +927,7 @@ get_captures_at_pos({bufnr}, {row}, {col}) • {col} (`integer`) Position column Return: ~ - (`{capture: string, lang: string, metadata: vim.treesitter.query.TSMetadata}[]`) + (`{capture: string, lang: string, metadata: vim.treesitter.query.TSMetadata, id: integer}[]`) get_node({opts}) *vim.treesitter.get_node()* Returns the smallest named node at the given position @@ -926,7 +959,7 @@ get_node_range({node_or_range}) *vim.treesitter.get_node_range()* Returns the node's range or an unpacked range table Parameters: ~ - • {node_or_range} (`TSNode|table`) Node or table of positions + • {node_or_range} (`TSNode|Range4`) Node or table of positions Return (multiple): ~ (`integer`) start_row @@ -1074,6 +1107,9 @@ start({bufnr}, {lang}) *vim.treesitter.start()* required for some plugins. In this case, add `vim.bo.syntax = 'on'` after the call to `start`. + Note: By default, the highlighter parses code asynchronously, using a + segment time of 3ms. + Example: >lua vim.api.nvim_create_autocmd( 'FileType', { pattern = 'tex', callback = function(args) @@ -1098,7 +1134,7 @@ stop({bufnr}) *vim.treesitter.stop()* ============================================================================== -Lua module: vim.treesitter.language *lua-treesitter-language* +Lua module: vim.treesitter.language *treesitter-language* add({lang}, {opts}) *vim.treesitter.language.add()* Load parser with name {lang} @@ -1162,7 +1198,7 @@ inspect({lang}) *vim.treesitter.language.inspect()* • {lang} (`string`) Language Return: ~ - (`table`) + (`TSLangInfo`) register({lang}, {filetype}) *vim.treesitter.language.register()* Register a parser named {lang} to be used for {filetype}(s). @@ -1178,6 +1214,10 @@ register({lang}, {filetype}) *vim.treesitter.language.register()* ============================================================================== Lua module: vim.treesitter.query *lua-treesitter-query* +This Lua |treesitter-query| interface allows you to create queries and use +them to parse text. See |vim.treesitter.query.parse()| for a working example. + + *vim.treesitter.query.add_directive()* add_directive({name}, {handler}, {opts}) Adds a new directive to be used in queries @@ -1308,21 +1348,30 @@ omnifunc({findstart}, {base}) *vim.treesitter.query.omnifunc()* • {base} (`string`) parse({lang}, {query}) *vim.treesitter.query.parse()* - Parse {query} as a string. (If the query is in a file, the caller should - read the contents into a string before calling). - - Returns a `Query` (see |lua-treesitter-query|) object which can be used to - search nodes in the syntax tree for the patterns defined in {query} using - the `iter_captures` and `iter_matches` methods. + Parses a {query} string and returns a `Query` object + (|lua-treesitter-query|), which can be used to search the tree for the + query patterns (via |Query:iter_captures()|, |Query:iter_matches()|), or + inspect the query via these fields: + • `captures`: a list of unique capture names defined in the query (alias: + `info.captures`). + • `info.patterns`: information about predicates. - Exposes `info` and `captures` with additional context about {query}. - • `captures` contains the list of unique capture names defined in {query}. - • `info.captures` also points to `captures`. - • `info.patterns` contains information about predicates. + Example: >lua + local query = vim.treesitter.query.parse('vimdoc', [[ + ; query + ((h1) @str + (#trim! @str 1 1 1 1)) + ]]) + local tree = vim.treesitter.get_parser():parse()[1] + for id, node, metadata in query:iter_captures(tree:root(), 0) do + -- Print the node name and source text. + vim.print({node:type(), vim.treesitter.get_node_text(node, vim.api.nvim_get_current_buf())}) + end +< Parameters: ~ • {lang} (`string`) Language to use for the query - • {query} (`string`) Query in s-expr syntax + • {query} (`string`) Query text, in s-expr syntax Return: ~ (`vim.treesitter.Query`) Parsed query @@ -1332,18 +1381,23 @@ parse({lang}, {query}) *vim.treesitter.query.parse()* *Query:iter_captures()* Query:iter_captures({node}, {source}, {start}, {stop}) - Iterate over all captures from all matches inside {node} - - {source} is needed if the query contains predicates; then the caller must - ensure to use a freshly parsed tree consistent with the current text of - the buffer (if relevant). {start} and {stop} can be used to limit matches - inside a row range (this is typically used with root node as the {node}, - i.e., to get syntax highlight matches in the current viewport). When - omitted, the {start} and {stop} row values are used from the given node. - - The iterator returns four values: a numeric id identifying the capture, - the captured node, metadata from any directives processing the match, and - the match itself. The following example shows how to get captures by name: >lua + Iterates over all captures from all matches in {node}. + + {source} is required if the query contains predicates; then the caller + must ensure to use a freshly parsed tree consistent with the current text + of the buffer (if relevant). {start} and {stop} can be used to limit + matches inside a row range (this is typically used with root node as the + {node}, i.e., to get syntax highlight matches in the current viewport). + When omitted, the {start} and {stop} row values are used from the given + node. + + The iterator returns four values: + 1. the numeric id identifying the capture + 2. the captured node + 3. metadata from any directives processing the match + 4. the match itself + + Example: how to get captures by name: >lua for id, node, metadata, match in query:iter_captures(tree:root(), bufnr, first, last) do local name = query.captures[id] -- name of the capture in the query -- typically useful info about the node: @@ -1367,8 +1421,8 @@ Query:iter_captures({node}, {source}, {start}, {stop}) Defaults to `node:end_()`. Return: ~ - (`fun(end_line: integer?): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch`) - capture id, capture node, metadata, match + (`fun(end_line: integer?): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch, TSTree`) + capture id, capture node, metadata, match, tree *Query:iter_matches()* Query:iter_matches({node}, {source}, {start}, {stop}, {opts}) @@ -1388,7 +1442,7 @@ Query:iter_matches({node}, {source}, {start}, {stop}, {opts}) -- `node` was captured by the `name` capture in the match local node_data = metadata[id] -- Node level metadata - ... use the info here ... + -- ... use the info here ... end end end @@ -1413,14 +1467,24 @@ Query:iter_matches({node}, {source}, {start}, {stop}, {opts}) compatibility and will be removed in a future release. Return: ~ - (`fun(): integer, table<integer, TSNode[]>, vim.treesitter.query.TSMetadata`) - pattern id, match, metadata + (`fun(): integer, table<integer, TSNode[]>, vim.treesitter.query.TSMetadata, TSTree`) + pattern id, match, metadata, tree set({lang}, {query_name}, {text}) *vim.treesitter.query.set()* Sets the runtime query named {query_name} for {lang} - This allows users to override any runtime files and/or configuration set - by plugins. + This allows users to override or extend any runtime files and/or + configuration set by plugins. + + For example, you could enable spellchecking of `C` identifiers with the + following code: >lua + vim.treesitter.query.set( + 'c', + 'highlights', + [[;inherits c + (identifier) @spell]]) + ]]) +< Parameters: ~ • {lang} (`string`) Language to use for the query @@ -1429,7 +1493,7 @@ set({lang}, {query_name}, {text}) *vim.treesitter.query.set()* ============================================================================== -Lua module: vim.treesitter.languagetree *lua-treesitter-languagetree* +Lua module: vim.treesitter.languagetree *treesitter-languagetree* A *LanguageTree* contains a tree of parsers: the root treesitter parser for {lang} and any "injected" language parsers, which themselves may inject other @@ -1465,6 +1529,9 @@ analysis on a tree should use a timer to throttle too frequent updates. LanguageTree:children() *LanguageTree:children()* Returns a map of language to child tree. + Return: ~ + (`table<string,vim.treesitter.LanguageTree>`) + LanguageTree:contains({range}) *LanguageTree:contains()* Determines whether {range} is contained in the |LanguageTree|. @@ -1514,7 +1581,8 @@ LanguageTree:invalidate({reload}) *LanguageTree:invalidate()* Parameters: ~ • {reload} (`boolean?`) -LanguageTree:is_valid({exclude_children}) *LanguageTree:is_valid()* + *LanguageTree:is_valid()* +LanguageTree:is_valid({exclude_children}, {range}) Returns whether this LanguageTree is valid, i.e., |LanguageTree:trees()| reflects the latest state of the source. If invalid, user should call |LanguageTree:parse()|. @@ -1522,6 +1590,7 @@ LanguageTree:is_valid({exclude_children}) *LanguageTree:is_valid()* Parameters: ~ • {exclude_children} (`boolean?`) whether to ignore the validity of children (default `false`) + • {range} (`Range?`) range to check for validity Return: ~ (`boolean`) @@ -1529,6 +1598,9 @@ LanguageTree:is_valid({exclude_children}) *LanguageTree:is_valid()* LanguageTree:lang() *LanguageTree:lang()* Gets the language of this tree node. + Return: ~ + (`string`) + *LanguageTree:language_for_range()* LanguageTree:language_for_range({range}) Gets the appropriate language that contains {range}. @@ -1577,7 +1649,13 @@ LanguageTree:node_for_range({range}, {opts}) Return: ~ (`TSNode?`) -LanguageTree:parse({range}) *LanguageTree:parse()* +LanguageTree:parent() *LanguageTree:parent()* + Returns the parent tree. `nil` for the root tree. + + Return: ~ + (`vim.treesitter.LanguageTree?`) + +LanguageTree:parse({range}, {on_parse}) *LanguageTree:parse()* Recursively parse all regions in the language tree using |treesitter-parsers| for the corresponding languages and run injection queries on the parsed trees to determine whether child trees should be @@ -1588,14 +1666,27 @@ LanguageTree:parse({range}) *LanguageTree:parse()* if {range} is `true`). Parameters: ~ - • {range} (`boolean|Range?`) Parse this range in the parser's source. - Set to `true` to run a complete parse of the source (Note: - Can be slow!) Set to `false|nil` to only parse regions with - empty ranges (typically only the root tree without - injections). + • {range} (`boolean|Range?`) Parse this range in the parser's + source. Set to `true` to run a complete parse of the + source (Note: Can be slow!) Set to `false|nil` to only + parse regions with empty ranges (typically only the root + tree without injections). + • {on_parse} (`fun(err?: string, trees?: table<integer, TSTree>)?`) + Function invoked when parsing completes. When provided and + `vim.g._ts_force_sync_parsing` is not set, parsing will + run asynchronously. The first argument to the function is + a string representing the error type, in case of a failure + (currently only possible for timeouts). The second + argument is the list of trees returned by the parse (upon + success), or `nil` if the parse timed out (determined by + 'redrawtime'). + + If parsing was still able to finish synchronously (within + 3ms), `parse()` returns the list of trees. Otherwise, it + returns `nil`. Return: ~ - (`table<integer, TSTree>`) + (`table<integer, TSTree>?`) *LanguageTree:register_cbs()* LanguageTree:register_cbs({cbs}, {recursive}) @@ -1624,6 +1715,9 @@ LanguageTree:register_cbs({cbs}, {recursive}) LanguageTree:source() *LanguageTree:source()* Returns the source content of the language tree (bufnr or string). + Return: ~ + (`integer|string`) + *LanguageTree:tree_for_range()* LanguageTree:tree_for_range({range}, {opts}) Gets the tree that contains {range}. diff --git a/runtime/doc/tui.txt b/runtime/doc/tui.txt index 9493f91b1e..96ac54abc5 100644 --- a/runtime/doc/tui.txt +++ b/runtime/doc/tui.txt @@ -280,191 +280,5 @@ colours of whitespace are immaterial, in practice they change the colours of cursors and selections that cross them. This may have a visible, but minor, effect on some UIs. -============================================================================== -Using the mouse *mouse-using* - - *mouse-mode-table* *mouse-overview* -Overview of what the mouse buttons do, when 'mousemodel' is "extend": - - *<S-LeftMouse>* *<A-RightMouse>* *<S-RightMouse>* *<RightDrag>* - *<RightRelease>* *<LeftDrag>* -Normal Mode: > - event position selection change action - cursor window - --------------------------------------------------------------------------- - <LeftMouse> yes end yes - <C-LeftMouse> yes end yes "CTRL-]" (2) - <S-LeftMouse> yes no change yes "*" (2) - <LeftDrag> yes start or extend (1) no - <LeftRelease> yes start or extend (1) no - <MiddleMouse> yes if not active no put - <MiddleMouse> yes if active no yank and put - <RightMouse> yes start or extend yes - <A-RightMouse> yes start or extend blockw. yes - <S-RightMouse> yes no change yes "#" (2) - <C-RightMouse> no no change no "CTRL-T" - <RightDrag> yes extend no - <RightRelease> yes extend no - -Insert or Replace Mode: > - event position selection change action - cursor window - --------------------------------------------------------------------------- - <LeftMouse> yes (cannot be active) yes - <C-LeftMouse> yes (cannot be active) yes "CTRL-O^]" (2) - <S-LeftMouse> yes (cannot be active) yes "CTRL-O*" (2) - <LeftDrag> yes start or extend (1) no like CTRL-O (1) - <LeftRelease> yes start or extend (1) no like CTRL-O (1) - <MiddleMouse> no (cannot be active) no put register - <RightMouse> yes start or extend yes like CTRL-O - <A-RightMouse> yes start or extend blockw. yes - <S-RightMouse> yes (cannot be active) yes "CTRL-O#" (2) - <C-RightMouse> no (cannot be active) no "CTRL-O CTRL-T" - -In a help window: > - event position selection change action - cursor window - --------------------------------------------------------------------------- - <2-LeftMouse> yes (cannot be active) no "^]" (jump to help tag) - -When 'mousemodel' is "popup", these are different: - - *<A-LeftMouse>* -Normal Mode: > - event position selection change action - cursor window - --------------------------------------------------------------------------- - <S-LeftMouse> yes start or extend (1) no - <A-LeftMouse> yes start/extend blockw no - <RightMouse> no popup menu no - -Insert or Replace Mode: > - event position selection change action - cursor window - --------------------------------------------------------------------------- - <S-LeftMouse> yes start or extend (1) no like CTRL-O (1) - <A-LeftMouse> yes start/extend blockw no - <RightMouse> no popup menu no - -(1) only if mouse pointer moved since press -(2) only if click is in same buffer - -Clicking the left mouse button causes the cursor to be positioned. If the -click is in another window that window is made the active window. When -editing the command-line the cursor can only be positioned on the -command-line. When in Insert mode Vim remains in Insert mode. If 'scrolloff' -is set, and the cursor is positioned within 'scrolloff' lines from the window -border, the text is scrolled. - -A selection can be started by pressing the left mouse button on the first -character, moving the mouse to the last character, then releasing the mouse -button. You will not always see the selection until you release the button, -only in some versions (GUI, Win32) will the dragging be shown immediately. -Note that you can make the text scroll by moving the mouse at least one -character in the first/last line in the window when 'scrolloff' is non-zero. - -In Normal, Visual and Select mode clicking the right mouse button causes the -Visual area to be extended. When 'mousemodel' is "popup", the left button has -to be used while keeping the shift key pressed. When clicking in a window -which is editing another buffer, the Visual or Select mode is stopped. - -In Normal, Visual and Select mode clicking the right mouse button with the alt -key pressed causes the Visual area to become blockwise. When 'mousemodel' is -"popup" the left button has to be used with the alt key. Note that this won't -work on systems where the window manager consumes the mouse events when the -alt key is pressed (it may move the window). - - *double-click* *<2-LeftMouse>* *<3-LeftMouse>* *<4-LeftMouse>* -Double, triple and quadruple clicks are supported when the GUI is active, for -Win32 and for an xterm. For selecting text, extra clicks extend the -selection: > - - click select - --------------------------------- - double word or % match - triple line - quadruple rectangular block - -Exception: In a Help window a double click jumps to help for the word that is -clicked on. - -A double click on a word selects that word. 'iskeyword' is used to specify -which characters are included in a word. A double click on a character -that has a match selects until that match (like using "v%"). If the match is -an #if/#else/#endif block, the selection becomes linewise. -For MS-Windows and xterm the time for double clicking can be set with the -'mousetime' option. For the other systems this time is defined outside of Vim. -An example, for using a double click to jump to the tag under the cursor: >vim - :map <2-LeftMouse> :exe "tag " .. expand("<cword>")<CR> - -Dragging the mouse with a double click (button-down, button-up, button-down -and then drag) will result in whole words to be selected. This continues -until the button is released, at which point the selection is per character -again. - -For scrolling with the mouse see |scroll-mouse-wheel|. - -In Insert mode, when a selection is started, Vim goes into Normal mode -temporarily. When Visual or Select mode ends, it returns to Insert mode. -This is like using CTRL-O in Insert mode. Select mode is used when the -'selectmode' option contains "mouse". - - *X1Mouse* *X1Drag* *X1Release* - *X2Mouse* *X2Drag* *X2Release* - *<MiddleRelease>* *<MiddleDrag>* -Mouse clicks can be mapped. The codes for mouse clicks are: > - code mouse button normal action - --------------------------------------------------------------------------- - <LeftMouse> left pressed set cursor position - <LeftDrag> left moved while pressed extend selection - <LeftRelease> left released set selection end - <MiddleMouse> middle pressed paste text at cursor position - <MiddleDrag> middle moved while pressed - - <MiddleRelease> middle released - - <RightMouse> right pressed extend selection - <RightDrag> right moved while pressed extend selection - <RightRelease> right released set selection end - <X1Mouse> X1 button pressed - - <X1Drag> X1 moved while pressed - - <X1Release> X1 button release - - <X2Mouse> X2 button pressed - - <X2Drag> X2 moved while pressed - - <X2Release> X2 button release - - -The X1 and X2 buttons refer to the extra buttons found on some mice. The -'Microsoft Explorer' mouse has these buttons available to the right thumb. -Currently X1 and X2 only work on Win32 and X11 environments. - -Examples: >vim - :noremap <MiddleMouse> <LeftMouse><MiddleMouse> -Paste at the position of the middle mouse button click (otherwise the paste -would be done at the cursor position). >vim - - :noremap <LeftRelease> <LeftRelease>y -Immediately yank the selection, when using Visual mode. - -Note the use of ":noremap" instead of "map" to avoid a recursive mapping. ->vim - :map <X1Mouse> <C-O> - :map <X2Mouse> <C-I> -Map the X1 and X2 buttons to go forwards and backwards in the jump list, see -|CTRL-O| and |CTRL-I|. - - *mouse-swap-buttons* -To swap the meaning of the left and right mouse buttons: >vim - :noremap <LeftMouse> <RightMouse> - :noremap <LeftDrag> <RightDrag> - :noremap <LeftRelease> <RightRelease> - :noremap <RightMouse> <LeftMouse> - :noremap <RightDrag> <LeftDrag> - :noremap <RightRelease> <LeftRelease> - :noremap g<LeftMouse> <C-RightMouse> - :noremap g<RightMouse> <C-LeftMouse> - :noremap! <LeftMouse> <RightMouse> - :noremap! <LeftDrag> <RightDrag> - :noremap! <LeftRelease> <RightRelease> - :noremap! <RightMouse> <LeftMouse> - :noremap! <RightDrag> <LeftDrag> - :noremap! <RightRelease> <LeftRelease> -< + vim:et:sw=2:tw=78:ts=8:ft=help:norl: diff --git a/runtime/doc/ui.txt b/runtime/doc/ui.txt index 4e8253c47a..d8a0e3d0d7 100644 --- a/runtime/doc/ui.txt +++ b/runtime/doc/ui.txt @@ -562,7 +562,7 @@ with the following possible keys: "ui": Builtin UI highlight. |highlight-groups| "syntax": Highlight applied to a buffer by a syntax declaration or other runtime/plugin functionality such as - |nvim_buf_add_highlight()| + |nvim_buf_set_extmark()| "terminal": highlight from a process running in a |terminal-emulator|. Contains no further semantic information. `ui_name`: Highlight name from |highlight-groups|. Only for "ui" kind. @@ -715,7 +715,7 @@ Activated by the `ext_cmdline` |ui-option|. This UI extension delegates presentation of the |cmdline| (except 'wildmenu'). For command-line 'wildmenu' UI events, activate |ui-popupmenu|. -["cmdline_show", content, pos, firstc, prompt, indent, level] ~ +["cmdline_show", content, pos, firstc, prompt, indent, level, hl_id] ~ content: List of [attrs, string] [[{}, "t"], [attrs, "est"], ...] @@ -728,8 +728,8 @@ For command-line 'wildmenu' UI events, activate |ui-popupmenu|. `firstc` and `prompt` are text, that if non-empty should be displayed in front of the command line. `firstc` always indicates built-in command lines such as `:` (ex command) and `/` `?` (search), - while `prompt` is an |input()| prompt. `indent` tells how many spaces - the content should be indented. + while `prompt` is an |input()| prompt, highlighted with `hl_id`. + `indent` tells how many spaces the content should be indented. The Nvim command line can be invoked recursively, for instance by typing `<c-r>=` at the command line prompt. The `level` field is used @@ -749,8 +749,9 @@ For command-line 'wildmenu' UI events, activate |ui-popupmenu|. Should be hidden at next cmdline_show. -["cmdline_hide"] ~ - Hide the cmdline. +["cmdline_hide", abort] ~ + Hide the cmdline. `abort` is true if the cmdline is hidden after an + aborting condition (|c_Esc| or |c_CTRL-C|). ["cmdline_block_show", lines] ~ Show a block of context to the current command line. For example if @@ -783,24 +784,33 @@ will be set to zero, but can be changed and used for the replacing cmdline or message window. Cmdline state is emitted as |ui-cmdline| events, which the UI must handle. -["msg_show", kind, content, replace_last] ~ +["msg_show", kind, content, replace_last, history] ~ Display a message to the user. kind Name indicating the message kind: - "" (empty) Unknown (consider a feature-request: |bugs|) + "" (empty) Unknown (consider a |feature-request|) + "bufwrite" |:write| message "confirm" |confirm()| or |:confirm| dialog - "confirm_sub" |:substitute| confirm dialog |:s_c| "emsg" Error (|errors|, internal error, |:throw|, …) "echo" |:echo| message "echomsg" |:echomsg| message "echoerr" |:echoerr| message + "completion" |ins-completion-menu| message + "list_cmd" List output for various commands (|:ls|, |:set|, …) "lua_error" Error in |:lua| code "lua_print" |print()| from |:lua| code "rpc_error" Error response from |rpcrequest()| "return_prompt" |press-enter| prompt after a multiple messages "quickfix" Quickfix navigation message + "search_cmd" Entered search command "search_count" Search count message ("S" flag of 'shortmess') + "shell_err" |:!cmd| shell stderr output + "shell_out" |:!cmd| shell stdout output + "shell_ret" |:!cmd| shell return code + "undo" |:undo| and |:redo| message + "verbose" 'verbose' message + "wildlist" 'wildmode' "list" message "wmsg" Warning ("search hit BOTTOM", |W10|, …) New kinds may be added in the future; clients should treat unknown kinds as the empty kind. @@ -819,6 +829,9 @@ must handle. true: Replace the message in the most-recent `msg_show` call, but any other visible message should still remain. + history + True if the message was added to the |:messages| history. + ["msg_clear"] ~ Clear all messages currently displayed by "msg_show". (Messages sent by other "msg_" events below will not be affected). diff --git a/runtime/doc/usr_02.txt b/runtime/doc/usr_02.txt index 1fc612de26..41cee4e540 100644 --- a/runtime/doc/usr_02.txt +++ b/runtime/doc/usr_02.txt @@ -684,6 +684,13 @@ Summary: *help-summary* > :help E128 < takes you to the |:function| command +27) Documentation for packages distributed with Vim have the form + package-<name>. So > + :help package-termdebug +< + will bring you to the help section for the included termdebug plugin and + how to enable it. + ============================================================================== diff --git a/runtime/doc/usr_05.txt b/runtime/doc/usr_05.txt index 698d1207d3..d75438cd22 100644 --- a/runtime/doc/usr_05.txt +++ b/runtime/doc/usr_05.txt @@ -235,7 +235,7 @@ an archive or as a repository. For an archive you can follow these steps: else. -Adding nohlsearch package *nohlsearch-install* +Adding nohlsearch package *nohlsearch-install* *package-nohlsearch* Load the plugin with this command: > packadd nohlsearch diff --git a/runtime/doc/usr_10.txt b/runtime/doc/usr_10.txt index 2f55aadef0..98337e4b76 100644 --- a/runtime/doc/usr_10.txt +++ b/runtime/doc/usr_10.txt @@ -192,7 +192,7 @@ following: > Vim finds the first occurrence of "Professor" and displays the text it is about to change. You get the following prompt: > - replace with Teacher (y/n/a/q/l/^E/^Y)? + replace with Teacher? (y)es/(n)o/(a)ll/(q)uit/(l)ast/scroll up(^E)/down(^Y) At this point, you must enter one of the following answers: diff --git a/runtime/doc/usr_25.txt b/runtime/doc/usr_25.txt index 955d2ae5f0..8dbe1332b5 100644 --- a/runtime/doc/usr_25.txt +++ b/runtime/doc/usr_25.txt @@ -190,15 +190,15 @@ This results in the following: story. ~ -JUSTIFYING TEXT +JUSTIFYING TEXT *justify* *:Justify* *Justify()* *package-justify* Vim has no built-in way of justifying text. However, there is a neat macro package that does the job. To use this package, execute the following -command: > +command: >vim :packadd justify -Or put this line in your |vimrc|: > +Or put this line in your |vimrc|: >vim packadd! justify diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt index 3202a70b76..f958491ccf 100644 --- a/runtime/doc/usr_41.txt +++ b/runtime/doc/usr_41.txt @@ -1103,7 +1103,8 @@ Various: *various-functions* did_filetype() check if a FileType autocommand was used eventhandler() check if invoked by an event handler getpid() get process ID of Vim - getscriptinfo() get list of sourced vim scripts + getscriptinfo() get list of sourced Vim scripts + getstacktrace() get current stack trace of Vim scripts libcall() call a function in an external library libcallnr() idem, returning a number diff --git a/runtime/doc/various.txt b/runtime/doc/various.txt index 1ded5154a7..662519d415 100644 --- a/runtime/doc/various.txt +++ b/runtime/doc/various.txt @@ -282,6 +282,16 @@ gx Opens the current filepath or URL (decided by requires using the call operator (&). > :!Write-Output "1`n2" | & "C:\Windows\System32\sort.exe" /r < + Vim builds command line using options 'shell', 'shcf', + 'sxq' and 'shq' in the following order: + `&sh &shcf &sxq &shq {cmd} &shq &sxq` + So setting both 'sxq' and 'shq' is possible but rarely + useful. Additional escaping inside `{cmd}` may also + be due to 'sxe' option. + + Also, all |cmdline-special| characters in {cmd} are + replaced by Vim before passing them to shell. + *E34* Any "!" in {cmd} is replaced with the previous external command (see also 'cpoptions'), unless @@ -524,7 +534,8 @@ gO Show a filetype-specific, navigable "outline" of the current buffer. For example, in a |help| buffer this shows the table of contents. - Currently works in |help| and |:Man| buffers. + Works in |help| and |:Man| buffers, or any buffer with + an active |LSP| client (|lsp-defaults|). [N]gs *gs* *:sl* *:sleep* :[N]sl[eep] [N][m] Do nothing for [N] seconds, or [N] milliseconds if [m] @@ -541,8 +552,12 @@ gO Show a filetype-specific, navigable "outline" of the Queued messages are processed during the sleep. *:sl!* *:sleep!* -:[N]sl[eep]! [N][m] Same as above. Unlike Vim, it does not hide the - cursor. |vim-differences| +:[N]sl[eep]! [N][m] Same as above, but hide the cursor. + + *g==* +g== Executes the current code block. + + Works in |help| buffers. ============================================================================== 2. Using Vim like less or more *less* diff --git a/runtime/doc/vietnamese.txt b/runtime/doc/vietnamese.txt new file mode 100644 index 0000000000..ed3fe9bc68 --- /dev/null +++ b/runtime/doc/vietnamese.txt @@ -0,0 +1,73 @@ +*vietnamese.txt* Nvim + + + VIM REFERENCE MANUAL by Phạm Bình An + + Type |gO| to see the table of contents. + +=============================================================================== +1. Introduction + *vietnamese-intro* +Vim supports Vietnamese language in the following ways: + +- Built-in |vietnamese-keymap|, which allows you to type Vietnamese characters + in |Insert-mode| and |search-commands| using US keyboard layout. +- Localization in Vietnamese. See |vietnamese-l10n| + +=============================================================================== +2. Vietnamese keymaps + *vietnamese-keymap* +To switch between languages you can use your system native keyboard switcher, +or use one of the Vietnamese keymaps included in the Vim distribution, like +below > + :set keymap=vietnamese-telex_utf-8 +< +See |'keymap'| for more information. + +In the latter case, you can type Vietnamese even if you do not have a +Vietnamese input method engine (IME) or you want Vim to be independent from a +system-wide keyboard settings (when |'imdisable'| is set). You can also |:map| +a key to switch between keyboards. + +Vim comes with the following Vietnamese keymaps: +- *vietnamese-telex_utf-8* Telex input method, |UTF-8| encoding. +- *vietnamese-viqr_utf-8* VIQR input method, |UTF-8| encoding. +- *vietnamese-vni_utf-8* VNI input method, |UTF-8| encoding. + + *vietnamese-ime_diff* +Since these keymaps were designed to be minimalistic, they do not support all +features of the corresponding input methods. The differences are described +below: + +- You can only type each character individually, entering the base letter first + and then the diacritics later. For example, to type the word `nến` using + |vietnamese-vni_utf-8|, you must type `ne61n`, not `nen61` or `ne6n1` +- For characters with more than 1 diacritic, you need to type vowel mark before + tone mark. For example, to type `ồ` using |vietnamese-telex_utf-8|, you need + to type `oof`, not `ofo`. +- With |vietnamese-telex_utf-8|, you need to type all uppercase letters to + produce uppercase characters with diacritics. For example, `Ừ` must be typed + as `UWF`. +- With |vietnamese-telex_utf-8|, the escape character `\` from VNI is added, + hence the confusing `ooo` input to type `oo` is removed, which could lead to + ambiguities. For example, to type the word `Đoòng`, you would type + `DDo\ofng`. +- Simple Telex (both v1 and v2), including the `w[]{}` style, is not + supported. +- Removing diacritics using `z` in Telex or `0` in VNI and VIQR is not supported. + +=============================================================================== +3. Localization + *vietnamese-l10n* +Vim |messages| are also available in Vietnamese. If you wish to see messages +in Vietnamese, you can run the command |:language| with an argument being the +name of the Vietnamese locale. For example, > + :language vi_VN +< or > + :language vi_VN.utf-8 +< +Note that the name of the Vietnamese locale may vary depending on your system. +See |mbyte-first| for details. + +=============================================================================== +vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 8fa94a2601..eae341da49 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -24,7 +24,7 @@ User configuration and data files are found in standard |base-directories| session information. |shada| ============================================================================== -Defaults *nvim-defaults* +Defaults *defaults* *nvim-defaults* - Filetype detection is enabled by default. This can be disabled by adding ":filetype off" to |init.vim|. @@ -39,7 +39,6 @@ Defaults *nvim-defaults* - 'autoindent' is enabled - 'autoread' is enabled (works in all UIs, including terminal) - 'background' defaults to "dark" (unless set automatically by the terminal/UI) -- 'backspace' defaults to "indent,eol,start" - 'backupdir' defaults to .,~/.local/state/nvim/backup// (|xdg|), auto-created - 'belloff' defaults to "all" - 'comments' includes "fb:•" @@ -89,7 +88,6 @@ Defaults *nvim-defaults* - 'undodir' defaults to ~/.local/state/nvim/undo// (|xdg|), auto-created - 'viewoptions' includes "unix,slash", excludes "options" - 'viminfo' includes "!" -- 'wildmenu' is enabled - 'wildoptions' defaults to "pum,tagfile" - |editorconfig| plugin is enabled, .editorconfig settings are applied. @@ -124,7 +122,7 @@ fully disable the mouse or popup-menu, do any of the following: < To remove the default popup-menu without disabling mouse: >vim aunmenu PopUp - autocmd! nvim_popupmenu + autocmd! nvim.popupmenu To remove only the "How-to disable mouse" menu item (and its separator): >vim aunmenu PopUp.How-to\ disable\ mouse @@ -172,7 +170,7 @@ DEFAULT AUTOCOMMANDS Default autocommands exist in the following groups. Use ":autocmd! {group}" to remove them and ":autocmd {group}" to see how they're defined. -nvim_terminal: +nvim.terminal: - BufReadCmd: Treats "term://" buffers as |terminal| buffers. |terminal-start| - TermClose: A |terminal| buffer started with no arguments (which thus uses 'shell') and which exits with no error is closed automatically. @@ -188,13 +186,17 @@ nvim_terminal: - 'textwidth' set to 0 - 'nowrap' - 'nolist' + - 'nonumber' + - 'norelativenumber' + - 'signcolumn' set to "no" + - 'foldcolumn' set to "0" - 'winhighlight' uses |hl-StatusLineTerm| and |hl-StatusLineTermNC| in place of |hl-StatusLine| and |hl-StatusLineNC| -nvim_cmdwin: +nvim.cmdwin: - CmdwinEnter: Limits syntax sync to maxlines=1 in the |cmdwin|. -nvim_swapfile: +nvim.swapfile: - SwapExists: Skips the swapfile prompt (sets |v:swapchoice| to "e") when the swapfile is owned by a running Nvim process. Shows |W325| "Ignoring swapfile…" message. @@ -289,7 +291,8 @@ Commands: User commands can support |:command-preview| to show results as you type - |:write| with "++p" flag creates parent directories. -Events: +Events (autocommands): +- Fixed inconsistent behavior in execution of nested autocommands #23368 - |RecordingEnter| - |RecordingLeave| - |SearchWrapped| @@ -297,6 +300,8 @@ Events: - |TabNewEntered| - |TermClose| - |TermOpen| +- |TermResponse| is fired for any OSC sequence received from the terminal, + instead of the Primary Device Attributes response. |v:termresponse| - |UIEnter| - |UILeave| @@ -323,7 +328,6 @@ Highlight groups: - |hl-MsgSeparator| highlights separator for scrolled messages - |hl-Substitute| - |hl-TermCursor| -- |hl-TermCursorNC| - |hl-WinSeparator| highlights window separators - |hl-Whitespace| highlights 'listchars' whitespace - |hl-WinBar| highlights 'winbar' @@ -343,11 +347,14 @@ Options: - `:set {option}<` removes local value for all |global-local| options. - `:setlocal {option}<` copies global value to local value for all options. +- 'ambiwidth' cannot be set to empty. - 'autoread' works in the terminal (if it supports "focus" events) +- 'background' cannot be set to empty. - 'cpoptions' flags: |cpo-_| -- 'diffopt' "linematch" feature +- 'eadirection' cannot be set to empty. - 'exrc' searches for ".nvim.lua", ".nvimrc", or ".exrc" files. The user is prompted whether to trust the file. +- 'fileformat' cannot be set to empty. - 'fillchars' flags: "msgsep", "horiz", "horizup", "horizdown", "vertleft", "vertright", "verthoriz" - 'foldcolumn' supports up to 9 dynamic/fixed columns @@ -359,14 +366,17 @@ Options: - "clean" removes unloaded buffers from the jumplist. - the |jumplist|, |changelist|, |alternate-file| or using |mark-motions|. - 'laststatus' global statusline support +- 'mousemodel' cannot be set to empty. - 'mousescroll' amount to scroll by when scrolling with a mouse - 'pumblend' pseudo-transparent popupmenu - 'scrollback' - 'shortmess' - "F" flag does not affect output from autocommands. - "q" flag fully hides macro recording message. -- 'signcolumn' supports up to 9 dynamic/fixed columns +- 'showcmdloc' cannot be set to empty. +- 'signcolumn' can show multiple signs (dynamic or fixed columns) - 'statuscolumn' full control of columns using 'statusline' format +- 'splitkeep' cannot be set to empty. - 'tabline' middle-click on tabpage label closes tabpage, and %@Func@foo%X can call any function on mouse-click - 'termpastefilter' @@ -374,6 +384,10 @@ Options: - 'winblend' pseudo-transparency in floating windows |api-floatwin| - 'winhighlight' window-local highlights +Performance: +- Signs are implemented using Nvim's internal "marktree" (btree) structure. +- Folds are not updated during insert-mode. + Providers: - If a Python interpreter is available on your `$PATH`, |:python| and |:python3| are always available. See |provider-python|. @@ -391,6 +405,7 @@ Shell: - |system()| does not support writing/reading "backgrounded" commands. |E5677| Signs: +- 'signcolumn' can show multiple signs. - Signs are removed if the associated line is deleted. - Signs placed twice with the same identifier in the same group are moved. @@ -400,6 +415,8 @@ Startup: - |-es| and |-Es| have improved behavior: - Quits automatically, don't need "-c qa!". - Skips swap-file dialog. + - Optimized for non-interactive scripts: disables swapfile, shada. +- |-l| Executes Lua scripts non-interactively. - |-s| reads Normal commands from stdin if the script name is "-". - Reading text (instead of commands) from stdin |--|: - works by default: "-" file is optional @@ -412,9 +429,12 @@ TUI: < *'term'* *E529* *E530* *E531* - 'term' reflects the terminal type derived from |$TERM| and other environment - checks. For debugging only; not reliable during startup. >vim - :echo &term -- "builtin_x" means one of the |builtin-terms| was chosen, because the expected + checks. Use `:echo &term` to get its value. For debugging only; not + reliable during startup. + - Note: If you want to detect when Nvim is running in a terminal, use + `has('gui_running')` |has()| or see |nvim_list_uis()| for an example of + how to inspect the UI channel. +- "builtin_x" means one of the |builtin-terms| was chosen, because the expected terminfo file was not found on the system. - Nvim will use 256-colour capability on Linux virtual terminals. Vim uses only 8 colours plus bright foreground on Linux VTs. @@ -445,6 +465,7 @@ Upstreamed features *nvim-upstreamed* These Nvim features were later integrated into Vim. +- 'diffopt' "linematch" feature - 'fillchars' flags: "eob" - 'jumpoptions' "stack" behavior - 'wildoptions' flags: "pum" enables popupmenu for wildmode completion @@ -583,9 +604,6 @@ Mappings: Motion: - The |jumplist| avoids useless/phantom jumps. -Performance: -- Folds are not updated during insert-mode. - Syntax highlighting: - syncolor.vim has been removed. Nvim now sets up default highlighting groups automatically for both light and dark backgrounds, regardless of whether or @@ -610,11 +628,10 @@ Working directory (Vim implemented some of these after Nvim): - `getcwd(-1)` is equivalent to `getcwd(-1, 0)` instead of returning the global working directory. Use `getcwd(-1, -1)` to get the global working directory. -Autocommands: -- Fixed inconsistent behavior in execution of nested autocommands: - https://github.com/neovim/neovim/issues/23368 -- |TermResponse| is fired for any OSC sequence received from the terminal, - instead of the Primary Device Attributes response. |v:termresponse| +Options: +- 'titlestring' uses printf-style '%' items (see: 'statusline') to implement + the default behaviour. The implementation is equivalent to setting + 'titlestring' to `%t%(\ %M%)%(\ \(%{expand(\"%:~:h\")}\)%)%a\ -\ Nvim`. ============================================================================== Missing features *nvim-missing* @@ -663,7 +680,6 @@ Commands: - :promptrepl - :scriptversion (always version 1) - :shell -- :sleep! (does not hide the cursor; same as :sleep) - :smile - :tearoff - :cstag @@ -683,7 +699,7 @@ Cscope: https://github.com/dhananjaylatkar/cscope_maps.nvim Eval: -- Vim9script +- *Vim9script* (the Vim 9+ flavor of Vimscript) is not supported. - *cscope_connection()* - *err_teapot()* - *js_encode()* diff --git a/runtime/doc/vvars.txt b/runtime/doc/vvars.txt index 0c349c4e57..0ebb54e38a 100644 --- a/runtime/doc/vvars.txt +++ b/runtime/doc/vvars.txt @@ -6,7 +6,8 @@ Predefined variables *vvars* -Some variables can be set by the user, but the type cannot be changed. +Most variables are read-only, when a variable can be set by the user, it will +be mentioned at the variable description below. The type cannot be changed. Type |gO| to see the table of contents. @@ -189,11 +190,14 @@ v:event changing window (or tab) on |DirChanged|. status Job status or exit code, -1 means "unknown". |TermClose| reason Reason for completion being done. |CompleteDone| + complete_word The word that was selected, empty if abandoned complete. + complete_type See |complete_info_mode| *v:exception* *exception-variable* v:exception The value of the exception most recently caught and not - finished. See also |v:throwpoint| and |throw-variables|. + finished. See also |v:stacktrace|, |v:throwpoint|, and + |throw-variables|. Example: >vim try throw "oops" @@ -584,6 +588,13 @@ v:shell_error endif < + *v:stacktrace* *stacktrace-variable* +v:stacktrace + The stack trace of the exception most recently caught and + not finished. Refer to |getstacktrace()| for the structure of + stack trace. See also |v:exception|, |v:throwpoint|, and + |throw-variables|. + *v:statusmsg* *statusmsg-variable* v:statusmsg Last given status message. @@ -677,7 +688,7 @@ v:this_session v:throwpoint The point where the exception most recently caught and not finished was thrown. Not set when commands are typed. See - also |v:exception| and |throw-variables|. + also |v:exception|, |v:stacktrace|, and |throw-variables|. Example: >vim try throw "oops" diff --git a/runtime/doc/windows.txt b/runtime/doc/windows.txt index 24aa7b1b11..6dd90f7e49 100644 --- a/runtime/doc/windows.txt +++ b/runtime/doc/windows.txt @@ -941,8 +941,8 @@ set in the preview window to be able to recognize it. The 'winfixheight' option is set to have it keep the same height when opening/closing other windows. - *:pta* *:ptag* -:pta[g][!] [tagname] + *:pt* *:ptag* +:pt[ag][!] [tagname] Does ":tag[!] [tagname]" and shows the found tag in a "Preview" window without changing the current buffer or cursor position. If a "Preview" window already exists, it is re-used @@ -978,6 +978,14 @@ CTRL-W g } *CTRL-W_g}* it. Make the new Preview window (if required) N high. If N is not given, 'previewheight' is used. + *:pb* *:pbuffer* +:[N]pb[uffer][!] [+cmd] [N] + Edit buffer [N] from the buffer list in the preview window. + If [N] is not given, the current buffer remains being edited. + See |:buffer-!| for [!]. This will also edit a buffer that is + not in the buffer list, without setting the 'buflisted' flag. + Also see |+cmd|. + *:ped* *:pedit* :ped[it][!] [++opt] [+cmd] {file} Edit {file} in the preview window. The preview window is @@ -985,6 +993,8 @@ CTRL-W g } *CTRL-W_g}* position isn't changed. Useful example: > :pedit +/fputc /usr/include/stdio.h < + Also see |++opt| and |+cmd|. + *:ps* *:psearch* :[range]ps[earch][!] [count] [/]pattern[/] Works like |:ijump| but shows the found match in the preview diff --git a/runtime/filetype.lua b/runtime/filetype.lua index 797033da06..730991a00c 100644 --- a/runtime/filetype.lua +++ b/runtime/filetype.lua @@ -13,8 +13,8 @@ vim.api.nvim_create_autocmd({ 'BufRead', 'BufNewFile', 'StdinReadPost' }, { end local ft, on_detect = vim.filetype.match({ -- The unexpanded file name is needed here. #27914 - -- Neither args.file nor args.match are guaranteed to be unexpanded. - filename = vim.fn.bufname(args.buf), + -- However, bufname() can't be used, as it doesn't work with :doautocmd. #31306 + filename = args.file, buf = args.buf, }) if not ft then diff --git a/runtime/ftplugin/c.vim b/runtime/ftplugin/c.vim index 8b2b784eb4..3b05ce182a 100644 --- a/runtime/ftplugin/c.vim +++ b/runtime/ftplugin/c.vim @@ -39,7 +39,7 @@ endif " When the matchit plugin is loaded, this makes the % command skip parens and " braces in comments properly. if !exists("b:match_words") - let b:match_words = '^\s*#\s*if\(\|def\|ndef\)\>:^\s*#\s*elif\>:^\s*#\s*else\>:^\s*#\s*endif\>' + let b:match_words = '^\s*#\s*if\%(\|def\|ndef\)\>:^\s*#\s*elif\%(\|def\|ndef\)\>:^\s*#\s*else\>:^\s*#\s*endif\>' let b:match_skip = 's:comment\|string\|character\|special' let b:undo_ftplugin ..= " | unlet! b:match_skip b:match_words" endif diff --git a/runtime/ftplugin/c3.vim b/runtime/ftplugin/c3.vim new file mode 100644 index 0000000000..6db665a03a --- /dev/null +++ b/runtime/ftplugin/c3.vim @@ -0,0 +1,14 @@ +" Vim filetype plugin +" Language: C3 +" Maintainer: Turiiya <34311583+ttytm@users.noreply.github.com> +" Last Change: 2024 Nov 24 + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin = 1 + +setl comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:///,:// +setl commentstring=//\ %s + +let b:undo_ftplugin = 'setl com< cms<' diff --git a/runtime/ftplugin/checkhealth.vim b/runtime/ftplugin/checkhealth.vim index 00f24a2912..cc53d723c2 100644 --- a/runtime/ftplugin/checkhealth.vim +++ b/runtime/ftplugin/checkhealth.vim @@ -1,6 +1,5 @@ " Vim filetype plugin " Language: Nvim :checkhealth buffer -" Last Change: 2022 Nov 10 if exists("b:did_ftplugin") finish diff --git a/runtime/ftplugin/dockerfile.vim b/runtime/ftplugin/dockerfile.vim index 2e3c447b59..e45bf4c1d8 100644 --- a/runtime/ftplugin/dockerfile.vim +++ b/runtime/ftplugin/dockerfile.vim @@ -1,7 +1,7 @@ " Vim filetype plugin " Language: Dockerfile " Maintainer: Honza Pokorny <http://honza.ca> -" Last Change: 2014 Aug 29 +" Last Change: 2024 Dec 20 " Only do this when not done yet for this buffer if exists("b:did_ftplugin") @@ -11,6 +11,6 @@ endif " Don't load another plugin for this buffer let b:did_ftplugin = 1 -let b:undo_ftplugin = "setl commentstring<" - setlocal commentstring=#\ %s + +let b:undo_ftplugin = "setl commentstring<" diff --git a/runtime/ftplugin/editorconfig.vim b/runtime/ftplugin/editorconfig.vim index 6d437351eb..1693a95c0b 100644 --- a/runtime/ftplugin/editorconfig.vim +++ b/runtime/ftplugin/editorconfig.vim @@ -1,7 +1,7 @@ " Vim filetype plugin " Language: EditorConfig " Maintainer: Riley Bruins <ribru17@gmail.com> -" Last Change: 2024 Jul 06 +" Last Change: 2025 Jan 10 if exists('b:did_ftplugin') finish @@ -10,4 +10,6 @@ let b:did_ftplugin = 1 setl comments=:#,:; commentstring=#\ %s -let b:undo_ftplugin = 'setl com< cms<' +setl omnifunc=syntaxcomplete#Complete + +let b:undo_ftplugin = 'setl com< cms< ofu<' diff --git a/runtime/ftplugin/gel.vim b/runtime/ftplugin/gel.vim new file mode 100644 index 0000000000..b1f4def2b8 --- /dev/null +++ b/runtime/ftplugin/gel.vim @@ -0,0 +1,13 @@ +" Vim filetype plugin file +" Language: TI Code Composer Studio General Extension Language +" Document: https://downloads.ti.com/ccs/esd/documents/users_guide/ccs_debug-gel.html +" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu> +" Last Change: 2024 Dec 25 + +if exists("b:did_ftplugin") | finish | endif +let b:did_ftplugin = 1 + +setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:///,:// +setlocal commentstring=/*\ %s\ */ + +let b:undo_ftplugin = "setl commentstring< comments<" diff --git a/runtime/ftplugin/graphql.vim b/runtime/ftplugin/graphql.vim index 56f6e36e20..1717ebf2cc 100644 --- a/runtime/ftplugin/graphql.vim +++ b/runtime/ftplugin/graphql.vim @@ -1,13 +1,22 @@ " Vim filetype plugin " Language: graphql -" Maintainer: Riley Bruins <ribru17@gmail.com> -" Last Change: 2024 May 18 +" Maintainer: Jon Parise <jon@indelible.org> +" Filenames: *.graphql *.graphqls *.gql +" URL: https://github.com/jparise/vim-graphql +" License: MIT <https://opensource.org/license/mit> +" Last Change: 2024 Dec 21 if exists('b:did_ftplugin') finish endif let b:did_ftplugin = 1 -setl comments=:# commentstring=#\ %s +setlocal comments=:# +setlocal commentstring=#\ %s +setlocal formatoptions-=t +setlocal iskeyword+=$,@-@ +setlocal softtabstop=2 +setlocal shiftwidth=2 +setlocal expandtab -let b:undo_ftplugin = 'setl com< cms<' +let b:undo_ftplugin = 'setlocal com< cms< fo< isk< sts< sw< et<' diff --git a/runtime/ftplugin/help.lua b/runtime/ftplugin/help.lua index 8d991be0e4..a6169a1d9d 100644 --- a/runtime/ftplugin/help.lua +++ b/runtime/ftplugin/help.lua @@ -31,5 +31,56 @@ vim.keymap.set('n', 'gO', function() require('vim.vimhelp').show_toc() end, { buffer = 0, silent = true }) -vim.b.undo_ftplugin = (vim.b.undo_ftplugin or '') .. '\n exe "nunmap <buffer> gO"' +-- Add "runnables" for Lua/Vimscript code examples. +---@type table<integer, { lang: string, code: string }> +local code_blocks = {} +local tree = vim.treesitter.get_parser():parse()[1] +local query = vim.treesitter.query.parse( + 'vimdoc', + [[ + (codeblock + (language) @_lang + . + (code) @code + (#any-of? @_lang "lua" "vim") + (#set! @code lang @_lang)) +]] +) +local run_message_ns = vim.api.nvim_create_namespace('nvim.vimdoc.run_message') + +vim.api.nvim_buf_clear_namespace(0, run_message_ns, 0, -1) +for _, match, metadata in query:iter_matches(tree:root(), 0, 0, -1) do + for id, nodes in pairs(match) do + local name = query.captures[id] + local node = nodes[1] + local start, _, end_ = node:parent():range() + + if name == 'code' then + vim.api.nvim_buf_set_extmark(0, run_message_ns, start, 0, { + virt_text = { { 'Run with `g==`', 'LspCodeLens' } }, + }) + local code = vim.treesitter.get_node_text(node, 0) + local lang_node = match[metadata[id].lang][1] --[[@as TSNode]] + local lang = vim.treesitter.get_node_text(lang_node, 0) + for i = start + 1, end_ do + code_blocks[i] = { lang = lang, code = code } + end + end + end +end + +vim.keymap.set('n', 'g==', function() + local pos = vim.api.nvim_win_get_cursor(0)[1] + local code_block = code_blocks[pos] + if not code_block then + vim.print('No code block found') + elseif code_block.lang == 'lua' then + vim.cmd.lua(code_block.code) + elseif code_block.lang == 'vim' then + vim.cmd(code_block.code) + end +end, { buffer = true }) + +vim.b.undo_ftplugin = (vim.b.undo_ftplugin or '') + .. '\n exe "nunmap <buffer> gO" | exe "nunmap <buffer> g=="' vim.b.undo_ftplugin = vim.b.undo_ftplugin .. ' | call v:lua.vim.treesitter.stop()' diff --git a/runtime/ftplugin/java.vim b/runtime/ftplugin/java.vim index 55b358374f..cfd25bce6c 100644 --- a/runtime/ftplugin/java.vim +++ b/runtime/ftplugin/java.vim @@ -3,7 +3,7 @@ " Maintainer: Aliaksei Budavei <0x000c70 AT gmail DOT com> " Former Maintainer: Dan Sharp " Repository: https://github.com/zzzyxwvut/java-vim.git -" Last Change: 2024 Sep 26 +" Last Change: 2024 Dec 25 " 2024 Jan 14 by Vim Project (browsefilter) " 2024 May 23 by Riley Bruins <ribru17@gmail.com> ('commentstring') @@ -90,10 +90,269 @@ if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") endif endif +"""" Support pre- and post-compiler actions for SpotBugs. +if (!empty(get(g:, 'spotbugs_properties', {})) || + \ !empty(get(b:, 'spotbugs_properties', {}))) && + \ filereadable($VIMRUNTIME . '/compiler/spotbugs.vim') + + function! s:SpotBugsGetProperty(name, default) abort + return get( + \ {s:spotbugs_properties_scope}spotbugs_properties, + \ a:name, + \ a:default) + endfunction + + function! s:SpotBugsHasProperty(name) abort + return has_key( + \ {s:spotbugs_properties_scope}spotbugs_properties, + \ a:name) + endfunction + + function! s:SpotBugsGetProperties() abort + return {s:spotbugs_properties_scope}spotbugs_properties + endfunction + + " Work around ":bar"s and ":autocmd"s. + function! JavaFileTypeExecuteActionOnce(cleanup_cmd, action_cmd) abort + try + execute a:cleanup_cmd + finally + execute a:action_cmd + endtry + endfunction + + if exists("b:spotbugs_properties") + let s:spotbugs_properties_scope = 'b:' + + " Merge global entries, if any, in buffer-local entries, favouring + " defined buffer-local ones. + call extend( + \ b:spotbugs_properties, + \ get(g:, 'spotbugs_properties', {}), + \ 'keep') + elseif exists("g:spotbugs_properties") + let s:spotbugs_properties_scope = 'g:' + endif + + let s:commands = {} + + for s:name in ['DefaultPreCompilerCommand', + \ 'DefaultPreCompilerTestCommand', + \ 'DefaultPostCompilerCommand'] + if s:SpotBugsHasProperty(s:name) + let s:commands[s:name] = remove( + \ s:SpotBugsGetProperties(), + \ s:name) + endif + endfor + + if s:SpotBugsHasProperty('compiler') + " XXX: Postpone loading the script until all state, if any, has been + " collected. + if !empty(s:commands) + let g:spotbugs#state = { + \ 'compiler': remove(s:SpotBugsGetProperties(), 'compiler'), + \ 'commands': copy(s:commands), + \ } + else + let g:spotbugs#state = { + \ 'compiler': remove(s:SpotBugsGetProperties(), 'compiler'), + \ } + endif + + " Merge default entries in global (or buffer-local) entries, favouring + " defined global (or buffer-local) ones. + call extend( + \ {s:spotbugs_properties_scope}spotbugs_properties, + \ spotbugs#DefaultProperties(), + \ 'keep') + elseif !empty(s:commands) + " XXX: Postpone loading the script until all state, if any, has been + " collected. + let g:spotbugs#state = {'commands': copy(s:commands)} + endif + + unlet s:commands s:name + let s:request = 0 + + if s:SpotBugsHasProperty('PostCompilerAction') + let s:request += 4 + endif + + if s:SpotBugsHasProperty('PreCompilerTestAction') + let s:dispatcher = printf('call call(%s, [])', + \ string(s:SpotBugsGetProperties().PreCompilerTestAction)) + let s:request += 2 + endif + + if s:SpotBugsHasProperty('PreCompilerAction') + let s:dispatcher = printf('call call(%s, [])', + \ string(s:SpotBugsGetProperties().PreCompilerAction)) + let s:request += 1 + endif + + " Adapt the tests for "s:FindClassFiles()" from "compiler/spotbugs.vim". + if (s:request == 3 || s:request == 7) && + \ (!empty(s:SpotBugsGetProperty('sourceDirPath', [])) && + \ !empty(s:SpotBugsGetProperty('classDirPath', [])) && + \ !empty(s:SpotBugsGetProperty('testSourceDirPath', [])) && + \ !empty(s:SpotBugsGetProperty('testClassDirPath', []))) + function! s:DispatchAction(paths_action_pairs) abort + let name = expand('%:p') + + for [paths, Action] in a:paths_action_pairs + for path in paths + if name =~# (path . '.\{-}\.java\=$') + call Action() + return + endif + endfor + endfor + endfunction + + let s:dir_cnt = min([ + \ len(s:SpotBugsGetProperties().sourceDirPath), + \ len(s:SpotBugsGetProperties().classDirPath)]) + let s:test_dir_cnt = min([ + \ len(s:SpotBugsGetProperties().testSourceDirPath), + \ len(s:SpotBugsGetProperties().testClassDirPath)]) + + " Do not break up path pairs with filtering! + let s:dispatcher = printf('call s:DispatchAction(%s)', + \ string([[s:SpotBugsGetProperties().sourceDirPath[0 : s:dir_cnt - 1], + \ s:SpotBugsGetProperties().PreCompilerAction], + \ [s:SpotBugsGetProperties().testSourceDirPath[0 : s:test_dir_cnt - 1], + \ s:SpotBugsGetProperties().PreCompilerTestAction]])) + unlet s:test_dir_cnt s:dir_cnt + endif + + if exists("s:dispatcher") + function! s:ExecuteActions(pre_action, post_action) abort + try + execute a:pre_action + catch /\<E42:/ + execute a:post_action + endtry + endfunction + endif + + if s:request + if exists("b:spotbugs_syntax_once") || empty(join(getline(1, 8), '')) + let s:actions = [{'event': 'User'}] + else + " XXX: Handle multiple FileType events when vimrc contains more + " than one filetype setting for the language, e.g.: + " :filetype plugin indent on + " :autocmd BufRead,BufNewFile *.java setlocal filetype=java ... + " XXX: DO NOT ADD b:spotbugs_syntax_once TO b:undo_ftplugin ! + let b:spotbugs_syntax_once = 1 + let s:actions = [{ + \ 'event': 'Syntax', + \ 'once': 1, + \ }, { + \ 'event': 'User', + \ }] + endif + + for s:idx in range(len(s:actions)) + if s:request == 7 || s:request == 6 || s:request == 5 + let s:actions[s:idx].cmd = printf('call s:ExecuteActions(%s, %s)', + \ string(s:dispatcher), + \ string(printf('compiler spotbugs | call call(%s, [])', + \ string(s:SpotBugsGetProperties().PostCompilerAction)))) + elseif s:request == 4 + let s:actions[s:idx].cmd = printf( + \ 'compiler spotbugs | call call(%s, [])', + \ string(s:SpotBugsGetProperties().PostCompilerAction)) + elseif s:request == 3 || s:request == 2 || s:request == 1 + let s:actions[s:idx].cmd = printf('call s:ExecuteActions(%s, %s)', + \ string(s:dispatcher), + \ string('compiler spotbugs')) + else + let s:actions[s:idx].cmd = '' + endif + endfor + + if !exists("#java_spotbugs") + augroup java_spotbugs + augroup END + endif + + " The events are defined in s:actions. + silent! autocmd! java_spotbugs User <buffer> + silent! autocmd! java_spotbugs Syntax <buffer> + + for s:action in s:actions + if has_key(s:action, 'once') + execute printf('autocmd java_spotbugs %s <buffer> ' . + \ 'call JavaFileTypeExecuteActionOnce(%s, %s)', + \ s:action.event, + \ string(printf('autocmd! java_spotbugs %s <buffer>', + \ s:action.event)), + \ string(s:action.cmd)) + else + execute printf('autocmd java_spotbugs %s <buffer> %s', + \ s:action.event, + \ s:action.cmd) + endif + endfor + + if s:SpotBugsHasProperty('PostCompilerActionExecutor') && + \ (s:request == 7 || s:request == 6 || + \ s:request == 5 || s:request == 4) + let s:augroup = s:SpotBugsGetProperty( + \ 'augroupForPostCompilerAction', + \ 'java_spotbugs_post') + let s:augroup = !empty(s:augroup) ? s:augroup : 'java_spotbugs_post' + + for s:candidate in ['java_spotbugs_post', s:augroup] + if !exists("#" . s:candidate) + execute printf('augroup %s | augroup END', s:candidate) + endif + endfor + + silent! autocmd! java_spotbugs_post User <buffer> + + " Define a User ":autocmd" to define a once-only ShellCmdPost + " ":autocmd" that will invoke "PostCompilerActionExecutor" and let + " it decide whether to proceed with ":compiler spotbugs" etc.; and + " seek explicit synchronisation with ":doautocmd ShellCmdPost" by + " omitting "nested" for "java_spotbugs_post" and "java_spotbugs". + execute printf('autocmd java_spotbugs_post User <buffer> ' . + \ 'call JavaFileTypeExecuteActionOnce(%s, %s)', + \ string(printf('autocmd! %s ShellCmdPost <buffer>', s:augroup)), + \ string(printf('autocmd %s ShellCmdPost <buffer> ' . + \ 'call JavaFileTypeExecuteActionOnce(%s, %s)', + \ s:augroup, + \ string(printf('autocmd! %s ShellCmdPost <buffer>', s:augroup)), + \ string(printf('call call(%s, [%s])', + \ string(s:SpotBugsGetProperties().PostCompilerActionExecutor), + \ string(printf('compiler spotbugs | call call(%s, [])', + \ string(s:SpotBugsGetProperties().PostCompilerAction)))))))) + endif + + unlet! s:candidate s:augroup s:action s:actions s:idx s:dispatcher + endif + + delfunction s:SpotBugsGetProperties + delfunction s:SpotBugsHasProperty + delfunction s:SpotBugsGetProperty + unlet! s:request s:spotbugs_properties_scope +endif + +function! JavaFileTypeCleanUp() abort + setlocal suffixes< suffixesadd< formatoptions< comments< commentstring< path< includeexpr< + unlet! b:browsefilter + + " The concatenated ":autocmd" removals may be misparsed as an ":autocmd". + " A _once-only_ ShellCmdPost ":autocmd" is always a call-site definition. + silent! autocmd! java_spotbugs User <buffer> + silent! autocmd! java_spotbugs Syntax <buffer> + silent! autocmd! java_spotbugs_post User <buffer> +endfunction + " Undo the stuff we changed. -let b:undo_ftplugin = "setlocal suffixes< suffixesadd<" . - \ " formatoptions< comments< commentstring< path< includeexpr<" . - \ " | unlet! b:browsefilter" +let b:undo_ftplugin = 'call JavaFileTypeCleanUp() | delfunction JavaFileTypeCleanUp' " See ":help vim9-mix". if !has("vim9script") @@ -114,6 +373,21 @@ if exists("s:zip_func_upgradable") setlocal suffixesadd< endif +if exists("*s:DispatchAction") + def! s:DispatchAction(paths_action_pairs: list<list<any>>) + const name: string = expand('%:p') + + for [paths: list<string>, Action: func: any] in paths_action_pairs + for path in paths + if name =~# (path .. '.\{-}\.java\=$') + Action() + return + endif + endfor + endfor + enddef +endif + " Restore the saved compatibility options. let &cpo = s:save_cpo unlet s:save_cpo diff --git a/runtime/ftplugin/jj.vim b/runtime/ftplugin/jjdescription.vim index cc5d700a30..cc5d700a30 100644 --- a/runtime/ftplugin/jj.vim +++ b/runtime/ftplugin/jjdescription.vim diff --git a/runtime/ftplugin/just.vim b/runtime/ftplugin/just.vim new file mode 100644 index 0000000000..6f2acddf96 --- /dev/null +++ b/runtime/ftplugin/just.vim @@ -0,0 +1,17 @@ +" Vim ftplugin file +" Language: Justfile +" Maintainer: Peter Benjamin <@pbnj> +" Last Change: 2025 Jan 19 +" Credits: The original author, Noah Bogart <https://github.com/NoahTheDuke/vim-just/> + +" Only do this when not done yet for this buffer +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +setlocal iskeyword+=- +setlocal comments=n:# +setlocal commentstring=#\ %s + +let b:undo_ftplugin = "setlocal iskeyword< comments< commentstring<" diff --git a/runtime/ftplugin/karel.vim b/runtime/ftplugin/karel.vim new file mode 100644 index 0000000000..8ccc2b32ce --- /dev/null +++ b/runtime/ftplugin/karel.vim @@ -0,0 +1,16 @@ +" Vim filetype plugin file +" Language: KAREL +" Last Change: 2024-11-18 +" Maintainer: Kirill Morozov <kirill@robotix.pro> +" Credits: Patrick Meiser-Knosowski for the initial implementation. + +if exists("b:did_ftplugin") + finish +endif +let b:did_ftplugin = 1 + +setlocal comments=:-- +setlocal commentstring=--\ %s +setlocal suffixesadd+=.kl,.KL + +let b:undo_ftplugin = "setlocal com< cms< sua<" diff --git a/runtime/ftplugin/kconfig.vim b/runtime/ftplugin/kconfig.vim index 767490701b..1c2857ec50 100644 --- a/runtime/ftplugin/kconfig.vim +++ b/runtime/ftplugin/kconfig.vim @@ -2,7 +2,7 @@ " Vim syntax file " Maintainer: Christian Brabandt <cb@256bit.org> " Previous Maintainer: Nikolai Weibull <now@bitwi.se> -" Latest Revision: 2024-04-12 +" Latest Revision: 2025 Jan 20 " License: Vim (see :h license) " Repository: https://github.com/chrisbra/vim-kconfig @@ -19,4 +19,5 @@ setlocal comments=:# commentstring=#\ %s formatoptions-=t formatoptions+=croql " For matchit.vim if exists("loaded_matchit") let b:match_words = '^\<menu\>:\<endmenu\>,^\<if\>:\<endif\>,^\<choice\>:\<endchoice\>' + let b:undo_ftplugin .= "| unlet! b:match_words" endif diff --git a/runtime/ftplugin/lnk.vim b/runtime/ftplugin/lnk.vim new file mode 100644 index 0000000000..8b280d9836 --- /dev/null +++ b/runtime/ftplugin/lnk.vim @@ -0,0 +1,14 @@ +" Vim filetype plugin file +" Language: TI linker command file +" Document: https://software-dl.ti.com/ccs/esd/documents/sdto_cgt_Linker-Command-File-Primer.html +" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu> +" Last Change: 2024 Dec 31 + +if exists("b:did_ftplugin") | finish | endif +let b:did_ftplugin = 1 + +setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:///,:// +setlocal commentstring=/*\ %s\ */ +setlocal iskeyword+=. + +let b:undo_ftplugin = "setl commentstring< comments< iskeyword<" diff --git a/runtime/ftplugin/lnkmap.vim b/runtime/ftplugin/lnkmap.vim new file mode 100644 index 0000000000..46fb070e71 --- /dev/null +++ b/runtime/ftplugin/lnkmap.vim @@ -0,0 +1,16 @@ +" Vim filetype plugin file +" Language: TI Linker map +" Document: https://downloads.ti.com/docs/esd/SPRUI03A/Content/SPRUI03A_HTML/linker_description.html +" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu> +" Last Change: 2024 Dec 25 + +if exists("b:did_ftplugin") + finish +endif + +" Don't load another plugin for this buffer +let b:did_ftplugin = 1 + +let b:undo_ftplugin = "setl iskeyword<" + +setl iskeyword+=. diff --git a/runtime/ftplugin/opencl.vim b/runtime/ftplugin/opencl.vim new file mode 100644 index 0000000000..e8570fbe95 --- /dev/null +++ b/runtime/ftplugin/opencl.vim @@ -0,0 +1,12 @@ +" Vim filetype plugin file +" Language: OpenCL +" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu> +" Last Change: 2024 Nov 19 + +if exists("b:did_ftplugin") | finish | endif +let b:did_ftplugin = 1 + +setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:///,:// +setlocal commentstring=/*\ %s\ */ define& include& + +let b:undo_ftplugin = "setl commentstring< comments<" diff --git a/runtime/ftplugin/proto.vim b/runtime/ftplugin/proto.vim new file mode 100644 index 0000000000..585f4461d3 --- /dev/null +++ b/runtime/ftplugin/proto.vim @@ -0,0 +1,18 @@ +" Vim filetype plugin +" Language: Protobuf +" Maintainer: David Pedersen <limero@me.com> +" Last Change: 2024 Dec 09 + +if exists('b:did_ftplugin') + finish +endif +let b:did_ftplugin = 1 + +setlocal formatoptions-=t formatoptions+=croql + +setlocal comments=s1:/*,mb:*,ex:*/,:// +setlocal commentstring=//\ %s + +let b:undo_ftplugin = "setlocal formatoptions< comments< commentstring<" + +" vim: sw=2 sts=2 et diff --git a/runtime/ftplugin/ptx.vim b/runtime/ftplugin/ptx.vim new file mode 100644 index 0000000000..12b127c8fc --- /dev/null +++ b/runtime/ftplugin/ptx.vim @@ -0,0 +1,16 @@ +" Vim filetype plugin file +" Language: Nvidia PTX (Parellel Thread Execution) +" Maintainer: Yinzuo Jiang <jiangyinzuo@foxmail.com> +" Last Change: 2024-12-05 + +if exists("b:did_ftplugin") + finish +endif + +let b:did_ftplugin = 1 + +" Comments in PTX follow C/C++ syntax +" See: https://docs.nvidia.com/cuda/parallel-thread-execution/#syntax +setlocal commentstring=//\ %s + +let b:undo_ftplugin = 'setl commentstring<' diff --git a/runtime/ftplugin/python.vim b/runtime/ftplugin/python.vim index c000296726..6f20468896 100644 --- a/runtime/ftplugin/python.vim +++ b/runtime/ftplugin/python.vim @@ -3,8 +3,9 @@ " Maintainer: Tom Picton <tom@tompicton.com> " Previous Maintainer: James Sully <sullyj3@gmail.com> " Previous Maintainer: Johannes Zellner <johannes@zellner.org> -" Last Change: 2024/05/13 -" https://github.com/tpict/vim-ftplugin-python +" Repository: https://github.com/tpict/vim-ftplugin-python +" Last Change: 2024/05/13 +" 2024 Nov 30 use pytest compiler (#16130) if exists("b:did_ftplugin") | finish | endif let b:did_ftplugin = 1 @@ -134,6 +135,11 @@ elseif executable('python') setlocal keywordprg=python\ -m\ pydoc endif +if expand('%:t') =~# '\v^test_.*\.py$|_test\.py$' && executable('pytest') + compiler pytest + let &l:makeprg .= ' %:S' +endif + " Script for filetype switching to undo the local stuff we may have changed let b:undo_ftplugin = 'setlocal cinkeys<' \ . '|setlocal comments<' @@ -148,6 +154,7 @@ let b:undo_ftplugin = 'setlocal cinkeys<' \ . '|setlocal softtabstop<' \ . '|setlocal suffixesadd<' \ . '|setlocal tabstop<' + \ . '|setlocal makeprg<' \ . '|silent! nunmap <buffer> [M' \ . '|silent! nunmap <buffer> [[' \ . '|silent! nunmap <buffer> []' diff --git a/runtime/ftplugin/query.lua b/runtime/ftplugin/query.lua index 32d615c65c..aa5cac2f07 100644 --- a/runtime/ftplugin/query.lua +++ b/runtime/ftplugin/query.lua @@ -1,6 +1,5 @@ -- Neovim filetype plugin file -- Language: Treesitter query --- Last Change: 2024 Jul 03 if vim.b.did_ftplugin == 1 then return @@ -22,7 +21,7 @@ local query_lint_on = vim.g.query_lint_on or { 'BufEnter', 'BufWrite' } if not vim.b.disable_query_linter and #query_lint_on > 0 then vim.api.nvim_create_autocmd(query_lint_on, { - group = vim.api.nvim_create_augroup('querylint', { clear = false }), + group = vim.api.nvim_create_augroup('nvim.querylint', { clear = false }), buffer = buf, callback = function() vim.treesitter.query.lint(buf) diff --git a/runtime/ftplugin/sh.vim b/runtime/ftplugin/sh.vim index 4c7695dcc6..54ae73b675 100644 --- a/runtime/ftplugin/sh.vim +++ b/runtime/ftplugin/sh.vim @@ -5,6 +5,7 @@ " Contributor: Enno Nagel <ennonagel+vim@gmail.com> " Eisuke Kawashima " Last Change: 2024 Sep 19 by Vim Project (compiler shellcheck) +" 2024 Dec 29 by Vim Project (improve setting shellcheck compiler) if exists("b:did_ftplugin") finish @@ -44,7 +45,11 @@ if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") let b:undo_ftplugin ..= " | unlet! b:browsefilter" endif -if get(b:, "is_bash", 0) +let s:is_sh = get(b:, "is_sh", get(g:, "is_sh", 0)) +let s:is_bash = get(b:, "is_bash", get(g:, "is_bash", 0)) +let s:is_kornshell = get(b:, "is_kornshell", get(g:, "is_kornshell", 0)) + +if s:is_bash if exists(':terminal') == 2 command! -buffer -nargs=1 ShKeywordPrg silent exe ':term bash -c "help "<args>" 2>/dev/null || man "<args>""' else @@ -52,14 +57,21 @@ if get(b:, "is_bash", 0) endif setlocal keywordprg=:ShKeywordPrg let b:undo_ftplugin ..= " | setl kp< | sil! delc -buffer ShKeywordPrg" +endif +if (s:is_sh || s:is_bash || s:is_kornshell) && executable('shellcheck') if !exists('current_compiler') compiler shellcheck + let b:undo_ftplugin ..= ' | compiler make' + endif +elseif s:is_bash + if !exists('current_compiler') + compiler bash + let b:undo_ftplugin ..= ' | compiler make' endif - let b:undo_ftplugin .= ' | compiler make' endif let &cpo = s:save_cpo -unlet s:save_cpo +unlet s:save_cpo s:is_sh s:is_bash s:is_kornshell " vim: nowrap sw=2 sts=2 ts=8 noet: diff --git a/runtime/ftplugin/shaderslang.vim b/runtime/ftplugin/shaderslang.vim new file mode 100644 index 0000000000..f3d1ab8c1c --- /dev/null +++ b/runtime/ftplugin/shaderslang.vim @@ -0,0 +1,54 @@ +" Vim filetype plugin file +" Language: Slang +" Maintainer: Austin Shijo <epestr@proton.me> +" Last Change: 2025 Jan 05 + +" Only do this when not done yet for this buffer +if exists("b:did_ftplugin") + finish +endif + +" Don't load another plugin for this buffer +let b:did_ftplugin = 1 + +" Using line continuation here. +let s:cpo_save = &cpo +set cpo-=C + +let b:undo_ftplugin = "setl fo< com< cms< inc<" + +" Set 'formatoptions' to break comment lines but not other lines, +" and insert the comment leader when hitting <CR> or using "o". +setlocal fo-=t fo+=croql + +" Set comment string (Slang uses C-style comments) +setlocal commentstring=//\ %s + +" Set 'comments' to format dashed lists in comments +setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,:///,:// + +" When the matchit plugin is loaded, this makes the % command skip parens and +" braces in comments properly, and adds support for shader-specific keywords +if exists("loaded_matchit") + " Add common shader control structures + let b:match_words = '{\|^\s*\<\(if\|for\|while\|switch\|struct\|class\)\>:}\|^\s*\<break\>,' .. + \ '^\s*#\s*if\(\|def\|ndef\)\>:^\s*#\s*elif\>:^\s*#\s*else\>:^\s*#\s*endif\>,' .. + \ '\[:\]' + let b:match_skip = 's:comment\|string\|character\|special' + let b:match_ignorecase = 0 + let b:undo_ftplugin ..= " | unlet! b:match_skip b:match_words b:match_ignorecase" +endif + +" Win32 and GTK can filter files in the browse dialog +if (has("gui_win32") || has("gui_gtk")) && !exists("b:browsefilter") + let b:browsefilter = "Slang Source Files (*.slang)\t*.slang\n" + if has("win32") + let b:browsefilter ..= "All Files (*.*)\t*\n" + else + let b:browsefilter ..= "All Files (*)\t*\n" + endif + let b:undo_ftplugin ..= " | unlet! b:browsefilter" +endif + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/ftplugin/tiasm.vim b/runtime/ftplugin/tiasm.vim new file mode 100644 index 0000000000..13a3dc64f7 --- /dev/null +++ b/runtime/ftplugin/tiasm.vim @@ -0,0 +1,18 @@ +" Vim filetype plugin file +" Language: TI linear assembly language +" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu> +" Last Change: 2025 Jan 08 + +if exists("b:did_ftplugin") | finish | endif +let b:did_ftplugin = 1 + +setlocal comments=:; +setlocal commentstring=;\ %s + +let b:undo_ftplugin = "setl commentstring< comments<" + +if exists("loaded_matchit") + let b:match_words = '^\s\+\.if\>:^\s\+\.elseif:^\s\+\.else\>:^\s\+\.endif\>,^\s\+\.group:^\s\+\.gmember:^\s\+\.endgroup,^\s\+\.loop:^\s\+\.break:^\s\+\.endloop,^\s\+\.macro:^\s\+\.mexit:^\s\+\.endm,^\s\+\.asmfunc:^\s\+\.endasmfunc,^\s\+\.c\?struct:^\s\+\.endstruct,^\s\+\.c\?union:^\s\+\.endunion,^\s\+\.c\?proc:^\s\+\.return:^\s\+\.endproc' + let b:match_ignorecase = 1 + let b:undo_ftplugin ..= " | unlet! b:match_ignorecase b:match_words" +endif diff --git a/runtime/ftplugin/typst.vim b/runtime/ftplugin/typst.vim index 3841e427f3..08929bbdbe 100644 --- a/runtime/ftplugin/typst.vim +++ b/runtime/ftplugin/typst.vim @@ -1,7 +1,8 @@ " Vim filetype plugin file " Language: Typst -" Maintainer: Gregory Anders -" Last Change: 2024 Oct 21 +" Previous Maintainer: Gregory Anders +" Maintainer: Luca Saccarola <github.e41mv@aleeas.com> +" Last Change: 2024 Dec 09 " Based on: https://github.com/kaarmu/typst.vim if exists('b:did_ftplugin') @@ -11,10 +12,14 @@ let b:did_ftplugin = 1 setlocal commentstring=//\ %s setlocal comments=s1:/*,mb:*,ex:*/,:// -setlocal formatoptions+=croq +setlocal formatoptions+=croqn +" Numbered Lists +setlocal formatlistpat=^\\s*\\d\\+[\\]:.)}\\t\ ]\\s* +" Unordered (-), Ordered (+) and definition (/) Lists +setlocal formatlistpat+=\\\|^\\s*[-+/\]\\s\\+ setlocal suffixesadd=.typ -let b:undo_ftplugin = 'setl cms< com< fo< sua<' +let b:undo_ftplugin = 'setl cms< com< fo< flp< sua<' if get(g:, 'typst_conceal', 0) setlocal conceallevel=2 diff --git a/runtime/ftplugin/vim.vim b/runtime/ftplugin/vim.vim index b5e8e693f6..6ba057fc03 100644 --- a/runtime/ftplugin/vim.vim +++ b/runtime/ftplugin/vim.vim @@ -1,9 +1,9 @@ " Vim filetype plugin " Language: Vim " Maintainer: Doug Kearns <dougkearns@gmail.com> -" Last Change: 2024 Apr 13 -" 2024 May 23 by Riley Bruins <ribru17@gmail.com> ('commentstring') +" Last Change: 2025 Jan 06 " Former Maintainer: Bram Moolenaar <Bram@vim.org> +" Contributors: Riley Bruins <ribru17@gmail.com> ('commentstring') " Only do this when not done yet for this buffer if exists("b:did_ftplugin") @@ -103,8 +103,8 @@ if exists("loaded_matchit") \ '\<try\>:\%(\%(^\||\)\s*\)\@<=\<cat\%[ch]\>:\%(\%(^\||\)\s*\)\@<=\<fina\%[lly]\>:\%(\%(^\||\)\s*\)\@<=\<endt\%[ry]\>,' .. \ '\<aug\%[roup]\s\+\%(END\>\)\@!\S:\<aug\%[roup]\s\+END\>,' .. \ '\<class\>:\<endclass\>,' .. - \ '\<inte\%[rface]\>:\<endinterface\>,' .. - \ '\<enu\%[m]\>:\<endenum\>,' + \ '\<interface\>:\<endinterface\>,' .. + \ '\<enum\>:\<endenum\>' " Ignore syntax region commands and settings, any 'en*' would clobber " if-endif. diff --git a/runtime/indent/graphql.vim b/runtime/indent/graphql.vim new file mode 100644 index 0000000000..dc0769b9a8 --- /dev/null +++ b/runtime/indent/graphql.vim @@ -0,0 +1,92 @@ +" Vim indent file +" Language: graphql +" Maintainer: Jon Parise <jon@indelible.org> +" Filenames: *.graphql *.graphqls *.gql +" URL: https://github.com/jparise/vim-graphql +" License: MIT <https://opensource.org/license/mit> +" Last Change: 2024 Dec 21 + +" Set our local options if indentation hasn't already been set up. +" This generally means we've been detected as the primary filetype. +if !exists('b:did_indent') + setlocal autoindent + setlocal nocindent + setlocal nolisp + setlocal nosmartindent + + setlocal indentexpr=GetGraphQLIndent() + setlocal indentkeys=0{,0},0),0[,0],0#,!^F,o,O + + let b:did_indent = 1 +endif + +" If our indentation function already exists, we have nothing more to do. +if exists('*GetGraphQLIndent') + finish +endif + +let s:cpo_save = &cpoptions +set cpoptions&vim + +" searchpair() skip expression that matches in comments and strings. +let s:pair_skip_expr = + \ 'synIDattr(synID(line("."), col("."), 0), "name") =~? "comment\\|string"' + +" Check if the character at lnum:col is inside a string. +function s:InString(lnum, col) + return synIDattr(synID(a:lnum, a:col, 1), 'name') ==# 'graphqlString' +endfunction + +function GetGraphQLIndent() + " If this is the first non-blank line, we have nothing more to do because + " all of our indentation rules are based on matching against earlier lines. + let l:prevlnum = prevnonblank(v:lnum - 1) + if l:prevlnum == 0 + return 0 + endif + + " If the previous line isn't GraphQL, assume we're part of a template + " string and indent this new line within it. + let l:stack = map(synstack(l:prevlnum, 1), "synIDattr(v:val, 'name')") + if get(l:stack, -1) !~# '^graphql' + return indent(l:prevlnum) + shiftwidth() + endif + + let l:line = getline(v:lnum) + + " If this line contains just a closing bracket, find its matching opening + " bracket and indent the closing bracket to match. + let l:col = matchend(l:line, '^\s*[]})]') + if l:col > 0 && !s:InString(v:lnum, l:col) + call cursor(v:lnum, l:col) + + let l:bracket = l:line[l:col - 1] + if l:bracket ==# '}' + let l:matched = searchpair('{', '', '}', 'bW', s:pair_skip_expr) + elseif l:bracket ==# ']' + let l:matched = searchpair('\[', '', '\]', 'bW', s:pair_skip_expr) + elseif l:bracket ==# ')' + let l:matched = searchpair('(', '', ')', 'bW', s:pair_skip_expr) + else + let l:matched = -1 + endif + + return l:matched > 0 ? indent(l:matched) : virtcol('.') - 1 + endif + + " If we're inside of a multiline string, continue with the same indentation. + if s:InString(v:lnum, matchend(l:line, '^\s*') + 1) + return indent(v:lnum) + endif + + " If the previous line ended with an opening bracket, indent this line. + if getline(l:prevlnum) =~# '\%(#.*\)\@<![[{(]\s*$' + return indent(l:prevlnum) + shiftwidth() + endif + + " Default to the existing indentation level. + return indent(l:prevlnum) +endfunction + +let &cpoptions = s:cpo_save +unlet s:cpo_save diff --git a/runtime/indent/just.vim b/runtime/indent/just.vim new file mode 100644 index 0000000000..d7f82b118f --- /dev/null +++ b/runtime/indent/just.vim @@ -0,0 +1,51 @@ +" Vim indent file +" Language: Justfile +" Maintainer: Peter Benjamin <@pbnj> +" Last Change: 2025 Jan 19 +" Credits: The original author, Noah Bogart <https://github.com/NoahTheDuke/vim-just/> + +" Only load this indent file when no other was loaded yet. +if exists("b:did_indent") + finish +endif +let b:did_indent = 1 + +setlocal indentexpr=GetJustfileIndent() +setlocal indentkeys=0},0),!^F,o,O,0=''',0=\"\"\" + +let b:undo_indent = "setlocal indentexpr< indentkeys<" + +if exists("*GetJustfileIndent") + finish +endif + +function GetJustfileIndent() + if v:lnum < 2 + return 0 + endif + + let prev_line = getline(v:lnum - 1) + let last_indent = indent(v:lnum - 1) + + if getline(v:lnum) =~ "\\v^\\s+%([})]|'''$|\"\"\"$)" + return last_indent - shiftwidth() + elseif prev_line =~ '\V#' + return last_indent + elseif prev_line =~ "\\v%([:{(]|^.*\\S.*%([^']'''|[^\"]\"\"\"))\\s*$" + return last_indent + shiftwidth() + elseif prev_line =~ '\\$' + if v:lnum == 2 || getline(v:lnum - 2) !~ '\\$' + if prev_line =~ '\v:\=@!' + return last_indent + shiftwidth() + shiftwidth() + else + return last_indent + shiftwidth() + endif + endif + elseif v:lnum > 2 && getline(v:lnum - 2) =~ '\\$' + return last_indent - shiftwidth() + elseif prev_line =~ '\v:\s*%(\h|\()' && prev_line !~ '\V:=' + return last_indent + shiftwidth() + endif + + return last_indent +endfunction diff --git a/runtime/indent/query.lua b/runtime/indent/query.lua index c5b4f1f03d..17b8e9d2ac 100644 --- a/runtime/indent/query.lua +++ b/runtime/indent/query.lua @@ -1,6 +1,5 @@ -- Neovim indent file -- Language: Treesitter query --- Last Change: 2024 Jul 03 -- it's a lisp! vim.cmd([[runtime! indent/lisp.vim]]) diff --git a/runtime/indent/typst.vim b/runtime/indent/typst.vim index 6aaa04a53a..c990b968c8 100644 --- a/runtime/indent/typst.vim +++ b/runtime/indent/typst.vim @@ -1,7 +1,8 @@ " Vim indent file " Language: Typst -" Maintainer: Gregory Anders <greg@gpanders.com> -" Last Change: 2024-07-14 +" Previous Maintainer: Gregory Anders +" Maintainer: Luca Saccarola <github.e41mv@aleeas.com> +" Last Change: 2024 Dec 09 " Based on: https://github.com/kaarmu/typst.vim if exists('b:did_indent') diff --git a/runtime/lua/_vim9script.lua b/runtime/lua/_vim9script.lua index 23c078cb87..29bafc53b2 100644 --- a/runtime/lua/_vim9script.lua +++ b/runtime/lua/_vim9script.lua @@ -145,7 +145,7 @@ local vim9 = (function() -- work well for calling ":source X" from within a vimscript/vim9script -- function M.make_source_cmd = function() - local group = vim.api.nvim_create_augroup('vim9script-source', {}) + local group = vim.api.nvim_create_augroup('nvim.vim9script_source', {}) vim.api.nvim_create_autocmd('SourceCmd', { pattern = '*.vim', group = group, diff --git a/runtime/lua/coxpcall.lua b/runtime/lua/coxpcall.lua index 6b179f1ef0..43e321eac3 100644 --- a/runtime/lua/coxpcall.lua +++ b/runtime/lua/coxpcall.lua @@ -1,6 +1,10 @@ ------------------------------------------------------------------------------- +-- (Not needed for LuaJIT or Lua 5.2+) +-- -- Coroutine safe xpcall and pcall versions -- +-- https://keplerproject.github.io/coxpcall/ +-- -- Encapsulates the protected calls with a coroutine based loop, so errors can -- be dealed without the usual Lua 5.x pcall/xpcall issues with coroutines -- yielding inside the call to pcall or xpcall. @@ -27,22 +31,23 @@ end -- No need to do anything if pcall and xpcall are already safe. if isCoroutineSafe(pcall) and isCoroutineSafe(xpcall) then - copcall = pcall - coxpcall = xpcall + _G.copcall = pcall + _G.coxpcall = xpcall return { pcall = pcall, xpcall = xpcall, running = coroutine.running } end ------------------------------------------------------------------------------- -- Implements xpcall with coroutines ------------------------------------------------------------------------------- -local performResume, handleReturnValue +local performResume local oldpcall, oldxpcall = pcall, xpcall local pack = table.pack or function(...) return {n = select("#", ...), ...} end local unpack = table.unpack or unpack local running = coroutine.running +--- @type table<thread,thread> local coromap = setmetatable({}, { __mode = "k" }) -function handleReturnValue(err, co, status, ...) +local function handleReturnValue(err, co, status, ...) if not status then return false, err(debug.traceback(co, (...)), ...) end @@ -57,11 +62,12 @@ function performResume(err, co, ...) return handleReturnValue(err, co, coroutine.resume(co, ...)) end +--- @diagnostic disable-next-line: unused-vararg local function id(trace, ...) return trace end -function coxpcall(f, err, ...) +function _G.coxpcall(f, err, ...) local current = running() if not current then if err == id then @@ -84,6 +90,7 @@ function coxpcall(f, err, ...) end end +--- @param coro? thread local function corunning(coro) if coro ~= nil then assert(type(coro)=="thread", "Bad argument; expected thread, got: "..type(coro)) @@ -101,7 +108,7 @@ end -- Implements pcall with coroutines ------------------------------------------------------------------------------- -function copcall(f, ...) +function _G.copcall(f, ...) return coxpcall(f, id, ...) end diff --git a/runtime/lua/editorconfig.lua b/runtime/lua/editorconfig.lua index e65d267a70..2093c4eb5c 100644 --- a/runtime/lua/editorconfig.lua +++ b/runtime/lua/editorconfig.lua @@ -151,7 +151,7 @@ function properties.trim_trailing_whitespace(bufnr, val) ) if val == 'true' then vim.api.nvim_create_autocmd('BufWritePre', { - group = 'editorconfig', + group = 'nvim.editorconfig', buffer = bufnr, callback = function() local view = vim.fn.winsaveview() @@ -163,7 +163,7 @@ function properties.trim_trailing_whitespace(bufnr, val) else vim.api.nvim_clear_autocmds({ event = 'BufWritePre', - group = 'editorconfig', + group = 'nvim.editorconfig', buffer = bufnr, }) end @@ -180,7 +180,7 @@ function properties.insert_final_newline(bufnr, val) local endofline = val == 'true' if vim.bo[bufnr].endofline ~= endofline then vim.api.nvim_create_autocmd('BufWritePre', { - group = 'editorconfig', + group = 'nvim.editorconfig', buffer = bufnr, once = true, callback = function() diff --git a/runtime/lua/man.lua b/runtime/lua/man.lua index 114c94f9e5..96cfdf523d 100644 --- a/runtime/lua/man.lua +++ b/runtime/lua/man.lua @@ -1,93 +1,84 @@ local api, fn = vim.api, vim.fn -local FIND_ARG = '-w' -local localfile_arg = true -- Always use -l if possible. #6683 - ----@type table[] -local buf_hls = {} - local M = {} -local function man_error(msg) - M.errormsg = 'man.lua: ' .. vim.inspect(msg) - error(M.errormsg) -end - --- Run a system command and timeout after 30 seconds. ----@param cmd string[] ----@param silent boolean? ----@param env? table<string,string|number> ----@return string +--- Run a system command and timeout after 10 seconds. +--- @param cmd string[] +--- @param silent boolean? +--- @param env? table<string,string|number> +--- @return string local function system(cmd, silent, env) local r = vim.system(cmd, { env = env, timeout = 10000 }):wait() - if r.code ~= 0 and not silent then - local cmd_str = table.concat(cmd, ' ') - man_error(string.format("command error '%s': %s", cmd_str, r.stderr)) + if not silent then + if r.code ~= 0 then + local cmd_str = table.concat(cmd, ' ') + error(string.format("command error '%s': %s", cmd_str, r.stderr)) + end + assert(r.stdout ~= '') end return assert(r.stdout) end ----@param line string ----@param linenr integer -local function highlight_line(line, linenr) - ---@type string[] +--- @enum Man.Attribute +local Attrs = { + None = 0, + Bold = 1, + Underline = 2, + Italic = 3, +} + +--- @param line string +--- @param row integer +--- @param hls {attr:Man.Attribute,row:integer,start:integer,final:integer}[] +--- @return string +local function render_line(line, row, hls) + --- @type string[] local chars = {} local prev_char = '' local overstrike, escape, osc8 = false, false, false - ---@type table<integer,{attr:integer,start:integer,final:integer}> - local hls = {} -- Store highlight groups as { attr, start, final } - - local NONE, BOLD, UNDERLINE, ITALIC = 0, 1, 2, 3 - local hl_groups = { [BOLD] = 'manBold', [UNDERLINE] = 'manUnderline', [ITALIC] = 'manItalic' } - local attr = NONE + local attr = Attrs.None local byte = 0 -- byte offset - local function end_attr_hl(attr_) - for i, hl in ipairs(hls) do - if hl.attr == attr_ and hl.final == -1 then - hl.final = byte - hls[i] = hl - end - end - end + local hls_start = #hls + 1 + --- @param code integer local function add_attr_hl(code) local continue_hl = true if code == 0 then - attr = NONE + attr = Attrs.None continue_hl = false elseif code == 1 then - attr = BOLD + attr = Attrs.Bold elseif code == 22 then - attr = BOLD + attr = Attrs.Bold continue_hl = false elseif code == 3 then - attr = ITALIC + attr = Attrs.Italic elseif code == 23 then - attr = ITALIC + attr = Attrs.Italic continue_hl = false elseif code == 4 then - attr = UNDERLINE + attr = Attrs.Underline elseif code == 24 then - attr = UNDERLINE + attr = Attrs.Underline continue_hl = false else - attr = NONE + attr = Attrs.None return end if continue_hl then - hls[#hls + 1] = { attr = attr, start = byte, final = -1 } + hls[#hls + 1] = { attr = attr, row = row, start = byte, final = -1 } else - if attr == NONE then - for a, _ in pairs(hl_groups) do - end_attr_hl(a) + for _, a in pairs(attr == Attrs.None and Attrs or { attr }) do + for i = hls_start, #hls do + if hls[i].attr == a and hls[i].final == -1 then + hls[i].final = byte + end end - else - end_attr_hl(attr) end end end @@ -100,11 +91,11 @@ local function highlight_line(line, linenr) if overstrike then local last_hl = hls[#hls] if char == prev_char then - if char == '_' and attr == ITALIC and last_hl and last_hl.final == byte then + if char == '_' and attr == Attrs.Italic and last_hl and last_hl.final == byte then -- This underscore is in the middle of an italic word - attr = ITALIC + attr = Attrs.Italic else - attr = BOLD + attr = Attrs.Bold end elseif prev_char == '_' then -- Even though underline is strictly what this should be. <bs>_ was used by nroff to @@ -113,26 +104,26 @@ local function highlight_line(line, linenr) -- See: -- - https://unix.stackexchange.com/questions/274658/purpose-of-ascii-text-with-overstriking-file-format/274795#274795 -- - https://cmd.inp.nsk.su/old/cmd2/manuals/unix/UNIX_Unleashed/ch08.htm - -- attr = UNDERLINE - attr = ITALIC + -- attr = Attrs.Underline + attr = Attrs.Italic elseif prev_char == '+' and char == 'o' then -- bullet (overstrike text '+^Ho') - attr = BOLD + attr = Attrs.Bold char = '·' elseif prev_char == '·' and char == 'o' then -- bullet (additional handling for '+^H+^Ho^Ho') - attr = BOLD + attr = Attrs.Bold char = '·' else -- use plain char - attr = NONE + attr = Attrs.None end -- Grow the previous highlight group if possible if last_hl and last_hl.attr == attr and last_hl.final == byte then last_hl.final = byte + #char else - hls[#hls + 1] = { attr = attr, start = byte, final = byte + #char } + hls[#hls + 1] = { attr = attr, row = row, start = byte, final = byte + #char } end overstrike = false @@ -151,14 +142,15 @@ local function highlight_line(line, linenr) -- We only want to match against SGR sequences, which consist of ESC -- followed by '[', then a series of parameter and intermediate bytes in -- the range 0x20 - 0x3f, then 'm'. (See ECMA-48, sections 5.4 & 8.3.117) - ---@type string? + --- @type string? local sgr = prev_char:match('^%[([\032-\063]*)m$') -- Ignore escape sequences with : characters, as specified by ITU's T.416 -- Open Document Architecture and interchange format. - if sgr and not string.find(sgr, ':') then - local match ---@type string? + if sgr and not sgr:find(':') then + local match --- @type string? while sgr and #sgr > 0 do -- Match against SGR parameters, which may be separated by ';' + --- @type string?, string? match, sgr = sgr:match('^(%d*);?(.*)') add_attr_hl(match + 0) -- coerce to number end @@ -184,55 +176,43 @@ local function highlight_line(line, linenr) end end - for _, hl in ipairs(hls) do - if hl.attr ~= NONE then - buf_hls[#buf_hls + 1] = { - 0, - -1, - hl_groups[hl.attr], - linenr - 1, - hl.start, - hl.final, - } - end - end - return table.concat(chars, '') end +local HlGroups = { + [Attrs.Bold] = 'manBold', + [Attrs.Underline] = 'manUnderline', + [Attrs.Italic] = 'manItalic', +} + local function highlight_man_page() local mod = vim.bo.modifiable vim.bo.modifiable = true local lines = api.nvim_buf_get_lines(0, 0, -1, false) + + --- @type {attr:Man.Attribute,row:integer,start:integer,final:integer}[] + local hls = {} + for i, line in ipairs(lines) do - lines[i] = highlight_line(line, i) + lines[i] = render_line(line, i - 1, hls) end + api.nvim_buf_set_lines(0, 0, -1, false, lines) - for _, args in ipairs(buf_hls) do - api.nvim_buf_add_highlight(unpack(args)) + for _, hl in ipairs(hls) do + if hl.attr ~= Attrs.None then + --- @diagnostic disable-next-line: deprecated + api.nvim_buf_add_highlight(0, -1, HlGroups[hl.attr], hl.row, hl.start, hl.final) + end end - buf_hls = {} vim.bo.modifiable = mod end --- replace spaces in a man page name with underscores --- intended for PostgreSQL, which has man pages like 'CREATE_TABLE(7)'; --- while editing SQL source code, it's nice to visually select 'CREATE TABLE' --- and hit 'K', which requires this transformation ----@param str string ----@return string -local function spaces_to_underscores(str) - local res = str:gsub('%s', '_') - return res -end - ----@param sect string|nil ----@param name string|nil ----@param silent boolean -local function get_path(sect, name, silent) +--- @param name? string +--- @param sect? string +local function get_path(name, sect) name = name or '' sect = sect or '' -- Some man implementations (OpenBSD) return all available paths from the @@ -253,14 +233,14 @@ local function get_path(sect, name, silent) -- -- Finally, we can avoid relying on -S or -s here since they are very -- inconsistently supported. Instead, call -w with a section and a name. - local cmd ---@type string[] + local cmd --- @type string[] if sect == '' then - cmd = { 'man', FIND_ARG, name } + cmd = { 'man', '-w', name } else - cmd = { 'man', FIND_ARG, sect, name } + cmd = { 'man', '-w', sect, name } end - local lines = system(cmd, silent) + local lines = system(cmd, true) local results = vim.split(lines, '\n', { trimempty = true }) if #results == 0 then @@ -276,87 +256,73 @@ local function get_path(sect, name, silent) end -- find any that match the specified name - ---@param v string + --- @param v string local namematches = vim.tbl_filter(function(v) local tail = fn.fnamemodify(v, ':t') - return string.find(tail, name, 1, true) + return tail:find(name, 1, true) ~= nil end, results) or {} local sectmatches = {} if #namematches > 0 and sect ~= '' then - ---@param v string + --- @param v string sectmatches = vim.tbl_filter(function(v) return fn.fnamemodify(v, ':e') == sect end, namematches) end - return fn.substitute(sectmatches[1] or namematches[1] or results[1], [[\n\+$]], '', '') + return (sectmatches[1] or namematches[1] or results[1]):gsub('\n+$', '') end ----@param text string ----@param pat_or_re string -local function matchstr(text, pat_or_re) - local re = type(pat_or_re) == 'string' and vim.regex(pat_or_re) or pat_or_re - - ---@type integer, integer - local s, e = re:match_str(text) - - if s == nil then - return +--- Attempt to extract the name and sect out of 'name(sect)' +--- otherwise just return the largest string of valid characters in ref +--- @param ref string +--- @return string? name +--- @return string? sect +--- @return string? err +local function parse_ref(ref) + if ref == '' or ref:sub(1, 1) == '-' then + return nil, nil, ('invalid manpage reference "%s"'):format(ref) end - return text:sub(vim.str_utfindex(text, 'utf-32', s) + 1, vim.str_utfindex(text, 'utf-32', e)) -end + -- match "<name>(<sect>)" + -- note: name can contain spaces + local name, sect = ref:match('([^()]+)%(([^()]+)%)') + if name then + -- see ':Man 3X curses' on why tolower. + -- TODO(nhooyr) Not sure if this is portable across OSs + -- but I have not seen a single uppercase section. + return name, sect:lower() + end --- attempt to extract the name and sect out of 'name(sect)' --- otherwise just return the largest string of valid characters in ref ----@param ref string ----@return string, string -local function extract_sect_and_name_ref(ref) - ref = ref or '' - if ref:sub(1, 1) == '-' then -- try ':Man -pandoc' with this disabled. - man_error("manpage name cannot start with '-'") - end - local ref1 = ref:match('[^()]+%([^()]+%)') - if not ref1 then - local name = ref:match('[^()]+') - if not name then - man_error('manpage reference cannot contain only parentheses: ' .. ref) - end - return '', name - end - local parts = vim.split(ref1, '(', { plain = true }) - -- see ':Man 3X curses' on why tolower. - -- TODO(nhooyr) Not sure if this is portable across OSs - -- but I have not seen a single uppercase section. - local sect = vim.split(parts[2] or '', ')', { plain = true })[1]:lower() - local name = parts[1] - return sect, name + name = ref:match('[^()]+') + if not name then + return nil, nil, ('invalid manpage reference "%s"'):format(ref) + end + return name end --- find_path attempts to find the path to a manpage --- based on the passed section and name. --- --- 1. If manpage could not be found with the given sect and name, --- then try all the sections in b:man_default_sects. --- 2. If it still could not be found, then we try again without a section. --- 3. If still not found but $MANSECT is set, then we try again with $MANSECT --- unset. --- 4. If a path still wasn't found, return nil. ----@param sect string? ----@param name string -function M.find_path(sect, name) +--- Attempts to find the path to a manpage based on the passed section and name. +--- +--- 1. If manpage could not be found with the given sect and name, +--- then try all the sections in b:man_default_sects. +--- 2. If it still could not be found, then we try again without a section. +--- 3. If still not found but $MANSECT is set, then we try again with $MANSECT +--- unset. +--- 4. If a path still wasn't found, return nil. +--- @param name string? +--- @param sect string? +--- @return string? path +function M._find_path(name, sect) if sect and sect ~= '' then - local ret = get_path(sect, name, true) + local ret = get_path(name, sect) if ret then return ret end end if vim.b.man_default_sects ~= nil then - local sects = vim.split(vim.b.man_default_sects, ',', { plain = true, trimempty = true }) - for _, sec in ipairs(sects) do - local ret = get_path(sec, name, true) + for sec in vim.gsplit(vim.b.man_default_sects, ',', { trimempty = true }) do + local ret = get_path(name, sec) if ret then return ret end @@ -364,17 +330,18 @@ function M.find_path(sect, name) end -- if none of the above worked, we will try with no section - local res_empty_sect = get_path('', name, true) - if res_empty_sect then - return res_empty_sect + local ret = get_path(name) + if ret then + return ret end -- if that still didn't work, we will check for $MANSECT and try again with it -- unset if vim.env.MANSECT then + --- @type string local mansect = vim.env.MANSECT vim.env.MANSECT = nil - local res = get_path('', name, true) + local res = get_path(name) vim.env.MANSECT = mansect if res then return res @@ -385,24 +352,27 @@ function M.find_path(sect, name) return nil end -local EXT_RE = vim.regex([[\.\%([glx]z\|bz2\|lzma\|Z\)$]]) - --- Extracts the name/section from the 'path/name.sect', because sometimes the actual section is --- more specific than what we provided to `man` (try `:Man 3 App::CLI`). --- Also on linux, name seems to be case-insensitive. So for `:Man PRIntf`, we --- still want the name of the buffer to be 'printf'. ----@param path string ----@return string, string -local function extract_sect_and_name_path(path) +--- Extracts the name/section from the 'path/name.sect', because sometimes the +--- actual section is more specific than what we provided to `man` +--- (try `:Man 3 App::CLI`). Also on linux, name seems to be case-insensitive. +--- So for `:Man PRIntf`, we still want the name of the buffer to be 'printf'. +--- @param path string +--- @return string name +--- @return string sect +local function parse_path(path) local tail = fn.fnamemodify(path, ':t') - if EXT_RE:match_str(path) then -- valid extensions + if + path:match('%.[glx]z$') + or path:match('%.bz2$') + or path:match('%.lzma$') + or path:match('%.Z$') + then tail = fn.fnamemodify(tail, ':r') end - local name, sect = tail:match('^(.+)%.([^.]+)$') - return sect, name + return tail:match('^(.+)%.([^.]+)$') end ----@return boolean +--- @return boolean local function find_man() if vim.bo.filetype == 'man' then return true @@ -430,22 +400,32 @@ local function set_options() vim.bo.filetype = 'man' end ----@param path string ----@param silent boolean? ----@return string +--- Always use -l if possible. #6683 +--- @type boolean? +local localfile_arg + +--- @param path string +--- @param silent boolean? +--- @return string local function get_page(path, silent) -- Disable hard-wrap by using a big $MANWIDTH (max 1000 on some systems #9065). -- Soft-wrap: ftplugin/man.lua sets wrap/breakindent/…. -- Hard-wrap: driven by `man`. - local manwidth ---@type integer|string + local manwidth --- @type integer|string if (vim.g.man_hardwrap or 1) ~= 1 then manwidth = 999 elseif vim.env.MANWIDTH then - manwidth = vim.env.MANWIDTH + manwidth = vim.env.MANWIDTH --- @type string|integer else manwidth = api.nvim_win_get_width(0) - vim.o.wrapmargin end + if localfile_arg == nil then + local mpath = get_path('man') + -- Check for -l support. + localfile_arg = (mpath and system({ 'man', '-l', mpath }, true) or '') ~= '' + end + local cmd = localfile_arg and { 'man', '-l', path } or { 'man', path } -- Force MANPAGER=cat to ensure Vim is not recursively invoked (by man-db). @@ -458,47 +438,17 @@ local function get_page(path, silent) }) end ----@param lnum integer ----@return string -local function getline(lnum) - ---@diagnostic disable-next-line - return fn.getline(lnum) -end - ----@param page string -local function put_page(page) - vim.bo.modifiable = true - vim.bo.readonly = false - vim.bo.swapfile = false - - api.nvim_buf_set_lines(0, 0, -1, false, vim.split(page, '\n')) - - while getline(1):match('^%s*$') do - api.nvim_buf_set_lines(0, 0, 1, false, {}) - end - -- XXX: nroff justifies text by filling it with whitespace. That interacts - -- badly with our use of $MANWIDTH=999. Hack around this by using a fixed - -- size for those whitespace regions. - -- Use try/catch to avoid setting v:errmsg. - vim.cmd([[ - try - keeppatterns keepjumps %s/\s\{199,}/\=repeat(' ', 10)/g - catch - endtry - ]]) - vim.cmd('1') -- Move cursor to first line - highlight_man_page() - set_options() -end - +--- @param path string +--- @param psect string local function format_candidate(path, psect) - if matchstr(path, [[\.\%(pdf\|in\)$]]) then -- invalid extensions + if vim.endswith(path, '.pdf') or vim.endswith(path, '.in') then + -- invalid extensions return '' end - local sect, name = extract_sect_and_name_path(path) + local name, sect = parse_path(path) if sect == psect then return name - elseif sect and name and matchstr(sect, psect .. '.\\+$') then -- invalid extensions + elseif sect:match(psect .. '.+$') then -- invalid extensions -- We include the section if the user provided section is a prefix -- of the actual section. return ('%s(%s)'):format(name, sect) @@ -506,63 +456,54 @@ local function format_candidate(path, psect) return '' end ----@generic T ----@param list T[] ----@param elem T ----@return T[] -local function move_elem_to_head(list, elem) - ---@diagnostic disable-next-line:no-unknown - local list1 = vim.tbl_filter(function(v) - return v ~= elem - end, list) - return { elem, unpack(list1) } -end - ----@param sect string ----@param name string ----@return string[] -local function get_paths(sect, name) +--- @param name string +--- @param sect? string +--- @return string[] paths +--- @return string? err +local function get_paths(name, sect) -- Try several sources for getting the list man directories: - -- 1. `man -w` (works on most systems) - -- 2. `manpath` + -- 1. `manpath -q` + -- 2. `man -w` (works on most systems) -- 3. $MANPATH - local mandirs_raw = vim.F.npcall(system, { 'man', FIND_ARG }) - or vim.F.npcall(system, { 'manpath', '-q' }) + -- + -- Note we prefer `manpath -q` because `man -w`: + -- - does not work on MacOS 14 and later. + -- - only returns '/usr/bin/man' on MacOS 13 and earlier. + --- @type string? + local mandirs_raw = vim.F.npcall(system, { 'manpath', '-q' }) + or vim.F.npcall(system, { 'man', '-w' }) or vim.env.MANPATH if not mandirs_raw then - man_error("Could not determine man directories from: 'man -w', 'manpath' or $MANPATH") + return {}, "Could not determine man directories from: 'man -w', 'manpath' or $MANPATH" end local mandirs = table.concat(vim.split(mandirs_raw, '[:\n]', { trimempty = true }), ',') - ---@type string[] + + sect = sect or '' + + --- @type string[] local paths = fn.globpath(mandirs, 'man[^\\/]*/' .. name .. '*.' .. sect .. '*', false, true) -- Prioritize the result from find_path as it obeys b:man_default_sects. - local first = M.find_path(sect, name) + local first = M._find_path(name, sect) if first then - paths = move_elem_to_head(paths, first) + --- @param v string + paths = vim.tbl_filter(function(v) + return v ~= first + end, paths) + table.insert(paths, 1, first) end return paths end ----@param sect string ----@param psect string ----@param name string ----@return string[] -local function complete(sect, psect, name) - local pages = get_paths(sect, name) - -- We remove duplicates in case the same manpage in different languages was found. - return fn.uniq(fn.sort(vim.tbl_map(function(v) - return format_candidate(v, psect) - end, pages) or {}, 'i')) -end - --- see extract_sect_and_name_ref on why tolower(sect) ----@param arg_lead string ----@param cmd_line string -function M.man_complete(arg_lead, cmd_line, _) +--- @param arg_lead string +--- @param cmd_line string +--- @return string? sect +--- @return string? psect +--- @return string? name +local function parse_cmdline(arg_lead, cmd_line) local args = vim.split(cmd_line, '%s+', { trimempty = true }) local cmd_offset = fn.index(args, 'Man') if cmd_offset > 0 then @@ -572,13 +513,13 @@ function M.man_complete(arg_lead, cmd_line, _) end if #args > 3 then - return {} + return end if #args == 1 then -- returning full completion is laggy. Require some arg_lead to complete - -- return complete('', '', '') - return {} + -- return '', '', '' + return end if arg_lead:match('^[^()]+%([^()]*$') then @@ -587,18 +528,19 @@ function M.man_complete(arg_lead, cmd_line, _) -- It will offer 'priclass.d(1m)' even though section is specified as 1. local tmp = vim.split(arg_lead, '(', { plain = true }) local name = tmp[1] + -- See extract_sect_and_name_ref on why :lower() local sect = (tmp[2] or ''):lower() - return complete(sect, '', name) + return sect, '', name end if not args[2]:match('^[^()]+$') then -- cursor (|) is at ':Man 3() |' or ':Man (3|' or ':Man 3() pri|' -- or ':Man 3() pri |' - return {} + return end if #args == 2 then - ---@type string, string + --- @type string, string local name, sect if arg_lead == '' then -- cursor (|) is at ':Man 1 |' @@ -614,52 +556,77 @@ function M.man_complete(arg_lead, cmd_line, _) name = arg_lead sect = '' end - return complete(sect, sect, name) + return sect, sect, name end if not arg_lead:match('[^()]+$') then -- cursor (|) is at ':Man 3 printf |' or ':Man 3 (pr)i|' - return {} + return end -- cursor (|) is at ':Man 3 pri|' - local name = arg_lead - local sect = args[2]:lower() - return complete(sect, sect, name) + local name, sect = arg_lead, args[2]:lower() + return sect, sect, name end ----@param pattern string ----@return {name:string,filename:string,cmd:string}[] -function M.goto_tag(pattern, _, _) - local sect, name = extract_sect_and_name_ref(pattern) +--- @param arg_lead string +--- @param cmd_line string +function M.man_complete(arg_lead, cmd_line) + local sect, psect, name = parse_cmdline(arg_lead, cmd_line) + if not (sect and psect and name) then + return {} + end - local paths = get_paths(sect, name) - ---@type {name:string,title:string}[] - local structured = {} + local pages = get_paths(name, sect) - for _, path in ipairs(paths) do - sect, name = extract_sect_and_name_path(path) - if sect and name then - structured[#structured + 1] = { - name = name, - title = name .. '(' .. sect .. ')', - } + -- We check for duplicates in case the same manpage in different languages + -- was found. + local pages_fmt = {} --- @type string[] + local pages_fmt_keys = {} --- @type table<string,true> + for _, v in ipairs(pages) do + local x = format_candidate(v, psect) + local xl = x:lower() -- ignore case when searching avoiding duplicates + if not pages_fmt_keys[xl] then + pages_fmt[#pages_fmt + 1] = x + pages_fmt_keys[xl] = true end end + table.sort(pages_fmt) + + return pages_fmt +end + +--- @param pattern string +--- @return {name:string,filename:string,cmd:string}[] +function M.goto_tag(pattern, _, _) + local name, sect, err = parse_ref(pattern) + if err then + error(err) + end + + local paths, err2 = get_paths(assert(name), sect) + if err2 then + error(err2) + end + + --- @type table[] + local ret = {} - ---@param entry {name:string,title:string} - return vim.tbl_map(function(entry) - return { - name = entry.name, - filename = 'man://' .. entry.title, + for _, path in ipairs(paths) do + local pname, psect = parse_path(path) + ret[#ret + 1] = { + name = pname, + filename = ('man://%s(%s)'):format(pname, psect), cmd = '1', } - end, structured) + end + + return ret end --- Called when Nvim is invoked as $MANPAGER. +--- Called when Nvim is invoked as $MANPAGER. function M.init_pager() - if getline(1):match('^%s*$') then + if fn.getline(1):match('^%s*$') then api.nvim_buf_set_lines(0, 0, 1, false, {}) else vim.cmd('keepjumps 1') @@ -667,9 +634,10 @@ function M.init_pager() highlight_man_page() -- Guess the ref from the heading (which is usually uppercase, so we cannot -- know the correct casing, cf. `man glDrawArraysInstanced`). - local ref = fn.substitute(matchstr(getline(1), [[^[^)]\+)]]) or '', ' ', '_', 'g') - local ok, res = pcall(extract_sect_and_name_ref, ref) - vim.b.man_sect = ok and res or '' + --- @type string + local ref = (fn.getline(1):match('^[^)]+%)') or ''):gsub(' ', '_') + local _, sect, err = pcall(parse_ref, ref) + vim.b.man_sect = err ~= nil and sect or '' if not fn.bufname('%'):match('man://') then -- Avoid duplicate buffers, E95. vim.cmd.file({ 'man://' .. fn.fnameescape(ref):lower(), mods = { silent = true } }) @@ -678,50 +646,64 @@ function M.init_pager() set_options() end ----@param count integer ----@param args string[] +--- Combine the name and sect into a manpage reference so that all +--- verification/extraction can be kept in a single function. +--- @param args string[] +--- @return string? ref +local function ref_from_args(args) + if #args <= 1 then + return args[1] + elseif args[1]:match('^%d$') or args[1]:match('^%d%a') or args[1]:match('^%a$') then + -- NB: Valid sections are not only digits, but also: + -- - <digit><word> (see POSIX mans), + -- - and even <letter> and <word> (see, for example, by tcl/tk) + -- NB2: don't optimize to :match("^%d"), as it will match manpages like + -- 441toppm and others whose name starts with digit + local sect = args[1] + table.remove(args, 1) + local name = table.concat(args, ' ') + return ('%s(%s)'):format(name, sect) + end + + return table.concat(args, ' ') +end + +--- @param count integer +--- @param args string[] +--- @return string? err function M.open_page(count, smods, args) - local ref ---@type string - if #args == 0 then + local ref = ref_from_args(args) + if not ref then ref = vim.bo.filetype == 'man' and fn.expand('<cWORD>') or fn.expand('<cword>') if ref == '' then - man_error('no identifier under cursor') - end - elseif #args == 1 then - ref = args[1] - else - -- Combine the name and sect into a manpage reference so that all - -- verification/extraction can be kept in a single function. - if args[1]:match('^%d$') or args[1]:match('^%d%a') or args[1]:match('^%a$') then - -- NB: Valid sections are not only digits, but also: - -- - <digit><word> (see POSIX mans), - -- - and even <letter> and <word> (see, for example, by tcl/tk) - -- NB2: don't optimize to :match("^%d"), as it will match manpages like - -- 441toppm and others whose name starts with digit - local sect = args[1] - table.remove(args, 1) - local name = table.concat(args, ' ') - ref = ('%s(%s)'):format(name, sect) - else - ref = table.concat(args, ' ') + return 'no identifier under cursor' end end - local sect, name = extract_sect_and_name_ref(ref) + local name, sect, err = parse_ref(ref) + if err then + return err + end + assert(name) + if count >= 0 then sect = tostring(count) end -- Try both spaces and underscores, use the first that exists. - local path = M.find_path(sect, name) - if path == nil then - path = M.find_path(sect, spaces_to_underscores(name)) - if path == nil then - man_error('no manual entry for ' .. name) + local path = M._find_path(name, sect) + if not path then + --- Replace spaces in a man page name with underscores + --- intended for PostgreSQL, which has man pages like 'CREATE_TABLE(7)'; + --- while editing SQL source code, it's nice to visually select 'CREATE TABLE' + --- and hit 'K', which requires this transformation + path = M._find_path(name:gsub('%s', '_'), sect) + if not path then + return 'no manual entry for ' .. name end end - sect, name = extract_sect_and_name_path(path) + name, sect = parse_path(path) local buf = api.nvim_get_current_buf() local save_tfu = vim.bo[buf].tagfunc vim.bo[buf].tagfunc = "v:lua.require'man'.goto_tag" @@ -744,24 +726,51 @@ function M.open_page(count, smods, args) if not ok then error(ret) - else - set_options() end + set_options() vim.b.man_sect = sect end --- Called when a man:// buffer is opened. +--- Called when a man:// buffer is opened. +--- @return string? err function M.read_page(ref) - local sect, name = extract_sect_and_name_ref(ref) - local path = M.find_path(sect, name) - if path == nil then - man_error('no manual entry for ' .. name) + local name, sect, err = parse_ref(ref) + if err then + return err + end + + local path = M._find_path(name, sect) + if not path then + return 'no manual entry for ' .. name end - sect = extract_sect_and_name_path(path) + + local _, sect1 = parse_path(path) local page = get_page(path) - vim.b.man_sect = sect - put_page(page) + + vim.b.man_sect = sect1 + vim.bo.modifiable = true + vim.bo.readonly = false + vim.bo.swapfile = false + + api.nvim_buf_set_lines(0, 0, -1, false, vim.split(page, '\n')) + + while fn.getline(1):match('^%s*$') do + api.nvim_buf_set_lines(0, 0, 1, false, {}) + end + -- XXX: nroff justifies text by filling it with whitespace. That interacts + -- badly with our use of $MANWIDTH=999. Hack around this by using a fixed + -- size for those whitespace regions. + -- Use try/catch to avoid setting v:errmsg. + vim.cmd([[ + try + keeppatterns keepjumps %s/\s\{199,}/\=repeat(' ', 10)/g + catch + endtry + ]]) + vim.cmd('1') -- Move cursor to first line + highlight_man_page() + set_options() end function M.show_toc() @@ -773,29 +782,18 @@ function M.show_toc() return end - ---@type {bufnr:integer, lnum:integer, text:string}[] + --- @type {bufnr:integer, lnum:integer, text:string}[] local toc = {} local lnum = 2 local last_line = fn.line('$') - 1 - local section_title_re = vim.regex([[^\%( \{3\}\)\=\S.*$]]) - local flag_title_re = vim.regex([[^\s\+\%(+\|-\)\S\+]]) while lnum and lnum < last_line do - local text = getline(lnum) - if section_title_re:match_str(text) then - -- if text is a section title + local text = fn.getline(lnum) + if text:match('^%s+[-+]%S') or text:match('^ %S') or text:match('^%S') then toc[#toc + 1] = { bufnr = bufnr, lnum = lnum, - text = text, - } - elseif flag_title_re:match_str(text) then - -- if text is a flag title. we strip whitespaces and prepend two - -- spaces to have a consistent format in the loclist. - toc[#toc + 1] = { - bufnr = bufnr, - lnum = lnum, - text = ' ' .. fn.substitute(text, [[^\s*\(.\{-}\)\s*$]], [[\1]], ''), + text = text:gsub('^%s+', ''):gsub('%s+$', ''), } end lnum = fn.nextnonblank(lnum + 1) @@ -807,19 +805,4 @@ function M.show_toc() vim.w.qf_toc = bufname end -local function init() - local path = get_path('', 'man', true) - local page ---@type string? - if path ~= nil then - -- Check for -l support. - page = get_page(path, true) - end - - if page == '' or page == nil then - localfile_arg = false - end -end - -init() - return M diff --git a/runtime/lua/tohtml.lua b/runtime/lua/tohtml.lua index ed42b28725..4415a8cdca 100644 --- a/runtime/lua/tohtml.lua +++ b/runtime/lua/tohtml.lua @@ -317,7 +317,7 @@ end --- @return nil|integer local function register_hl(state, hl) if type(hl) == 'table' then - hl = hl[#hl] + hl = hl[#hl] --- @type string|integer end if type(hl) == 'nil' then return @@ -492,7 +492,7 @@ local function _styletable_extmarks_highlight(state, extmark, namespaces) end ---TODO(altermo) LSP semantic tokens (and some other extmarks) are only ---generated in visible lines, and not in the whole buffer. - if (namespaces[extmark[4].ns_id] or ''):find('vim_lsp_semantic_tokens') then + if (namespaces[extmark[4].ns_id] or ''):find('nvim.lsp.semantic_tokens') then notify('lsp semantic tokens are not supported, HTML may be incorrect') return end @@ -514,7 +514,7 @@ local function _styletable_extmarks_virt_text(state, extmark, namespaces) end ---TODO(altermo) LSP semantic tokens (and some other extmarks) are only ---generated in visible lines, and not in the whole buffer. - if (namespaces[extmark[4].ns_id] or ''):find('vim_lsp_inlayhint') then + if (namespaces[extmark[4].ns_id] or ''):find('nvim.lsp.inlayhint') then notify('lsp inlay hints are not supported, HTML may be incorrect') return end @@ -1162,7 +1162,9 @@ local function extend_pre(out, state) s = s .. _pre_text_to_html(state, row) end local true_line_len = #line + 1 - for k in pairs(style_line) do + for k in + pairs(style_line --[[@as table<string,any>]]) + do if type(k) == 'number' and k > true_line_len then true_line_len = k end diff --git a/runtime/lua/vim/_defaults.lua b/runtime/lua/vim/_defaults.lua index 06f6ed6829..69204e3fe6 100644 --- a/runtime/lua/vim/_defaults.lua +++ b/runtime/lua/vim/_defaults.lua @@ -49,10 +49,10 @@ do vim.keymap.set('x', '*', function() return _visual_search('/') - end, { desc = ':help v_star-default', expr = true, silent = true }) + end, { desc = ':help v_star-default', expr = true, replace_keycodes = false }) vim.keymap.set('x', '#', function() return _visual_search('?') - end, { desc = ':help v_#-default', expr = true, silent = true }) + end, { desc = ':help v_#-default', expr = true, replace_keycodes = false }) end --- Map Y to y$. This mimics the behavior of D and C. See |Y-default| @@ -222,9 +222,9 @@ do --- Execute a command and print errors without a stacktrace. --- @param opts table Arguments to |nvim_cmd()| local function cmd(opts) - local _, err = pcall(vim.api.nvim_cmd, opts, {}) - if err then - vim.api.nvim_err_writeln(err:sub(#'Vim:' + 1)) + local ok, err = pcall(vim.api.nvim_cmd, opts, {}) + if not ok then + vim.api.nvim_echo({ { err:sub(#'Vim:' + 1) } }, true, { err = true }) end end @@ -412,7 +412,7 @@ do end end - local nvim_popupmenu_augroup = vim.api.nvim_create_augroup('nvim_popupmenu', {}) + local nvim_popupmenu_augroup = vim.api.nvim_create_augroup('nvim.popupmenu', {}) vim.api.nvim_create_autocmd('MenuPopup', { pattern = '*', group = nvim_popupmenu_augroup, @@ -429,13 +429,13 @@ end --- Default autocommands. See |default-autocmds| do - local nvim_terminal_augroup = vim.api.nvim_create_augroup('nvim_terminal', {}) + local nvim_terminal_augroup = vim.api.nvim_create_augroup('nvim.terminal', {}) vim.api.nvim_create_autocmd('BufReadCmd', { pattern = 'term://*', group = nvim_terminal_augroup, desc = 'Treat term:// buffers as terminal buffers', nested = true, - command = "if !exists('b:term_title')|call termopen(matchstr(expand(\"<amatch>\"), '\\c\\mterm://\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), {'cwd': expand(get(matchlist(expand(\"<amatch>\"), '\\c\\mterm://\\(.\\{-}\\)//'), 1, ''))})", + command = "if !exists('b:term_title')|call jobstart(matchstr(expand(\"<amatch>\"), '\\c\\mterm://\\%(.\\{-}//\\%(\\d\\+:\\)\\?\\)\\?\\zs.*'), {'term': v:true, 'cwd': expand(get(matchlist(expand(\"<amatch>\"), '\\c\\mterm://\\(.\\{-}\\)//'), 1, ''))})", }) vim.api.nvim_create_autocmd({ 'TermClose' }, { @@ -492,6 +492,10 @@ do vim.bo.textwidth = 0 vim.wo[0][0].wrap = false vim.wo[0][0].list = false + vim.wo[0][0].number = false + vim.wo[0][0].relativenumber = false + vim.wo[0][0].signcolumn = 'no' + vim.wo[0][0].foldcolumn = '0' -- This is gross. Proper list options support when? local winhl = vim.o.winhighlight @@ -505,14 +509,14 @@ do vim.api.nvim_create_autocmd('CmdwinEnter', { pattern = '[:>]', desc = 'Limit syntax sync to maxlines=1 in the command window', - group = vim.api.nvim_create_augroup('nvim_cmdwin', {}), + group = vim.api.nvim_create_augroup('nvim.cmdwin', {}), command = 'syntax sync minlines=1 maxlines=1', }) vim.api.nvim_create_autocmd('SwapExists', { pattern = '*', desc = 'Skip the swapfile prompt when the swapfile is owned by a running Nvim process', - group = vim.api.nvim_create_augroup('nvim_swapfile', {}), + group = vim.api.nvim_create_augroup('nvim.swapfile', {}), callback = function() local info = vim.fn.swapinfo(vim.v.swapname) local user = vim.uv.os_get_passwd().username @@ -539,15 +543,16 @@ do end if tty then - local group = vim.api.nvim_create_augroup('nvim_tty', {}) + local group = vim.api.nvim_create_augroup('nvim.tty', {}) --- Set an option after startup (so that OptionSet is fired), but only if not --- already set by the user. --- --- @param option string Option name --- @param value any Option value - local function setoption(option, value) - if vim.api.nvim_get_option_info2(option, {}).was_set then + --- @param force boolean? Always set the value, even if already set + local function setoption(option, value, force) + if not force and vim.api.nvim_get_option_info2(option, {}).was_set then -- Don't do anything if option is already set return end @@ -563,7 +568,7 @@ do once = true, nested = true, callback = function() - setoption(option, value) + setoption(option, value, force) end, }) end @@ -645,11 +650,15 @@ do return nil, nil, nil end - local timer = assert(vim.uv.new_timer()) - + -- This autocommand updates the value of 'background' anytime we receive + -- an OSC 11 response from the terminal emulator. If the user has set + -- 'background' explicitly then we will delete this autocommand, + -- effectively disabling automatic background setting. + local force = false local id = vim.api.nvim_create_autocmd('TermResponse', { group = group, nested = true, + desc = "Update the value of 'background' automatically based on the terminal emulator's background color", callback = function(args) local resp = args.data ---@type string local r, g, b = parseosc11(resp) @@ -661,27 +670,33 @@ do if rr and gg and bb then local luminance = (0.299 * rr) + (0.587 * gg) + (0.114 * bb) local bg = luminance < 0.5 and 'dark' or 'light' - setoption('background', bg) + setoption('background', bg, force) + + -- On the first query response, don't force setting the option in + -- case the user has already set it manually. If they have, then + -- this autocommand will be deleted. If they haven't, then we do + -- want to force setting the option to override the value set by + -- this autocommand. + if not force then + force = true + end end + end + end, + }) - return true + vim.api.nvim_create_autocmd('VimEnter', { + group = group, + nested = true, + once = true, + callback = function() + if vim.api.nvim_get_option_info2('background', {}).was_set then + vim.api.nvim_del_autocmd(id) end end, }) io.stdout:write('\027]11;?\007') - - timer:start(1000, 0, function() - -- Delete the autocommand if no response was received - vim.schedule(function() - -- Suppress error if autocommand has already been deleted - pcall(vim.api.nvim_del_autocmd, id) - end) - - if not timer:is_closing() then - timer:close() - end - end) end --- If the TUI (term_has_truecolor) was able to determine that the host diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 44f17b3f85..a77ea9bb91 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -58,6 +58,7 @@ vim._extra = { --- @private vim.log = { + --- @enum vim.log.levels levels = { TRACE = 0, DEBUG = 1, @@ -92,7 +93,7 @@ local utfs = { --- --- -- Runs synchronously: --- local obj = vim.system({'echo', 'hello'}, { text = true }):wait() ---- -- { code = 0, signal = 0, stdout = 'hello', stderr = '' } +--- -- { code = 0, signal = 0, stdout = 'hello\n', stderr = '' } --- --- ``` --- @@ -390,7 +391,7 @@ end local VIM_CMD_ARG_MAX = 20 ---- Executes Vim script commands. +--- Executes Vimscript (|Ex-commands|). --- --- Note that `vim.cmd` can be indexed with a command name to return a callable function to the --- command. @@ -424,8 +425,9 @@ local VIM_CMD_ARG_MAX = 20 --- vim.cmd.colorscheme('blue') --- ``` --- +---@diagnostic disable-next-line: undefined-doc-param ---@param command string|table Command(s) to execute. ---- If a string, executes multiple lines of Vim script at once. In this +--- If a string, executes multiple lines of Vimscript at once. In this --- case, it is an alias to |nvim_exec2()|, where `opts.output` is set --- to false. Thus it works identical to |:source|. --- If a table, executes a single command. In this case, it is an alias @@ -440,10 +442,12 @@ vim.cmd = setmetatable({}, { return '' end end, + --- @param t table<string,function> __index = function(t, command) t[command] = function(...) - local opts + local opts --- @type vim.api.keyset.cmd if select('#', ...) == 1 and type(select(1, ...)) == 'table' then + --- @type vim.api.keyset.cmd opts = select(1, ...) -- Move indexed positions in opts to opt.args @@ -454,6 +458,7 @@ vim.cmd = setmetatable({}, { break end opts.args[i] = opts[i] + --- @diagnostic disable-next-line: no-unknown opts[i] = nil end end @@ -528,7 +533,7 @@ function vim.region(bufnr, pos1, pos2, regtype, inclusive) end if pos1[1] > pos2[1] or (pos1[1] == pos2[1] and pos1[2] > pos2[2]) then - pos1, pos2 = pos2, pos1 + pos1, pos2 = pos2, pos1 --- @type [integer, integer], [integer, integer] end -- getpos() may return {0,0,0,0} @@ -620,13 +625,8 @@ end ---@param opts table|nil Optional parameters. Unused by default. ---@diagnostic disable-next-line: unused-local function vim.notify(msg, level, opts) -- luacheck: no unused args - if level == vim.log.levels.ERROR then - vim.api.nvim_err_writeln(msg) - elseif level == vim.log.levels.WARN then - vim.api.nvim_echo({ { msg, 'WarningMsg' } }, true, {}) - else - vim.api.nvim_echo({ { msg } }, true, {}) - end + local chunks = { { msg, level == vim.log.levels.WARN and 'WarningMsg' or nil } } + vim.api.nvim_echo(chunks, true, { err = level == vim.log.levels.ERROR }) end do @@ -705,6 +705,7 @@ function vim._on_key(buf, typed_buf) local discard = false for k, v in pairs(on_key_cbs) do local fn = v[1] + --- @type boolean, any local ok, rv = xpcall(function() return fn(buf, typed_buf) end, debug.traceback) @@ -832,6 +833,7 @@ function vim.str_utfindex(s, encoding, index, strict_indexing) -- Return (multiple): ~ -- (`integer`) UTF-32 index -- (`integer`) UTF-16 index + --- @diagnostic disable-next-line: redundant-return-value return col32, col16 end @@ -1004,7 +1006,7 @@ function vim._expand_pat(pat, env) or vim.v == final_env and { 'v:', 'var' } or { nil, nil } ) - assert(prefix, "Can't resolve final_env") + assert(prefix and type, "Can't resolve final_env") local vars = vim.fn.getcompletion(prefix .. match_part, type) --- @type string[] insert_keys(vim .iter(vars) diff --git a/runtime/lua/vim/_inspector.lua b/runtime/lua/vim/_inspector.lua index fccf4b8dbe..35063dffca 100644 --- a/runtime/lua/vim/_inspector.lua +++ b/runtime/lua/vim/_inspector.lua @@ -1,3 +1,5 @@ +--- @diagnostic disable:no-unknown + --- @class vim._inspector.Filter --- @inlinedoc --- @@ -53,7 +55,7 @@ function vim.inspect_pos(bufnr, row, col, filter) local cursor = vim.api.nvim_win_get_cursor(win) row, col = cursor[1] - 1, cursor[2] end - bufnr = bufnr == 0 and vim.api.nvim_get_current_buf() or bufnr + bufnr = vim._resolve_bufnr(bufnr) local results = { treesitter = {}, --- @type table[] @@ -78,6 +80,7 @@ function vim.inspect_pos(bufnr, row, col, filter) -- treesitter if filter.treesitter then for _, capture in pairs(vim.treesitter.get_captures_at_pos(bufnr, row, col)) do + --- @diagnostic disable-next-line: inject-field capture.hl_group = '@' .. capture.capture .. '.' .. capture.lang results.treesitter[#results.treesitter + 1] = resolve_hl(capture) end @@ -128,13 +131,13 @@ function vim.inspect_pos(bufnr, row, col, filter) if filter.semantic_tokens then results.semantic_tokens = vim.tbl_filter(function(extmark) - return extmark.ns:find('vim_lsp_semantic_tokens') == 1 + return extmark.ns:find('nvim.lsp.semantic_tokens') == 1 end, extmarks) end if filter.extmarks then results.extmarks = vim.tbl_filter(function(extmark) - return extmark.ns:find('vim_lsp_semantic_tokens') ~= 1 + return extmark.ns:find('nvim.lsp.semantic_tokens') ~= 1 and (filter.extmarks == 'all' or extmark.opts.hl_group) end, extmarks) end @@ -146,6 +149,13 @@ end --- ---Can also be shown with `:Inspect`. [:Inspect]() --- +---Example: To bind this function to the vim-scriptease +---inspired `zS` in Normal mode: +--- +---```lua +---vim.keymap.set('n', 'zS', vim.show_pos) +---``` +--- ---@since 11 ---@param bufnr? integer defaults to the current buffer ---@param row? integer row to inspect, 0-based. Defaults to the row of the current cursor @@ -171,7 +181,7 @@ function vim.show_pos(bufnr, row, col, filter) if data.hl_group ~= data.hl_group_link then append('links to ', 'MoreMsg') append(data.hl_group_link, data.hl_group_link) - append(' ') + append(' ') end if comment then append(comment, 'Comment') @@ -184,7 +194,14 @@ function vim.show_pos(bufnr, row, col, filter) append('Treesitter', 'Title') nl() for _, capture in ipairs(items.treesitter) do - item(capture, capture.lang) + item( + capture, + string.format( + 'priority: %d language: %s', + capture.metadata.priority or vim.hl.priorities.treesitter, + capture.lang + ) + ) end nl() end diff --git a/runtime/lua/vim/_meta/api.lua b/runtime/lua/vim/_meta/api.lua index 3c9b9d4f44..3d10729d23 100644 --- a/runtime/lua/vim/_meta/api.lua +++ b/runtime/lua/vim/_meta/api.lua @@ -3,6 +3,10 @@ -- DO NOT EDIT error('Cannot require a meta file') +--- This file embeds vimdoc as the function descriptions +--- so ignore any doc related errors. +--- @diagnostic disable: undefined-doc-name,luadoc-miss-symbol + vim.api = {} --- @private @@ -163,35 +167,14 @@ function vim.api.nvim__stats() end --- @return any function vim.api.nvim__unpack(str) end ---- Adds a highlight to buffer. ---- ---- Useful for plugins that dynamically generate highlights to a buffer ---- (like a semantic highlighter or linter). The function adds a single ---- highlight to a buffer. Unlike `matchaddpos()` highlights follow changes to ---- line numbering (as lines are inserted/removed above the highlighted line), ---- like signs and marks do. ---- ---- Namespaces are used for batch deletion/updating of a set of highlights. To ---- create a namespace, use `nvim_create_namespace()` which returns a namespace ---- id. Pass it in to this function as `ns_id` to add highlights to the ---- namespace. All highlights in the same namespace can then be cleared with ---- single call to `nvim_buf_clear_namespace()`. If the highlight never will be ---- deleted by an API call, pass `ns_id = -1`. ---- ---- As a shorthand, `ns_id = 0` can be used to create a new namespace for the ---- highlight, the allocated id is then returned. If `hl_group` is the empty ---- string no highlight is added, but a new `ns_id` is still returned. This is ---- supported for backwards compatibility, new code should use ---- `nvim_create_namespace()` to create a new empty namespace. ---- ---- @param buffer integer Buffer handle, or 0 for current buffer ---- @param ns_id integer namespace to use or -1 for ungrouped highlight ---- @param hl_group string Name of the highlight group to use ---- @param line integer Line to highlight (zero-indexed) ---- @param col_start integer Start of (byte-indexed) column range to highlight ---- @param col_end integer End of (byte-indexed) column range to highlight, ---- or -1 to highlight to end of line ---- @return integer # The ns_id that was used +--- @deprecated +--- @param buffer integer +--- @param ns_id integer +--- @param hl_group string +--- @param line integer +--- @param col_start integer +--- @param col_end integer +--- @return integer function vim.api.nvim_buf_add_highlight(buffer, ns_id, hl_group, line, col_start, col_end) end --- Activates buffer-update events on a channel, or as Lua callbacks. @@ -272,12 +255,12 @@ function vim.api.nvim_buf_attach(buffer, send_buffer, opts) end --- This temporarily switches current buffer to "buffer". --- If the current window already shows "buffer", the window is not switched. --- If a window inside the current tabpage (including a float) already shows the ---- buffer, then one of these windows will be set as current window temporarily. +--- buffer, then one of those windows will be set as current window temporarily. --- Otherwise a temporary scratch window (called the "autocmd window" for --- historical reasons) will be used. --- --- This is useful e.g. to call Vimscript functions that only work with the ---- current buffer/window currently, like `termopen()`. +--- current buffer/window currently, like `jobstart(…, {'term': v:true})`. --- --- @param buffer integer Buffer handle, or 0 for current buffer --- @param fun function Function to call inside the buffer (currently Lua callable @@ -452,7 +435,7 @@ function vim.api.nvim_buf_get_extmarks(buffer, ns_id, start, end_, opts) end --- --- @param buffer integer Buffer handle, or 0 for current buffer --- @param mode string Mode short-name ("n", "i", "v", ...) ---- @return vim.api.keyset.keymap[] # Array of |maparg()|-like dictionaries describing mappings. +--- @return vim.api.keyset.get_keymap[] # Array of |maparg()|-like dictionaries describing mappings. --- The "buffer" key holds the associated buffer handle. function vim.api.nvim_buf_get_keymap(buffer, mode) end @@ -595,6 +578,9 @@ function vim.api.nvim_buf_line_count(buffer) end --- - hl_group : highlight group used for the text range. This and below --- highlight groups can be supplied either as a string or as an integer, --- the latter of which can be obtained using `nvim_get_hl_id_by_name()`. +--- +--- Multiple highlight groups can be stacked by passing an array (highest +--- priority last). --- - hl_eol : when true, for a multiline highlight covering the --- EOL of a line, continue the highlight for the rest --- of the screen line (just like for diff and @@ -607,6 +593,15 @@ function vim.api.nvim_buf_line_count(buffer) end --- (highest priority last). --- - virt_text_pos : position of virtual text. Possible values: --- - "eol": right after eol character (default). +--- - "eol_right_align": display right aligned in the window +--- unless the virtual text is longer than +--- the space available. If the virtual +--- text is too long, it is truncated to +--- fit in the window after the EOL +--- character. If the line is wrapped, the +--- virtual text is shown after the end of +--- the line rather than the previous +--- screen line. --- - "overlay": display over the specified column, without --- shifting the underlying text. --- - "right_align": display right aligned in the window. @@ -885,10 +880,8 @@ function vim.api.nvim_cmd(cmd, opts) end --- --- On execution error: fails with Vimscript error, updates v:errmsg. --- ---- Prefer using `nvim_cmd()` or `nvim_exec2()` over this. To evaluate multiple lines of Vim script ---- or an Ex command directly, use `nvim_exec2()`. To construct an Ex command using a structured ---- format and then execute it, use `nvim_cmd()`. To modify an Ex command before evaluating it, use ---- `nvim_parse_cmd()` in conjunction with `nvim_cmd()`. +--- Prefer `nvim_cmd()` or `nvim_exec2()` instead. To modify an Ex command in a structured way +--- before executing it, modify the result of `nvim_parse_cmd()` then pass it to `nvim_cmd()`. --- --- @param command string Ex command string function vim.api.nvim_command(command) end @@ -963,9 +956,9 @@ function vim.api.nvim_create_augroup(name, opts) end --- - id: (number) autocommand id --- - event: (string) name of the triggered event `autocmd-events` --- - group: (number|nil) autocommand group id, if any ---- - match: (string) expanded value of [<amatch>] ---- - buf: (number) expanded value of [<abuf>] ---- - file: (string) expanded value of [<afile>] +--- - file: (string) [<afile>] (not expanded to a full path) +--- - match: (string) [<amatch>] (expanded to a full path) +--- - buf: (number) [<abuf>] --- - data: (any) arbitrary data passed from [nvim_exec_autocmds()] [event-data]() --- - command (string) optional: Vim command to execute on event. Cannot be used with --- {callback} @@ -989,7 +982,7 @@ function vim.api.nvim_create_buf(listed, scratch) end --- Creates a new namespace or gets an existing one. [namespace]() --- --- Namespaces are used for buffer highlights and virtual text, see ---- `nvim_buf_add_highlight()` and `nvim_buf_set_extmark()`. +--- `nvim_buf_set_extmark()`. --- --- Namespaces can be named or anonymous. If `name` matches an existing --- namespace, the associated id is returned. If `name` is an empty string @@ -1012,7 +1005,7 @@ function vim.api.nvim_create_namespace(name) end --- ``` --- --- @param name string Name of the new user command. Must begin with an uppercase letter. ---- @param command any Replacement command to execute when this user command is executed. When called +--- @param command string|fun(args: vim.api.keyset.create_user_command.command_args) Replacement command to execute when this user command is executed. When called --- from Lua, the command can also be a Lua function. The function is called with a --- single table argument that contains the following keys: --- - name: (string) Command name @@ -1099,29 +1092,28 @@ function vim.api.nvim_del_user_command(name) end --- @param name string Variable name function vim.api.nvim_del_var(name) end ---- Echo a message. +--- Prints a message given by a list of `[text, hl_group]` "chunks". +--- +--- Example: +--- ```lua +--- vim.api.nvim_echo({ { 'chunk1-line1\nchunk1-line2\n' }, { 'chunk2-line1' } }, true, {}) +--- ``` --- ---- @param chunks any[] A list of `[text, hl_group]` arrays, each representing a ---- text chunk with specified highlight group name or ID. ---- `hl_group` element can be omitted for no highlight. +--- @param chunks any[] List of `[text, hl_group]` pairs, where each is a `text` string highlighted by +--- the (optional) name or ID `hl_group`. --- @param history boolean if true, add to `message-history`. --- @param opts vim.api.keyset.echo_opts Optional parameters. ---- - verbose: Message is printed as a result of 'verbose' option. ---- If Nvim was invoked with -V3log_file, the message will be ---- redirected to the log_file and suppressed from direct output. +--- - err: Treat the message like `:echoerr`. Sets `hl_group` to `hl-ErrorMsg` by default. +--- - verbose: Message is controlled by the 'verbose' option. Nvim invoked with `-V3log` +--- will write the message to the "log" file instead of standard output. function vim.api.nvim_echo(chunks, history, opts) end ---- Writes a message to the Vim error buffer. Does not append "\n", the ---- message is buffered (won't display) until a linefeed is written. ---- ---- @param str string Message +--- @deprecated +--- @param str string function vim.api.nvim_err_write(str) end ---- Writes a message to the Vim error buffer. Appends "\n", so the buffer is ---- flushed (and displayed). ---- ---- @see vim.api.nvim_err_write ---- @param str string Message +--- @deprecated +--- @param str string function vim.api.nvim_err_writeln(str) end --- Evaluates a Vimscript `expression`. Dicts and Lists are recursively expanded. @@ -1152,7 +1144,9 @@ function vim.api.nvim_eval(expr) end --- the "highlights" key in {opts} is true. Each element of the array is a --- |Dict| with these keys: --- - start: (number) Byte index (0-based) of first character that uses the highlight. ---- - group: (string) Name of highlight group. +--- - group: (string) Name of highlight group. May be removed in the future, use +--- `groups` instead. +--- - groups: (array) Names of stacked highlight groups (highest priority last). function vim.api.nvim_eval_statusline(str, opts) end --- @deprecated @@ -1256,31 +1250,34 @@ function vim.api.nvim_get_all_options_info() end --- match any combination of them. --- --- @param opts vim.api.keyset.get_autocmds Dict with at least one of the following: ---- - group (string|integer): the autocommand group name or id to match against. ---- - event (string|array): event or events to match against `autocmd-events`. ---- - pattern (string|array): pattern or patterns to match against `autocmd-pattern`. ---- Cannot be used with {buffer} ---- - buffer: Buffer number or list of buffer numbers for buffer local autocommands +--- - buffer: (integer) Buffer number or list of buffer numbers for buffer local autocommands --- `autocmd-buflocal`. Cannot be used with {pattern} +--- - event: (string|table) event or events to match against `autocmd-events`. +--- - id: (integer) Autocommand ID to match. +--- - group: (string|table) the autocommand group name or id to match against. +--- - pattern: (string|table) pattern or patterns to match against `autocmd-pattern`. +--- Cannot be used with {buffer} --- @return vim.api.keyset.get_autocmds.ret[] # Array of autocommands matching the criteria, with each item --- containing the following fields: ---- - id (number): the autocommand id (only when defined with the API). ---- - group (integer): the autocommand group id. ---- - group_name (string): the autocommand group name. ---- - desc (string): the autocommand description. ---- - event (string): the autocommand event. ---- - command (string): the autocommand command. Note: this will be empty if a callback is set. ---- - callback (function|string|nil): Lua function or name of a Vim script function +--- - buffer: (integer) the buffer number. +--- - buflocal: (boolean) true if the autocommand is buffer local. +--- - command: (string) the autocommand command. Note: this will be empty if a callback is set. +--- - callback: (function|string|nil): Lua function or name of a Vim script function --- which is executed when this autocommand is triggered. ---- - once (boolean): whether the autocommand is only run once. ---- - pattern (string): the autocommand pattern. +--- - desc: (string) the autocommand description. +--- - event: (string) the autocommand event. +--- - id: (integer) the autocommand id (only when defined with the API). +--- - group: (integer) the autocommand group id. +--- - group_name: (string) the autocommand group name. +--- - once: (boolean) whether the autocommand is only run once. +--- - pattern: (string) the autocommand pattern. --- If the autocommand is buffer local |autocmd-buffer-local|: ---- - buflocal (boolean): true if the autocommand is buffer local. ---- - buffer (number): the buffer number. function vim.api.nvim_get_autocmds(opts) end --- Gets information about a channel. --- +--- See `nvim_list_uis()` for an example of how to get channel info. +--- --- @param chan integer channel_id, or 0 for current channel --- @return table<string,any> # Channel info dict with these keys: --- - "id" Channel id. @@ -1298,8 +1295,8 @@ function vim.api.nvim_get_autocmds(opts) end --- "/dev/pts/1". If unknown, the key will still be present if a pty is used (e.g. --- for conpty on Windows). --- - "buffer" (optional) Buffer connected to |terminal| instance. ---- - "client" (optional) Info about the peer (client on the other end of the RPC channel), ---- which it provided via |nvim_set_client_info()|. +--- - "client" (optional) Info about the peer (client on the other end of the channel), as set +--- by |nvim_set_client_info()|. --- function vim.api.nvim_get_chan_info(chan) end @@ -1416,7 +1413,7 @@ function vim.api.nvim_get_hl_ns(opts) end --- Gets a list of global (non-buffer-local) `mapping` definitions. --- --- @param mode string Mode short-name ("n", "i", "v", ...) ---- @return vim.api.keyset.keymap[] # Array of |maparg()|-like dictionaries describing mappings. +--- @return vim.api.keyset.get_keymap[] # Array of |maparg()|-like dictionaries describing mappings. --- The "buffer" key is always zero. function vim.api.nvim_get_keymap(mode) end @@ -1621,6 +1618,14 @@ function vim.api.nvim_list_tabpages() end --- Gets a list of dictionaries representing attached UIs. --- +--- Example: The Nvim builtin `TUI` sets its channel info as described in `startup-tui`. In +--- particular, it sets `client.name` to "nvim-tui". So you can check if the TUI is running by +--- inspecting the client name of each UI: +--- +--- ```lua +--- vim.print(vim.api.nvim_get_chan_info(vim.api.nvim_list_uis()[1].chan).client.name) +--- ``` +--- --- @return any[] # Array of UI dictionaries, each with these keys: --- - "height" Requested height of the UI --- - "width" Requested width of the UI @@ -1640,21 +1645,17 @@ function vim.api.nvim_list_wins() end --- @return any function vim.api.nvim_load_context(dict) end ---- Notify the user with a message ---- ---- Relays the call to vim.notify . By default forwards your message in the ---- echo area but can be overridden to trigger desktop notifications. ---- ---- @param msg string Message to display to the user ---- @param log_level integer The log level ---- @param opts table<string,any> Reserved for future use. +--- @deprecated +--- @param msg string +--- @param log_level integer +--- @param opts table<string,any> --- @return any function vim.api.nvim_notify(msg, log_level, opts) end --- Open a terminal instance in a buffer --- --- By default (and currently the only option) the terminal will not be ---- connected to an external process. Instead, input send on the channel +--- connected to an external process. Instead, input sent on the channel --- will be echoed directly by the terminal. This is useful to display --- ANSI terminal sequences returned as part of a rpc message, or similar. --- @@ -1665,6 +1666,19 @@ function vim.api.nvim_notify(msg, log_level, opts) end --- Then `nvim_chan_send()` can be called immediately to process sequences --- in a virtual terminal having the intended size. --- +--- Example: this `TermHl` command can be used to display and highlight raw ANSI termcodes, so you +--- can use Nvim as a "scrollback pager" (for terminals like kitty): [ansi-colorize]() +--- [terminal-scrollback-pager]() +--- +--- ```lua +--- vim.api.nvim_create_user_command('TermHl', function() +--- local b = vim.api.nvim_create_buf(false, true) +--- local chan = vim.api.nvim_open_term(b, {}) +--- vim.api.nvim_chan_send(chan, table.concat(vim.api.nvim_buf_get_lines(0, 0, -1, false), '\n')) +--- vim.api.nvim_win_set_buf(0, b) +--- end, { desc = 'Highlights ANSI termcodes in curbuf' }) +--- ``` +--- --- @param buffer integer the buffer to use (expected to be empty) --- @param opts vim.api.keyset.open_term Optional parameters. --- - on_input: Lua callback for input sent, i e keypresses in terminal @@ -1738,10 +1752,12 @@ function vim.api.nvim_open_term(buffer, opts) end --- @param config vim.api.keyset.win_config Map defining the window configuration. Keys: --- - relative: Sets the window layout to "floating", placed at (row,col) --- coordinates relative to: ---- - "editor" The global editor grid ---- - "win" Window given by the `win` field, or current window. ---- - "cursor" Cursor position in current window. ---- - "mouse" Mouse position +--- - "cursor" Cursor position in current window. +--- - "editor" The global editor grid. +--- - "laststatus" 'laststatus' if present, or last row. +--- - "mouse" Mouse position. +--- - "tabline" Tabline if present, or first row. +--- - "win" Window given by the `win` field, or current window. --- - win: `window-ID` window to split, or relative window when creating a --- float (relative="win"). --- - anchor: Decides which corner of the float to place at (row,col): @@ -1849,10 +1865,8 @@ function vim.api.nvim_open_term(buffer, opts) end --- @return integer # Window handle, or 0 on error function vim.api.nvim_open_win(buffer, enter, config) end ---- Writes a message to the Vim output buffer. Does not append "\n", the ---- message is buffered (won't display) until a linefeed is written. ---- ---- @param str string Message +--- @deprecated +--- @param str string function vim.api.nvim_out_write(str) end --- Parse command line. @@ -2124,8 +2138,8 @@ function vim.api.nvim_set_current_win(window) end --- ``` --- ["start", tick] --- ``` ---- - on_buf: called for each buffer being redrawn (before ---- window callbacks) +--- - on_buf: called for each buffer being redrawn (once per edit, +--- before window callbacks) --- ``` --- ["buf", bufnr, tick] --- ``` diff --git a/runtime/lua/vim/_meta/api_keysets.lua b/runtime/lua/vim/_meta/api_keysets.lua index bf184dee2d..4d0665872b 100644 --- a/runtime/lua/vim/_meta/api_keysets.lua +++ b/runtime/lua/vim/_meta/api_keysets.lua @@ -4,11 +4,11 @@ error('Cannot require a meta file') --- @class vim.api.keyset.buf_attach ---- @field on_lines? function ---- @field on_bytes? function ---- @field on_changedtick? function ---- @field on_detach? function ---- @field on_reload? function +--- @field on_lines? fun(_: "lines", bufnr: integer, changedtick: integer, first: integer, last_old: integer, last_new: integer, byte_count: integer, deleted_codepoints?: integer, deleted_codeunits?: integer): boolean? +--- @field on_bytes? fun(_: "bytes", bufnr: integer, changedtick: integer, start_row: integer, start_col: integer, start_byte: integer, old_end_row: integer, old_end_col: integer, old_end_byte: integer, new_end_row: integer, new_end_col: integer, new_end_byte: integer): boolean? +--- @field on_changedtick? fun(_: "changedtick", bufnr: integer, changedtick: integer) +--- @field on_detach? fun(_: "detach", bufnr: integer) +--- @field on_reload? fun(_: "reload", bufnr: integer) --- @field utf_sizes? boolean --- @field preview? boolean @@ -18,9 +18,9 @@ error('Cannot require a meta file') --- @class vim.api.keyset.clear_autocmds --- @field buffer? integer ---- @field event? any ---- @field group? any ---- @field pattern? any +--- @field event? string|string[] +--- @field group? integer|string +--- @field pattern? string|string[] --- @class vim.api.keyset.cmd --- @field cmd? string @@ -28,12 +28,12 @@ error('Cannot require a meta file') --- @field count? integer --- @field reg? string --- @field bang? boolean ---- @field args? any[] +--- @field args? string[] --- @field magic? table<string,any> --- @field mods? table<string,any> ---- @field nargs? any ---- @field addr? any ---- @field nextcmd? any +--- @field nargs? integer|string +--- @field addr? string +--- @field nextcmd? string --- @class vim.api.keyset.cmd_magic --- @field file? boolean @@ -72,22 +72,23 @@ error('Cannot require a meta file') --- @field info? string --- @class vim.api.keyset.context ---- @field types? any[] +--- @field types? string[] --- @class vim.api.keyset.create_augroup ---- @field clear? any +--- @field clear? boolean --- @class vim.api.keyset.create_autocmd --- @field buffer? integer ---- @field callback? any +--- @field callback? string|(fun(args: vim.api.keyset.create_autocmd.callback_args): boolean?) --- @field command? string --- @field desc? string ---- @field group? any +--- @field group? integer|string --- @field nested? boolean --- @field once? boolean ---- @field pattern? any +--- @field pattern? string|string[] --- @class vim.api.keyset.echo_opts +--- @field err? boolean --- @field verbose? boolean --- @class vim.api.keyset.empty @@ -103,19 +104,20 @@ error('Cannot require a meta file') --- @class vim.api.keyset.exec_autocmds --- @field buffer? integer ---- @field group? any +--- @field group? integer|string --- @field modeline? boolean ---- @field pattern? any +--- @field pattern? string|string[] --- @field data? any --- @class vim.api.keyset.exec_opts --- @field output? boolean --- @class vim.api.keyset.get_autocmds ---- @field event? any ---- @field group? any ---- @field pattern? any ---- @field buffer? any +--- @field event? string|string[] +--- @field group? integer|string +--- @field pattern? string|string[] +--- @field buffer? integer|integer[] +--- @field id? integer --- @class vim.api.keyset.get_commands --- @field builtin? boolean @@ -154,17 +156,17 @@ error('Cannot require a meta file') --- @field altfont? boolean --- @field nocombine? boolean --- @field default? boolean ---- @field cterm? any ---- @field foreground? any ---- @field fg? any ---- @field background? any ---- @field bg? any ---- @field ctermfg? any ---- @field ctermbg? any ---- @field special? any ---- @field sp? any ---- @field link? any ---- @field global_link? any +--- @field cterm? integer|string +--- @field foreground? integer|string +--- @field fg? integer|string +--- @field background? integer|string +--- @field bg? integer|string +--- @field ctermfg? integer|string +--- @field ctermbg? integer|string +--- @field special? integer|string +--- @field sp? integer|string +--- @field link? integer|string +--- @field global_link? integer|string --- @field fallback? boolean --- @field blend? integer --- @field fg_indexed? boolean @@ -201,7 +203,7 @@ error('Cannot require a meta file') --- @field wins? any[] --- @class vim.api.keyset.open_term ---- @field on_input? function +--- @field on_input? fun(_: "input", term: integer, bufnr: integer, data: any) --- @field force_crlf? boolean --- @class vim.api.keyset.option @@ -227,20 +229,20 @@ error('Cannot require a meta file') --- @field do_source? boolean --- @class vim.api.keyset.set_decoration_provider ---- @field on_start? function ---- @field on_buf? function ---- @field on_win? function ---- @field on_line? function ---- @field on_end? function ---- @field _on_hl_def? function ---- @field _on_spell_nav? function +--- @field on_start? fun(_: "start", tick: integer): boolean? +--- @field on_buf? fun(_: "buf", bufnr: integer, tick: integer) +--- @field on_win? fun(_: "win", winid: integer, bufnr: integer, toprow: integer, botrow: integer): boolean? +--- @field on_line? fun(_: "line", winid: integer, bufnr: integer, row: integer): boolean? +--- @field on_end? fun(_: "end", tick: integer) +--- @field _on_hl_def? fun(_: "hl_def") +--- @field _on_spell_nav? fun(_: "spell_nav") --- @class vim.api.keyset.set_extmark --- @field id? integer --- @field end_line? integer --- @field end_row? integer --- @field end_col? integer ---- @field hl_group? number|string +--- @field hl_group? any --- @field virt_text? any[] --- @field virt_text_pos? string --- @field virt_text_win_col? integer @@ -258,10 +260,10 @@ error('Cannot require a meta file') --- @field virt_lines_leftcol? boolean --- @field strict? boolean --- @field sign_text? string ---- @field sign_hl_group? number|string ---- @field number_hl_group? number|string ---- @field line_hl_group? number|string ---- @field cursorline_hl_group? number|string +--- @field sign_hl_group? integer|string +--- @field number_hl_group? integer|string +--- @field line_hl_group? integer|string +--- @field cursorline_hl_group? integer|string --- @field conceal? string --- @field spell? boolean --- @field ui_watched? boolean @@ -292,7 +294,7 @@ error('Cannot require a meta file') --- @field relative? string --- @field split? string --- @field win? integer ---- @field bufpos? any[] +--- @field bufpos? integer[] --- @field external? boolean --- @field focusable? boolean --- @field mouse? boolean @@ -315,12 +317,12 @@ error('Cannot require a meta file') --- @field end_vcol? integer --- @class vim.api.keyset.xdl_diff ---- @field on_hunk? function +--- @field on_hunk? fun(start_a: integer, count_a: integer, start_b: integer, count_b: integer): integer? --- @field result_type? string --- @field algorithm? string --- @field ctxlen? integer --- @field interhunkctxlen? integer ---- @field linematch? any +--- @field linematch? boolean|integer --- @field ignore_whitespace? boolean --- @field ignore_whitespace_change? boolean --- @field ignore_whitespace_change_at_eol? boolean diff --git a/runtime/lua/vim/_meta/api_keysets_extra.lua b/runtime/lua/vim/_meta/api_keysets_extra.lua index 81bce50746..fbef6fa3bc 100644 --- a/runtime/lua/vim/_meta/api_keysets_extra.lua +++ b/runtime/lua/vim/_meta/api_keysets_extra.lua @@ -73,6 +73,51 @@ error('Cannot require a meta file') --- @field buflocal? boolean --- @field buffer? integer +--- @class vim.api.keyset.create_autocmd.callback_args +--- @field id integer autocommand id +--- @field event string name of the triggered event |autocmd-events| +--- @field group? integer autocommand group id, if any +--- @field match string expanded value of <amatch> +--- @field buf integer expanded value of <abuf> +--- @field file string expanded value of <afile> +--- @field data? any arbitrary data passed from |nvim_exec_autocmds()| *event-data* + +--- @class vim.api.keyset.create_user_command.command_args +--- @field name string Command name +--- +--- The args passed to the command, if any <args> +--- @field args string +--- +--- The args split by unescaped whitespace +--- (when more than one argument is allowed), if any <f-args> +--- @field fargs string[] +--- +--- Number of arguments |:command-nargs| +--- @field nargs string +--- +--- "true" if the command was executed with a ! modifier <bang> +--- @field bang boolean +--- +--- The starting line of the command range <line1> +--- @field line1 integer +--- +--- The final line of the command range <line2> +--- @field line2 integer +--- +--- The number of items in the command range: 0, 1, or 2 <range> +--- @field range integer +--- +--- Any count supplied <count> +--- @field count integer +--- The optional register, if specified <reg> +--- @field reg string +--- Command modifiers, if any <mods> +--- @field mods string +--- +--- Command modifiers in a structured format. Has the same structure as the +--- "mods" key of |nvim_parse_cmd()|. +--- @field smods table + --- @class vim.api.keyset.command_info --- @field name string --- @field definition string @@ -114,6 +159,7 @@ error('Cannot require a meta file') --- @field bg? integer --- @field sp? integer --- @field default? true +--- @field link? string --- @field blend? integer --- @field cterm? vim.api.keyset.hl_info.cterm @@ -127,6 +173,26 @@ error('Cannot require a meta file') --- @field force? true --- @field cterm? vim.api.keyset.hl_info.cterm +--- @class vim.api.keyset.get_keymap +--- @field abbr? 0|1 +--- @field buffer? 0|1 +--- @field callback? function +--- @field desc? string +--- @field expr? 0|1 +--- @field lhs? string +--- @field lhsraw? string +--- @field lhsrawalt? string +--- @field lnum? integer +--- @field mode? string +--- @field mode_bits? integer +--- @field noremap? 0|1 +--- @field nowait? 0|1 +--- @field rhs? string +--- @field script? 0|1 +--- @field scriptversion? integer +--- @field sid? integer +--- @field silent? 0|1 + --- @class vim.api.keyset.get_mode --- @field blocking boolean --- @field mode string diff --git a/runtime/lua/vim/_meta/builtin.lua b/runtime/lua/vim/_meta/builtin.lua index b8779b66fe..9fa2e242c4 100644 --- a/runtime/lua/vim/_meta/builtin.lua +++ b/runtime/lua/vim/_meta/builtin.lua @@ -233,9 +233,8 @@ function vim.wait(time, callback, interval, fast_only) end --- {callback} receives event name plus additional parameters. See |ui-popupmenu| --- and the sections below for event format for respective events. --- ---- Callbacks for `msg_show` events are executed in |api-fast| context unless ---- Nvim will wait for input, in which case messages should be shown ---- immediately. +--- Callbacks for `msg_show` events are executed in |api-fast| context; showing +--- the message should be scheduled. --- --- Excessive errors inside the callback will result in forced detachment. --- diff --git a/runtime/lua/vim/_meta/json.lua b/runtime/lua/vim/_meta/json.lua index 07d89aafc8..0d59de5fa6 100644 --- a/runtime/lua/vim/_meta/json.lua +++ b/runtime/lua/vim/_meta/json.lua @@ -25,15 +25,18 @@ vim.json = {} --- ---@param str string Stringified JSON data. ---@param opts? table<string,any> Options table with keys: ---- - luanil: (table) Table with keys: ---- * object: (boolean) When true, converts `null` in JSON objects ---- to Lua `nil` instead of |vim.NIL|. ---- * array: (boolean) When true, converts `null` in JSON arrays ---- to Lua `nil` instead of |vim.NIL|. +--- - luanil: (table) Table with keys: +--- - object: (boolean) When true, converts `null` in JSON objects +--- to Lua `nil` instead of |vim.NIL|. +--- - array: (boolean) When true, converts `null` in JSON arrays +--- to Lua `nil` instead of |vim.NIL|. ---@return any function vim.json.decode(str, opts) end --- Encodes (or "packs") Lua object {obj} as JSON in a Lua string. ---@param obj any +---@param opts? table<string,any> Options table with keys: +--- - escape_slash: (boolean) (default false) Escape slash +--- characters "/" in string values. ---@return string -function vim.json.encode(obj) end +function vim.json.encode(obj, opts) end diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index cb783720ac..52c556867f 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -52,7 +52,7 @@ vim.go.ari = vim.go.allowrevins --- set to one of CJK locales. See Unicode Standard Annex #11 --- (https://www.unicode.org/reports/tr11). --- ---- @type string +--- @type 'single'|'double' vim.o.ambiwidth = "single" vim.o.ambw = vim.o.ambiwidth vim.go.ambiwidth = vim.o.ambiwidth @@ -208,7 +208,7 @@ vim.go.awa = vim.go.autowriteall --- will change. To use other settings, place ":highlight" commands AFTER --- the setting of the 'background' option. --- ---- @type string +--- @type 'light'|'dark' vim.o.background = "dark" vim.o.bg = vim.o.background vim.go.background = vim.o.background @@ -595,7 +595,7 @@ vim.wo.briopt = vim.wo.breakindentopt --- This option is used together with 'buftype' and 'swapfile' to specify --- special kinds of buffers. See `special-buffers`. --- ---- @type string +--- @type ''|'hide'|'unload'|'delete'|'wipe' vim.o.bufhidden = "" vim.o.bh = vim.o.bufhidden vim.bo.bufhidden = vim.o.bufhidden @@ -658,7 +658,7 @@ vim.bo.bl = vim.bo.buflisted --- without saving. For writing there must be matching `BufWriteCmd|, --- |FileWriteCmd` or `FileAppendCmd` autocommands. --- ---- @type string +--- @type ''|'acwrite'|'help'|'nofile'|'nowrite'|'quickfix'|'terminal'|'prompt' vim.o.buftype = "" vim.o.bt = vim.o.buftype vim.bo.buftype = vim.o.buftype @@ -1086,9 +1086,9 @@ vim.go.cia = vim.go.completeitemalign --- a match from the menu. Only works in combination with --- "menu" or "menuone". No effect if "longest" is present. --- ---- noselect Do not select a match in the menu, force the user to ---- select one from the menu. Only works in combination with ---- "menu" or "menuone". +--- noselect Same as "noinsert", except that no menu item is +--- pre-selected. If both "noinsert" and "noselect" are +--- present, "noselect" has precedence. --- --- fuzzy Enable `fuzzy-matching` for completion candidates. This --- allows for more flexible and intuitive matching, where @@ -1098,6 +1098,16 @@ vim.go.cia = vim.go.completeitemalign --- list of alternatives, but not how the candidates are --- collected (using different completion types). --- +--- nosort Disable sorting of completion candidates based on fuzzy +--- scores when "fuzzy" is enabled. Candidates will appear +--- in their original order. +--- +--- preinsert +--- Preinsert the portion of the first candidate word that is +--- not part of the current completion leader and using the +--- `hl-ComplMatchIns` highlight group. Does not work when +--- "fuzzy" is also included. +--- --- @type string vim.o.completeopt = "menu,preview" vim.o.cot = vim.o.completeopt @@ -1118,7 +1128,7 @@ vim.go.cot = vim.go.completeopt --- For Insert mode completion the buffer-local value is used. For --- command line completion the global value is used. --- ---- @type string +--- @type ''|'slash'|'backslash' vim.o.completeslash = "" vim.o.csl = vim.o.completeslash vim.bo.completeslash = vim.o.completeslash @@ -1621,11 +1631,20 @@ vim.go.dex = vim.go.diffexpr --- Option settings for diff mode. It can consist of the following items. --- All are optional. Items must be separated by a comma. --- ---- filler Show filler lines, to keep the text ---- synchronized with a window that has inserted ---- lines at the same position. Mostly useful ---- when windows are side-by-side and 'scrollbind' ---- is set. +--- algorithm:{text} Use the specified diff algorithm with the +--- internal diff engine. Currently supported +--- algorithms are: +--- myers the default algorithm +--- minimal spend extra time to generate the +--- smallest possible diff +--- patience patience diff algorithm +--- histogram histogram diff algorithm +--- +--- closeoff When a window is closed where 'diff' is set +--- and there is only one window remaining in the +--- same tab page with 'diff' set, execute +--- `:diffoff` in that window. This undoes a +--- `:diffsplit` command. --- --- context:{n} Use a context of {n} lines between a change --- and a fold that contains unchanged lines. @@ -1636,6 +1655,23 @@ vim.go.dex = vim.go.diffexpr --- value (999999) to disable folding completely. --- See `fold-diff`. --- +--- filler Show filler lines, to keep the text +--- synchronized with a window that has inserted +--- lines at the same position. Mostly useful +--- when windows are side-by-side and 'scrollbind' +--- is set. +--- +--- foldcolumn:{n} Set the 'foldcolumn' option to {n} when +--- starting diff mode. Without this 2 is used. +--- +--- followwrap Follow the 'wrap' option and leave as it is. +--- +--- horizontal Start diff mode with horizontal splits (unless +--- explicitly specified otherwise). +--- +--- hiddenoff Do not use diff mode for a buffer when it +--- becomes hidden. +--- --- iblank Ignore changes where lines are all blank. Adds --- the "-B" flag to the "diff" command if --- 'diffexpr' is empty. Check the documentation @@ -1649,6 +1685,17 @@ vim.go.dex = vim.go.diffexpr --- are considered the same. Adds the "-i" flag --- to the "diff" command if 'diffexpr' is empty. --- +--- indent-heuristic +--- Use the indent heuristic for the internal +--- diff library. +--- +--- internal Use the internal diff library. This is +--- ignored when 'diffexpr' is set. *E960* +--- When running out of memory when writing a +--- buffer this item will be ignored for diffs +--- involving that buffer. Set the 'verbose' +--- option to see when this happens. +--- --- iwhite Ignore changes in amount of white space. Adds --- the "-b" flag to the "diff" command if --- 'diffexpr' is empty. Check the documentation @@ -1668,56 +1715,19 @@ vim.go.dex = vim.go.diffexpr --- of the "diff" command for what this does --- exactly. --- ---- horizontal Start diff mode with horizontal splits (unless ---- explicitly specified otherwise). +--- linematch:{n} Align and mark changes between the most +--- similar lines between the buffers. When the +--- total number of lines in the diff hunk exceeds +--- {n}, the lines will not be aligned because for +--- very large diff hunks there will be a +--- noticeable lag. A reasonable setting is +--- "linematch:60", as this will enable alignment +--- for a 2 buffer diff hunk of 30 lines each, +--- or a 3 buffer diff hunk of 20 lines each. --- --- vertical Start diff mode with vertical splits (unless --- explicitly specified otherwise). --- ---- closeoff When a window is closed where 'diff' is set ---- and there is only one window remaining in the ---- same tab page with 'diff' set, execute ---- `:diffoff` in that window. This undoes a ---- `:diffsplit` command. ---- ---- hiddenoff Do not use diff mode for a buffer when it ---- becomes hidden. ---- ---- foldcolumn:{n} Set the 'foldcolumn' option to {n} when ---- starting diff mode. Without this 2 is used. ---- ---- followwrap Follow the 'wrap' option and leave as it is. ---- ---- internal Use the internal diff library. This is ---- ignored when 'diffexpr' is set. *E960* ---- When running out of memory when writing a ---- buffer this item will be ignored for diffs ---- involving that buffer. Set the 'verbose' ---- option to see when this happens. ---- ---- indent-heuristic ---- Use the indent heuristic for the internal ---- diff library. ---- ---- linematch:{n} Enable a second stage diff on each generated ---- hunk in order to align lines. When the total ---- number of lines in a hunk exceeds {n}, the ---- second stage diff will not be performed as ---- very large hunks can cause noticeable lag. A ---- recommended setting is "linematch:60", as this ---- will enable alignment for a 2 buffer diff with ---- hunks of up to 30 lines each, or a 3 buffer ---- diff with hunks of up to 20 lines each. ---- ---- algorithm:{text} Use the specified diff algorithm with the ---- internal diff engine. Currently supported ---- algorithms are: ---- myers the default algorithm ---- minimal spend extra time to generate the ---- smallest possible diff ---- patience patience diff algorithm ---- histogram histogram diff algorithm ---- --- Examples: --- --- ```vim @@ -1824,7 +1834,7 @@ vim.go.dy = vim.go.display --- hor horizontally, height of windows is not affected --- both width and height of windows is affected --- ---- @type string +--- @type 'both'|'ver'|'hor' vim.o.eadirection = "both" vim.o.ead = vim.o.eadirection vim.go.eadirection = vim.o.eadirection @@ -2126,7 +2136,7 @@ vim.go.fencs = vim.go.fileencodings --- option is set, because the file would be different when written. --- This option cannot be changed when 'modifiable' is off. --- ---- @type string +--- @type 'unix'|'dos'|'mac' vim.o.fileformat = "unix" vim.o.ff = vim.o.fileformat vim.bo.fileformat = vim.o.fileformat @@ -2382,7 +2392,7 @@ vim.go.fcl = vim.go.foldclose --- "[1-9]": to display a fixed number of columns --- See `folding`. --- ---- @type string +--- @type 'auto'|'auto:1'|'auto:2'|'auto:3'|'auto:4'|'auto:5'|'auto:6'|'auto:7'|'auto:8'|'auto:9'|'0'|'1'|'2'|'3'|'4'|'5'|'6'|'7'|'8'|'9' vim.o.foldcolumn = "0" vim.o.fdc = vim.o.foldcolumn vim.wo.foldcolumn = vim.o.foldcolumn @@ -2479,7 +2489,7 @@ vim.wo.fmr = vim.wo.foldmarker --- `fold-syntax` syntax Syntax highlighting items specify folds. --- `fold-diff` diff Fold text that is not changed. --- ---- @type string +--- @type 'manual'|'expr'|'marker'|'indent'|'syntax'|'diff' vim.o.foldmethod = "manual" vim.o.fdm = vim.o.foldmethod vim.wo.foldmethod = vim.o.foldmethod @@ -2783,6 +2793,7 @@ vim.go.gp = vim.go.grepprg --- ci Command-line Insert mode --- cr Command-line Replace mode --- sm showmatch in Insert mode +--- t Terminal mode --- a all modes --- The argument-list is a dash separated list of these arguments: --- hor{N} horizontal bar, {N} percent of the character height @@ -2802,7 +2813,8 @@ vim.go.gp = vim.go.grepprg --- ```vim --- set guicursor=n:blinkon0 --- ``` ---- - Default is "blinkon0" for each mode. +--- +--- Default is "blinkon0" for each mode. --- {group-name} --- Highlight group that decides the color and font of the --- cursor. @@ -2848,7 +2860,7 @@ vim.go.gp = vim.go.grepprg --- --- --- @type string -vim.o.guicursor = "n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20" +vim.o.guicursor = "n-v-c-sm:block,i-ci-ve:ver25,r-cr-o:hor20,t:block-blinkon500-blinkoff500-TermCursor" vim.o.gcr = vim.o.guicursor vim.go.guicursor = vim.o.guicursor vim.go.gcr = vim.go.guicursor @@ -3016,7 +3028,7 @@ vim.go.hid = vim.go.hidden --- A history of ":" commands, and a history of previous search patterns --- is remembered. This option decides how many entries may be stored in ---- each of these histories (see `cmdline-editing` and 'msghistory' for +--- each of these histories (see `cmdline-editing` and 'messagesopt' for --- the number of messages to remember). --- The maximum value is 10000. --- @@ -3142,7 +3154,7 @@ vim.bo.ims = vim.bo.imsearch --- 'redrawtime') then 'inccommand' is automatically disabled until --- `Command-line-mode` is done. --- ---- @type string +--- @type 'nosplit'|'split'|'' vim.o.inccommand = "nosplit" vim.o.icm = vim.o.inccommand vim.go.inccommand = vim.o.inccommand @@ -4084,6 +4096,31 @@ vim.o.mis = vim.o.menuitems vim.go.menuitems = vim.o.menuitems vim.go.mis = vim.go.menuitems +--- Option settings for outputting messages. It can consist of the +--- following items. Items must be separated by a comma. +--- +--- hit-enter Use a `hit-enter` prompt when the message is longer than +--- 'cmdheight' size. +--- +--- wait:{n} Instead of using a `hit-enter` prompt, simply wait for +--- {n} milliseconds so that the user has a chance to read +--- the message. The maximum value of {n} is 10000. Use +--- 0 to disable the wait (but then the user may miss an +--- important message). +--- This item is ignored when "hit-enter" is present, but +--- required when "hit-enter" is not present. +--- +--- history:{n} Determines how many entries are remembered in the +--- `:messages` history. The maximum value is 10000. +--- Setting it to zero clears the message history. +--- This item must always be present. +--- +--- @type string +vim.o.messagesopt = "hit-enter,history:500" +vim.o.mopt = vim.o.messagesopt +vim.go.messagesopt = vim.o.messagesopt +vim.go.mopt = vim.go.messagesopt + --- Parameters for `:mkspell`. This tunes when to start compressing the --- word tree. Compression can be slow when there are many words, but --- it's needed to avoid running out of memory. The amount of memory used @@ -4327,7 +4364,7 @@ vim.go.mh = vim.go.mousehide --- "g<LeftMouse>" is "<C-LeftMouse> (jump to tag under mouse click) --- "g<RightMouse>" is "<C-RightMouse> ("CTRL-T") --- ---- @type string +--- @type 'extend'|'popup'|'popup_setpos' vim.o.mousemodel = "popup_setpos" vim.o.mousem = vim.o.mousemodel vim.go.mousemodel = vim.o.mousemodel @@ -4379,15 +4416,6 @@ vim.o.mouset = vim.o.mousetime vim.go.mousetime = vim.o.mousetime vim.go.mouset = vim.go.mousetime ---- Determines how many entries are remembered in the `:messages` history. ---- The maximum value is 10000. ---- ---- @type integer -vim.o.msghistory = 500 -vim.o.mhi = vim.o.msghistory -vim.go.msghistory = vim.o.msghistory -vim.go.mhi = vim.go.msghistory - --- This defines what bases Vim will consider for numbers when using the --- CTRL-A and CTRL-X commands for adding to and subtracting from a number --- respectively; see `CTRL-A` for more info on these commands. @@ -4827,8 +4855,8 @@ vim.go.redrawdebug = vim.o.redrawdebug vim.go.rdb = vim.go.redrawdebug --- Time in milliseconds for redrawing the display. Applies to ---- 'hlsearch', 'inccommand', `:match` highlighting and syntax ---- highlighting. +--- 'hlsearch', 'inccommand', `:match` highlighting, syntax highlighting, +--- and async `LanguageTree:parse()`. --- When redrawing takes more than this many milliseconds no further --- matches will be highlighted. --- For syntax highlighting the time applies per window. When over the @@ -4994,6 +5022,7 @@ vim.go.ruf = vim.go.rulerformat --- indent/ indent scripts `indent-expression` --- keymap/ key mapping files `mbyte-keymap` --- lang/ menu translations `:menutrans` +--- lsp/ LSP client configurations `lsp-config` --- lua/ `Lua` plugins --- menu.vim GUI menus `menu.vim` --- pack/ packages `:packadd` @@ -5197,11 +5226,13 @@ vim.go.sect = vim.go.sections --- selection. --- When "old" is used and 'virtualedit' allows the cursor to move past --- the end of line the line break still isn't included. +--- When "exclusive" is used, cursor position in visual mode will be +--- adjusted for inclusive motions `inclusive-motion-selection-exclusive`. --- Note that when "exclusive" is used and selecting from the end --- backwards, you cannot include the last character of a line, when --- starting in Normal mode and 'virtualedit' empty. --- ---- @type string +--- @type 'inclusive'|'exclusive'|'old' vim.o.selection = "inclusive" vim.o.sel = vim.o.selection vim.go.selection = vim.o.selection @@ -5767,7 +5798,7 @@ vim.go.sc = vim.go.showcmd --- place the text. Without a custom 'statusline' or 'tabline' it will be --- displayed in a convenient location. --- ---- @type string +--- @type 'last'|'statusline'|'tabline' vim.o.showcmdloc = "last" vim.o.sloc = vim.o.showcmdloc vim.go.showcmdloc = vim.o.showcmdloc @@ -5899,7 +5930,7 @@ vim.go.siso = vim.go.sidescrolloff --- "number" display signs in the 'number' column. If the number --- column is not present, then behaves like "auto". --- ---- @type string +--- @type 'yes'|'no'|'auto'|'auto:1'|'auto:2'|'auto:3'|'auto:4'|'auto:5'|'auto:6'|'auto:7'|'auto:8'|'auto:9'|'yes:1'|'yes:2'|'yes:3'|'yes:4'|'yes:5'|'yes:6'|'yes:7'|'yes:8'|'yes:9'|'number' vim.o.signcolumn = "auto" vim.o.scl = vim.o.signcolumn vim.wo.signcolumn = vim.o.signcolumn @@ -6207,7 +6238,7 @@ vim.go.sb = vim.go.splitbelow --- with the previous cursor position. For "screen", the text cannot always --- be kept on the same screen line when 'wrap' is enabled. --- ---- @type string +--- @type 'cursor'|'screen'|'topline' vim.o.splitkeep = "cursor" vim.o.spk = vim.o.splitkeep vim.go.splitkeep = vim.o.splitkeep @@ -6310,6 +6341,7 @@ vim.wo.stc = vim.wo.statuscolumn --- All fields except the {item} are optional. A single percent sign can --- be given as "%%". --- +--- *stl-%!* --- When the option starts with "%!" then it is used as an expression, --- evaluated and the result is used as the option value. Example: --- @@ -6854,7 +6886,7 @@ vim.go.tbs = vim.go.tagbsearch --- match Match case --- smart Ignore case unless an upper case letter is used --- ---- @type string +--- @type 'followic'|'ignore'|'match'|'followscs'|'smart' vim.o.tagcase = "followic" vim.o.tc = vim.o.tagcase vim.bo.tagcase = vim.o.tagcase @@ -7122,6 +7154,13 @@ vim.go.titleold = vim.o.titleold --- expanded according to the rules used for 'statusline'. If it contains --- an invalid '%' format, the value is used as-is and no error or warning --- will be given when the value is set. +--- +--- The default behaviour is equivalent to: +--- +--- ```vim +--- set titlestring=%t%(\ %M%)%(\ \(%{expand(\"%:~:h\")}\)%)%a\ -\ Nvim +--- ``` +--- --- This option cannot be set in a modeline when 'modelineexpr' is off. --- --- Example: @@ -7729,7 +7768,7 @@ vim.go.wop = vim.go.wildoptions --- key is never used for the menu. --- This option is not used for <F10>; on Win32. --- ---- @type string +--- @type 'yes'|'menu'|'no' vim.o.winaltkeys = "menu" vim.o.wak = vim.o.winaltkeys vim.go.winaltkeys = vim.o.winaltkeys diff --git a/runtime/lua/vim/_meta/vimfn.lua b/runtime/lua/vim/_meta/vimfn.lua index 5eb15e1eee..098c0e907a 100644 --- a/runtime/lua/vim/_meta/vimfn.lua +++ b/runtime/lua/vim/_meta/vimfn.lua @@ -1023,16 +1023,22 @@ function vim.fn.complete_check() end --- See |complete_info_mode| for the values. --- pum_visible |TRUE| if popup menu is visible. --- See |pumvisible()|. ---- items List of completion matches. Each item is a ---- dictionary containing the entries "word", +--- items List of all completion candidates. Each item +--- is a dictionary containing the entries "word", --- "abbr", "menu", "kind", "info" and "user_data". --- See |complete-items|. +--- matches Same as "items", but only returns items that +--- are matching current query. If both "matches" +--- and "items" are in "what", the returned list +--- will still be named "items", but each item +--- will have an additional "match" field. --- selected Selected item index. First index is zero. --- Index is -1 if no item is selected (showing --- typed text only, or the last completion after --- no item is selected when using the <Up> or --- <Down> keys) ---- inserted Inserted string. [NOT IMPLEMENTED YET] +--- completed Return a dictionary containing the entries of +--- the currently selected index item. --- preview_winid Info floating preview window id. --- preview_bufnr Info floating preview buffer id. --- @@ -1147,8 +1153,9 @@ function vim.fn.confirm(msg, choices, default, type) end --- A |Dictionary| is copied in a similar way as a |List|. --- Also see |deepcopy()|. --- ---- @param expr any ---- @return any +--- @generic T +--- @param expr T +--- @return T function vim.fn.copy(expr) end --- Return the cosine of {expr}, measured in radians, as a |Float|. @@ -1228,7 +1235,7 @@ function vim.fn.ctxpush(types) end --- --- @param context table --- @param index? integer ---- @return any +--- @return integer function vim.fn.ctxset(context, index) end --- Returns the size of the |context-stack|. @@ -1308,9 +1315,10 @@ function vim.fn.debugbreak(pid) end --- {noref} set to 1 will fail. --- Also see |copy()|. --- ---- @param expr any +--- @generic T +--- @param expr T --- @param noref? boolean ---- @return any +--- @return T function vim.fn.deepcopy(expr, noref) end --- Without {flags} or with {flags} empty: Deletes the file by the @@ -1421,7 +1429,7 @@ function vim.fn.dictwatcherdel(dict, pattern, callback) end --- editing another buffer to set 'filetype' and load a syntax --- file. --- ---- @return any +--- @return integer function vim.fn.did_filetype() end --- Returns the number of filler lines above line {lnum}. @@ -1433,7 +1441,7 @@ function vim.fn.did_filetype() end --- Returns 0 if the current window is not in diff mode. --- --- @param lnum integer ---- @return any +--- @return integer function vim.fn.diff_filler(lnum) end --- Returns the highlight ID for diff mode at line {lnum} column @@ -1468,7 +1476,7 @@ function vim.fn.diff_hlID(lnum, col) end --- < --- --- @param chars string ---- @return any +--- @return string function vim.fn.digraph_get(chars) end --- Return a list of digraphs. If the {listall} argument is given @@ -1486,7 +1494,7 @@ function vim.fn.digraph_get(chars) end --- < --- --- @param listall? boolean ---- @return any +--- @return string[][] function vim.fn.digraph_getlist(listall) end --- Add digraph {chars} to the list. {chars} must be a string @@ -1538,7 +1546,7 @@ function vim.fn.digraph_setlist(digraphlist) end --- - A |Blob| is empty when its length is zero. --- --- @param expr any ---- @return any +--- @return integer function vim.fn.empty(expr) end --- Return all of environment variables as dictionary. You can @@ -1561,7 +1569,7 @@ function vim.fn.environ() end --- --- @param string string --- @param chars string ---- @return any +--- @return string function vim.fn.escape(string, chars) end --- Evaluate {string} and return the result. Especially useful to @@ -2368,7 +2376,7 @@ function vim.fn.foldtextresult(lnum) end --- --- @param expr1 string|table --- @param expr2 string|function ---- @return any +--- @return string|table function vim.fn.foreach(expr1, expr2) end --- Get the full command name from a short abbreviated command @@ -2675,7 +2683,7 @@ function vim.fn.getbufinfo(dict) end --- @param buf integer|string --- @param lnum integer --- @param end_? integer ---- @return any +--- @return string[] function vim.fn.getbufline(buf, lnum, end_) end --- Just like `getbufline()` but only get one line and return it @@ -2740,12 +2748,14 @@ function vim.fn.getcellwidths() end function vim.fn.getchangelist(buf) end --- Get a single character from the user or input stream. ---- If {expr} is omitted, wait until a character is available. +--- If {expr} is omitted or is -1, wait until a character is +--- available. --- If {expr} is 0, only get a character when one is available. --- Return zero otherwise. --- If {expr} is 1, only check if a character is available, it is --- not consumed. Return zero if no character available. ---- If you prefer always getting a string use |getcharstr()|. +--- If you prefer always getting a string use |getcharstr()|, or +--- specify |FALSE| as "number" in {opts}. --- --- Without {expr} and when {expr} is 0 a whole character or --- special key is returned. If it is a single character, the @@ -2755,7 +2765,8 @@ function vim.fn.getchangelist(buf) end --- starting with 0x80 (decimal: 128). This is the same value as --- the String "\<Key>", e.g., "\<Left>". The returned value is --- also a String when a modifier (shift, control, alt) was used ---- that is not included in the character. +--- that is not included in the character. |keytrans()| can also +--- be used to convert a returned String into a readable form. --- --- When {expr} is 0 and Esc is typed, there will be a short delay --- while Vim waits to see if this is the start of an escape @@ -2767,6 +2778,32 @@ function vim.fn.getchangelist(buf) end --- --- Use getcharmod() to obtain any additional modifiers. --- +--- The optional argument {opts} is a Dict and supports the +--- following items: +--- +--- cursor A String specifying cursor behavior +--- when waiting for a character. +--- "hide": hide the cursor. +--- "keep": keep current cursor unchanged. +--- "msg": move cursor to message area. +--- (default: automagically decide +--- between "keep" and "msg") +--- +--- number If |TRUE|, return a Number when getting +--- a single character. +--- If |FALSE|, the return value is always +--- converted to a String, and an empty +--- String (instead of 0) is returned when +--- no character is available. +--- (default: |TRUE|) +--- +--- simplify If |TRUE|, include modifiers in the +--- character if possible. E.g., return +--- the same value for CTRL-I and <Tab>. +--- If |FALSE|, don't include modifiers in +--- the character. +--- (default: |TRUE|) +--- --- When the user clicks a mouse button, the mouse event will be --- returned. The position can then be found in |v:mouse_col|, --- |v:mouse_lnum|, |v:mouse_winid| and |v:mouse_win|. @@ -2803,9 +2840,10 @@ function vim.fn.getchangelist(buf) end --- endfunction --- < --- ---- @param expr? 0|1 ---- @return integer -function vim.fn.getchar(expr) end +--- @param expr? -1|0|1 +--- @param opts? table +--- @return integer|string +function vim.fn.getchar(expr, opts) end --- The result is a Number which is the state of the modifiers for --- the last obtained character with getchar() or in another way. @@ -2864,20 +2902,13 @@ function vim.fn.getcharpos(expr) end --- @return table function vim.fn.getcharsearch() end ---- Get a single character from the user or input stream as a ---- string. ---- If {expr} is omitted, wait until a character is available. ---- If {expr} is 0 or false, only get a character when one is ---- available. Return an empty string otherwise. ---- If {expr} is 1 or true, only check if a character is ---- available, it is not consumed. Return an empty string ---- if no character is available. ---- Otherwise this works like |getchar()|, except that a number ---- result is converted to a string. ---- ---- @param expr? 0|1 +--- The same as |getchar()|, except that this always returns a +--- String, and "number" isn't allowed in {opts}. +--- +--- @param expr? -1|0|1 +--- @param opts? table --- @return string -function vim.fn.getcharstr(expr) end +function vim.fn.getcharstr(expr, opts) end --- Return completion pattern of the current command-line. --- Only works when the command line is being edited, thus @@ -2943,7 +2974,7 @@ function vim.fn.getcmdprompt() end --- Also see |getcmdpos()|, |setcmdpos()|, |getcmdline()| and --- |setcmdline()|. --- ---- @return any +--- @return integer function vim.fn.getcmdscreenpos() end --- Return the current command-line type. Possible return values @@ -3763,6 +3794,20 @@ function vim.fn.getregtype(regname) end --- @return vim.fn.getscriptinfo.ret[] function vim.fn.getscriptinfo(opts) end +--- Returns the current stack trace of Vim scripts. +--- Stack trace is a |List|, of which each item is a |Dictionary| +--- with the following items: +--- funcref The funcref if the stack is at a function, +--- otherwise this item is omitted. +--- event The string of the event description if the +--- stack is at an autocmd event, otherwise this +--- item is omitted. +--- lnum The line number in the script on the stack. +--- filepath The file path of the script on the stack. +--- +--- @return table[] +function vim.fn.getstacktrace() end + --- If {tabnr} is not specified, then information about all the --- tab pages is returned as a |List|. Each List item is a --- |Dictionary|. Otherwise, {tabnr} specifies the tab page @@ -3869,7 +3914,7 @@ function vim.fn.gettagstack(winnr) end --- strings. --- --- @param text string ---- @return any +--- @return string function vim.fn.gettext(text) end --- Returns information about windows as a |List| with Dictionaries. @@ -3885,6 +3930,8 @@ function vim.fn.gettext(text) end --- botline last complete displayed buffer line --- bufnr number of buffer in the window --- height window height (excluding winbar) +--- leftcol first column displayed; only used when +--- 'wrap' is off --- loclist 1 if showing a location list --- quickfix 1 if quickfix or location list window --- terminal 1 if a terminal window @@ -4018,7 +4065,7 @@ function vim.fn.glob(expr, nosuf, list, alllinks) end --- a backslash usually means a path separator. --- --- @param string string ---- @return any +--- @return string function vim.fn.glob2regpat(string) end --- Perform glob() for String {expr} on all directories in {path} @@ -4352,7 +4399,7 @@ function vim.fn.hostname() end --- @param string string --- @param from string --- @param to string ---- @return any +--- @return string function vim.fn.iconv(string, from, to) end --- Returns a |String| which is a unique identifier of the @@ -4372,7 +4419,7 @@ function vim.fn.iconv(string, from, to) end --- reuse identifiers of the garbage-collected ones. --- --- @param expr any ---- @return any +--- @return string function vim.fn.id(expr) end --- The result is a Number, which is indent of line {lnum} in the @@ -4416,7 +4463,7 @@ function vim.fn.indent(lnum) end --- @param expr any --- @param start? integer --- @param ic? boolean ---- @return any +--- @return integer function vim.fn.index(object, expr, start, ic) end --- Returns the index of an item in {object} where {expr} is @@ -4460,14 +4507,14 @@ function vim.fn.index(object, expr, start, ic) end --- @param object any --- @param expr any --- @param opts? table ---- @return any +--- @return integer function vim.fn.indexof(object, expr, opts) end --- --- @param prompt string --- @param text? string --- @param completion? string ---- @return any +--- @return string function vim.fn.input(prompt, text, completion) end --- The result is a String, which is whatever the user typed on @@ -4581,7 +4628,7 @@ function vim.fn.input(prompt, text, completion) end --- < --- --- @param opts table ---- @return any +--- @return string function vim.fn.input(opts) end --- @deprecated @@ -4616,7 +4663,7 @@ function vim.fn.inputlist(textlist) end --- called. Calling it more often is harmless though. --- Returns TRUE when there is nothing to restore, FALSE otherwise. --- ---- @return any +--- @return integer function vim.fn.inputrestore() end --- Preserve typeahead (also from mappings) and clear it, so that @@ -4626,7 +4673,7 @@ function vim.fn.inputrestore() end --- many inputrestore() calls. --- Returns TRUE when out of memory, FALSE otherwise. --- ---- @return any +--- @return integer function vim.fn.inputsave() end --- This function acts much like the |input()| function with but @@ -4641,7 +4688,7 @@ function vim.fn.inputsave() end --- --- @param prompt string --- @param text? string ---- @return any +--- @return string function vim.fn.inputsecret(prompt, text) end --- When {object} is a |List| or a |Blob| insert {item} at the start @@ -4687,8 +4734,8 @@ function vim.fn.interrupt() end --- let bits = invert(bits) --- < --- ---- @param expr number ---- @return any +--- @param expr integer +--- @return integer function vim.fn.invert(expr) end --- The result is a Number, which is |TRUE| when {path} is an @@ -4767,7 +4814,7 @@ function vim.fn.isnan(expr) end --- cases, items() returns a List with the index and the value at --- the index. --- ---- @param dict any +--- @param dict table --- @return any function vim.fn.items(dict) end @@ -4801,7 +4848,7 @@ function vim.fn.jobresize(job, width, height) end --- @return any function vim.fn.jobsend(...) end ---- Note: Prefer |vim.system()| in Lua (unless using the `pty` option). +--- Note: Prefer |vim.system()| in Lua (unless using `rpc`, `pty`, or `term`). --- --- Spawns {cmd} as a job. --- If {cmd} is a List it runs directly (no 'shell'). @@ -4809,8 +4856,11 @@ function vim.fn.jobsend(...) end --- call jobstart(split(&shell) + split(&shellcmdflag) + ['{cmd}']) --- <(See |shell-unquoting| for details.) --- ---- Example: >vim ---- call jobstart('nvim -h', {'on_stdout':{j,d,e->append(line('.'),d)}}) +--- Example: start a job and handle its output: >vim +--- call jobstart(['nvim', '-h'], {'on_stdout':{j,d,e->append(line('.'),d)}}) +--- < +--- Example: start a job in a |terminal| connected to the current buffer: >vim +--- call jobstart(['nvim', '-h'], {'term':v:true}) --- < --- Returns |job-id| on success, 0 on invalid arguments (or job --- table is full), -1 if {cmd}[0] or 'shell' is not executable. @@ -4875,6 +4925,10 @@ function vim.fn.jobsend(...) end --- stdin: (string) Either "pipe" (default) to connect the --- job's stdin to a channel or "null" to disconnect --- stdin. +--- term: (boolean) Spawns {cmd} in a new pseudo-terminal session +--- connected to the current (unmodified) buffer. Implies "pty". +--- Default "height" and "width" are set to the current window +--- dimensions. |jobstart()|. Defaults $TERM to "xterm-256color". --- width: (number) Width of the `pty` terminal. --- --- {opts} is passed as |self| dictionary to the callback; the @@ -4888,7 +4942,7 @@ function vim.fn.jobsend(...) end --- --- @param cmd string|string[] --- @param opts? table ---- @return any +--- @return integer function vim.fn.jobstart(cmd, opts) end --- Stop |job-id| {id} by sending SIGTERM to the job process. If @@ -4901,7 +4955,7 @@ function vim.fn.jobstart(cmd, opts) end --- exited or stopped. --- --- @param id integer ---- @return any +--- @return integer function vim.fn.jobstop(id) end --- Waits for jobs and their |on_exit| handlers to complete. @@ -4926,7 +4980,7 @@ function vim.fn.jobstop(id) end --- --- @param jobs integer[] --- @param timeout? integer ---- @return any +--- @return integer[] function vim.fn.jobwait(jobs, timeout) end --- Join the items in {list} together into one String. @@ -4941,7 +4995,7 @@ function vim.fn.jobwait(jobs, timeout) end --- --- @param list any[] --- @param sep? string ---- @return any +--- @return string function vim.fn.join(list, sep) end --- Convert {expr} from JSON object. Accepts |readfile()|-style @@ -4974,14 +5028,14 @@ function vim.fn.json_decode(expr) end --- |Blob|s are converted to arrays of the individual bytes. --- --- @param expr any ---- @return any +--- @return string function vim.fn.json_encode(expr) end --- Return a |List| with all the keys of {dict}. The |List| is in --- arbitrary order. Also see |items()| and |values()|. --- --- @param dict table ---- @return any +--- @return string[] function vim.fn.keys(dict) end --- Turn the internal byte representation of keys into a form that @@ -4991,7 +5045,7 @@ function vim.fn.keys(dict) end --- < <C-Home> --- --- @param string string ---- @return any +--- @return string function vim.fn.keytrans(string) end --- @deprecated @@ -5010,8 +5064,8 @@ function vim.fn.last_buffer_nr() end --- |Dictionary| is returned. --- Otherwise an error is given and returns zero. --- ---- @param expr any ---- @return any +--- @param expr any[] +--- @return integer function vim.fn.len(expr) end --- Call function {funcname} in the run-time library {libname} @@ -5122,7 +5176,7 @@ function vim.fn.line2byte(lnum) end --- When {lnum} is invalid, -1 is returned. --- --- @param lnum integer ---- @return any +--- @return integer function vim.fn.lispindent(lnum) end --- Return a Blob concatenating all the number values in {list}. @@ -5135,7 +5189,7 @@ function vim.fn.lispindent(lnum) end --- |blob2list()| does the opposite. --- --- @param list any[] ---- @return any +--- @return string function vim.fn.list2blob(list) end --- Convert each number in {list} to a character string can @@ -5155,13 +5209,13 @@ function vim.fn.list2blob(list) end --- --- @param list any[] --- @param utf8? boolean ---- @return any +--- @return string function vim.fn.list2str(list, utf8) end --- Return the current time, measured as seconds since 1st Jan --- 1970. See also |strftime()|, |strptime()| and |getftime()|. --- ---- @return any +--- @return integer function vim.fn.localtime() end --- Return the natural logarithm (base e) of {expr} as a |Float|. @@ -5175,7 +5229,7 @@ function vim.fn.localtime() end --- < 5.0 --- --- @param expr number ---- @return any +--- @return number function vim.fn.log(expr) end --- Return the logarithm of Float {expr} to base 10 as a |Float|. @@ -5188,7 +5242,7 @@ function vim.fn.log(expr) end --- < -2.0 --- --- @param expr number ---- @return any +--- @return number function vim.fn.log10(expr) end --- {expr1} must be a |List|, |String|, |Blob| or |Dictionary|. @@ -5950,7 +6004,7 @@ function vim.fn.matchstrpos(expr, pat, start, count) end --- an error. An empty |List| or |Dictionary| results in zero. --- --- @param expr any ---- @return any +--- @return number function vim.fn.max(expr) end --- Returns a |List| of |Dictionaries| describing |menus| (defined @@ -6088,7 +6142,7 @@ function vim.fn.menu_info(name, mode) end --- an error. An empty |List| or |Dictionary| results in zero. --- --- @param expr any ---- @return any +--- @return number function vim.fn.min(expr) end --- Create directory {name}. @@ -6133,7 +6187,7 @@ function vim.fn.min(expr) end --- @param name string --- @param flags? string --- @param prot? string ---- @return any +--- @return integer function vim.fn.mkdir(name, flags, prot) end --- Return a string that indicates the current mode. @@ -6296,7 +6350,7 @@ function vim.fn.msgpackparse(data) end --- See also |prevnonblank()|. --- --- @param lnum integer ---- @return any +--- @return integer function vim.fn.nextnonblank(lnum) end --- Return a string with a single character, which has the number @@ -6315,7 +6369,7 @@ function vim.fn.nextnonblank(lnum) end --- --- @param expr integer --- @param utf8? boolean ---- @return any +--- @return string function vim.fn.nr2char(expr, utf8) end --- Bitwise OR on the two arguments. The arguments are converted @@ -6349,7 +6403,7 @@ vim.fn['or'] = function(expr, expr1) end --- --- @param path string --- @param len? integer ---- @return any +--- @return string function vim.fn.pathshorten(path, len) end --- Evaluate |perl| expression {expr} and return its result @@ -6383,7 +6437,7 @@ function vim.fn.perleval(expr) end --- --- @param x number --- @param y number ---- @return any +--- @return number function vim.fn.pow(x, y) end --- Return the line number of the first line at or above {lnum} @@ -6395,7 +6449,7 @@ function vim.fn.pow(x, y) end --- Also see |nextnonblank()|. --- --- @param lnum integer ---- @return any +--- @return integer function vim.fn.prevnonblank(lnum) end --- Return a String with {fmt}, where "%" items are replaced by @@ -7014,10 +7068,11 @@ function vim.fn.readfile(fname, type, max) end --- echo reduce('xyz', { acc, val -> acc .. ',' .. val }) --- < --- +--- @generic T --- @param object any ---- @param func function +--- @param func fun(accumulator: T, current: any): any --- @param initial? any ---- @return any +--- @return T function vim.fn.reduce(object, func, initial) end --- Returns the single letter name of the register being executed. @@ -7170,7 +7225,7 @@ function vim.fn.remove(dict, key) end --- --- @param from string --- @param to string ---- @return any +--- @return integer function vim.fn.rename(from, to) end --- Repeat {expr} {count} times and return the concatenated @@ -7200,7 +7255,7 @@ vim.fn['repeat'] = function(expr, count) end --- path name) and also keeps a trailing path separator. --- --- @param filename string ---- @return any +--- @return string function vim.fn.resolve(filename) end --- Reverse the order of items in {object}. {object} can be a @@ -7213,8 +7268,9 @@ function vim.fn.resolve(filename) end --- let revlist = reverse(copy(mylist)) --- < --- ---- @param object any ---- @return any +--- @generic T +--- @param object T[] +--- @return T[] function vim.fn.reverse(object) end --- Round off {expr} to the nearest integral value and return it @@ -7231,7 +7287,7 @@ function vim.fn.reverse(object) end --- < -5.0 --- --- @param expr number ---- @return any +--- @return number function vim.fn.round(expr) end --- Sends {event} to {channel} via |RPC| and returns immediately. @@ -7242,9 +7298,9 @@ function vim.fn.round(expr) end --- --- @param channel integer --- @param event string ---- @param args? any ---- @return any -function vim.fn.rpcnotify(channel, event, args) end +--- @param ... any +--- @return integer +function vim.fn.rpcnotify(channel, event, ...) end --- Sends a request to {channel} to invoke {method} via --- |RPC| and blocks until a response is received. @@ -7254,9 +7310,9 @@ function vim.fn.rpcnotify(channel, event, args) end --- --- @param channel integer --- @param method string ---- @param args? any +--- @param ... any --- @return any -function vim.fn.rpcrequest(channel, method, args) end +function vim.fn.rpcrequest(channel, method, ...) end --- @deprecated --- Deprecated. Replace >vim @@ -7300,7 +7356,7 @@ function vim.fn.rubyeval(expr) end --- --- @param row integer --- @param col integer ---- @return any +--- @return integer function vim.fn.screenattr(row, col) end --- The result is a Number, which is the character at position @@ -7314,7 +7370,7 @@ function vim.fn.screenattr(row, col) end --- --- @param row integer --- @param col integer ---- @return any +--- @return integer function vim.fn.screenchar(row, col) end --- The result is a |List| of Numbers. The first number is the same @@ -7325,7 +7381,7 @@ function vim.fn.screenchar(row, col) end --- --- @param row integer --- @param col integer ---- @return any +--- @return integer[] function vim.fn.screenchars(row, col) end --- The result is a Number, which is the current screen column of @@ -7342,7 +7398,7 @@ function vim.fn.screenchars(row, col) end --- noremap GG <Cmd>echom screencol()<CR> --- < --- ---- @return any +--- @return integer[] function vim.fn.screencol() end --- The result is a Dict with the screen position of the text @@ -7381,7 +7437,7 @@ function vim.fn.screenpos(winid, lnum, col) end --- --- Note: Same restrictions as with |screencol()|. --- ---- @return any +--- @return integer function vim.fn.screenrow() end --- The result is a String that contains the base character and @@ -7393,7 +7449,7 @@ function vim.fn.screenrow() end --- --- @param row integer --- @param col integer ---- @return any +--- @return string function vim.fn.screenstring(row, col) end --- Search for regexp pattern {pattern}. The search starts at the @@ -7505,7 +7561,7 @@ function vim.fn.screenstring(row, col) end --- @param stopline? integer --- @param timeout? integer --- @param skip? string|function ---- @return any +--- @return integer function vim.fn.search(pattern, flags, stopline, timeout, skip) end --- Get or update the last search count, like what is displayed @@ -7798,7 +7854,7 @@ function vim.fn.searchpos(pattern, flags, stopline, timeout, skip) end --- echo serverlist() --- < --- ---- @return any +--- @return string[] function vim.fn.serverlist() end --- Opens a socket or named pipe at {address} and listens for @@ -7835,7 +7891,7 @@ function vim.fn.serverlist() end --- < --- --- @param address? string ---- @return any +--- @return string function vim.fn.serverstart(address) end --- Closes the pipe or socket at {address}. @@ -7844,7 +7900,7 @@ function vim.fn.serverstart(address) end --- address in |serverlist()|. --- --- @param address string ---- @return any +--- @return integer function vim.fn.serverstop(address) end --- Set line {lnum} to {text} in buffer {buf}. This works like @@ -7874,7 +7930,7 @@ function vim.fn.serverstop(address) end --- @param buf integer|string --- @param lnum integer --- @param text string|string[] ---- @return any +--- @return integer function vim.fn.setbufline(buf, lnum, text) end --- Set option or local variable {varname} in buffer {buf} to @@ -7979,7 +8035,7 @@ function vim.fn.setcharsearch(dict) end --- --- @param str string --- @param pos? integer ---- @return any +--- @return integer function vim.fn.setcmdline(str, pos) end --- Set the cursor position in the command line to byte position @@ -8289,7 +8345,7 @@ function vim.fn.setpos(expr, list) end --- @param list vim.quickfix.entry[] --- @param action? string --- @param what? vim.fn.setqflist.what ---- @return any +--- @return integer function vim.fn.setqflist(list, action, what) end --- Set the register {regname} to {value}. @@ -8442,7 +8498,7 @@ function vim.fn.setwinvar(nr, varname, val) end --- checksum of {string}. --- --- @param string string ---- @return any +--- @return string function vim.fn.sha256(string) end --- Escape {string} for use as a shell command argument. @@ -8478,7 +8534,7 @@ function vim.fn.sha256(string) end --- --- @param string string --- @param special? boolean ---- @return any +--- @return string function vim.fn.shellescape(string, special) end --- Returns the effective value of 'shiftwidth'. This is the @@ -8930,7 +8986,7 @@ function vim.fn.sign_unplacelist(list) end --- links before simplifying the path name, use |resolve()|. --- --- @param filename string ---- @return any +--- @return string function vim.fn.simplify(filename) end --- Return the sine of {expr}, measured in radians, as a |Float|. @@ -8943,7 +8999,7 @@ function vim.fn.simplify(filename) end --- < 0.763301 --- --- @param expr number ---- @return any +--- @return number function vim.fn.sin(expr) end --- Return the hyperbolic sine of {expr} as a |Float| in the range @@ -9077,10 +9133,11 @@ function vim.fn.sockconnect(mode, address, opts) end --- eval mylist->sort({i1, i2 -> i1 - i2}) --- < --- ---- @param list any +--- @generic T +--- @param list T[] --- @param how? string|function --- @param dict? any ---- @return any +--- @return T[] function vim.fn.sort(list, how, dict) end --- Return the sound-folded equivalent of {word}. Uses the first @@ -9091,7 +9148,7 @@ function vim.fn.sort(list, how, dict) end --- the method can be quite slow. --- --- @param word string ---- @return any +--- @return string function vim.fn.soundfold(word) end --- Without argument: The result is the badly spelled word under @@ -9144,7 +9201,7 @@ function vim.fn.spellbadword(sentence) end --- @param word string --- @param max? integer --- @param capital? boolean ---- @return any +--- @return string[] function vim.fn.spellsuggest(word, max, capital) end --- Make a |List| out of {string}. When {pattern} is omitted or @@ -9174,7 +9231,7 @@ function vim.fn.spellsuggest(word, max, capital) end --- @param string string --- @param pattern? string --- @param keepempty? boolean ---- @return any +--- @return string[] function vim.fn.split(string, pattern, keepempty) end --- Return the non-negative square root of Float {expr} as a @@ -9326,6 +9383,7 @@ function vim.fn.str2float(string, quoted) end --- and exists only for backwards-compatibility. --- With UTF-8 composing characters are handled properly: >vim --- echo str2list("á") " returns [97, 769] +--- < --- --- @param string string --- @param utf8? boolean @@ -10160,23 +10218,12 @@ function vim.fn.tanh(expr) end --- @return string function vim.fn.tempname() end ---- Spawns {cmd} in a new pseudo-terminal session connected ---- to the current (unmodified) buffer. Parameters and behavior ---- are the same as |jobstart()| except "pty", "width", "height", ---- and "TERM" are ignored: "height" and "width" are taken from ---- the current window. Note that termopen() implies a "pty" arg ---- to jobstart(), and thus has the implications documented at ---- |jobstart()|. ---- ---- Returns the same values as jobstart(). ---- ---- Terminal environment is initialized as in |jobstart-env|, ---- except $TERM is set to "xterm-256color". Full behavior is ---- described in |terminal|. +--- @deprecated +--- Use |jobstart()| with `{term: v:true}` instead. --- --- @param cmd string|string[] --- @param opts? table ---- @return any +--- @return integer function vim.fn.termopen(cmd, opts) end --- Return a list with information about timers. @@ -10576,7 +10623,7 @@ function vim.fn.virtcol(expr, list, winid) end --- @param winid integer --- @param lnum integer --- @param col integer ---- @return any +--- @return integer function vim.fn.virtcol2col(winid, lnum, col) end --- The result is a String, which describes the last Visual mode @@ -10597,7 +10644,7 @@ function vim.fn.virtcol2col(winid, lnum, col) end --- the old value is returned. See |non-zero-arg|. --- --- @param expr? boolean ---- @return any +--- @return string function vim.fn.visualmode(expr) end --- Waits until {condition} evaluates to |TRUE|, where {condition} @@ -10714,7 +10761,7 @@ function vim.fn.win_id2tabwin(expr) end --- Return 0 if the window cannot be found in the current tabpage. --- --- @param expr integer ---- @return any +--- @return integer function vim.fn.win_id2win(expr) end --- Move window {nr}'s vertical separator (i.e., the right border) @@ -10868,7 +10915,7 @@ function vim.fn.winheight(nr) end --- < --- --- @param tabnr? integer ---- @return any +--- @return any[] function vim.fn.winlayout(tabnr) end --- The result is a Number, which is the screen line of the cursor @@ -10912,7 +10959,7 @@ function vim.fn.winline() end --- < --- --- @param arg? string|integer ---- @return any +--- @return integer function vim.fn.winnr(arg) end --- Returns a sequence of |:resize| commands that should restore @@ -10925,7 +10972,7 @@ function vim.fn.winnr(arg) end --- exe cmd --- < --- ---- @return any +--- @return string function vim.fn.winrestcmd() end --- Uses the |Dictionary| returned by |winsaveview()| to restore @@ -10990,7 +11037,7 @@ function vim.fn.winsaveview() end --- option. --- --- @param nr integer ---- @return any +--- @return integer function vim.fn.winwidth(nr) end --- The result is a dictionary of byte/chars/word statistics for @@ -11075,7 +11122,7 @@ function vim.fn.writefile(object, fname, flags) end --- let bits = xor(bits, 0x80) --- < --- ---- @param expr number ---- @param expr1 number ---- @return any +--- @param expr integer +--- @param expr1 integer +--- @return integer function vim.fn.xor(expr, expr1) end diff --git a/runtime/lua/vim/_meta/vvars.lua b/runtime/lua/vim/_meta/vvars.lua index 8784fdbac9..c1b8695bbf 100644 --- a/runtime/lua/vim/_meta/vvars.lua +++ b/runtime/lua/vim/_meta/vvars.lua @@ -15,7 +15,7 @@ vim.v.argv = ... --- Argument for evaluating 'formatexpr' and used for the typed --- character when using <expr> in an abbreviation `:map-<expr>`. --- It is also used by the `InsertCharPre` and `InsertEnter` events. ---- @type any +--- @type string vim.v.char = ... --- The name of the character encoding of a file to be converted. @@ -60,7 +60,7 @@ vim.v.collate = ... --- mode. --- Note: Plugins can modify the value to emulate the builtin --- `CompleteDone` event behavior. ---- @type any +--- @type vim.v.completed_item vim.v.completed_item = ... --- The count given for the last Normal mode command. Can be used @@ -90,7 +90,7 @@ vim.v.count1 = ... --- This variable can not be set directly, use the `:language` --- command. --- See `multi-lang`. ---- @type any +--- @type string vim.v.ctype = ... --- Normally zero. When a deadly signal is caught it's set to @@ -197,11 +197,14 @@ vim.v.errors = ... --- changing window (or tab) on `DirChanged`. --- status Job status or exit code, -1 means "unknown". `TermClose` --- reason Reason for completion being done. `CompleteDone` ---- @type any +--- complete_word The word that was selected, empty if abandoned complete. +--- complete_type See `complete_info_mode` +--- @type vim.v.event vim.v.event = ... --- The value of the exception most recently caught and not ---- finished. See also `v:throwpoint` and `throw-variables`. +--- finished. See also `v:stacktrace`, `v:throwpoint`, and +--- `throw-variables`. --- Example: --- --- ```vim @@ -223,7 +226,7 @@ vim.v.exception = ... --- ```vim --- :au VimLeave * echo "Exit value is " .. v:exiting --- ``` ---- @type any +--- @type integer? vim.v.exiting = ... --- Special value used to put "false" in JSON and msgpack. See @@ -419,7 +422,7 @@ vim.v.mouse_winid = ... --- and `msgpackdump()`. All types inside dictionary are fixed --- (not editable) empty lists. To check whether some list is one --- of msgpack types, use `is` operator. ---- @type any +--- @type table vim.v.msgpack_types = ... --- Special value used to put "null" in JSON and NIL in msgpack. @@ -563,7 +566,7 @@ vim.v.relnum = ... --- typed command. --- This can be used to find out why your script causes the --- hit-enter prompt. ---- @type any +--- @type string vim.v.scrollstart = ... --- Search direction: 1 after a forward search, 0 after a @@ -614,6 +617,13 @@ vim.v.servername = ... --- @type integer vim.v.shell_error = ... +--- The stack trace of the exception most recently caught and +--- not finished. Refer to `getstacktrace()` for the structure of +--- stack trace. See also `v:exception`, `v:throwpoint`, and +--- `throw-variables`. +--- @type table[] +vim.v.stacktrace = ... + --- Last given status message. --- Modifiable (can be set). --- @type string @@ -705,18 +715,18 @@ vim.v.termrequest = ... vim.v.termresponse = ... --- Must be set before using `test_garbagecollect_now()`. ---- @type any +--- @type integer vim.v.testing = ... --- Full filename of the last loaded or saved session file. --- Empty when no session file has been saved. See `:mksession`. --- Modifiable (can be set). ---- @type any +--- @type string vim.v.this_session = ... --- The point where the exception most recently caught and not --- finished was thrown. Not set when commands are typed. See ---- also `v:exception` and `throw-variables`. +--- also `v:exception`, `v:stacktrace`, and `throw-variables`. --- Example: --- --- ```vim @@ -728,7 +738,7 @@ vim.v.this_session = ... --- ``` --- --- Output: "Exception from test.vim, line 2" ---- @type any +--- @type string vim.v.throwpoint = ... --- Special value used to put "true" in JSON and msgpack. See diff --git a/runtime/lua/vim/_meta/vvars_extra.lua b/runtime/lua/vim/_meta/vvars_extra.lua new file mode 100644 index 0000000000..7ef3021e89 --- /dev/null +++ b/runtime/lua/vim/_meta/vvars_extra.lua @@ -0,0 +1,77 @@ +--- @meta _ +error('Cannot require a meta file') + +--- Extra types for vim.v dictionary fields + +--- @class vim.v.completed_item +--- @field word? string the text that will be inserted, mandatory +--- abbreviation of "word"; when not empty it is used in the menu instead of "word" +--- @field abbr? string +--- extra text for the popup menu, displayed after "word" or "abbr" +--- @field menu? string +--- more information about the item, can be displayed in a preview window +--- @field info? string +--- @field kind? string single letter indicating the type of completion +--- when non-zero case is to be ignored when comparing items to be equal; when +--- omitted zero is used, thus items that only differ in case are added +--- @field icase? integer +--- when non-zero, always treat this item to be equal when comparing. Which +--- means, "equal=1" disables filtering of this item. +--- @field equal? integer +--- when non-zero this match will be added even when an item with the same word +--- is already present. +--- @field dup? integer +--- when non-zero this match will be added even when it is an empty string +--- @field empty? integer +--- custom data which is associated with the item and available +--- in |v:completed_item|; it can be any type; defaults to an empty string +--- @field user_data? any +--- an additional highlight group whose attributes are combined +--- with |hl-PmenuSel| and |hl-Pmenu| or |hl-PmenuMatchSel| and |hl-PmenuMatch| +--- highlight attributes in the popup menu to apply cterm and gui properties +--- (with higher priority) like strikethrough to the completion items abbreviation +--- @field abbr_hlgroup? string +--- an additional highlight group specifically for setting the highlight +--- attributes of the completion kind. When this field is present, it will +--- override the |hl-PmenuKind| highlight group, allowing for the customization +--- of ctermfg and guifg properties for the completion kind +--- @field kind_hlgroup? string + +--- @class vim.v.event +--- Whether the event triggered during an aborting condition (e.g. |c_Esc| or +--- |c_CTRL-C| for |CmdlineLeave|). +--- @field abort? boolean +--- @field chan? integer See |channel-id| +--- @field info? table Dict of arbitrary event data. +--- @field cmdlevel? integer Level of cmdline. +--- @field cmdtype? string Type of cmdline, |cmdline-char|. +--- @field cwd? string Current working directory. +--- @field inclusive? boolean Motion is |inclusive|, else exclusive. +--- @field scope? string Event-specific scope name. +--- Current |operator|. Also set for Ex commands (unlike |v:operator|). For +--- example if |TextYankPost| is triggered by the |:yank| Ex command then +--- `v:event.operator` is "y". +--- @field operator? string +--- Text stored in the register as a |readfile()|-style list of lines. +--- @field regcontents? string +--- Requested register (e.g "x" for "xyy) or the empty string for an unnamed operation. +--- @field regname? string +--- @field regtype? string Type of register as returned by |getregtype()|. +--- @field visual? boolean Selection is visual (as opposed to, e.g., via motion). +--- @field completed_item? vim.v.completed_item +--- Current selected complete item on |CompleteChanged|, Is `{}` when no +--- complete item selected. +--- @field height? integer +--- @field width? integer Height of popup menu on |CompleteChanged| +--- @field row? integer Width of popup menu on |CompleteChanged| +--- Col count of popup menu on |CompleteChanged|, relative to screen. +--- @field col? integer +--- @field size? integer Total number of completion items on |CompleteChanged|. +--- Is |v:true| if popup menu have scrollbar, or |v:false| if not. +--- @field scrollbar? boolean +--- Is |v:true| if the event fired while changing window (or tab) on |DirChanged|. +--- @field changed_window? boolean +--- @field status? boolean Job status or exit code, -1 means "unknown". |TermClose| +--- @field reason? string Reason for completion being done. |CompleteDone| +--- The word that was selected, empty if abandoned complete. @field complete_word? string +--- @field complete_type? string See |complete_info_mode| diff --git a/runtime/lua/vim/_options.lua b/runtime/lua/vim/_options.lua index 77d7054626..973ad87ee8 100644 --- a/runtime/lua/vim/_options.lua +++ b/runtime/lua/vim/_options.lua @@ -229,10 +229,8 @@ end --- global value of a |global-local| option, see |:setglobal|. --- </pre> ---- Get or set |options|. Like `:set`. Invalid key is an error. ---- ---- Note: this works on both buffer-scoped and window-scoped options using the ---- current buffer and window. +--- Get or set |options|. Works like `:set`, so buffer/window-scoped options target the current +--- buffer/window. Invalid key is an error. --- --- Example: --- @@ -690,6 +688,7 @@ local function remove_value(info, current, new) end local function create_option_accessor(scope) + --- @diagnostic disable-next-line: no-unknown local option_mt local function make_option(name, value) @@ -698,6 +697,7 @@ local function create_option_accessor(scope) if type(value) == 'table' and getmetatable(value) == option_mt then assert(name == value._name, "must be the same value, otherwise that's weird.") + --- @diagnostic disable-next-line: no-unknown value = value._value end @@ -721,6 +721,7 @@ local function create_option_accessor(scope) end, append = function(self, right) + --- @diagnostic disable-next-line: no-unknown self._value = add_value(self._info, self._value, right) self:_set() end, @@ -730,6 +731,7 @@ local function create_option_accessor(scope) end, prepend = function(self, right) + --- @diagnostic disable-next-line: no-unknown self._value = prepend_value(self._info, self._value, right) self:_set() end, @@ -739,6 +741,7 @@ local function create_option_accessor(scope) end, remove = function(self, right) + --- @diagnostic disable-next-line: no-unknown self._value = remove_value(self._info, self._value, right) self:_set() end, @@ -770,7 +773,7 @@ end --- --- --- A special interface |vim.opt| exists for conveniently interacting with list- ---- and map-style option from Lua: It allows accessing them as Lua tables and +--- and map-style options from Lua: It allows accessing them as Lua tables and --- offers object-oriented method for adding and removing entries. --- --- Examples: ~ diff --git a/runtime/lua/vim/_system.lua b/runtime/lua/vim/_system.lua index ce5dbffeaa..157172447a 100644 --- a/runtime/lua/vim/_system.lua +++ b/runtime/lua/vim/_system.lua @@ -47,15 +47,6 @@ local function close_handle(handle) end end ----@param state vim.SystemState -local function close_handles(state) - close_handle(state.handle) - close_handle(state.stdin) - close_handle(state.stdout) - close_handle(state.stderr) - close_handle(state.timer) -end - --- @class vim.SystemObj --- @field cmd string[] --- @field pid integer @@ -88,7 +79,8 @@ function SystemObj:_timeout(signal) self:kill(signal or SIG.TERM) end -local MAX_TIMEOUT = 2 ^ 31 +-- Use max 32-bit signed int value to avoid overflow on 32-bit systems. #31633 +local MAX_TIMEOUT = 2 ^ 31 - 1 --- @param timeout? integer --- @return vim.SystemCompleted @@ -132,9 +124,7 @@ function SystemObj:write(data) -- (https://github.com/neovim/neovim/pull/17620#discussion_r820775616) stdin:write('', function() stdin:shutdown(function() - if stdin then - stdin:close() - end + close_handle(stdin) end) end) end @@ -146,25 +136,52 @@ function SystemObj:is_closing() return handle == nil or handle:is_closing() or false end ----@param output fun(err:string?, data: string?)|false ----@return uv.uv_stream_t? ----@return fun(err:string?, data: string?)? Handler -local function setup_output(output) - if output == nil then - return assert(uv.new_pipe(false)), nil +--- @param output? uv.read_start.callback|false +--- @param text? boolean +--- @return uv.uv_stream_t? pipe +--- @return uv.read_start.callback? handler +--- @return string[]? data +local function setup_output(output, text) + if output == false then + return end + local bucket --- @type string[]? + local handler --- @type uv.read_start.callback + if type(output) == 'function' then - return assert(uv.new_pipe(false)), output + handler = output + else + bucket = {} + handler = function(err, data) + if err then + error(err) + end + if text and data then + bucket[#bucket + 1] = data:gsub('\r\n', '\n') + else + bucket[#bucket + 1] = data + end + end end - assert(output == false) - return nil, nil + local pipe = assert(uv.new_pipe(false)) + + --- @type uv.read_start.callback + local function handler_with_close(err, data) + handler(err, data) + if data == nil then + pipe:read_stop() + pipe:close() + end + end + + return pipe, handler_with_close, bucket end ----@param input string|string[]|true|nil ----@return uv.uv_stream_t? ----@return string|string[]? +--- @param input? string|string[]|boolean +--- @return uv.uv_stream_t? +--- @return string|string[]? local function setup_input(input) if not input then return @@ -208,28 +225,6 @@ local function setup_env(env, clear_env) return renv end ---- @param stream uv.uv_stream_t ---- @param text? boolean ---- @param bucket string[] ---- @return fun(err: string?, data: string?) -local function default_handler(stream, text, bucket) - return function(err, data) - if err then - error(err) - end - if data ~= nil then - if text then - bucket[#bucket + 1] = data:gsub('\r\n', '\n') - else - bucket[#bucket + 1] = data - end - else - stream:read_stop() - stream:close() - end - end -end - local is_win = vim.fn.has('win32') == 1 local M = {} @@ -255,9 +250,9 @@ local function spawn(cmd, opts, on_exit, on_error) return handle, pid_or_err --[[@as integer]] end ----@param timeout integer ----@param cb fun() ----@return uv.uv_timer_t +--- @param timeout integer +--- @param cb fun() +--- @return uv.uv_timer_t local function timer_oneshot(timeout, cb) local timer = assert(uv.new_timer()) timer:start(timeout, 0, function() @@ -273,7 +268,12 @@ end --- @param signal integer --- @param on_exit fun(result: vim.SystemCompleted)? local function _on_exit(state, code, signal, on_exit) - close_handles(state) + close_handle(state.handle) + close_handle(state.stdin) + close_handle(state.timer) + + -- #30846: Do not close stdout/stderr here, as they may still have data to + -- read. They will be closed in uv.read_start on EOF. local check = assert(uv.new_check()) check:start(function() @@ -311,6 +311,15 @@ local function _on_exit(state, code, signal, on_exit) end) end +--- @param state vim.SystemState +local function _on_error(state) + close_handle(state.handle) + close_handle(state.stdin) + close_handle(state.stdout) + close_handle(state.stderr) + close_handle(state.timer) +end + --- Run a system command --- --- @param cmd string[] @@ -324,8 +333,8 @@ function M.run(cmd, opts, on_exit) opts = opts or {} - local stdout, stdout_handler = setup_output(opts.stdout) - local stderr, stderr_handler = setup_output(opts.stderr) + local stdout, stdout_handler, stdout_data = setup_output(opts.stdout, opts.text) + local stderr, stderr_handler, stderr_data = setup_output(opts.stderr, opts.text) local stdin, towrite = setup_input(opts.stdin) --- @type vim.SystemState @@ -335,7 +344,9 @@ function M.run(cmd, opts, on_exit) timeout = opts.timeout, stdin = stdin, stdout = stdout, + stdout_data = stdout_data, stderr = stderr, + stderr_data = stderr_data, } --- @diagnostic disable-next-line:missing-fields @@ -350,17 +361,15 @@ function M.run(cmd, opts, on_exit) }, function(code, signal) _on_exit(state, code, signal, on_exit) end, function() - close_handles(state) + _on_error(state) end) - if stdout then - state.stdout_data = {} - stdout:read_start(stdout_handler or default_handler(stdout, opts.text, state.stdout_data)) + if stdout and stdout_handler then + stdout:read_start(stdout_handler) end - if stderr then - state.stderr_data = {} - stderr:read_start(stderr_handler or default_handler(stderr, opts.text, state.stderr_data)) + if stderr and stderr_handler then + stderr:read_start(stderr_handler) end local obj = new_systemobj(state) diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 4fb8c6a686..621945aedd 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -2,6 +2,20 @@ local api, if_nil = vim.api, vim.F.if_nil local M = {} +--- @param title string +--- @return integer? +local function get_qf_id_for_title(title) + local lastqflist = vim.fn.getqflist({ nr = '$' }) + for i = 1, lastqflist.nr do + local qflist = vim.fn.getqflist({ nr = i, id = 0, title = 0 }) + if qflist.title == title then + return qflist.id + end + end + + return nil +end + --- [diagnostic-structure]() --- --- Diagnostics use the same indexing as the rest of the Nvim API (i.e. 0-based @@ -56,9 +70,13 @@ local M = {} --- Use virtual text for diagnostics. If multiple diagnostics are set for a --- namespace, one prefix per diagnostic + the last diagnostic message are --- shown. ---- (default: `true`) +--- (default: `false`) --- @field virtual_text? boolean|vim.diagnostic.Opts.VirtualText|fun(namespace: integer, bufnr:integer): vim.diagnostic.Opts.VirtualText --- +--- Use virtual lines for diagnostics. +--- (default: `false`) +--- @field virtual_lines? boolean|vim.diagnostic.Opts.VirtualLines|fun(namespace: integer, bufnr:integer): vim.diagnostic.Opts.VirtualLines +--- --- Use signs for diagnostics |diagnostic-signs|. --- (default: `true`) --- @field signs? boolean|vim.diagnostic.Opts.Signs|fun(namespace: integer, bufnr:integer): vim.diagnostic.Opts.Signs @@ -87,6 +105,7 @@ local M = {} --- @field update_in_insert boolean --- @field underline vim.diagnostic.Opts.Underline --- @field virtual_text vim.diagnostic.Opts.VirtualText +--- @field virtual_lines vim.diagnostic.Opts.VirtualLines --- @field signs vim.diagnostic.Opts.Signs --- @field severity_sort {reverse?:boolean} @@ -131,10 +150,11 @@ local M = {} --- Overrides the setting from |vim.diagnostic.config()|. --- @field source? boolean|'if_many' --- ---- A function that takes a diagnostic as input and returns a string. ---- The return value is the text used to display the diagnostic. +--- A function that takes a diagnostic as input and returns a string or nil. +--- If the return value is nil, the diagnostic is not displayed by the handler. +--- Else the output text is used to display the diagnostic. --- Overrides the setting from |vim.diagnostic.config()|. ---- @field format? fun(diagnostic:vim.Diagnostic): string +--- @field format? fun(diagnostic:vim.Diagnostic): string? --- --- Prefix each diagnostic in the floating window: --- - If a `function`, {i} is the index of the diagnostic being evaluated and @@ -170,6 +190,10 @@ local M = {} --- severity |diagnostic-severity| --- @field severity? vim.diagnostic.SeverityFilter --- +--- Only show diagnostics for the current line. +--- (default `false`) +--- @field current_line? boolean +--- --- Include the diagnostic source in virtual text. Use `'if_many'` to only --- show sources if there is more than one diagnostic source in the buffer. --- Otherwise, any truthy value means to always show the diagnostic source. @@ -188,7 +212,7 @@ local M = {} --- This can be used to render an LSP diagnostic error code. --- @field suffix? string|(fun(diagnostic:vim.Diagnostic): string) --- ---- The return value is the text used to display the diagnostic. Example: +--- If not nil, the return value is the text used to display the diagnostic. Example: --- ```lua --- function(diagnostic) --- if diagnostic.severity == vim.diagnostic.severity.ERROR then @@ -197,7 +221,8 @@ local M = {} --- return diagnostic.message --- end --- ``` ---- @field format? fun(diagnostic:vim.Diagnostic): string +--- If the return value is nil, the diagnostic is not displayed by the handler. +--- @field format? fun(diagnostic:vim.Diagnostic): string? --- --- See |nvim_buf_set_extmark()|. --- @field hl_mode? 'replace'|'combine'|'blend' @@ -206,7 +231,7 @@ local M = {} --- @field virt_text? [string,any][] --- --- See |nvim_buf_set_extmark()|. ---- @field virt_text_pos? 'eol'|'overlay'|'right_align'|'inline' +--- @field virt_text_pos? 'eol'|'eol_right_align'|'inline'|'overlay'|'right_align' --- --- See |nvim_buf_set_extmark()|. --- @field virt_text_win_col? integer @@ -214,6 +239,17 @@ local M = {} --- See |nvim_buf_set_extmark()|. --- @field virt_text_hide? boolean +--- @class vim.diagnostic.Opts.VirtualLines +--- +--- Only show diagnostics for the current line. +--- (default: `false`) +--- @field current_line? boolean +--- +--- A function that takes a diagnostic as input and returns a string or nil. +--- If the return value is nil, the diagnostic is not displayed by the handler. +--- Else the output text is used to display the diagnostic. +--- @field format? fun(diagnostic:vim.Diagnostic): string? + --- @class vim.diagnostic.Opts.Signs --- --- Only show virtual text for diagnostics matching the given @@ -298,7 +334,8 @@ M.severity = { local global_diagnostic_options = { signs = true, underline = true, - virtual_text = true, + virtual_text = false, + virtual_lines = false, float = true, update_in_insert = false, severity_sort = false, @@ -342,7 +379,7 @@ local bufnr_and_namespace_cacher_mt = { -- bufnr -> ns -> Diagnostic[] local diagnostic_cache = {} --- @type table<integer,table<integer,vim.Diagnostic[]>> do - local group = api.nvim_create_augroup('DiagnosticBufWipeout', {}) + local group = api.nvim_create_augroup('nvim.diagnostic.buf_wipeout', {}) setmetatable(diagnostic_cache, { --- @param t table<integer,vim.Diagnostic[]> --- @param bufnr integer @@ -473,15 +510,21 @@ local function prefix_source(diagnostics) end, diagnostics) end +--- @param format fun(vim.Diagnostic): string? --- @param diagnostics vim.Diagnostic[] --- @return vim.Diagnostic[] local function reformat_diagnostics(format, diagnostics) vim.validate('format', format, 'function') vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics') - local formatted = vim.deepcopy(diagnostics, true) - for _, diagnostic in ipairs(formatted) do - diagnostic.message = format(diagnostic) + local formatted = {} + for _, diagnostic in ipairs(diagnostics) do + local message = format(diagnostic) + if message ~= nil then + local formatted_diagnostic = vim.deepcopy(diagnostic, true) + formatted_diagnostic.message = message + table.insert(formatted, formatted_diagnostic) + end end return formatted end @@ -567,17 +610,11 @@ end -- TODO(lewis6991): these highlight maps can only be indexed with an integer, however there usage -- implies they can be indexed with any vim.diagnostic.Severity local virtual_text_highlight_map = make_highlight_map('VirtualText') +local virtual_lines_highlight_map = make_highlight_map('VirtualLines') local underline_highlight_map = make_highlight_map('Underline') local floating_highlight_map = make_highlight_map('Floating') local sign_highlight_map = make_highlight_map('Sign') -local function get_bufnr(bufnr) - if not bufnr or bufnr == 0 then - return api.nvim_get_current_buf() - end - return bufnr -end - --- @param diagnostics vim.Diagnostic[] --- @return table<integer,vim.Diagnostic[]> local function diagnostic_lines(diagnostics) @@ -597,6 +634,26 @@ local function diagnostic_lines(diagnostics) return diagnostics_by_line end +--- @param diagnostics table<integer, vim.Diagnostic[]> +--- @return vim.Diagnostic[] +local function diagnostics_at_cursor(diagnostics) + local lnum = api.nvim_win_get_cursor(0)[1] - 1 + + if diagnostics[lnum] ~= nil then + return diagnostics[lnum] + end + + local cursor_diagnostics = {} + for _, line_diags in pairs(diagnostics) do + for _, diag in ipairs(line_diags) do + if diag.end_lnum and lnum >= diag.lnum and lnum <= diag.end_lnum then + table.insert(cursor_diagnostics, diag) + end + end + end + return cursor_diagnostics +end + --- @param namespace integer --- @param bufnr integer --- @param diagnostics vim.Diagnostic[] @@ -640,7 +697,7 @@ end --- @param namespace integer --- @param bufnr? integer local function save_extmarks(namespace, bufnr) - bufnr = get_bufnr(bufnr) + bufnr = vim._resolve_bufnr(bufnr) if not diagnostic_attached_buffers[bufnr] then api.nvim_buf_attach(bufnr, false, { on_lines = function(_, _, _, _, _, last) @@ -812,7 +869,7 @@ local function get_diagnostics(bufnr, opts, clamp) end end elseif namespace == nil then - bufnr = get_bufnr(bufnr) + bufnr = vim._resolve_bufnr(bufnr) for iter_namespace in pairs(diagnostic_cache[bufnr]) do add_all_diags(bufnr, diagnostic_cache[bufnr][iter_namespace]) end @@ -823,7 +880,7 @@ local function get_diagnostics(bufnr, opts, clamp) end end else - bufnr = get_bufnr(bufnr) + bufnr = vim._resolve_bufnr(bufnr) for _, iter_namespace in ipairs(namespace) do add_all_diags(bufnr, diagnostic_cache[bufnr][iter_namespace] or {}) end @@ -847,13 +904,34 @@ local function set_list(loclist, opts) -- numbers beyond the end of the buffer local diagnostics = get_diagnostics(bufnr, opts --[[@as vim.diagnostic.GetOpts]], false) local items = M.toqflist(diagnostics) + local qf_id = nil if loclist then - vim.fn.setloclist(winnr, {}, ' ', { title = title, items = items }) + vim.fn.setloclist(winnr, {}, 'u', { title = title, items = items }) else - vim.fn.setqflist({}, ' ', { title = title, items = items }) + qf_id = get_qf_id_for_title(title) + + -- If we already have a diagnostics quickfix, update it rather than creating a new one. + -- This avoids polluting the finite set of quickfix lists, and preserves the currently selected + -- entry. + vim.fn.setqflist({}, qf_id and 'u' or ' ', { + title = title, + items = items, + id = qf_id, + }) end + if open then - api.nvim_command(loclist and 'lwindow' or 'botright cwindow') + if not loclist then + -- First navigate to the diagnostics quickfix list. + --- @type integer + local nr = vim.fn.getqflist({ id = qf_id, nr = 0 }).nr + api.nvim_command(('silent %dchistory'):format(nr)) + + -- Now open the quickfix list. + api.nvim_command('botright cwindow') + else + api.nvim_command('lwindow') + end end end @@ -1081,7 +1159,7 @@ function M.set(namespace, bufnr, diagnostics, opts) vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics') vim.validate('opts', opts, 'table', true) - bufnr = get_bufnr(bufnr) + bufnr = vim._resolve_bufnr(bufnr) if vim.tbl_isempty(diagnostics) then diagnostic_cache[bufnr][namespace] = nil @@ -1361,17 +1439,13 @@ M.handlers.signs = { vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics') vim.validate('opts', opts, 'table', true) - bufnr = get_bufnr(bufnr) + bufnr = vim._resolve_bufnr(bufnr) opts = opts or {} if not api.nvim_buf_is_loaded(bufnr) then return end - if opts.signs and opts.signs.severity then - diagnostics = filter_by_severity(opts.signs.severity, diagnostics) - end - -- 10 is the default sign priority when none is explicitly specified local priority = opts.signs and opts.signs.priority or 10 local get_priority = severity_to_extmark_priority(priority, opts) @@ -1379,7 +1453,7 @@ M.handlers.signs = { local ns = M.get_namespace(namespace) if not ns.user_data.sign_ns then ns.user_data.sign_ns = - api.nvim_create_namespace(string.format('%s/diagnostic/signs', ns.name)) + api.nvim_create_namespace(string.format('nvim.%s.diagnostic.signs', ns.name)) end -- Handle legacy diagnostic sign definitions @@ -1467,21 +1541,17 @@ M.handlers.underline = { vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics') vim.validate('opts', opts, 'table', true) - bufnr = get_bufnr(bufnr) + bufnr = vim._resolve_bufnr(bufnr) opts = opts or {} if not vim.api.nvim_buf_is_loaded(bufnr) then return end - if opts.underline and opts.underline.severity then - diagnostics = filter_by_severity(opts.underline.severity, diagnostics) - end - local ns = M.get_namespace(namespace) if not ns.user_data.underline_ns then ns.user_data.underline_ns = - api.nvim_create_namespace(string.format('%s/diagnostic/underline', ns.name)) + api.nvim_create_namespace(string.format('nvim.%s.diagnostic.underline', ns.name)) end local underline_ns = ns.user_data.underline_ns @@ -1524,6 +1594,28 @@ M.handlers.underline = { end, } +--- @param namespace integer +--- @param bufnr integer +--- @param diagnostics table<integer, vim.Diagnostic[]> +--- @param opts vim.diagnostic.Opts.VirtualText +local function render_virtual_text(namespace, bufnr, diagnostics, opts) + api.nvim_buf_clear_namespace(bufnr, namespace, 0, -1) + + for line, line_diagnostics in pairs(diagnostics) do + local virt_texts = M._get_virt_text_chunks(line_diagnostics, opts) + + if virt_texts then + api.nvim_buf_set_extmark(bufnr, namespace, line, 0, { + hl_mode = opts.hl_mode or 'combine', + virt_text = virt_texts, + virt_text_pos = opts.virt_text_pos, + virt_text_hide = opts.virt_text_hide, + virt_text_win_col = opts.virt_text_win_col, + }) + end + end +end + M.handlers.virtual_text = { show = function(namespace, bufnr, diagnostics, opts) vim.validate('namespace', namespace, 'number') @@ -1531,14 +1623,13 @@ M.handlers.virtual_text = { vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics') vim.validate('opts', opts, 'table', true) - bufnr = get_bufnr(bufnr) + bufnr = vim._resolve_bufnr(bufnr) opts = opts or {} if not vim.api.nvim_buf_is_loaded(bufnr) then return end - local severity --- @type vim.diagnostic.SeverityFilter? if opts.virtual_text then if opts.virtual_text.format then diagnostics = reformat_diagnostics(opts.virtual_text.format, diagnostics) @@ -1549,36 +1640,51 @@ M.handlers.virtual_text = { then diagnostics = prefix_source(diagnostics) end - if opts.virtual_text.severity then - severity = opts.virtual_text.severity - end end local ns = M.get_namespace(namespace) if not ns.user_data.virt_text_ns then ns.user_data.virt_text_ns = - api.nvim_create_namespace(string.format('%s/diagnostic/virtual_text', ns.name)) + api.nvim_create_namespace(string.format('nvim.%s.diagnostic.virtual_text', ns.name)) + end + if not ns.user_data.virt_text_augroup then + ns.user_data.virt_text_augroup = api.nvim_create_augroup( + string.format('nvim.%s.diagnostic.virt_text', ns.name), + { clear = true } + ) end - local virt_text_ns = ns.user_data.virt_text_ns - local buffer_line_diagnostics = diagnostic_lines(diagnostics) - for line, line_diagnostics in pairs(buffer_line_diagnostics) do - if severity then - line_diagnostics = filter_by_severity(severity, line_diagnostics) - end - local virt_texts = M._get_virt_text_chunks(line_diagnostics, opts.virtual_text) - - if virt_texts then - api.nvim_buf_set_extmark(bufnr, virt_text_ns, line, 0, { - hl_mode = opts.virtual_text.hl_mode or 'combine', - virt_text = virt_texts, - virt_text_pos = opts.virtual_text.virt_text_pos, - virt_text_hide = opts.virtual_text.virt_text_hide, - virt_text_win_col = opts.virtual_text.virt_text_win_col, - }) - end + api.nvim_clear_autocmds({ group = ns.user_data.virt_text_augroup, buffer = bufnr }) + + local line_diagnostics = diagnostic_lines(diagnostics) + + if opts.virtual_text.current_line == true then + api.nvim_create_autocmd('CursorMoved', { + buffer = bufnr, + group = ns.user_data.virt_text_augroup, + callback = function() + local lnum = api.nvim_win_get_cursor(0)[1] - 1 + render_virtual_text( + ns.user_data.virt_text_ns, + bufnr, + { [lnum] = diagnostics_at_cursor(line_diagnostics) }, + opts.virtual_text + ) + end, + }) + -- Also show diagnostics for the current line before the first CursorMoved event. + local lnum = api.nvim_win_get_cursor(0)[1] - 1 + render_virtual_text( + ns.user_data.virt_text_ns, + bufnr, + { [lnum] = diagnostics_at_cursor(line_diagnostics) }, + opts.virtual_text + ) + else + render_virtual_text(ns.user_data.virt_text_ns, bufnr, line_diagnostics, opts.virtual_text) end - save_extmarks(virt_text_ns, bufnr) + + save_extmarks(ns.user_data.virt_text_ns, bufnr) end, hide = function(namespace, bufnr) local ns = M.get_namespace(namespace) @@ -1587,6 +1693,262 @@ M.handlers.virtual_text = { if api.nvim_buf_is_valid(bufnr) then api.nvim_buf_clear_namespace(bufnr, ns.user_data.virt_text_ns, 0, -1) end + api.nvim_clear_autocmds({ group = ns.user_data.virt_text_augroup, buffer = bufnr }) + end + end, +} + +--- Some characters (like tabs) take up more than one cell. Additionally, inline +--- virtual text can make the distance between 2 columns larger. +--- A diagnostic aligned under such characters needs to account for that and that +--- many spaces to its left. +--- @param bufnr integer +--- @param lnum integer +--- @param start_col integer +--- @param end_col integer +--- @return integer +local function distance_between_cols(bufnr, lnum, start_col, end_col) + return api.nvim_buf_call(bufnr, function() + local s = vim.fn.virtcol({ lnum + 1, start_col }) + local e = vim.fn.virtcol({ lnum + 1, end_col + 1 }) + return e - 1 - s + end) +end + +--- @param namespace integer +--- @param bufnr integer +--- @param diagnostics vim.Diagnostic[] +local function render_virtual_lines(namespace, bufnr, diagnostics) + table.sort(diagnostics, function(d1, d2) + if d1.lnum == d2.lnum then + return d1.col < d2.col + else + return d1.lnum < d2.lnum + end + end) + + api.nvim_buf_clear_namespace(bufnr, namespace, 0, -1) + + if not next(diagnostics) then + return + end + + -- This loop reads each line, putting them into stacks with some extra data since + -- rendering each line requires understanding what is beneath it. + local ElementType = { Space = 1, Diagnostic = 2, Overlap = 3, Blank = 4 } ---@enum ElementType + local line_stacks = {} ---@type table<integer, {[1]:ElementType, [2]:string|vim.diagnostic.Severity|vim.Diagnostic}[]> + local prev_lnum = -1 + local prev_col = 0 + for _, diag in ipairs(diagnostics) do + if not line_stacks[diag.lnum] then + line_stacks[diag.lnum] = {} + end + + local stack = line_stacks[diag.lnum] + + if diag.lnum ~= prev_lnum then + table.insert(stack, { + ElementType.Space, + string.rep(' ', distance_between_cols(bufnr, diag.lnum, 0, diag.col)), + }) + elseif diag.col ~= prev_col then + table.insert(stack, { + ElementType.Space, + string.rep( + ' ', + -- +1 because indexing starts at 0 in one API but at 1 in the other. + -- -1 for non-first lines, since the previous column was already drawn. + distance_between_cols(bufnr, diag.lnum, prev_col + 1, diag.col) - 1 + ), + }) + else + table.insert(stack, { ElementType.Overlap, diag.severity }) + end + + if diag.message:find('^%s*$') then + table.insert(stack, { ElementType.Blank, diag }) + else + table.insert(stack, { ElementType.Diagnostic, diag }) + end + + prev_lnum, prev_col = diag.lnum, diag.col + end + + local chars = { + cross = '┼', + horizontal = '─', + horizontal_up = '┴', + up_right = '└', + vertical = '│', + vertical_right = '├', + } + + for lnum, stack in pairs(line_stacks) do + local virt_lines = {} + + -- Note that we read in the order opposite to insertion. + for i = #stack, 1, -1 do + if stack[i][1] == ElementType.Diagnostic then + local diagnostic = stack[i][2] + local left = {} ---@type {[1]:string, [2]:string} + local overlap = false + local multi = false + + -- Iterate the stack for this line to find elements on the left. + for j = 1, i - 1 do + local type = stack[j][1] + local data = stack[j][2] + if type == ElementType.Space then + if multi then + ---@cast data string + table.insert(left, { + string.rep(chars.horizontal, data:len()), + virtual_lines_highlight_map[diagnostic.severity], + }) + else + table.insert(left, { data, '' }) + end + elseif type == ElementType.Diagnostic then + -- If an overlap follows this line, don't add an extra column. + if stack[j + 1][1] ~= ElementType.Overlap then + table.insert(left, { chars.vertical, virtual_lines_highlight_map[data.severity] }) + end + overlap = false + elseif type == ElementType.Blank then + if multi then + table.insert( + left, + { chars.horizontal_up, virtual_lines_highlight_map[data.severity] } + ) + else + table.insert(left, { chars.up_right, virtual_lines_highlight_map[data.severity] }) + end + multi = true + elseif type == ElementType.Overlap then + overlap = true + end + end + + local center_char ---@type string + if overlap and multi then + center_char = chars.cross + elseif overlap then + center_char = chars.vertical_right + elseif multi then + center_char = chars.horizontal_up + else + center_char = chars.up_right + end + local center = { + { + string.format('%s%s', center_char, string.rep(chars.horizontal, 4) .. ' '), + virtual_lines_highlight_map[diagnostic.severity], + }, + } + + -- We can draw on the left side if and only if: + -- a. Is the last one stacked this line. + -- b. Has enough space on the left. + -- c. Is just one line. + -- d. Is not an overlap. + local msg ---@type string + if diagnostic.code then + msg = string.format('%s: %s', diagnostic.code, diagnostic.message) + else + msg = diagnostic.message + end + for msg_line in msg:gmatch('([^\n]+)') do + local vline = {} + vim.list_extend(vline, left) + vim.list_extend(vline, center) + vim.list_extend(vline, { { msg_line, virtual_lines_highlight_map[diagnostic.severity] } }) + + table.insert(virt_lines, vline) + + -- Special-case for continuation lines: + if overlap then + center = { + { chars.vertical, virtual_lines_highlight_map[diagnostic.severity] }, + { ' ', '' }, + } + else + center = { { ' ', '' } } + end + end + end + end + + api.nvim_buf_set_extmark(bufnr, namespace, lnum, 0, { virt_lines = virt_lines }) + end +end + +M.handlers.virtual_lines = { + show = function(namespace, bufnr, diagnostics, opts) + vim.validate('namespace', namespace, 'number') + vim.validate('bufnr', bufnr, 'number') + vim.validate('diagnostics', diagnostics, vim.islist, 'a list of diagnostics') + vim.validate('opts', opts, 'table', true) + + bufnr = vim._resolve_bufnr(bufnr) + opts = opts or {} + + if not api.nvim_buf_is_loaded(bufnr) then + return + end + + local ns = M.get_namespace(namespace) + if not ns.user_data.virt_lines_ns then + ns.user_data.virt_lines_ns = + api.nvim_create_namespace(string.format('nvim.%s.diagnostic.virtual_lines', ns.name)) + end + if not ns.user_data.virt_lines_augroup then + ns.user_data.virt_lines_augroup = api.nvim_create_augroup( + string.format('nvim.%s.diagnostic.virt_lines', ns.name), + { clear = true } + ) + end + + api.nvim_clear_autocmds({ group = ns.user_data.virt_lines_augroup, buffer = bufnr }) + + if opts.virtual_lines.format then + diagnostics = reformat_diagnostics(opts.virtual_lines.format, diagnostics) + end + + if opts.virtual_lines.current_line == true then + -- Create a mapping from line -> diagnostics so that we can quickly get the + -- diagnostics we need when the cursor line doesn't change. + local line_diagnostics = diagnostic_lines(diagnostics) + api.nvim_create_autocmd('CursorMoved', { + buffer = bufnr, + group = ns.user_data.virt_lines_augroup, + callback = function() + render_virtual_lines( + ns.user_data.virt_lines_ns, + bufnr, + diagnostics_at_cursor(line_diagnostics) + ) + end, + }) + -- Also show diagnostics for the current line before the first CursorMoved event. + render_virtual_lines( + ns.user_data.virt_lines_ns, + bufnr, + diagnostics_at_cursor(line_diagnostics) + ) + else + render_virtual_lines(ns.user_data.virt_lines_ns, bufnr, diagnostics) + end + + save_extmarks(ns.user_data.virt_lines_ns, bufnr) + end, + hide = function(namespace, bufnr) + local ns = M.get_namespace(namespace) + if ns.user_data.virt_lines_ns then + diagnostic_cache_extmarks[bufnr][ns.user_data.virt_lines_ns] = {} + if api.nvim_buf_is_valid(bufnr) then + api.nvim_buf_clear_namespace(bufnr, ns.user_data.virt_lines_ns, 0, -1) + end + api.nvim_clear_autocmds({ group = ns.user_data.virt_lines_augroup, buffer = bufnr }) end end, } @@ -1656,7 +2018,7 @@ function M.hide(namespace, bufnr) vim.validate('namespace', namespace, 'number', true) vim.validate('bufnr', bufnr, 'number', true) - local buffers = bufnr and { get_bufnr(bufnr) } or vim.tbl_keys(diagnostic_cache) + local buffers = bufnr and { vim._resolve_bufnr(bufnr) } or vim.tbl_keys(diagnostic_cache) for _, iter_bufnr in ipairs(buffers) do local namespaces = namespace and { namespace } or vim.tbl_keys(diagnostic_cache[iter_bufnr]) for _, iter_namespace in ipairs(namespaces) do @@ -1683,7 +2045,7 @@ function M.is_enabled(filter) return vim.tbl_isempty(diagnostic_disabled) and not diagnostic_disabled[1] end - local bufnr = get_bufnr(filter.bufnr) + local bufnr = vim._resolve_bufnr(filter.bufnr) if type(diagnostic_disabled[bufnr]) == 'table' then return not diagnostic_disabled[bufnr][filter.ns_id] end @@ -1724,7 +2086,7 @@ function M.show(namespace, bufnr, diagnostics, opts) end else -- namespace is nil - bufnr = get_bufnr(bufnr) + bufnr = vim._resolve_bufnr(bufnr) for iter_namespace in pairs(diagnostic_cache[bufnr]) do M.show(iter_namespace, bufnr, nil, opts) end @@ -1770,7 +2132,8 @@ function M.show(namespace, bufnr, diagnostics, opts) for handler_name, handler in pairs(M.handlers) do if handler.show and opts_res[handler_name] then - handler.show(namespace, bufnr, diagnostics, opts_res) + local filtered = filter_by_severity(opts_res[handler_name].severity, diagnostics) + handler.show(namespace, bufnr, filtered, opts_res) end end end @@ -1791,7 +2154,7 @@ function M.open_float(opts, ...) end opts = opts or {} - bufnr = get_bufnr(bufnr or opts.bufnr) + bufnr = vim._resolve_bufnr(bufnr or opts.bufnr) do -- Resolve options with user settings from vim.diagnostic.config @@ -1961,17 +2324,24 @@ function M.open_float(opts, ...) if not opts.focus_id then opts.focus_id = scope end + + --- @diagnostic disable-next-line: param-type-mismatch local float_bufnr, winnr = vim.lsp.util.open_floating_preview(lines, 'plaintext', opts) + vim.bo[float_bufnr].path = vim.bo[bufnr].path + + --- @diagnostic disable-next-line: deprecated + local add_highlight = api.nvim_buf_add_highlight + for i, hl in ipairs(highlights) do local line = lines[i] local prefix_len = hl.prefix and hl.prefix.length or 0 local suffix_len = hl.suffix and hl.suffix.length or 0 if prefix_len > 0 then - api.nvim_buf_add_highlight(float_bufnr, -1, hl.prefix.hlname, i - 1, 0, prefix_len) + add_highlight(float_bufnr, -1, hl.prefix.hlname, i - 1, 0, prefix_len) end - api.nvim_buf_add_highlight(float_bufnr, -1, hl.hlname, i - 1, prefix_len, #line - suffix_len) + add_highlight(float_bufnr, -1, hl.hlname, i - 1, prefix_len, #line - suffix_len) if suffix_len > 0 then - api.nvim_buf_add_highlight(float_bufnr, -1, hl.suffix.hlname, i - 1, #line - suffix_len, -1) + add_highlight(float_bufnr, -1, hl.suffix.hlname, i - 1, #line - suffix_len, -1) end end @@ -1993,7 +2363,7 @@ function M.reset(namespace, bufnr) vim.validate('namespace', namespace, 'number', true) vim.validate('bufnr', bufnr, 'number', true) - local buffers = bufnr and { get_bufnr(bufnr) } or vim.tbl_keys(diagnostic_cache) + local buffers = bufnr and { vim._resolve_bufnr(bufnr) } or vim.tbl_keys(diagnostic_cache) for _, iter_bufnr in ipairs(buffers) do local namespaces = namespace and { namespace } or vim.tbl_keys(diagnostic_cache[iter_bufnr]) for _, iter_namespace in ipairs(namespaces) do @@ -2024,7 +2394,8 @@ end --- (default: `true`) --- @field open? boolean --- ---- Title of quickfix list. Defaults to "Diagnostics". +--- Title of quickfix list. Defaults to "Diagnostics". If there's already a quickfix list with this +--- title, it's updated. If not, a new quickfix list is created. --- @field title? string --- --- See |diagnostic-severity|. @@ -2131,7 +2502,7 @@ function M.enable(enable, filter) ns.disabled = not enable end else - bufnr = get_bufnr(bufnr) + bufnr = vim._resolve_bufnr(bufnr) if not ns_id then diagnostic_disabled[bufnr] = (not enable) and true or nil else diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index e1e73d63fe..cc7358ee49 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -275,6 +275,9 @@ local extension = { mdh = 'c', epro = 'c', qc = 'c', + c3 = 'c3', + c3i = 'c3', + c3t = 'c3', cabal = 'cabal', cairo = 'cairo', capnp = 'capnp', @@ -300,6 +303,7 @@ local extension = { cho = 'chordpro', chordpro = 'chordpro', ck = 'chuck', + cl = detect.cl, eni = 'cl', icl = 'clean', cljx = 'clojure', @@ -349,6 +353,7 @@ local extension = { cql = 'cqlang', crm = 'crm', cr = 'crystal', + cake = 'cs', csx = 'cs', cs = 'cs', csc = 'csc', @@ -500,6 +505,7 @@ local extension = { gdshader = 'gdshader', shader = 'gdshader', ged = 'gedcom', + gel = 'gel', gmi = 'gemtext', gemini = 'gemtext', gift = 'gift', @@ -588,6 +594,7 @@ local extension = { hw = detect.hw, module = detect.hw, pkg = detect.hw, + hy = 'hy', iba = 'ibasic', ibi = 'ibasic', icn = 'icon', @@ -612,6 +619,7 @@ local extension = { janet = 'janet', jav = 'java', java = 'java', + jsh = 'java', jj = 'javacc', jjt = 'javacc', es = 'javascript', @@ -624,7 +632,7 @@ local extension = { clp = 'jess', jgr = 'jgraph', jinja = 'jinja', - jjdescription = 'jj', + jjdescription = 'jjdescription', j73 = 'jovial', jov = 'jovial', jovial = 'jovial', @@ -653,6 +661,10 @@ local extension = { jsp = 'jsp', jl = 'julia', just = 'just', + Just = 'just', + JUST = 'just', + kl = 'karel', + KL = 'karel', kdl = 'kdl', kv = 'kivy', kix = 'kix', @@ -666,6 +678,7 @@ local extension = { k = 'kwt', ACE = 'lace', ace = 'lace', + lalrpop = 'lalrpop', latte = 'latte', lte = 'latte', ld = 'ld', @@ -686,13 +699,11 @@ local extension = { ily = 'lilypond', liquid = 'liquid', liq = 'liquidsoap', - cl = 'lisp', L = 'lisp', lisp = 'lisp', el = 'lisp', lsp = 'lisp', asd = 'lisp', - stsg = 'lisp', lt = 'lite', lite = 'lite', livemd = 'livebook', @@ -721,14 +732,14 @@ local extension = { mc = detect.mc, quake = 'm3quake', m4 = function(path, bufnr) - path = path:lower() - return not (path:find('html%.m4$') or path:find('fvwm2rc')) and 'm4' or nil + local pathl = path:lower() + return not (pathl:find('html%.m4$') or pathl:find('fvwm2rc')) and 'm4' or nil end, eml = 'mail', mk = detect.make, mak = detect.make, page = 'mallard', - map = 'map', + map = detect_line1('^%*+$', 'lnkmap', 'map'), mws = 'maple', mpl = 'maple', mv = 'maple', @@ -738,6 +749,7 @@ local extension = { mkd = detect.markdown, markdown = detect.markdown, mdown = detect.markdown, + masm = 'masm', mhtml = 'mason', mason = 'mason', master = 'master', @@ -800,6 +812,7 @@ local extension = { n1ql = 'n1ql', nql = 'n1ql', nanorc = 'nanorc', + nasm = 'nasm', NSA = 'natural', NSC = 'natural', NSG = 'natural', @@ -834,6 +847,7 @@ local extension = { tr = 'nroff', nsi = 'nsis', nsh = 'nsis', + nt = 'ntriples', nu = 'nu', obj = 'obj', objdump = 'objdump', @@ -953,11 +967,14 @@ local extension = { ps1xml = 'ps1xml', psf = 'psf', psl = 'psl', + ptx = 'ptx', pug = 'pug', purs = 'purescript', arr = 'pyret', pxd = 'pyrex', + pxi = 'pyrex', pyx = 'pyrex', + ['pyx+'] = 'pyrex', pyw = 'python', py = 'python', pyi = 'python', @@ -1050,16 +1067,17 @@ local extension = { builder = 'ruby', rake = 'ruby', rs = 'rust', + sa = detect.sa, sage = 'sage', sls = 'salt', sas = 'sas', sass = 'sass', - sa = 'sather', sbt = 'sbt', scala = 'scala', ss = 'scheme', scm = 'scheme', sld = 'scheme', + stsg = 'scheme', sce = 'scilab', sci = 'scilab', scss = 'scss', @@ -1082,6 +1100,7 @@ local extension = { la = 'sh', lai = 'sh', mdd = 'sh', + slang = 'shaderslang', sieve = 'sieve', siv = 'sieve', sig = detect.sig, @@ -1224,6 +1243,7 @@ local extension = { toml = 'toml', tpp = 'tpp', treetop = 'treetop', + trig = 'trig', slt = 'tsalt', tsscl = 'tsscl', tssgm = 'tssgm', @@ -1323,6 +1343,7 @@ local extension = { xlb = 'xml', xlc = 'xml', xba = 'xml', + slnx = 'xml', xpm = detect_line1('XPM2', 'xpm2', 'xpm'), xpm2 = 'xpm2', xqy = 'xquery', @@ -1378,7 +1399,7 @@ local extension = { txt = detect.txt, xml = detect.xml, y = detect.y, - cmd = detect_line1('^/%*', 'rexx', 'dosbatch'), + cmd = detect.cmd, rul = detect.rul, cpy = detect_line1('^##', 'python', 'cobol'), dsl = detect_line1('^%s*<!', 'dsl', 'structurizr'), @@ -1427,6 +1448,7 @@ local filename = { ['/etc/asound.conf'] = 'alsaconf', ['build.xml'] = 'ant', ['.htaccess'] = 'apache', + APKBUILD = 'apkbuild', ['apt.conf'] = 'aptconf', ['/.aptitude/config'] = 'aptconf', ['=tagging-method'] = 'arch', @@ -1485,6 +1507,7 @@ local filename = { ['NEWS.dch'] = 'debchangelog', ['NEWS.Debian'] = 'debchangelog', ['/debian/control'] = 'debcontrol', + ['/DEBIAN/control'] = 'debcontrol', ['/debian/copyright'] = 'debcopyright', ['/etc/apt/sources.list'] = 'debsources', ['denyhosts.conf'] = 'denyhosts', @@ -1532,6 +1555,8 @@ local filename = { ['filter-rules'] = 'elmfilt', ['exim.conf'] = 'exim', exports = 'exports', + fennelrc = 'fennel', + ['.fennelrc'] = 'fennel', ['.fetchmailrc'] = 'fetchmail', fvSchemes = detect.foam, fvSolution = detect.foam, @@ -1553,6 +1578,12 @@ local filename = { ['.gitmodules'] = 'gitconfig', ['.gitattributes'] = 'gitattributes', ['.gitignore'] = 'gitignore', + ['.ignore'] = 'gitignore', + ['.dockerignore'] = 'gitignore', + ['.fdignore'] = 'gitignore', + ['.npmignore'] = 'gitignore', + ['.rgignore'] = 'gitignore', + ['.vscodeignore'] = 'gitignore', ['gitolite.conf'] = 'gitolite', ['git-rebase-todo'] = 'gitrebase', gkrellmrc = 'gkrellmrc', @@ -1587,6 +1618,7 @@ local filename = { ['/etc/host.conf'] = 'hostconf', ['/etc/hosts.allow'] = 'hostsaccess', ['/etc/hosts.deny'] = 'hostsaccess', + ['.hy-history'] = 'hy', ['hyprland.conf'] = 'hyprlang', ['hyprpaper.conf'] = 'hyprlang', ['hypridle.conf'] = 'hyprlang', @@ -1608,6 +1640,7 @@ local filename = { ['.lintstagedrc'] = 'json', ['deno.lock'] = 'json', ['flake.lock'] = 'json', + ['.swcrc'] = 'json', ['.babelrc'] = 'jsonc', ['.eslintrc'] = 'jsonc', ['.hintrc'] = 'jsonc', @@ -1617,9 +1650,13 @@ local filename = { ['.luaurc'] = 'jsonc', ['.swrc'] = 'jsonc', ['.vsconfig'] = 'jsonc', + ['bun.lock'] = 'jsonc', ['.justfile'] = 'just', + ['.Justfile'] = 'just', + ['.JUSTFILE'] = 'just', ['justfile'] = 'just', ['Justfile'] = 'just', + ['JUSTFILE'] = 'just', Kconfig = 'kconfig', ['Kconfig.debug'] = 'kconfig', ['Config.in'] = 'kconfig', @@ -1762,6 +1799,7 @@ local filename = { ['Rantfile'] = 'ruby', Vagrantfile = 'ruby', ['smb.conf'] = 'samba', + ['.lips_repl_history'] = 'scheme', screenrc = 'screen', ['.screenrc'] = 'screen', ['/etc/sensors3.conf'] = 'sensors', @@ -1783,7 +1821,6 @@ local filename = { ['.kshrc'] = detect.ksh, ['.profile'] = detect.sh, ['/etc/profile'] = detect.sh, - APKBUILD = detect.bash, PKGBUILD = detect.bash, ['.tcshrc'] = detect.tcsh, ['tcsh.login'] = detect.tcsh, @@ -1860,11 +1897,17 @@ local filename = { ['/etc/blkid.tab'] = 'xml', ['/etc/blkid.tab.old'] = 'xml', ['fonts.conf'] = 'xml', + ['Directory.Packages.props'] = 'xml', + ['Directory.Build.props'] = 'xml', + ['Directory.Build.targets'] = 'xml', ['.clangd'] = 'yaml', ['.clang-format'] = 'yaml', ['.clang-tidy'] = 'yaml', + ['pixi.lock'] = 'yaml', ['yarn.lock'] = 'yaml', matplotlibrc = 'yaml', + ['.condarc'] = 'yaml', + condarc = 'yaml', zathurarc = 'zathurarc', ['/etc/zprofile'] = 'zsh', ['.zlogin'] = 'zsh', @@ -2141,8 +2184,8 @@ local pattern = { ['/gitolite%-admin/conf/'] = starsetf('gitolite'), ['/%.i3/config$'] = 'i3config', ['/i3/config$'] = 'i3config', - ['/supertux2/config$'] = 'lisp', ['/%.mplayer/config$'] = 'mplayerconf', + ['/supertux2/config$'] = 'scheme', ['/neofetch/config%.conf$'] = 'sh', ['/%.ssh/config$'] = 'sshconfig', ['/%.sway/config$'] = 'swayconfig', @@ -2210,8 +2253,10 @@ local pattern = { ['^dictd.*%.conf$'] = 'dictdconf', ['/lxqt/.*%.conf$'] = 'dosini', ['/screengrab/.*%.conf$'] = 'dosini', + ['/%.config/fd/ignore$'] = 'gitignore', ['^${GNUPGHOME}/gpg%.conf$'] = 'gpg', ['/boot/grub/grub%.conf$'] = 'grub', + ['/hypr/.*%.conf$'] = 'hyprlang', ['^lilo%.conf'] = starsetf('lilo'), ['^named.*%.conf$'] = 'named', ['^rndc.*%.conf$'] = 'named', @@ -2313,6 +2358,7 @@ local pattern = { ['%.cmake%.in$'] = 'cmake', ['^crontab%.'] = starsetf('crontab'), ['^cvs%d+$'] = 'cvs', + ['/DEBIAN/control$'] = 'debcontrol', ['^php%.ini%-'] = 'dosini', ['^php%-fpm%.conf'] = 'dosini', ['^www%.conf'] = 'dosini', @@ -2338,6 +2384,8 @@ local pattern = { ['%.html%.m4$'] = 'htmlm4', ['^JAM.*%.'] = starsetf('jam'), ['^Prl.*%.'] = starsetf('jam'), + ['^${HOME}/.*/Code/User/.*%.json$'] = 'jsonc', + ['^${HOME}/.*/VSCodium/User/.*%.json$'] = 'jsonc', ['%.properties_..$'] = 'jproperties', ['%.properties_.._..$'] = 'jproperties', ['%.properties_.._.._'] = starsetf('jproperties'), diff --git a/runtime/lua/vim/filetype/detect.lua b/runtime/lua/vim/filetype/detect.lua index 98b001bd51..fc0b45ecd8 100644 --- a/runtime/lua/vim/filetype/detect.lua +++ b/runtime/lua/vim/filetype/detect.lua @@ -34,6 +34,12 @@ local matchregex = vim.filetype._matchregex -- can be detected from the first five lines of the file. --- @type vim.filetype.mapfn function M.asm(path, bufnr) + -- tiasm uses `* comment` + local lines = table.concat(getlines(bufnr, 1, 10), '\n') + if findany(lines, { '^%*', '\n%*', 'Texas Instruments Incorporated' }) then + return 'tiasm' + end + local syntax = vim.b[bufnr].asmsyntax if not syntax or syntax == '' then syntax = M.asm_syntax(path, bufnr) @@ -181,6 +187,16 @@ function M.changelog(_, bufnr) end --- @type vim.filetype.mapfn +function M.cl(_, bufnr) + local lines = table.concat(getlines(bufnr, 1, 4)) + if lines:match('/%*') then + return 'opencl' + else + return 'lisp' + end +end + +--- @type vim.filetype.mapfn function M.class(_, bufnr) -- Check if not a Java class (starts with '\xca\xfe\xba\xbe') if not getline(bufnr, 1):find('^\202\254\186\190') then @@ -209,6 +225,24 @@ function M.cls(_, bufnr) return 'st' end +--- *.cmd is close to a Batch file, but on OS/2 Rexx files and TI linker command files also use *.cmd. +--- lnk: `/* comment */`, `// comment`, and `--linker-option=value` +--- rexx: `/* comment */`, `-- comment` +--- @type vim.filetype.mapfn +function M.cmd(_, bufnr) + local lines = table.concat(getlines(bufnr, 1, 20)) + if matchregex(lines, [[MEMORY\|SECTIONS\|\%(^\|\n\)--\S\|\%(^\|\n\)//]]) then + return 'lnk' + else + local line1 = getline(bufnr, 1) + if line1:find('^/%*') then + return 'rexx' + else + return 'dosbatch' + end + end +end + --- @type vim.filetype.mapfn function M.conf(path, bufnr) if fn.did_filetype() ~= 0 or path:find(vim.g.ft_ignore_pat) then @@ -227,7 +261,8 @@ end --- Debian Control --- @type vim.filetype.mapfn function M.control(_, bufnr) - if getline(bufnr, 1):find('^Source:') then + local line1 = getline(bufnr, 1) + if line1 and findany(line1, { '^Source:', '^Package:' }) then return 'debcontrol' end end @@ -722,7 +757,7 @@ function M.html(_, bufnr) if matchregex( line, - [[@\(if\|for\|defer\|switch\)\|\*\(ngIf\|ngFor\|ngSwitch\|ngTemplateOutlet\)\|ng-template\|ng-content\|{{.*}}]] + [[@\(if\|for\|defer\|switch\)\|\*\(ngIf\|ngFor\|ngSwitch\|ngTemplateOutlet\)\|ng-template\|ng-content]] ) then return 'htmlangular' @@ -846,7 +881,7 @@ end --- (refactor of filetype.vim since the patterns are case-insensitive) --- @type vim.filetype.mapfn function M.log(path, _) - path = path:lower() + path = path:lower() --- @type string LuaLS bug if findany( path, @@ -1132,7 +1167,7 @@ end --- @type vim.filetype.mapfn function M.perl(path, bufnr) local dir_name = vim.fs.dirname(path) - if fn.expand(path, '%:e') == 't' and (dir_name == 't' or dir_name == 'xt') then + if fn.fnamemodify(path, '%:e') == 't' and (dir_name == 't' or dir_name == 'xt') then return 'perl' end local first_line = getline(bufnr, 1) @@ -1340,7 +1375,7 @@ end local udev_rules_pattern = '^%s*udev_rules%s*=%s*"([%^"]+)/*".*' --- @type vim.filetype.mapfn function M.rules(path) - path = path:lower() + path = path:lower() --- @type string LuaLS bug if findany(path, { '/etc/udev/.*%.rules$', @@ -1363,7 +1398,7 @@ function M.rules(path) if not ok then return 'hog' end - local dir = fn.expand(path, ':h') + local dir = fn.fnamemodify(path, ':h') for _, line in ipairs(config_lines) do local match = line:match(udev_rules_pattern) if match then @@ -1395,6 +1430,15 @@ function M.sig(_, bufnr) end end +--- @type vim.filetype.mapfn +function M.sa(_, bufnr) + local lines = table.concat(getlines(bufnr, 1, 4), '\n') + if findany(lines, { '^;', '\n;' }) then + return 'tiasm' + end + return 'sather' +end + -- This function checks the first 25 lines of file extension "sc" to resolve -- detection between scala and SuperCollider --- @type vim.filetype.mapfn @@ -1719,7 +1763,7 @@ function M.v(_, bufnr) return vim.g.filetype_v end local in_comment = 0 - for _, line in ipairs(getlines(bufnr, 1, 200)) do + for _, line in ipairs(getlines(bufnr, 1, 500)) do if line:find('^%s*/%*') then in_comment = 1 end @@ -1733,7 +1777,7 @@ function M.v(_, bufnr) or line:find('%(%*') and not line:find('/[/*].*%(%*') then return 'coq' - elseif findany(line, { ';%s*$', ';%s*/[/*]' }) then + elseif findany(line, { ';%s*$', ';%s*/[/*]', '^%s*module%s+%w+%s*%(' }) then return 'verilog' end end @@ -1833,6 +1877,7 @@ local patterns_hashbang = { ruby = 'ruby', ['node\\(js\\)\\=\\>\\|js\\>'] = { 'javascript', { vim_regex = true } }, ['rhino\\>'] = { 'javascript', { vim_regex = true } }, + just = 'just', -- BC calculator ['^bc\\>'] = { 'bc', { vim_regex = true } }, ['sed\\>'] = { 'sed', { vim_regex = true } }, diff --git a/runtime/lua/vim/fs.lua b/runtime/lua/vim/fs.lua index d91eeaf02f..8b4242223a 100644 --- a/runtime/lua/vim/fs.lua +++ b/runtime/lua/vim/fs.lua @@ -1,3 +1,15 @@ +--- @brief <pre>help +--- *vim.fs.exists()* +--- Use |uv.fs_stat()| to check a file's type, and whether it exists. +--- +--- Example: +--- +--- >lua +--- if vim.uv.fs_stat(file) then +--- vim.print("file exists") +--- end +--- < + local uv = vim.uv local M = {} @@ -93,14 +105,23 @@ function M.basename(file) return file:match('/$') and '' or (file:match('[^/]*$')) end ---- Concatenate directories and/or file paths into a single path with normalization ---- (e.g., `"foo/"` and `"bar"` get joined to `"foo/bar"`) +--- Concatenates partial paths (one absolute or relative path followed by zero or more relative +--- paths). Slashes are normalized: redundant slashes are removed, and (on Windows) backslashes are +--- replaced with forward-slashes. +--- +--- Examples: +--- - "foo/", "/bar" => "foo/bar" +--- - Windows: "a\foo\", "\bar" => "a/foo/bar" --- ---@since 12 ---@param ... string ---@return string function M.joinpath(...) - return (table.concat({ ... }, '/'):gsub('//+', '/')) + local path = table.concat({ ... }, '/') + if iswin then + path = path:gsub('\\', '/') + end + return (path:gsub('//+', '/')) end ---@alias Iterator fun(): string?, string? @@ -115,6 +136,7 @@ end --- - skip: (fun(dir_name: string): boolean)|nil Predicate --- to control traversal. Return false to stop searching the current directory. --- Only useful when depth > 1 +--- - follow: boolean|nil Follow symbolic links. (default: true) --- ---@return Iterator over items in {path}. Each iteration yields two values: "name" and "type". --- "name" is the basename of the item relative to {path}. @@ -126,6 +148,7 @@ function M.dir(path, opts) vim.validate('path', path, 'string') vim.validate('depth', opts.depth, 'number', true) vim.validate('skip', opts.skip, 'function', true) + vim.validate('follow', opts.follow, 'boolean', true) path = M.normalize(path) if not opts.depth or opts.depth == 1 then @@ -156,7 +179,9 @@ function M.dir(path, opts) if opts.depth and level < opts.depth - and t == 'directory' + and (t == 'directory' or (t == 'link' and opts.follow ~= false and (vim.uv.fs_stat( + M.joinpath(path, f) + ) or {}).type == 'directory')) and (not opts.skip or opts.skip(f) ~= false) then dirs[#dirs + 1] = { f, level + 1 } @@ -190,6 +215,10 @@ end --- Use `math.huge` to place no limit on the number of matches. --- (default: `1`) --- @field limit? number +--- +--- Follow symbolic links. +--- (default: `true`) +--- @field follow? boolean --- Find files or directories (or other items as specified by `opts.type`) in the given path. --- @@ -213,7 +242,7 @@ end --- --- -- get all files ending with .cpp or .hpp inside lib/ --- local cpp_hpp = vim.fs.find(function(name, path) ---- return name:match('.*%.[ch]pp$') and path:match('[/\\\\]lib$') +--- return name:match('.*%.[ch]pp$') and path:match('[/\\]lib$') --- end, {limit = math.huge, type = 'file'}) --- ``` --- @@ -223,6 +252,7 @@ end --- If {names} is a function, it is called for each traversed item with args: --- - name: base name of the current item --- - path: full path of the current item +--- --- The function should return `true` if the given item is considered a match. --- ---@param opts vim.fs.find.Opts Optional keyword arguments: @@ -235,6 +265,7 @@ function M.find(names, opts) vim.validate('stop', opts.stop, 'string', true) vim.validate('type', opts.type, 'string', true) vim.validate('limit', opts.limit, 'number', true) + vim.validate('follow', opts.follow, 'boolean', true) if type(names) == 'string' then names = { names } @@ -324,7 +355,14 @@ function M.find(names, opts) end end - if type_ == 'directory' then + if + type_ == 'directory' + or ( + type_ == 'link' + and opts.follow ~= false + and (vim.uv.fs_stat(f) or {}).type == 'directory' + ) + then dirs[#dirs + 1] = f end end @@ -493,6 +531,27 @@ local function path_resolve_dot(path) return (is_path_absolute and '/' or '') .. table.concat(new_path_components, '/') end +--- Expand tilde (~) character at the beginning of the path to the user's home directory. +--- +--- @param path string Path to expand. +--- @param sep string|nil Path separator to use. Uses os_sep by default. +--- @return string Expanded path. +local function expand_home(path, sep) + sep = sep or os_sep + + if vim.startswith(path, '~') then + local home = uv.os_homedir() or '~' --- @type string + + if home:sub(-1) == sep then + home = home:sub(1, -2) + end + + path = home .. path:sub(2) --- @type string + end + + return path +end + --- @class vim.fs.normalize.Opts --- @inlinedoc --- @@ -556,18 +615,12 @@ function M.normalize(path, opts) return '' end - -- Expand ~ to users home directory - if vim.startswith(path, '~') then - local home = uv.os_homedir() or '~' - if home:sub(-1) == os_sep_local then - home = home:sub(1, -2) - end - path = home .. path:sub(2) - end + -- Expand ~ to user's home directory + path = expand_home(path, os_sep_local) -- Expand environment variables if `opts.expand_env` isn't `false` if opts.expand_env == nil or opts.expand_env then - path = path:gsub('%$([%w_]+)', uv.os_getenv) + path = path:gsub('%$([%w_]+)', uv.os_getenv) --- @type string end if win then @@ -593,8 +646,8 @@ function M.normalize(path, opts) return prefix .. path end - -- Remove extraneous slashes from the prefix - prefix = prefix:gsub('/+', '/') + -- Ensure capital drive and remove extraneous slashes from the prefix + prefix = prefix:gsub('^%a:', string.upper):gsub('/+', '/') end if not opts._fast then @@ -667,4 +720,75 @@ function M.rm(path, opts) end end +--- Convert path to an absolute path. A tilde (~) character at the beginning of the path is expanded +--- to the user's home directory. Does not check if the path exists, normalize the path, resolve +--- symlinks or hardlinks (including `.` and `..`), or expand environment variables. If the path is +--- already absolute, it is returned unchanged. Also converts `\` path separators to `/`. +--- +--- @param path string Path +--- @return string Absolute path +function M.abspath(path) + vim.validate('path', path, 'string') + + -- Expand ~ to user's home directory + path = expand_home(path) + + -- Convert path separator to `/` + path = path:gsub(os_sep, '/') + + local prefix = '' + + if iswin then + prefix, path = split_windows_path(path) + end + + if prefix == '//' or vim.startswith(path, '/') then + -- Path is already absolute, do nothing + return prefix .. path + end + + -- Windows allows paths like C:foo/bar, these paths are relative to the current working directory + -- of the drive specified in the path + local cwd = (iswin and prefix:match('^%w:$')) and uv.fs_realpath(prefix) or uv.cwd() + assert(cwd ~= nil) + -- Convert cwd path separator to `/` + cwd = cwd:gsub(os_sep, '/') + + -- Prefix is not needed for expanding relative paths, as `cwd` already contains it. + return M.joinpath(cwd, path) +end + +--- Gets `target` path relative to `base`, or `nil` if `base` is not an ancestor. +--- +--- Example: +--- +--- ```lua +--- vim.fs.relpath('/var', '/var/lib') -- 'lib' +--- vim.fs.relpath('/var', '/usr/bin') -- nil +--- ``` +--- +--- @param base string +--- @param target string +--- @param opts table? Reserved for future use +--- @return string|nil +function M.relpath(base, target, opts) + vim.validate('base', base, 'string') + vim.validate('target', target, 'string') + vim.validate('opts', opts, 'table', true) + + base = vim.fs.normalize(vim.fs.abspath(base)) + target = vim.fs.normalize(vim.fs.abspath(target)) + if base == target then + return '.' + end + + local prefix = '' + if iswin then + prefix, base = split_windows_path(base) + end + base = prefix .. base .. (base ~= '/' and '/' or '') + + return vim.startswith(target, base) and target:sub(#base + 1) or nil +end + return M diff --git a/runtime/lua/vim/func.lua b/runtime/lua/vim/func.lua index f71659ffb4..fc8fa62c71 100644 --- a/runtime/lua/vim/func.lua +++ b/runtime/lua/vim/func.lua @@ -3,9 +3,6 @@ local M = {} -- TODO(lewis6991): Private for now until: -- - There are other places in the codebase that could benefit from this -- (e.g. LSP), but might require other changes to accommodate. --- - Invalidation of the cache needs to be controllable. Using weak tables --- is an acceptable invalidation policy, but it shouldn't be the only --- one. -- - I don't think the story around `hash` is completely thought out. We -- may be able to have a good default hash by hashing each argument, -- so basically a better 'concat'. @@ -17,6 +14,10 @@ local M = {} --- Internally uses a |lua-weaktable| to cache the results of {fn} meaning the --- cache will be invalidated whenever Lua does garbage collection. --- +--- The cache can also be manually invalidated by calling `:clear()` on the returned object. +--- Calling this function with no arguments clears the entire cache; otherwise, the arguments will +--- be interpreted as function inputs, and only the cache entry at their hash will be cleared. +--- --- The memoized function returns shared references so be wary about --- mutating return values. --- @@ -32,11 +33,12 @@ local M = {} --- first n arguments passed to {fn}. --- --- @param fn F Function to memoize. ---- @param strong? boolean Do not use a weak table +--- @param weak? boolean Use a weak table (default `true`) --- @return F # Memoized version of {fn} --- @nodoc -function M._memoize(hash, fn, strong) - return require('vim.func._memoize')(hash, fn, strong) +function M._memoize(hash, fn, weak) + -- this is wrapped in a function to lazily require the module + return require('vim.func._memoize')(hash, fn, weak) end return M diff --git a/runtime/lua/vim/func/_memoize.lua b/runtime/lua/vim/func/_memoize.lua index 6e557905a7..c46f878067 100644 --- a/runtime/lua/vim/func/_memoize.lua +++ b/runtime/lua/vim/func/_memoize.lua @@ -1,5 +1,7 @@ --- Module for private utility functions +--- @alias vim.func.MemoObj { _hash: (fun(...): any), _weak: boolean?, _cache: table<any> } + --- @param argc integer? --- @return fun(...): any local function concat_hash(argc) @@ -33,29 +35,49 @@ local function resolve_hash(hash) return hash end +--- @param weak boolean? +--- @return table +local create_cache = function(weak) + return setmetatable({}, { + __mode = weak ~= false and 'kv', + }) +end + --- @generic F: function --- @param hash integer|string|fun(...): any --- @param fn F ---- @param strong? boolean +--- @param weak? boolean --- @return F -return function(hash, fn, strong) +return function(hash, fn, weak) vim.validate('hash', hash, { 'number', 'string', 'function' }) vim.validate('fn', fn, 'function') + vim.validate('weak', weak, 'boolean', true) - ---@type table<any,table<any,any>> - local cache = {} - if not strong then - setmetatable(cache, { __mode = 'kv' }) - end - - hash = resolve_hash(hash) + --- @type vim.func.MemoObj + local obj = { + _cache = create_cache(weak), + _hash = resolve_hash(hash), + _weak = weak, + --- @param self vim.func.MemoObj + clear = function(self, ...) + if select('#', ...) == 0 then + self._cache = create_cache(self._weak) + return + end + local key = self._hash(...) + self._cache[key] = nil + end, + } - return function(...) - local key = hash(...) - if cache[key] == nil then - cache[key] = vim.F.pack_len(fn(...)) - end - - return vim.F.unpack_len(cache[key]) - end + return setmetatable(obj, { + --- @param self vim.func.MemoObj + __call = function(self, ...) + local key = self._hash(...) + local cache = self._cache + if cache[key] == nil then + cache[key] = vim.F.pack_len(fn(...)) + end + return vim.F.unpack_len(cache[key]) + end, + }) end diff --git a/runtime/lua/vim/glob.lua b/runtime/lua/vim/glob.lua index 4f86d5e1ca..242c70d4b2 100644 --- a/runtime/lua/vim/glob.lua +++ b/runtime/lua/vim/glob.lua @@ -53,6 +53,7 @@ function M.to_lpeg(pattern) end -- luacheck: pop + --- @diagnostic disable-next-line: missing-fields local p = P({ 'Pattern', Pattern = V('Elem') ^ -1 * V('End'), diff --git a/runtime/lua/vim/health.lua b/runtime/lua/vim/health.lua index 52a7a13966..a265e2b901 100644 --- a/runtime/lua/vim/health.lua +++ b/runtime/lua/vim/health.lua @@ -11,7 +11,7 @@ --- < --- Plugin authors are encouraged to write new healthchecks. |health-dev| --- ---- Commands *health-commands* +--- COMMANDS *health-commands* --- --- *:che* *:checkhealth* --- :che[ckhealth] Run all healthchecks. @@ -39,6 +39,23 @@ --- :checkhealth vim* --- < --- +--- USAGE *health-usage* +--- +--- Local mappings in the healthcheck buffer: +--- +--- q Closes the window. +--- +--- Global configuration: +--- +--- *g:health* +--- g:health Dictionary with the following optional keys: +--- - `style` (`'float'|nil`) Set to "float" to display :checkhealth in +--- a floating window instead of the default behavior. +--- +--- Example: >lua +--- vim.g.health = { style = 'float' } +--- +--- -------------------------------------------------------------------------------- --- Create a healthcheck *health-dev* --- --- Healthchecks are functions that check the user environment, configuration, or @@ -101,7 +118,7 @@ local function filepath_to_healthcheck(path) func = 'health#' .. name .. '#check' filetype = 'v' else - local subpath = path:gsub('.*lua/', '') + local subpath = path:gsub('.*/lua/', '') if vim.fs.basename(subpath) == 'health.lua' then -- */health.lua name = vim.fs.dirname(subpath) @@ -109,7 +126,7 @@ local function filepath_to_healthcheck(path) -- */health/init.lua name = vim.fs.dirname(vim.fs.dirname(subpath)) end - name = name:gsub('/', '.') + name = assert(name:gsub('/', '.')) --- @type string func = 'require("' .. name .. '.health").check()' filetype = 'l' @@ -218,7 +235,7 @@ local function format_report_message(status, msg, ...) -- Report each suggestion for _, v in ipairs(varargs) do if v then - output = output .. '\n - ' .. indent_after_line1(v, 6) + output = output .. '\n - ' .. indent_after_line1(v, 6) --- @type string end end end @@ -331,13 +348,31 @@ function M._check(mods, plugin_names) local emptybuf = vim.fn.bufnr('$') == 1 and vim.fn.getline(1) == '' and 1 == vim.fn.line('$') - -- When no command modifiers are used: - -- - If the current buffer is empty, open healthcheck directly. - -- - If not specified otherwise open healthcheck in a tab. - local buf_cmd = #mods > 0 and (mods .. ' sbuffer') or emptybuf and 'buffer' or 'tab sbuffer' - local bufnr = vim.api.nvim_create_buf(true, true) - vim.cmd(buf_cmd .. ' ' .. bufnr) + if + vim.g.health + and type(vim.g.health) == 'table' + and vim.tbl_get(vim.g.health, 'style') == 'float' + then + local max_height = math.floor(vim.o.lines * 0.8) + local max_width = 80 + local float_bufnr, float_winid = vim.lsp.util.open_floating_preview({}, '', { + height = max_height, + width = max_width, + offset_x = math.floor((vim.o.columns - max_width) / 2), + offset_y = math.floor((vim.o.lines - max_height) / 2) - 1, + relative = 'editor', + }) + vim.api.nvim_set_current_win(float_winid) + vim.bo[float_bufnr].modifiable = true + vim.wo[float_winid].list = false + else + -- When no command modifiers are used: + -- - If the current buffer is empty, open healthcheck directly. + -- - If not specified otherwise open healthcheck in a tab. + local buf_cmd = #mods > 0 and (mods .. ' sbuffer') or emptybuf and 'buffer' or 'tab sbuffer' + vim.cmd(buf_cmd .. ' ' .. bufnr) + end if vim.fn.bufexists('health://') == 1 then vim.cmd.bwipe('health://') @@ -407,6 +442,16 @@ function M._check(mods, plugin_names) -- Clear the 'Running healthchecks...' message. vim.cmd.redraw() vim.print('') + + -- Quit with 'q' inside healthcheck buffers. + vim.keymap.set('n', 'q', function() + if not pcall(vim.cmd.close) then + vim.cmd.bdelete() + end + end, { buffer = bufnr, silent = true, noremap = true, nowait = true }) + + -- Once we're done writing checks, set nomodifiable. + vim.bo[bufnr].modifiable = false end return M diff --git a/runtime/lua/vim/health/health.lua b/runtime/lua/vim/health/health.lua index d226f35f9a..dd6fe7f608 100644 --- a/runtime/lua/vim/health/health.lua +++ b/runtime/lua/vim/health/health.lua @@ -183,13 +183,16 @@ end local function check_rplugin_manifest() health.start('Remote Plugins') - local existing_rplugins = {} - for _, item in ipairs(vim.fn['remote#host#PluginsForHost']('python3')) do + local existing_rplugins = {} --- @type table<string,string> + --- @type {path:string}[] + local items = vim.fn['remote#host#PluginsForHost']('python3') + for _, item in ipairs(items) do existing_rplugins[item.path] = 'python3' end local require_update = false local handle_path = function(path) + --- @type string[] local python_glob = vim.fn.glob(path .. '/rplugin/python*', true, true) if vim.tbl_isempty(python_glob) then return @@ -198,6 +201,7 @@ local function check_rplugin_manifest() local python_dir = python_glob[1] local python_version = vim.fs.basename(python_dir) + --- @type string[] local scripts = vim.fn.glob(python_dir .. '/*.py', true, true) vim.list_extend(scripts, vim.fn.glob(python_dir .. '/*/__init__.py', true, true)) @@ -227,7 +231,10 @@ local function check_rplugin_manifest() end end - for _, path in ipairs(vim.fn.map(vim.split(vim.o.runtimepath, ','), 'resolve(v:val)')) do + --- @type string[] + local paths = vim.fn.map(vim.split(vim.o.runtimepath, ','), 'resolve(v:val)') + + for _, path in ipairs(paths) do handle_path(path) end diff --git a/runtime/lua/vim/hl.lua b/runtime/lua/vim/hl.lua index 099efa3c61..070748d31e 100644 --- a/runtime/lua/vim/hl.lua +++ b/runtime/lua/vim/hl.lua @@ -17,6 +17,9 @@ M.priorities = { user = 200, } +local range_timer --- @type uv.uv_timer_t? +local range_hl_clear --- @type fun()? + --- @class vim.hl.range.Opts --- @inlinedoc --- @@ -31,6 +34,10 @@ M.priorities = { --- Highlight priority --- (default: `vim.hl.priorities.user`) --- @field priority? integer +--- +--- Time in ms before highlight is cleared +--- (default: -1 no timeout) +--- @field timeout? integer --- Apply highlight group to range of text. --- @@ -45,6 +52,7 @@ function M.range(bufnr, ns, higroup, start, finish, opts) local regtype = opts.regtype or 'v' local inclusive = opts.inclusive or false local priority = opts.priority or M.priorities.user + local timeout = opts.timeout or -1 local v_maxcol = vim.v.maxcol @@ -100,6 +108,19 @@ function M.range(bufnr, ns, higroup, start, finish, opts) end end + if range_timer and not range_timer:is_closing() then + range_timer:close() + assert(range_hl_clear) + range_hl_clear() + end + + range_hl_clear = function() + range_timer = nil + range_hl_clear = nil + pcall(vim.api.nvim_buf_clear_namespace, bufnr, ns, 0, -1) + pcall(vim.api.nvim__ns_set, { wins = {} }) + end + for _, res in ipairs(region) do local start_row = res[1][2] - 1 local start_col = res[1][3] - 1 @@ -113,11 +134,13 @@ function M.range(bufnr, ns, higroup, start, finish, opts) strict = false, }) end + + if timeout ~= -1 then + range_timer = vim.defer_fn(range_hl_clear, timeout) + end end -local yank_ns = api.nvim_create_namespace('hlyank') -local yank_timer --- @type uv.uv_timer_t? -local yank_cancel --- @type fun()? +local yank_ns = api.nvim_create_namespace('nvim.hlyank') --- Highlight the yanked text during a |TextYankPost| event. --- @@ -152,31 +175,17 @@ function M.on_yank(opts) end local higroup = opts.higroup or 'IncSearch' - local timeout = opts.timeout or 150 local bufnr = vim.api.nvim_get_current_buf() local winid = vim.api.nvim_get_current_win() - if yank_timer then - yank_timer:close() - assert(yank_cancel) - yank_cancel() - end vim.api.nvim__ns_set(yank_ns, { wins = { winid } }) M.range(bufnr, yank_ns, higroup, "'[", "']", { regtype = event.regtype, inclusive = event.inclusive, priority = opts.priority or M.priorities.user, + timeout = opts.timeout or 150, }) - - yank_cancel = function() - yank_timer = nil - yank_cancel = nil - pcall(vim.api.nvim_buf_clear_namespace, bufnr, yank_ns, 0, -1) - pcall(vim.api.nvim__ns_set, { wins = {} }) - end - - yank_timer = vim.defer_fn(yank_cancel, timeout) end return M diff --git a/runtime/lua/vim/inspect.lua b/runtime/lua/vim/inspect.lua index c232f69590..cdf34897d4 100644 --- a/runtime/lua/vim/inspect.lua +++ b/runtime/lua/vim/inspect.lua @@ -1,3 +1,4 @@ +--- @diagnostic disable: no-unknown local inspect = { _VERSION = 'inspect.lua 3.1.0', _URL = 'http://github.com/kikito/inspect.lua', diff --git a/runtime/lua/vim/loader.lua b/runtime/lua/vim/loader.lua index 0cce0ab21d..c7158673fe 100644 --- a/runtime/lua/vim/loader.lua +++ b/runtime/lua/vim/loader.lua @@ -399,50 +399,57 @@ function M.reset(path) end end ---- Enables the experimental Lua module loader: ---- * overrides loadfile +--- Enables or disables the experimental Lua module loader: +--- +--- Enable (`enable=true`): +--- * overrides |loadfile()| --- * adds the Lua loader using the byte-compilation cache --- * adds the libs loader --- * removes the default Nvim loader --- ---- @since 0 -function M.enable() - if M.enabled then - return - end - M.enabled = true - vim.fn.mkdir(vim.fn.fnamemodify(M.path, ':p'), 'p') - _G.loadfile = loadfile_cached - -- add Lua loader - table.insert(loaders, 2, loader_cached) - -- add libs loader - table.insert(loaders, 3, loader_lib_cached) - -- remove Nvim loader - for l, loader in ipairs(loaders) do - if loader == vim._load_package then - table.remove(loaders, l) - break - end - end -end - ---- Disables the experimental Lua module loader: +--- Disable (`enable=false`): --- * removes the loaders --- * adds the default Nvim loader --- --- @since 0 -function M.disable() - if not M.enabled then +--- +--- @param enable? (boolean) true/nil to enable, false to disable +function M.enable(enable) + enable = enable == nil and true or enable + if enable == M.enabled then return end - M.enabled = false - _G.loadfile = _loadfile - for l, loader in ipairs(loaders) do - if loader == loader_cached or loader == loader_lib_cached then - table.remove(loaders, l) + M.enabled = enable + + if enable then + vim.fn.mkdir(vim.fn.fnamemodify(M.path, ':p'), 'p') + _G.loadfile = loadfile_cached + -- add Lua loader + table.insert(loaders, 2, loader_cached) + -- add libs loader + table.insert(loaders, 3, loader_lib_cached) + -- remove Nvim loader + for l, loader in ipairs(loaders) do + if loader == vim._load_package then + table.remove(loaders, l) + break + end + end + else + _G.loadfile = _loadfile + for l, loader in ipairs(loaders) do + if loader == loader_cached or loader == loader_lib_cached then + table.remove(loaders, l) + end end + table.insert(loaders, 2, vim._load_package) end - table.insert(loaders, 2, vim._load_package) +end + +--- @deprecated +function M.disable() + vim.deprecate('vim.loader.disable', 'vim.loader.enable(false)', '0.12') + vim.loader.enable(false) end --- Tracks the time spent in a function diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index 0de3b4ee4d..a45f9adeb6 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -3,6 +3,7 @@ local validate = vim.validate local lsp = vim._defer_require('vim.lsp', { _changetracking = ..., --- @module 'vim.lsp._changetracking' + _folding_range = ..., --- @module 'vim.lsp._folding_range' _snippet_grammar = ..., --- @module 'vim.lsp._snippet_grammar' _tagfunc = ..., --- @module 'vim.lsp._tagfunc' _watchfiles = ..., --- @module 'vim.lsp._watchfiles' @@ -57,6 +58,7 @@ lsp._request_name_to_capability = { [ms.textDocument_documentHighlight] = { 'documentHighlightProvider' }, [ms.textDocument_documentLink] = { 'documentLinkProvider' }, [ms.textDocument_documentSymbol] = { 'documentSymbolProvider' }, + [ms.textDocument_foldingRange] = { 'foldingRangeProvider' }, [ms.textDocument_formatting] = { 'documentFormattingProvider' }, [ms.textDocument_hover] = { 'hoverProvider' }, [ms.textDocument_implementation] = { 'implementationProvider' }, @@ -87,18 +89,6 @@ lsp._request_name_to_capability = { -- TODO improve handling of scratch buffers with LSP attached. ---- Returns the buffer number for the given {bufnr}. ---- ----@param bufnr (integer|nil) Buffer number to resolve. Defaults to current buffer ----@return integer bufnr -local function resolve_bufnr(bufnr) - validate('bufnr', bufnr, 'number', true) - if bufnr == nil or bufnr == 0 then - return api.nvim_get_current_buf() - end - return bufnr -end - ---@private --- Called by the client when trying to call a method that's not --- supported in any of the servers registered for the current buffer. @@ -112,6 +102,22 @@ function lsp._unsupported_method(method) return msg end +---@private +---@param workspace_folders string|lsp.WorkspaceFolder[]? +---@return lsp.WorkspaceFolder[]? +function lsp._get_workspace_folders(workspace_folders) + if type(workspace_folders) == 'table' then + return workspace_folders + elseif type(workspace_folders) == 'string' then + return { + { + uri = vim.uri_from_fname(workspace_folders), + name = workspace_folders, + }, + } + end +end + local wait_result_reason = { [-1] = 'timeout', [-2] = 'interrupted', [-3] = 'error' } local format_line_ending = { @@ -194,34 +200,393 @@ local function reuse_client_default(client, config) return false end - if config.root_dir then - local root = vim.uri_from_fname(config.root_dir) - for _, dir in ipairs(client.workspace_folders or {}) do - -- note: do not need to check client.root_dir since that should be client.workspace_folders[1] - if root == dir.uri then - return true + local config_folders = lsp._get_workspace_folders(config.workspace_folders or config.root_dir) + + if not config_folders or not next(config_folders) then + -- Reuse if the client was configured with no workspace folders + local client_config_folders = + lsp._get_workspace_folders(client.config.workspace_folders or client.config.root_dir) + return not client_config_folders or not next(client_config_folders) + end + + for _, config_folder in ipairs(config_folders) do + local found = false + for _, client_folder in ipairs(client.workspace_folders or {}) do + if config_folder.uri == client_folder.uri then + found = true + break end end + if not found then + return false + end end - -- TODO(lewis6991): also check config.workspace_folders + return true +end - return false +--- Reset defaults set by `set_defaults`. +--- Must only be called if the last client attached to a buffer exits. +local function reset_defaults(bufnr) + if vim.bo[bufnr].tagfunc == 'v:lua.vim.lsp.tagfunc' then + vim.bo[bufnr].tagfunc = nil + end + if vim.bo[bufnr].omnifunc == 'v:lua.vim.lsp.omnifunc' then + vim.bo[bufnr].omnifunc = nil + end + if vim.bo[bufnr].formatexpr == 'v:lua.vim.lsp.formatexpr()' then + vim.bo[bufnr].formatexpr = nil + end + vim._with({ buf = bufnr }, function() + local keymap = vim.fn.maparg('K', 'n', false, true) + if keymap and keymap.callback == vim.lsp.buf.hover and keymap.buffer == 1 then + vim.keymap.del('n', 'K', { buffer = bufnr }) + end + end) end ---- @class vim.lsp.start.Opts ---- @inlinedoc +--- @param code integer +--- @param signal integer +--- @param client_id integer +local function on_client_exit(code, signal, client_id) + local client = all_clients[client_id] + + vim.schedule(function() + for bufnr in pairs(client.attached_buffers) do + if client and client.attached_buffers[bufnr] and api.nvim_buf_is_valid(bufnr) then + api.nvim_exec_autocmds('LspDetach', { + buffer = bufnr, + modeline = false, + data = { client_id = client_id }, + }) + end + + client.attached_buffers[bufnr] = nil + + if #lsp.get_clients({ bufnr = bufnr, _uninitialized = true }) == 0 then + reset_defaults(bufnr) + end + end + + local namespace = vim.lsp.diagnostic.get_namespace(client_id) + vim.diagnostic.reset(namespace) + end) + + local name = client.name or 'unknown' + + -- Schedule the deletion of the client object so that it exists in the execution of LspDetach + -- autocommands + vim.schedule(function() + all_clients[client_id] = nil + + -- Client can be absent if executable starts, but initialize fails + -- init/attach won't have happened + if client then + changetracking.reset(client) + end + if code ~= 0 or (signal ~= 0 and signal ~= 15) then + local msg = string.format( + 'Client %s quit with exit code %s and signal %s. Check log for errors: %s', + name, + code, + signal, + lsp.get_log_path() + ) + vim.notify(msg, vim.log.levels.WARN) + end + end) +end + +--- Creates and initializes a client with the given configuration. +--- @param config vim.lsp.ClientConfig Configuration for the server. +--- @return integer? client_id |vim.lsp.get_client_by_id()| Note: client may not be +--- fully initialized. Use `on_init` to do any actions once +--- the client has been initialized. +--- @return string? # Error message, if any +local function create_and_initialize_client(config) + local ok, res = pcall(require('vim.lsp.client').create, config) + if not ok then + return nil, res --[[@as string]] + end + + local client = assert(res) + + --- @diagnostic disable-next-line: invisible + table.insert(client._on_exit_cbs, on_client_exit) + + all_clients[client.id] = client + + client:initialize() + + return client.id, nil +end + +--- @class vim.lsp.Config : vim.lsp.ClientConfig +--- +--- See `cmd` in [vim.lsp.ClientConfig]. +--- @field cmd? string[]|fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient +--- +--- Filetypes the client will attach to, if activated by `vim.lsp.enable()`. +--- If not provided, then the client will attach to all filetypes. +--- @field filetypes? string[] +--- +--- Directory markers (.e.g. '.git/') where the LSP server will base its workspaceFolders, +--- rootUri, and rootPath on initialization. Unused if `root_dir` is provided. +--- @field root_markers? string[] +--- +--- Directory where the LSP server will base its workspaceFolders, rootUri, and rootPath on +--- initialization. If a function, it accepts a single callback argument which must be called with +--- the value of root_dir to use. The LSP server will not be started until the callback is called. +--- @field root_dir? string|fun(cb:fun(string)) --- --- Predicate used to decide if a client should be re-used. Used on all --- running clients. The default implementation re-uses a client if name and --- root_dir matches. --- @field reuse_client? fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean + +--- Update the configuration for an LSP client. +--- +--- Use name '*' to set default configuration for all clients. +--- +--- Can also be table-assigned to redefine the configuration for a client. +--- +--- Examples: +--- +--- - Add a root marker for all clients: +--- ```lua +--- vim.lsp.config('*', { +--- root_markers = { '.git' }, +--- }) +--- ``` +--- - Add additional capabilities to all clients: +--- ```lua +--- vim.lsp.config('*', { +--- capabilities = { +--- textDocument = { +--- semanticTokens = { +--- multilineTokenSupport = true, +--- } +--- } +--- } +--- }) +--- ``` +--- - (Re-)define the configuration for clangd: +--- ```lua +--- vim.lsp.config.clangd = { +--- cmd = { +--- 'clangd', +--- '--clang-tidy', +--- '--background-index', +--- '--offset-encoding=utf-8', +--- }, +--- root_markers = { '.clangd', 'compile_commands.json' }, +--- filetypes = { 'c', 'cpp' }, +--- } +--- ``` +--- - Get configuration for luals: +--- ```lua +--- local cfg = vim.lsp.config.luals +--- ``` +--- +--- @param name string +--- @param cfg vim.lsp.Config +--- @diagnostic disable-next-line:assign-type-mismatch +function lsp.config(name, cfg) + local _, _ = name, cfg -- ignore unused + -- dummy proto for docs +end + +lsp._enabled_configs = {} --- @type table<string,{resolved_config:vim.lsp.Config?}> + +--- If a config in vim.lsp.config() is accessed then the resolved config becomes invalid. +--- @param name string +local function invalidate_enabled_config(name) + if name == '*' then + for _, v in pairs(lsp._enabled_configs) do + v.resolved_config = nil + end + elseif lsp._enabled_configs[name] then + lsp._enabled_configs[name].resolved_config = nil + end +end + +--- @nodoc +--- @class vim.lsp.config +--- @field [string] vim.lsp.Config +--- @field package _configs table<string,vim.lsp.Config> +lsp.config = setmetatable({ _configs = {} }, { + --- @param self vim.lsp.config + --- @param name string + --- @return vim.lsp.Config + __index = function(self, name) + validate('name', name, 'string') + + local rconfig = lsp._enabled_configs[name] or {} + self._configs[name] = self._configs[name] or {} + + if not rconfig.resolved_config then + -- Resolve configs from lsp/*.lua + -- Calls to vim.lsp.config in lsp/* have a lower precedence than calls from other sites. + local rtp_config = {} ---@type vim.lsp.Config + for _, v in ipairs(api.nvim_get_runtime_file(('lsp/%s.lua'):format(name), true)) do + local config = assert(loadfile(v))() ---@type any? + if type(config) == 'table' then + rtp_config = vim.tbl_deep_extend('force', rtp_config, config) + else + log.warn(string.format('%s does not return a table, ignoring', v)) + end + end + + rconfig.resolved_config = vim.tbl_deep_extend( + 'force', + lsp.config._configs['*'] or {}, + rtp_config, + lsp.config._configs[name] or {} + ) + rconfig.resolved_config.name = name + end + + return rconfig.resolved_config + end, + + --- @param self vim.lsp.config + --- @param name string + --- @param cfg vim.lsp.Config + __newindex = function(self, name, cfg) + validate('name', name, 'string') + validate('cfg', cfg, 'table') + invalidate_enabled_config(name) + self._configs[name] = cfg + end, + + --- @param self vim.lsp.config + --- @param name string + --- @param cfg vim.lsp.Config + __call = function(self, name, cfg) + validate('name', name, 'string') + validate('cfg', cfg, 'table') + invalidate_enabled_config(name) + self[name] = vim.tbl_deep_extend('force', self._configs[name] or {}, cfg) + end, +}) + +local lsp_enable_autocmd_id --- @type integer? + +--- @param bufnr integer +local function lsp_enable_callback(bufnr) + -- Only ever attach to buffers that represent an actual file. + if vim.bo[bufnr].buftype ~= '' then + return + end + + --- @param config vim.lsp.Config + local function can_start(config) + if config.filetypes and not vim.tbl_contains(config.filetypes, vim.bo[bufnr].filetype) then + return false + elseif type(config.cmd) == 'table' and vim.fn.executable(config.cmd[1]) == 0 then + return false + end + + return true + end + + --- @param config vim.lsp.Config + local function start(config) + return vim.lsp.start(config, { + bufnr = bufnr, + reuse_client = config.reuse_client, + _root_markers = config.root_markers, + }) + end + + for name in vim.spairs(lsp._enabled_configs) do + local config = lsp.config[name] + validate('cmd', config.cmd, { 'function', 'table' }) + validate('cmd', config.reuse_client, 'function', true) + + if can_start(config) then + -- Deepcopy config so changes done in the client + -- do not propagate back to the enabled configs. + config = vim.deepcopy(config) + + if type(config.root_dir) == 'function' then + ---@param root_dir string + config.root_dir(function(root_dir) + config.root_dir = root_dir + vim.schedule(function() + start(config) + end) + end) + else + start(config) + end + end + end +end + +--- Enable an LSP server to automatically start when opening a buffer. +--- +--- Uses configuration defined with `vim.lsp.config`. +--- +--- Examples: +--- +--- ```lua +--- vim.lsp.enable('clangd') +--- +--- vim.lsp.enable({'luals', 'pyright'}) +--- ``` +--- +--- @param name string|string[] Name(s) of client(s) to enable. +--- @param enable? boolean `true|nil` to enable, `false` to disable. +function lsp.enable(name, enable) + validate('name', name, { 'string', 'table' }) + + local names = vim._ensure_list(name) --[[@as string[] ]] + for _, nm in ipairs(names) do + if nm == '*' then + error('Invalid name') + end + lsp._enabled_configs[nm] = enable ~= false and {} or nil + end + + if not next(lsp._enabled_configs) then + if lsp_enable_autocmd_id then + api.nvim_del_autocmd(lsp_enable_autocmd_id) + lsp_enable_autocmd_id = nil + end + return + end + + -- Only ever create autocmd once to reuse computation of config merging. + lsp_enable_autocmd_id = lsp_enable_autocmd_id + or api.nvim_create_autocmd('FileType', { + group = api.nvim_create_augroup('nvim.lsp.enable', {}), + callback = function(args) + lsp_enable_callback(args.buf) + end, + }) +end + +--- @class vim.lsp.start.Opts +--- @inlinedoc +--- +--- Predicate used to decide if a client should be re-used. Used on all +--- running clients. The default implementation re-uses a client if it has the +--- same name and if the given workspace folders (or root_dir) are all included +--- in the client's workspace folders. +--- @field reuse_client? fun(client: vim.lsp.Client, config: vim.lsp.ClientConfig): boolean --- --- Buffer handle to attach to if starting or re-using a client (0 for current). --- @field bufnr? integer --- +--- Whether to attach the client to a buffer (default true). +--- If set to `false`, `reuse_client` and `bufnr` will be ignored. +--- @field attach? boolean +--- --- Suppress error reporting if the LSP server fails to start (default false). --- @field silent? boolean +--- +--- @field package _root_markers? string[] --- Create a new LSP client and start a language server or reuses an already --- running client if one is found matching `name` and `root_dir`. @@ -237,10 +602,10 @@ end --- }) --- ``` --- ---- See |vim.lsp.start_client()| for all available options. The most important are: +--- See |vim.lsp.ClientConfig| for all available options. The most important are: --- --- - `name` arbitrary name for the LSP client. Should be unique per language server. ---- - `cmd` command string[] or function, described at |vim.lsp.start_client()|. +--- - `cmd` command string[] or function. --- - `root_dir` path to the project root. By default this is used to decide if an existing client --- should be re-used. The example above uses |vim.fs.root()| to detect the root by traversing --- the file system upwards starting from the current directory until either a `pyproject.toml` @@ -260,36 +625,46 @@ end --- `ftplugin/<filetype_name>.lua` (See |ftplugin-name|) --- --- @param config vim.lsp.ClientConfig Configuration for the server. ---- @param opts vim.lsp.start.Opts? Optional keyword arguments +--- @param opts vim.lsp.start.Opts? Optional keyword arguments. --- @return integer? client_id function lsp.start(config, opts) opts = opts or {} local reuse_client = opts.reuse_client or reuse_client_default - local bufnr = resolve_bufnr(opts.bufnr) + local bufnr = vim._resolve_bufnr(opts.bufnr) + + if not config.root_dir and opts._root_markers then + config = vim.deepcopy(config) + config.root_dir = vim.fs.root(bufnr, opts._root_markers) + end for _, client in pairs(all_clients) do if reuse_client(client, config) then + if opts.attach == false then + return client.id + end + if lsp.buf_attach_client(bufnr, client.id) then return client.id - else - return nil end + return end end - local client_id, err = lsp.start_client(config) + local client_id, err = create_and_initialize_client(config) if err then if not opts.silent then vim.notify(err, vim.log.levels.WARN) end - return nil + return end - if client_id and lsp.buf_attach_client(bufnr, client_id) then + if opts.attach == false then return client_id end - return nil + if client_id and lsp.buf_attach_client(bufnr, client_id) then + return client_id + end end --- Consumes the latest progress messages from all clients and formats them as a string. @@ -349,17 +724,17 @@ end ---@param bufnr integer function lsp._set_defaults(client, bufnr) if - client.supports_method(ms.textDocument_definition) and is_empty_or_default(bufnr, 'tagfunc') + client:supports_method(ms.textDocument_definition) and is_empty_or_default(bufnr, 'tagfunc') then vim.bo[bufnr].tagfunc = 'v:lua.vim.lsp.tagfunc' end if - client.supports_method(ms.textDocument_completion) and is_empty_or_default(bufnr, 'omnifunc') + client:supports_method(ms.textDocument_completion) and is_empty_or_default(bufnr, 'omnifunc') then vim.bo[bufnr].omnifunc = 'v:lua.vim.lsp.omnifunc' end if - client.supports_method(ms.textDocument_rangeFormatting) + client:supports_method(ms.textDocument_rangeFormatting) and is_empty_or_default(bufnr, 'formatprg') and is_empty_or_default(bufnr, 'formatexpr') then @@ -367,90 +742,21 @@ function lsp._set_defaults(client, bufnr) end vim._with({ buf = bufnr }, function() if - client.supports_method(ms.textDocument_hover) + client:supports_method(ms.textDocument_hover) and is_empty_or_default(bufnr, 'keywordprg') and vim.fn.maparg('K', 'n', false, false) == '' then - vim.keymap.set('n', 'K', vim.lsp.buf.hover, { buffer = bufnr, desc = 'vim.lsp.buf.hover()' }) + vim.keymap.set('n', 'K', function() + vim.lsp.buf.hover() + end, { buffer = bufnr, desc = 'vim.lsp.buf.hover()' }) end end) - if client.supports_method(ms.textDocument_diagnostic) then + if client:supports_method(ms.textDocument_diagnostic) then lsp.diagnostic._enable(bufnr) end end ---- Reset defaults set by `set_defaults`. ---- Must only be called if the last client attached to a buffer exits. -local function reset_defaults(bufnr) - if vim.bo[bufnr].tagfunc == 'v:lua.vim.lsp.tagfunc' then - vim.bo[bufnr].tagfunc = nil - end - if vim.bo[bufnr].omnifunc == 'v:lua.vim.lsp.omnifunc' then - vim.bo[bufnr].omnifunc = nil - end - if vim.bo[bufnr].formatexpr == 'v:lua.vim.lsp.formatexpr()' then - vim.bo[bufnr].formatexpr = nil - end - vim._with({ buf = bufnr }, function() - local keymap = vim.fn.maparg('K', 'n', false, true) - if keymap and keymap.callback == vim.lsp.buf.hover and keymap.buffer == 1 then - vim.keymap.del('n', 'K', { buffer = bufnr }) - end - end) -end - ---- @param code integer ---- @param signal integer ---- @param client_id integer -local function on_client_exit(code, signal, client_id) - local client = all_clients[client_id] - - vim.schedule(function() - for bufnr in pairs(client.attached_buffers) do - if client and client.attached_buffers[bufnr] and api.nvim_buf_is_valid(bufnr) then - api.nvim_exec_autocmds('LspDetach', { - buffer = bufnr, - modeline = false, - data = { client_id = client_id }, - }) - end - - client.attached_buffers[bufnr] = nil - - if #lsp.get_clients({ bufnr = bufnr, _uninitialized = true }) == 0 then - reset_defaults(bufnr) - end - end - - local namespace = vim.lsp.diagnostic.get_namespace(client_id) - vim.diagnostic.reset(namespace) - end) - - local name = client.name or 'unknown' - - -- Schedule the deletion of the client object so that it exists in the execution of LspDetach - -- autocommands - vim.schedule(function() - all_clients[client_id] = nil - - -- Client can be absent if executable starts, but initialize fails - -- init/attach won't have happened - if client then - changetracking.reset(client) - end - if code ~= 0 or (signal ~= 0 and signal ~= 15) then - local msg = string.format( - 'Client %s quit with exit code %s and signal %s. Check log for errors: %s', - name, - code, - signal, - lsp.get_log_path() - ) - vim.notify(msg, vim.log.levels.WARN) - end - end) -end - +--- @deprecated --- Starts and initializes a client with the given configuration. --- @param config vim.lsp.ClientConfig Configuration for the server. --- @return integer? client_id |vim.lsp.get_client_by_id()| Note: client may not be @@ -458,39 +764,26 @@ end --- the client has been initialized. --- @return string? # Error message, if any function lsp.start_client(config) - local ok, res = pcall(require('vim.lsp.client').create, config) - if not ok then - return nil, res --[[@as string]] - end - - local client = assert(res) - - --- @diagnostic disable-next-line: invisible - table.insert(client._on_exit_cbs, on_client_exit) - - all_clients[client.id] = client - - client:initialize() - - return client.id, nil + vim.deprecate('vim.lsp.start_client()', 'vim.lsp.start()', '0.13') + return create_and_initialize_client(config) end ---Buffer lifecycle handler for textDocument/didSave --- @param bufnr integer local function text_document_did_save_handler(bufnr) - bufnr = resolve_bufnr(bufnr) + bufnr = vim._resolve_bufnr(bufnr) local uri = vim.uri_from_bufnr(bufnr) local text = once(lsp._buf_get_full_text) for _, client in ipairs(lsp.get_clients({ bufnr = bufnr })) do local name = api.nvim_buf_get_name(bufnr) local old_name = changetracking._get_and_set_name(client, bufnr, name) if old_name and name ~= old_name then - client.notify(ms.textDocument_didClose, { + client:notify(ms.textDocument_didClose, { textDocument = { uri = vim.uri_from_fname(old_name), }, }) - client.notify(ms.textDocument_didOpen, { + client:notify(ms.textDocument_didOpen, { textDocument = { version = 0, uri = uri, @@ -506,7 +799,7 @@ local function text_document_did_save_handler(bufnr) if type(save_capability) == 'table' and save_capability.includeText then included_text = text(bufnr) end - client.notify(ms.textDocument_didSave, { + client:notify(ms.textDocument_didSave, { textDocument = { uri = uri, }, @@ -527,10 +820,10 @@ local function buf_detach_client(bufnr, client) changetracking.reset_buf(client, bufnr) - if client.supports_method(ms.textDocument_didClose) then + if client:supports_method(ms.textDocument_didClose) then local uri = vim.uri_from_bufnr(bufnr) local params = { textDocument = { uri = uri } } - client.notify(ms.textDocument_didClose, params) + client:notify(ms.textDocument_didClose, params) end client.attached_buffers[bufnr] = nil @@ -550,7 +843,7 @@ local function buf_attach(bufnr) attached_buffers[bufnr] = true local uri = vim.uri_from_bufnr(bufnr) - local augroup = ('lsp_b_%d_save'):format(bufnr) + local augroup = ('nvim.lsp.b_%d_save'):format(bufnr) local group = api.nvim_create_augroup(augroup, { clear = true }) api.nvim_create_autocmd('BufWritePre', { group = group, @@ -564,12 +857,12 @@ local function buf_attach(bufnr) }, reason = protocol.TextDocumentSaveReason.Manual, ---@type integer } - if client.supports_method(ms.textDocument_willSave) then - client.notify(ms.textDocument_willSave, params) + if client:supports_method(ms.textDocument_willSave) then + client:notify(ms.textDocument_willSave, params) end - if client.supports_method(ms.textDocument_willSaveWaitUntil) then + if client:supports_method(ms.textDocument_willSaveWaitUntil) then local result, err = - client.request_sync(ms.textDocument_willSaveWaitUntil, params, 1000, ctx.buf) + client:request_sync(ms.textDocument_willSaveWaitUntil, params, 1000, ctx.buf) if result and result.result then util.apply_text_edits(result.result, ctx.buf, client.offset_encoding) elseif err then @@ -603,8 +896,8 @@ local function buf_attach(bufnr) local params = { textDocument = { uri = uri } } for _, client in ipairs(clients) do changetracking.reset_buf(client, bufnr) - if client.supports_method(ms.textDocument_didClose) then - client.notify(ms.textDocument_didClose, params) + if client:supports_method(ms.textDocument_didClose) then + client:notify(ms.textDocument_didClose, params) end end for _, client in ipairs(clients) do @@ -639,7 +932,7 @@ end function lsp.buf_attach_client(bufnr, client_id) validate('bufnr', bufnr, 'number', true) validate('client_id', client_id, 'number') - bufnr = resolve_bufnr(bufnr) + bufnr = vim._resolve_bufnr(bufnr) if not api.nvim_buf_is_loaded(bufnr) then log.warn(string.format('buf_attach_client called on unloaded buffer (id: %d): ', bufnr)) return false @@ -662,7 +955,7 @@ function lsp.buf_attach_client(bufnr, client_id) -- Send didOpen for the client if it is initialized. If it isn't initialized -- then it will send didOpen on initialize. if client.initialized then - client:_on_attach(bufnr) + client:on_attach(bufnr) end return true end @@ -676,7 +969,7 @@ end function lsp.buf_detach_client(bufnr, client_id) validate('bufnr', bufnr, 'number', true) validate('client_id', client_id, 'number') - bufnr = resolve_bufnr(bufnr) + bufnr = vim._resolve_bufnr(bufnr) local client = all_clients[client_id] if not client or not client.attached_buffers[bufnr] then @@ -740,13 +1033,13 @@ function lsp.stop_client(client_id, force) for _, id in ipairs(ids) do if type(id) == 'table' then if id.stop then - id.stop(force) + id:stop(force) end else --- @cast id -vim.lsp.Client local client = all_clients[id] if client then - client.stop(force) + client:stop(force) end end end @@ -782,7 +1075,7 @@ function lsp.get_clients(filter) local clients = {} --- @type vim.lsp.Client[] - local bufnr = filter.bufnr and resolve_bufnr(filter.bufnr) + local bufnr = filter.bufnr and vim._resolve_bufnr(filter.bufnr) for _, client in pairs(all_clients) do if @@ -790,7 +1083,7 @@ function lsp.get_clients(filter) and (filter.id == nil or client.id == filter.id) and (filter.bufnr == nil or client.attached_buffers[bufnr]) and (filter.name == nil or client.name == filter.name) - and (filter.method == nil or client.supports_method(filter.method, { bufnr = filter.bufnr })) + and (filter.method == nil or client:supports_method(filter.method, filter.bufnr)) and (filter._uninitialized or client.initialized) then clients[#clients + 1] = client @@ -812,7 +1105,7 @@ api.nvim_create_autocmd('VimLeavePre', { local active_clients = lsp.get_clients() log.info('exit_handler', active_clients) for _, client in pairs(all_clients) do - client.stop() + client:stop() end local timeouts = {} --- @type table<integer,integer> @@ -847,7 +1140,7 @@ api.nvim_create_autocmd('VimLeavePre', { if not vim.wait(max_timeout, check_clients_closed, poll_time) then for client_id, client in pairs(active_clients) do if timeouts[client_id] ~= nil then - client.stop(true) + client:stop(true) end end end @@ -878,16 +1171,16 @@ function lsp.buf_request(bufnr, method, params, handler, on_unsupported) validate('handler', handler, 'function', true) validate('on_unsupported', on_unsupported, 'function', true) - bufnr = resolve_bufnr(bufnr) + bufnr = vim._resolve_bufnr(bufnr) local method_supported = false local clients = lsp.get_clients({ bufnr = bufnr }) local client_request_ids = {} --- @type table<integer,integer> for _, client in ipairs(clients) do - if client.supports_method(method, { bufnr = bufnr }) then + if client:supports_method(method, bufnr) then method_supported = true local cparams = type(params) == 'function' and params(client, bufnr) or params --[[@as table?]] - local request_success, request_id = client.request(method, cparams, handler, bufnr) + local request_success, request_id = client:request(method, cparams, handler, bufnr) -- This could only fail if the client shut down in the time since we looked -- it up and we did the request, which should be rare. if request_success then @@ -910,7 +1203,7 @@ function lsp.buf_request(bufnr, method, params, handler, on_unsupported) local function _cancel_all_requests() for client_id, request_id in pairs(client_request_ids) do local client = all_clients[client_id] - client.cancel_request(request_id) + client:cancel_request(request_id) end end @@ -1049,7 +1342,7 @@ function lsp.formatexpr(opts) end local bufnr = api.nvim_get_current_buf() for _, client in pairs(lsp.get_clients({ bufnr = bufnr })) do - if client.supports_method(ms.textDocument_rangeFormatting) then + if client:supports_method(ms.textDocument_rangeFormatting) then local params = util.make_formatting_params() local end_line = vim.fn.getline(end_lnum) --[[@as string]] local end_col = vim.str_utfindex(end_line, client.offset_encoding) @@ -1065,7 +1358,7 @@ function lsp.formatexpr(opts) }, } local response = - client.request_sync(ms.textDocument_rangeFormatting, params, timeout_ms, bufnr) + client:request_sync(ms.textDocument_rangeFormatting, params, timeout_ms, bufnr) if response and response.result then lsp.util.apply_text_edits(response.result, bufnr, client.offset_encoding) return 0 @@ -1092,6 +1385,55 @@ function lsp.tagfunc(pattern, flags) return vim.lsp._tagfunc(pattern, flags) end +--- Provides an interface between the built-in client and a `foldexpr` function. +--- +--- To use, check for the "textDocument/foldingRange" capability in an +--- |LspAttach| autocommand. Example: +--- +--- ```lua +--- vim.api.nvim_create_autocmd('LspAttach', { +--- callback = function(args) +--- local client = vim.lsp.get_client_by_id(args.data.client_id) +--- if client:supports_method('textDocument/foldingRange') then +--- local win = vim.api.nvim_get_current_win() +--- vim.wo[win][0].foldmethod = 'expr' +--- vim.wo[win][0].foldexpr = 'v:lua.vim.lsp.foldexpr()' +--- end +--- end, +--- }) +--- ``` +--- +---@param lnum integer line number +function lsp.foldexpr(lnum) + return vim.lsp._folding_range.foldexpr(lnum) +end + +--- Close all {kind} of folds in the the window with {winid}. +--- +--- To automatically fold imports when opening a file, you can use an autocmd: +--- +--- ```lua +--- vim.api.nvim_create_autocmd('LspNotify', { +--- callback = function(args) +--- if args.data.method == 'textDocument/didOpen' then +--- vim.lsp.foldclose('imports', vim.fn.bufwinid(args.buf)) +--- end +--- end, +--- }) +--- ``` +--- +---@param kind lsp.FoldingRangeKind Kind to close, one of "comment", "imports" or "region". +---@param winid? integer Defaults to the current window. +function lsp.foldclose(kind, winid) + return vim.lsp._folding_range.foldclose(kind, winid) +end + +--- Provides a `foldtext` function that shows the `collapsedText` retrieved, +--- defaults to the first folded line if `collapsedText` is not provided. +function lsp.foldtext() + return vim.lsp._folding_range.foldtext() +end + ---Checks whether a client is stopped. --- ---@param client_id (integer) @@ -1110,7 +1452,7 @@ end function lsp.buf_get_clients(bufnr) vim.deprecate('vim.lsp.buf_get_clients()', 'vim.lsp.get_clients()', '0.12') local result = {} --- @type table<integer,vim.lsp.Client> - for _, client in ipairs(lsp.get_clients({ bufnr = resolve_bufnr(bufnr) })) do + for _, client in ipairs(lsp.get_clients({ bufnr = vim._resolve_bufnr(bufnr) })) do result[client.id] = client end return result @@ -1164,7 +1506,7 @@ function lsp.for_each_buffer_client(bufnr, fn) 'lsp.get_clients({ bufnr = bufnr }) with regular loop', '0.12' ) - bufnr = resolve_bufnr(bufnr) + bufnr = vim._resolve_bufnr(bufnr) for _, client in pairs(lsp.get_clients({ bufnr = bufnr })) do fn(client, client.id, bufnr) @@ -1181,44 +1523,6 @@ function lsp.with(handler, override_config) end end ---- Helper function to use when implementing a handler. ---- This will check that all of the keys in the user configuration ---- are valid keys and make sense to include for this handler. ---- ---- Will error on invalid keys (i.e. keys that do not exist in the options) ---- @param name string ---- @param options table<string,any> ---- @param user_config table<string,any> -function lsp._with_extend(name, options, user_config) - user_config = user_config or {} - - local resulting_config = {} --- @type table<string,any> - for k, v in pairs(user_config) do - if options[k] == nil then - error( - debug.traceback( - string.format( - 'Invalid option for `%s`: %s. Valid options are:\n%s', - name, - k, - vim.inspect(vim.tbl_keys(options)) - ) - ) - ) - end - - resulting_config[k] = v - end - - for k, v in pairs(options) do - if resulting_config[k] == nil then - resulting_config[k] = v - end - end - - return resulting_config -end - --- Registry for client side commands. --- This is an extension point for plugins to handle custom commands which are --- not part of the core language server protocol specification. @@ -1227,7 +1531,7 @@ end --- and the value is a function which is called if any LSP action --- (code action, code lenses, ...) triggers the command. --- ---- If a LSP response contains a command for which no matching entry is +--- If an LSP response contains a command for which no matching entry is --- available in this registry, the command will be executed via the LSP server --- using `workspace/executeCommand`. --- diff --git a/runtime/lua/vim/lsp/_changetracking.lua b/runtime/lua/vim/lsp/_changetracking.lua index b2be53269f..265a74c8fa 100644 --- a/runtime/lua/vim/lsp/_changetracking.lua +++ b/runtime/lua/vim/lsp/_changetracking.lua @@ -18,14 +18,14 @@ local M = {} --- --- None: One group for all clients --- Full: One group for all clients ---- Incremental: One group per `offset_encoding` +--- Incremental: One group per `position_encoding` --- --- Sending changes can be debounced per buffer. To simplify the implementation the --- smallest debounce interval is used and we don't group clients by different intervals. --- --- @class vim.lsp.CTGroup --- @field sync_kind integer TextDocumentSyncKind, considers config.flags.allow_incremental_sync ---- @field offset_encoding "utf-8"|"utf-16"|"utf-32" +--- @field position_encoding "utf-8"|"utf-16"|"utf-32" --- --- @class vim.lsp.CTBufferState --- @field name string name of the buffer @@ -40,13 +40,13 @@ local M = {} --- @class vim.lsp.CTGroupState --- @field buffers table<integer,vim.lsp.CTBufferState> --- @field debounce integer debounce duration in ms ---- @field clients table<integer, table> clients using this state. {client_id, client} +--- @field clients table<integer, vim.lsp.Client> clients using this state. {client_id, client} ---@param group vim.lsp.CTGroup ---@return string local function group_key(group) if group.sync_kind == protocol.TextDocumentSyncKind.Incremental then - return tostring(group.sync_kind) .. '\0' .. group.offset_encoding + return tostring(group.sync_kind) .. '\0' .. group.position_encoding end return tostring(group.sync_kind) end @@ -64,7 +64,7 @@ local state_by_group = setmetatable({}, { ---@param client vim.lsp.Client ---@return vim.lsp.CTGroup local function get_group(client) - local allow_inc_sync = vim.F.if_nil(client.flags.allow_incremental_sync, true) --- @type boolean + local allow_inc_sync = vim.F.if_nil(client.flags.allow_incremental_sync, true) local change_capability = vim.tbl_get(client.server_capabilities, 'textDocumentSync', 'change') local sync_kind = change_capability or protocol.TextDocumentSyncKind.None if not allow_inc_sync and change_capability == protocol.TextDocumentSyncKind.Incremental then @@ -72,7 +72,7 @@ local function get_group(client) end return { sync_kind = sync_kind, - offset_encoding = client.offset_encoding, + position_encoding = client.offset_encoding, } end @@ -273,8 +273,8 @@ local function send_changes(bufnr, sync_kind, state, buf_state) end local uri = vim.uri_from_bufnr(bufnr) for _, client in pairs(state.clients) do - if not client.is_stopped() and vim.lsp.buf_is_attached(bufnr, client.id) then - client.notify(protocol.Methods.textDocument_didChange, { + if not client:is_stopped() and vim.lsp.buf_is_attached(bufnr, client.id) then + client:notify(protocol.Methods.textDocument_didChange, { textDocument = { uri = uri, version = util.buf_versions[bufnr], @@ -310,7 +310,7 @@ local function send_changes_for_group(bufnr, firstline, lastline, new_lastline, -- The contents would further change and startline/endline may no longer fit local changes = incremental_changes( buf_state, - group.offset_encoding, + group.position_encoding, bufnr, firstline, lastline, diff --git a/runtime/lua/vim/lsp/_folding_range.lua b/runtime/lua/vim/lsp/_folding_range.lua new file mode 100644 index 0000000000..66eb81db6e --- /dev/null +++ b/runtime/lua/vim/lsp/_folding_range.lua @@ -0,0 +1,373 @@ +local util = require('vim.lsp.util') +local log = require('vim.lsp.log') +local ms = require('vim.lsp.protocol').Methods +local api = vim.api + +local M = {} + +---@class (private) vim.lsp.folding_range.BufState +--- +---@field version? integer +--- +--- Never use this directly, `renew()` the cached foldinfo +--- then use on demand via `row_*` fields. +--- +--- Index In the form of client_id -> ranges +---@field client_ranges table<integer, lsp.FoldingRange[]?> +--- +--- Index in the form of row -> [foldlevel, mark] +---@field row_level table<integer, [integer, ">" | "<"?]?> +--- +--- Index in the form of start_row -> kinds +---@field row_kinds table<integer, table<lsp.FoldingRangeKind, true?>?>> +--- +--- Index in the form of start_row -> collapsed_text +---@field row_text table<integer, string?> + +---@type table<integer, vim.lsp.folding_range.BufState?> +local bufstates = {} + +--- Renew the cached foldinfo in the buffer. +---@param bufnr integer +local function renew(bufnr) + local bufstate = assert(bufstates[bufnr]) + + ---@type table<integer, [integer, ">" | "<"?]?> + local row_level = {} + ---@type table<integer, table<lsp.FoldingRangeKind, true?>?>> + local row_kinds = {} + ---@type table<integer, string?> + local row_text = {} + + for _, ranges in pairs(bufstate.client_ranges) do + for _, range in ipairs(ranges) do + local start_row = range.startLine + local end_row = range.endLine + -- Adding folds within a single line is not supported by Nvim. + if start_row ~= end_row then + row_text[start_row] = range.collapsedText + + local kind = range.kind + if kind then + local kinds = row_kinds[start_row] or {} + kinds[kind] = true + row_kinds[start_row] = kinds + end + + for row = start_row, end_row do + local level = row_level[row] or { 0 } + level[1] = level[1] + 1 + row_level[row] = level + end + row_level[start_row][2] = '>' + row_level[end_row][2] = '<' + end + end + end + + bufstate.row_level = row_level + bufstate.row_kinds = row_kinds + bufstate.row_text = row_text +end + +--- Renew the cached foldinfo then force `foldexpr()` to be re-evaluated, +--- without opening folds. +---@param bufnr integer +local function foldupdate(bufnr) + renew(bufnr) + for _, winid in ipairs(vim.fn.win_findbuf(bufnr)) do + local wininfo = vim.fn.getwininfo(winid)[1] + if wininfo and wininfo.tabnr == vim.fn.tabpagenr() then + if vim.wo[winid].foldmethod == 'expr' then + vim._foldupdate(winid, 0, api.nvim_buf_line_count(bufnr)) + end + end + end +end + +--- Whether `foldupdate()` is scheduled for the buffer with `bufnr`. +--- +--- Index in the form of bufnr -> true? +---@type table<integer, true?> +local scheduled_foldupdate = {} + +--- Schedule `foldupdate()` after leaving insert mode. +---@param bufnr integer +local function schedule_foldupdate(bufnr) + if not scheduled_foldupdate[bufnr] then + scheduled_foldupdate[bufnr] = true + api.nvim_create_autocmd('InsertLeave', { + buffer = bufnr, + once = true, + callback = function() + foldupdate(bufnr) + scheduled_foldupdate[bufnr] = nil + end, + }) + end +end + +---@param results table<integer,{err: lsp.ResponseError?, result: lsp.FoldingRange[]?}> +---@type lsp.MultiHandler +local function multi_handler(results, ctx) + local bufnr = assert(ctx.bufnr) + -- Handling responses from outdated buffer only causes performance overhead. + if util.buf_versions[bufnr] ~= ctx.version then + return + end + + local bufstate = assert(bufstates[bufnr]) + for client_id, result in pairs(results) do + if result.err then + log.error(result.err) + else + bufstate.client_ranges[client_id] = result.result + end + end + bufstate.version = ctx.version + + if api.nvim_get_mode().mode:match('^i') then + -- `foldUpdate()` is guarded in insert mode. + schedule_foldupdate(bufnr) + else + foldupdate(bufnr) + end +end + +---@param result lsp.FoldingRange[]? +---@type lsp.Handler +local function handler(err, result, ctx) + multi_handler({ [ctx.client_id] = { err = err, result = result } }, ctx) +end + +--- Request `textDocument/foldingRange` from the server. +--- `foldupdate()` is scheduled once after the request is completed. +---@param bufnr integer +---@param client? vim.lsp.Client The client whose server supports `foldingRange`. +local function request(bufnr, client) + ---@type lsp.FoldingRangeParams + local params = { textDocument = util.make_text_document_params(bufnr) } + + if client then + client:request(ms.textDocument_foldingRange, params, handler, bufnr) + return + end + + if not next(vim.lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_foldingRange })) then + return + end + + vim.lsp.buf_request_all(bufnr, ms.textDocument_foldingRange, params, multi_handler) +end + +-- NOTE: +-- `bufstate` and event hooks are interdependent: +-- * `bufstate` needs event hooks for correctness. +-- * event hooks require the previous `bufstate` for updates. +-- Since they are manually created and destroyed, +-- we ensure their lifecycles are always synchronized. +-- +-- TODO(ofseed): +-- 1. Implement clearing `bufstate` and event hooks +-- when no clients in the buffer support the corresponding method. +-- 2. Then generalize this state management to other LSP modules. +local augroup_setup = api.nvim_create_augroup('nvim.lsp.folding_range.setup', {}) + +--- Initialize `bufstate` and event hooks, then request folding ranges. +--- Manage their lifecycle within this function. +---@param bufnr integer +---@return vim.lsp.folding_range.BufState? +local function setup(bufnr) + if not api.nvim_buf_is_loaded(bufnr) then + return + end + + -- Register the new `bufstate`. + bufstates[bufnr] = { + client_ranges = {}, + row_level = {}, + row_kinds = {}, + row_text = {}, + } + + -- Event hooks from `buf_attach` can't be removed externally. + -- Hooks and `bufstate` share the same lifecycle; + -- they should self-destroy if `bufstate == nil`. + api.nvim_buf_attach(bufnr, false, { + -- `on_detach` also runs on buffer reload (`:e`). + -- Ensure `bufstate` and hooks are cleared to avoid duplication or leftover states. + on_detach = function() + bufstates[bufnr] = nil + api.nvim_clear_autocmds({ buffer = bufnr, group = augroup_setup }) + end, + -- Reset `bufstate` and request folding ranges. + on_reload = function() + bufstates[bufnr] = { + client_ranges = {}, + row_level = {}, + row_kinds = {}, + row_text = {}, + } + request(bufnr) + end, + --- Sync changed rows with their previous foldlevels before applying new ones. + on_bytes = function(_, _, _, start_row, _, _, old_row, _, _, new_row, _, _) + if bufstates[bufnr] == nil then + return true + end + local row_level = bufstates[bufnr].row_level + if next(row_level) == nil then + return + end + local row = new_row - old_row + if row > 0 then + vim._list_insert(row_level, start_row, start_row + math.abs(row) - 1, { -1 }) + -- If the previous row ends a fold, + -- Nvim treats the first row after consecutive `-1`s as a new fold start, + -- which is not the desired behavior. + local prev_level = row_level[start_row - 1] + if prev_level and prev_level[2] == '<' then + row_level[start_row] = { prev_level[1] - 1 } + end + elseif row < 0 then + vim._list_remove(row_level, start_row, start_row + math.abs(row) - 1) + end + end, + }) + api.nvim_create_autocmd('LspDetach', { + group = augroup_setup, + buffer = bufnr, + callback = function(args) + if not api.nvim_buf_is_loaded(bufnr) then + return + end + + ---@type integer + local client_id = args.data.client_id + bufstates[bufnr].client_ranges[client_id] = nil + + ---@type vim.lsp.Client[] + local clients = vim + .iter(vim.lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_foldingRange })) + ---@param client vim.lsp.Client + :filter(function(client) + return client.id ~= client_id + end) + :totable() + if #clients == 0 then + bufstates[bufnr] = { + client_ranges = {}, + row_level = {}, + row_kinds = {}, + row_text = {}, + } + end + + foldupdate(bufnr) + end, + }) + api.nvim_create_autocmd('LspAttach', { + group = augroup_setup, + buffer = bufnr, + callback = function(args) + local client = assert(vim.lsp.get_client_by_id(args.data.client_id)) + if client:supports_method(vim.lsp.protocol.Methods.textDocument_foldingRange, bufnr) then + request(bufnr, client) + end + end, + }) + api.nvim_create_autocmd('LspNotify', { + group = augroup_setup, + buffer = bufnr, + callback = function(args) + local client = assert(vim.lsp.get_client_by_id(args.data.client_id)) + if + client:supports_method(ms.textDocument_foldingRange, bufnr) + and ( + args.data.method == ms.textDocument_didChange + or args.data.method == ms.textDocument_didOpen + ) + then + request(bufnr, client) + end + end, + }) + + request(bufnr) + + return bufstates[bufnr] +end + +---@param kind lsp.FoldingRangeKind +---@param winid integer +local function foldclose(kind, winid) + vim._with({ win = winid }, function() + local bufnr = api.nvim_win_get_buf(winid) + local row_kinds = bufstates[bufnr].row_kinds + -- Reverse traverse to ensure that the smallest ranges are closed first. + for row = api.nvim_buf_line_count(bufnr) - 1, 0, -1 do + local kinds = row_kinds[row] + if kinds and kinds[kind] then + vim.cmd(row + 1 .. 'foldclose') + end + end + end) +end + +---@param kind lsp.FoldingRangeKind +---@param winid? integer +function M.foldclose(kind, winid) + vim.validate('kind', kind, 'string') + vim.validate('winid', winid, 'number', true) + + winid = winid or api.nvim_get_current_win() + local bufnr = api.nvim_win_get_buf(winid) + local bufstate = bufstates[bufnr] + if not bufstate then + return + end + + if bufstate.version == util.buf_versions[bufnr] then + foldclose(kind, winid) + return + end + -- Schedule `foldclose()` if the buffer is not up-to-date. + + if not next(vim.lsp.get_clients({ bufnr = bufnr, method = ms.textDocument_foldingRange })) then + return + end + ---@type lsp.FoldingRangeParams + local params = { textDocument = util.make_text_document_params(bufnr) } + vim.lsp.buf_request_all(bufnr, ms.textDocument_foldingRange, params, function(...) + multi_handler(...) + foldclose(kind, winid) + end) +end + +---@return string +function M.foldtext() + local bufnr = api.nvim_get_current_buf() + local lnum = vim.v.foldstart + local row = lnum - 1 + local bufstate = bufstates[bufnr] + if bufstate and bufstate.row_text[row] then + return bufstate.row_text[row] + end + return vim.fn.getline(lnum) +end + +---@param lnum? integer +---@return string level +function M.foldexpr(lnum) + local bufnr = api.nvim_get_current_buf() + local bufstate = bufstates[bufnr] or setup(bufnr) + if not bufstate then + return '0' + end + + local row = (lnum or vim.v.lnum) - 1 + local level = bufstate.row_level[row] + return level and (level[2] or '') .. (level[1] or '0') or '0' +end + +return M diff --git a/runtime/lua/vim/lsp/_meta.lua b/runtime/lua/vim/lsp/_meta.lua index bf693ccc57..589a49c003 100644 --- a/runtime/lua/vim/lsp/_meta.lua +++ b/runtime/lua/vim/lsp/_meta.lua @@ -1,8 +1,8 @@ ---@meta error('Cannot require a meta file') ----@alias lsp.Handler fun(err: lsp.ResponseError?, result: any, context: lsp.HandlerContext): ...any ----@alias lsp.MultiHandler fun(results: table<integer,{err: lsp.ResponseError?, result: any}>, context: lsp.HandlerContext): ...any +---@alias lsp.Handler fun(err: lsp.ResponseError?, result: any, context: lsp.HandlerContext, config?: table): ...any +---@alias lsp.MultiHandler fun(results: table<integer,{err: lsp.ResponseError?, result: any}>, context: lsp.HandlerContext, config?: table): ...any ---@class lsp.HandlerContext ---@field method string diff --git a/runtime/lua/vim/lsp/_snippet_grammar.lua b/runtime/lua/vim/lsp/_snippet_grammar.lua index 9318fefcbc..f06d6e9afd 100644 --- a/runtime/lua/vim/lsp/_snippet_grammar.lua +++ b/runtime/lua/vim/lsp/_snippet_grammar.lua @@ -127,6 +127,7 @@ local function node(type) end -- stylua: ignore +--- @diagnostic disable-next-line: missing-fields local G = P({ 'snippet'; snippet = Ct(Cg( diff --git a/runtime/lua/vim/lsp/_tagfunc.lua b/runtime/lua/vim/lsp/_tagfunc.lua index f75d43f373..554f0cb991 100644 --- a/runtime/lua/vim/lsp/_tagfunc.lua +++ b/runtime/lua/vim/lsp/_tagfunc.lua @@ -6,12 +6,12 @@ local ms = lsp.protocol.Methods ---@param name string ---@param range lsp.Range ---@param uri string ----@param offset_encoding string +---@param position_encoding string ---@return {name: string, filename: string, cmd: string, kind?: string} -local function mk_tag_item(name, range, uri, offset_encoding) +local function mk_tag_item(name, range, uri, position_encoding) local bufnr = vim.uri_to_bufnr(uri) -- This is get_line_byte_from_position is 0-indexed, call cursor expects a 1-indexed position - local byte = util._get_line_byte_from_position(bufnr, range.start, offset_encoding) + 1 + local byte = util._get_line_byte_from_position(bufnr, range.start, position_encoding) + 1 return { name = name, filename = vim.uri_to_fname(uri), @@ -32,9 +32,9 @@ local function query_definition(pattern) --- @param range lsp.Range --- @param uri string - --- @param offset_encoding string - local add = function(range, uri, offset_encoding) - table.insert(results, mk_tag_item(pattern, range, uri, offset_encoding)) + --- @param position_encoding string + local add = function(range, uri, position_encoding) + table.insert(results, mk_tag_item(pattern, range, uri, position_encoding)) end local remaining = #clients @@ -59,7 +59,7 @@ local function query_definition(pattern) remaining = remaining - 1 end local params = util.make_position_params(win, client.offset_encoding) - client.request(ms.textDocument_definition, params, on_response, bufnr) + client:request(ms.textDocument_definition, params, on_response, bufnr) end vim.wait(1000, function() return remaining == 0 @@ -78,11 +78,11 @@ local function query_workspace_symbols(pattern) local results = {} for client_id, responses in pairs(assert(results_by_client)) do local client = lsp.get_client_by_id(client_id) - local offset_encoding = client and client.offset_encoding or 'utf-16' + local position_encoding = client and client.offset_encoding or 'utf-16' local symbols = responses.result --[[@as lsp.SymbolInformation[]|nil]] for _, symbol in pairs(symbols or {}) do local loc = symbol.location - local item = mk_tag_item(symbol.name, loc.range, loc.uri, offset_encoding) + local item = mk_tag_item(symbol.name, loc.range, loc.uri, position_encoding) item.kind = lsp.protocol.SymbolKind[symbol.kind] or 'Unknown' table.insert(results, item) end diff --git a/runtime/lua/vim/lsp/_transport.lua b/runtime/lua/vim/lsp/_transport.lua new file mode 100644 index 0000000000..19ff2a8ab0 --- /dev/null +++ b/runtime/lua/vim/lsp/_transport.lua @@ -0,0 +1,182 @@ +local uv = vim.uv +local log = require('vim.lsp.log') + +local is_win = vim.fn.has('win32') == 1 + +--- Checks whether a given path exists and is a directory. +---@param filename string path to check +---@return boolean +local function is_dir(filename) + local stat = uv.fs_stat(filename) + return stat and stat.type == 'directory' or false +end + +--- @class (private) vim.lsp.rpc.Transport +--- @field write fun(self: vim.lsp.rpc.Transport, msg: string) +--- @field is_closing fun(self: vim.lsp.rpc.Transport): boolean +--- @field terminate fun(self: vim.lsp.rpc.Transport) + +--- @class (private,exact) vim.lsp.rpc.Transport.Run : vim.lsp.rpc.Transport +--- @field new fun(): vim.lsp.rpc.Transport.Run +--- @field sysobj? vim.SystemObj +local TransportRun = {} + +--- @return vim.lsp.rpc.Transport.Run +function TransportRun.new() + return setmetatable({}, { __index = TransportRun }) +end + +--- @param cmd string[] Command to start the LSP server. +--- @param extra_spawn_params? vim.lsp.rpc.ExtraSpawnParams +--- @param on_read fun(err: any, data: string) +--- @param on_exit fun(code: integer, signal: integer) +function TransportRun:run(cmd, extra_spawn_params, on_read, on_exit) + local function on_stderr(_, chunk) + if chunk then + log.error('rpc', cmd[1], 'stderr', chunk) + end + end + + extra_spawn_params = extra_spawn_params or {} + + if extra_spawn_params.cwd then + assert(is_dir(extra_spawn_params.cwd), 'cwd must be a directory') + end + + local detached = not is_win + if extra_spawn_params.detached ~= nil then + detached = extra_spawn_params.detached + end + + local ok, sysobj_or_err = pcall(vim.system, cmd, { + stdin = true, + stdout = on_read, + stderr = on_stderr, + cwd = extra_spawn_params.cwd, + env = extra_spawn_params.env, + detach = detached, + }, function(obj) + on_exit(obj.code, obj.signal) + end) + + if not ok then + local err = sysobj_or_err --[[@as string]] + local sfx = err:match('ENOENT') + and '. The language server is either not installed, missing from PATH, or not executable.' + or string.format(' with error message: %s', err) + + error(('Spawning language server with cmd: `%s` failed%s'):format(vim.inspect(cmd), sfx)) + end + + self.sysobj = sysobj_or_err --[[@as vim.SystemObj]] +end + +function TransportRun:write(msg) + assert(self.sysobj):write(msg) +end + +function TransportRun:is_closing() + return self.sysobj == nil or self.sysobj:is_closing() +end + +function TransportRun:terminate() + assert(self.sysobj):kill(15) +end + +--- @class (private,exact) vim.lsp.rpc.Transport.Connect : vim.lsp.rpc.Transport +--- @field new fun(): vim.lsp.rpc.Transport.Connect +--- @field handle? uv.uv_pipe_t|uv.uv_tcp_t +--- Connect returns a PublicClient synchronously so the caller +--- can immediately send messages before the connection is established +--- -> Need to buffer them until that happens +--- @field connected boolean +--- @field closing boolean +--- @field msgbuf vim.Ringbuf +--- @field on_exit? fun(code: integer, signal: integer) +local TransportConnect = {} + +--- @return vim.lsp.rpc.Transport.Connect +function TransportConnect.new() + return setmetatable({ + connected = false, + -- size should be enough because the client can't really do anything until initialization is done + -- which required a response from the server - implying the connection got established + msgbuf = vim.ringbuf(10), + closing = false, + }, { __index = TransportConnect }) +end + +--- @param host_or_path string +--- @param port? integer +--- @param on_read fun(err: any, data: string) +--- @param on_exit? fun(code: integer, signal: integer) +function TransportConnect:connect(host_or_path, port, on_read, on_exit) + self.on_exit = on_exit + self.handle = ( + port and assert(uv.new_tcp(), 'Could not create new TCP socket') + or assert(uv.new_pipe(false), 'Pipe could not be opened.') + ) + + local function on_connect(err) + if err then + local address = not port and host_or_path or (host_or_path .. ':' .. port) + vim.schedule(function() + vim.notify( + string.format('Could not connect to %s, reason: %s', address, vim.inspect(err)), + vim.log.levels.WARN + ) + end) + return + end + self.handle:read_start(on_read) + self.connected = true + for msg in self.msgbuf do + self.handle:write(msg) + end + end + + if not port then + self.handle:connect(host_or_path, on_connect) + return + end + + --- @diagnostic disable-next-line:param-type-mismatch bad UV typing + local info = uv.getaddrinfo(host_or_path, nil) + local resolved_host = info and info[1] and info[1].addr or host_or_path + self.handle:connect(resolved_host, port, on_connect) +end + +function TransportConnect:write(msg) + if self.connected then + local _, err = self.handle:write(msg) + if err and not self.closing then + log.error('Error on handle:write: %q', err) + end + return + end + + self.msgbuf:push(msg) +end + +function TransportConnect:is_closing() + return self.closing +end + +function TransportConnect:terminate() + if self.closing then + return + end + self.closing = true + if self.handle then + self.handle:shutdown() + self.handle:close() + end + if self.on_exit then + self.on_exit(0, 0) + end +end + +return { + TransportRun = TransportRun, + TransportConnect = TransportConnect, +} diff --git a/runtime/lua/vim/lsp/_watchfiles.lua b/runtime/lua/vim/lsp/_watchfiles.lua index c4cdb5aea8..4711b3cc9b 100644 --- a/runtime/lua/vim/lsp/_watchfiles.lua +++ b/runtime/lua/vim/lsp/_watchfiles.lua @@ -116,7 +116,7 @@ function M.register(reg, client_id) local params = { changes = change_queues[client_id], } - client.notify(ms.workspace_didChangeWatchedFiles, params) + client:notify(ms.workspace_didChangeWatchedFiles, params) queue_timers[client_id] = nil change_queues[client_id] = nil change_cache[client_id] = nil @@ -174,6 +174,7 @@ function M.cancel(client_id) cancel() end end + cancels[client_id] = nil end return M diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 6383855a30..48aa809ebd 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -20,7 +20,7 @@ local function client_positional_params(params) end end -local hover_ns = api.nvim_create_namespace('vim_lsp_hover_range') +local hover_ns = api.nvim_create_namespace('nvim.lsp.hover_range') --- @class vim.lsp.buf.hover.Opts : vim.lsp.util.open_floating_preview.Opts --- @field silent? boolean @@ -232,7 +232,7 @@ local function get_locations(method, opts) end for _, client in ipairs(clients) do local params = util.make_position_params(win, client.offset_encoding) - client.request(method, params, function(_, result) + client:request(method, params, function(_, result) on_response(_, result, client) end) end @@ -252,13 +252,13 @@ end --- vim.lsp.buf.definition({ on_list = on_list }) --- vim.lsp.buf.references(nil, { on_list = on_list }) --- ``` +--- @field on_list? fun(t: vim.lsp.LocationOpts.OnList) --- ---- If you prefer loclist instead of qflist: +--- Whether to use the |location-list| or the |quickfix| list in the default handler. --- ```lua --- vim.lsp.buf.definition({ loclist = true }) ---- vim.lsp.buf.references(nil, { loclist = true }) +--- vim.lsp.buf.references(nil, { loclist = false }) --- ``` ---- @field on_list? fun(t: vim.lsp.LocationOpts.OnList) --- @field loclist? boolean --- @class vim.lsp.LocationOpts.OnList @@ -324,12 +324,11 @@ local function process_signature_help_results(results) return signatures end -local sig_help_ns = api.nvim_create_namespace('vim_lsp_signature_help') +local sig_help_ns = api.nvim_create_namespace('nvim.lsp.signature_help') --- @class vim.lsp.buf.signature_help.Opts : vim.lsp.util.open_floating_preview.Opts --- @field silent? boolean --- TODO(lewis6991): support multiple clients --- Displays signature information about the symbol under the cursor in a --- floating window. --- @param config? vim.lsp.buf.signature_help.Opts @@ -356,6 +355,7 @@ function M.signature_help(config) local ft = vim.bo[ctx.bufnr].filetype local total = #signatures + local can_cycle = total > 1 and config.focusable local idx = 0 --- @param update_win? integer @@ -371,7 +371,7 @@ function M.signature_help(config) return end - local sfx = total > 1 and string.format(' (%d/%d) (<C-s> to cycle)', idx, total) or '' + local sfx = can_cycle and string.format(' (%d/%d) (<C-s> to cycle)', idx, total) or '' local title = string.format('Signature Help: %s%s', client.name, sfx) if config.border then config.title = title @@ -402,7 +402,7 @@ function M.signature_help(config) local fbuf, fwin = show_signature() - if total > 1 then + if can_cycle then vim.keymap.set('n', '<C-s>', function() show_signature(fwin) end, { @@ -423,7 +423,7 @@ end --- ---@see vim.lsp.protocol.CompletionTriggerKind function M.completion(context) - vim.depends('vim.lsp.buf.completion', 'vim.lsp.commpletion.trigger', '0.12') + vim.depends('vim.lsp.buf.completion', 'vim.lsp.completion.trigger', '0.12') return lsp.buf_request( 0, ms.textDocument_completion, @@ -450,10 +450,10 @@ local function range_from_selection(bufnr, mode) -- A user can start visual selection at the end and move backwards -- Normalize the range to start < end if start_row == end_row and end_col < start_col then - end_col, start_col = start_col, end_col + end_col, start_col = start_col, end_col --- @type integer, integer elseif end_row < start_row then - start_row, end_row = end_row, start_row - start_col, end_col = end_col, start_col + start_row, end_row = end_row, start_row --- @type integer, integer + start_col, end_col = end_col, start_col --- @type integer, integer end if mode == 'V' then start_col = 1 @@ -487,7 +487,7 @@ end --- ```lua --- -- Never request typescript-language-server for formatting --- vim.lsp.buf.format { ---- filter = function(client) return client.name ~= "tsserver" end +--- filter = function(client) return client.name ~= "ts_ls" end --- } --- ``` --- @field filter? fun(client: vim.lsp.Client): boolean? @@ -519,7 +519,7 @@ end --- @param opts? vim.lsp.buf.format.Opts function M.format(opts) opts = opts or {} - local bufnr = opts.bufnr or api.nvim_get_current_buf() + local bufnr = vim._resolve_bufnr(opts.bufnr) local mode = api.nvim_get_mode().mode local range = opts.range -- Try to use visual selection if no range is given @@ -553,27 +553,34 @@ function M.format(opts) --- @param client vim.lsp.Client --- @param params lsp.DocumentFormattingParams - --- @return lsp.DocumentFormattingParams + --- @return lsp.DocumentFormattingParams|lsp.DocumentRangeFormattingParams|lsp.DocumentRangesFormattingParams local function set_range(client, params) - local to_lsp_range = function(r) ---@return lsp.DocumentRangeFormattingParams|lsp.DocumentRangesFormattingParams + --- @param r {start:[integer,integer],end:[integer, integer]} + local function to_lsp_range(r) return util.make_given_range_params(r.start, r['end'], bufnr, client.offset_encoding).range end + local ret = params --[[@as lsp.DocumentFormattingParams|lsp.DocumentRangeFormattingParams|lsp.DocumentRangesFormattingParams]] if passed_multiple_ranges then - params.ranges = vim.tbl_map(to_lsp_range, range) + ret = params --[[@as lsp.DocumentRangesFormattingParams]] + --- @cast range {start:[integer,integer],end:[integer, integer]} + ret.ranges = vim.tbl_map(to_lsp_range, range) elseif range then - params.range = to_lsp_range(range) + ret = params --[[@as lsp.DocumentRangeFormattingParams]] + ret.range = to_lsp_range(range) end - return params + return ret end if opts.async then + --- @param idx? integer + --- @param client? vim.lsp.Client local function do_format(idx, client) - if not client then + if not idx or not client then return end local params = set_range(client, util.make_formatting_params(opts.formatting_options)) - client.request(method, params, function(...) + client:request(method, params, function(...) local handler = client.handlers[method] or lsp.handlers[method] handler(...) do_format(next(clients, idx)) @@ -584,7 +591,7 @@ function M.format(opts) local timeout_ms = opts.timeout_ms or 1000 for _, client in pairs(clients) do local params = set_range(client, util.make_formatting_params(opts.formatting_options)) - local result, err = client.request_sync(method, params, timeout_ms, bufnr) + local result, err = client:request_sync(method, params, timeout_ms, bufnr) if result and result.result then util.apply_text_edits(result.result, bufnr, client.offset_encoding) elseif err then @@ -615,7 +622,7 @@ end ---@param opts? vim.lsp.buf.rename.Opts Additional options: function M.rename(new_name, opts) opts = opts or {} - local bufnr = opts.bufnr or api.nvim_get_current_buf() + local bufnr = vim._resolve_bufnr(opts.bufnr) local clients = lsp.get_clients({ bufnr = bufnr, name = opts.name, @@ -636,38 +643,40 @@ function M.rename(new_name, opts) local cword = vim.fn.expand('<cword>') --- @param range lsp.Range - --- @param offset_encoding string - local function get_text_at_range(range, offset_encoding) + --- @param position_encoding string + local function get_text_at_range(range, position_encoding) return api.nvim_buf_get_text( bufnr, range.start.line, - util._get_line_byte_from_position(bufnr, range.start, offset_encoding), + util._get_line_byte_from_position(bufnr, range.start, position_encoding), range['end'].line, - util._get_line_byte_from_position(bufnr, range['end'], offset_encoding), + util._get_line_byte_from_position(bufnr, range['end'], position_encoding), {} )[1] end + --- @param idx? integer + --- @param client? vim.lsp.Client local function try_use_client(idx, client) - if not client then + if not idx or not client then return end --- @param name string local function rename(name) - local params = util.make_position_params(win, client.offset_encoding) + local params = util.make_position_params(win, client.offset_encoding) --[[@as lsp.RenameParams]] params.newName = name local handler = client.handlers[ms.textDocument_rename] or lsp.handlers[ms.textDocument_rename] - client.request(ms.textDocument_rename, params, function(...) + client:request(ms.textDocument_rename, params, function(...) handler(...) try_use_client(next(clients, idx)) end, bufnr) end - if client.supports_method(ms.textDocument_prepareRename) then + if client:supports_method(ms.textDocument_prepareRename) then local params = util.make_position_params(win, client.offset_encoding) - client.request(ms.textDocument_prepareRename, params, function(err, result) + client:request(ms.textDocument_prepareRename, params, function(err, result) if err or result == nil then if next(clients, idx) then try_use_client(next(clients, idx)) @@ -706,7 +715,7 @@ function M.rename(new_name, opts) end, bufnr) else assert( - client.supports_method(ms.textDocument_rename), + client:supports_method(ms.textDocument_rename), 'Client must support textDocument/rename' ) if new_name then @@ -732,7 +741,7 @@ end --- Lists all the references to the symbol under the cursor in the quickfix window. --- ----@param context (table|nil) Context for the request +---@param context lsp.ReferenceContext? Context for the request ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_references ---@param opts? vim.lsp.ListOpts function M.references(context, opts) @@ -781,7 +790,7 @@ function M.references(context, opts) params.context = context or { includeDeclaration = true, } - client.request(ms.textDocument_references, params, function(_, result) + client:request(ms.textDocument_references, params, function(_, result) local items = util.locations_to_items(result or {}, client.offset_encoding) vim.list_extend(all_items, items) remaining = remaining - 1 @@ -792,9 +801,10 @@ function M.references(context, opts) end end ---- Lists all symbols in the current buffer in the quickfix window. +--- Lists all symbols in the current buffer in the |location-list|. --- @param opts? vim.lsp.ListOpts function M.document_symbol(opts) + opts = vim.tbl_deep_extend('keep', opts or {}, { loclist = true }) local params = { textDocument = util.make_text_document_params() } request_with_opts(ms.textDocument_documentSymbol, params, opts) end @@ -813,7 +823,7 @@ local function request_with_id(client_id, method, params, handler, bufnr) ) return end - client.request(method, params, handler, bufnr) + client:request(method, params, handler, bufnr) end --- @param item lsp.TypeHierarchyItem|lsp.CallHierarchyItem @@ -880,7 +890,7 @@ local function hierarchy(method) for _, client in ipairs(clients) do local params = util.make_position_params(win, client.offset_encoding) --- @param result lsp.CallHierarchyItem[]|lsp.TypeHierarchyItem[]? - client.request(prepare_method, params, function(err, result, ctx) + client:request(prepare_method, params, function(err, result, ctx) if err then vim.notify(err.message, vim.log.levels.WARN) elseif result then @@ -1131,8 +1141,8 @@ local function on_code_action_results(results, opts) local action = choice.action local bufnr = assert(choice.ctx.bufnr, 'Must have buffer number') - if not action.edit and client.supports_method(ms.codeAction_resolve) then - client.request(ms.codeAction_resolve, action, function(err, resolved_action) + if not action.edit and client:supports_method(ms.codeAction_resolve) then + client:request(ms.codeAction_resolve, action, function(err, resolved_action) if err then if action.command then apply_action(action, client, choice.ctx) @@ -1224,6 +1234,7 @@ function M.code_action(opts) for _, client in ipairs(clients) do ---@type lsp.CodeActionParams local params + if opts.range then assert(type(opts.range) == 'table', 'code_action range must be a table') local start = assert(opts.range.start, 'range must have a `start` property') @@ -1236,6 +1247,9 @@ function M.code_action(opts) else params = util.make_range_params(win, client.offset_encoding) end + + --- @cast params lsp.CodeActionParams + if context.diagnostics then params.context = context else @@ -1253,7 +1267,7 @@ function M.code_action(opts) }) end - client.request(ms.textDocument_codeAction, params, on_result, bufnr) + client:request(ms.textDocument_codeAction, params, on_result, bufnr) end end diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua index 11ecb87507..253ccc48f4 100644 --- a/runtime/lua/vim/lsp/client.lua +++ b/runtime/lua/vim/lsp/client.lua @@ -75,17 +75,17 @@ local validate = vim.validate --- --- Map with language server specific settings. --- See the {settings} in |vim.lsp.Client|. ---- @field settings? table +--- @field settings? lsp.LSPObject --- --- Table that maps string of clientside commands to user-defined functions. ---- Commands passed to start_client take precedence over the global command registry. Each key +--- Commands passed to `start()` take precedence over the global command registry. Each key --- must be a unique command name, and the value is a function which is called if any LSP action --- (code action, code lenses, ...) triggers the command. --- @field commands? table<string,fun(command: lsp.Command, ctx: table)> --- --- Values to pass in the initialization request as `initializationOptions`. See `initialize` in --- the LSP spec. ---- @field init_options? table +--- @field init_options? lsp.LSPObject --- --- Name in log messages. --- (default: client-id) @@ -94,7 +94,8 @@ local validate = vim.validate --- Language ID as string. Defaults to the buffer filetype. --- @field get_language_id? fun(bufnr: integer, filetype: string): string --- ---- The encoding that the LSP server expects. Client does not verify this is correct. +--- Called "position encoding" in LSP spec, the encoding that the LSP server expects. +--- Client does not verify this is correct. --- @field offset_encoding? 'utf-8'|'utf-16'|'utf-32' --- --- Callback invoked when the client operation throws an error. `code` is a number describing the error. @@ -103,7 +104,7 @@ local validate = vim.validate --- @field on_error? fun(code: integer, err: string) --- --- Callback invoked before the LSP "initialize" phase, where `params` contains the parameters ---- being sent to the server and `config` is the config that was passed to |vim.lsp.start_client()|. +--- being sent to the server and `config` is the config that was passed to |vim.lsp.start()|. --- You can use this to modify parameters before they are sent. --- @field before_init? fun(params: lsp.InitializeParams, config: vim.lsp.ClientConfig) --- @@ -148,8 +149,10 @@ local validate = vim.validate --- See |vim.lsp.rpc.start()|. --- @field rpc vim.lsp.rpc.PublicClient --- ---- The encoding used for communicating with the server. You can modify this in ---- the `config`'s `on_init` method before text is sent to the server. +--- Called "position encoding" in LSP spec, +--- the encoding used for communicating with the server. +--- You can modify this in the `config`'s `on_init` method +--- before text is sent to the server. --- @field offset_encoding string --- --- The handlers used by the client as described in |lsp-handler|. @@ -161,16 +164,20 @@ local validate = vim.validate --- for an active request, or "cancel" for a cancel request. It will be --- "complete" ephemerally while executing |LspRequest| autocmds when replies --- are received from the server. ---- @field requests table<integer,{ type: string, bufnr: integer, method: string}> +--- @field requests table<integer,{ type: string, bufnr: integer, method: string}?> --- --- copy of the table that was passed by the user ---- to |vim.lsp.start_client()|. +--- to |vim.lsp.start()|. --- @field config vim.lsp.ClientConfig --- --- Response from the server sent on `initialize` describing the server's --- capabilities. --- @field server_capabilities lsp.ServerCapabilities? --- +--- Response from the server sent on `initialize` describing information about +--- the server. +--- @field server_info lsp.ServerInfo? +--- --- A ring buffer (|vim.ringbuf()|) containing progress messages --- sent by the server. --- @field progress vim.lsp.Client.Progress @@ -186,9 +193,6 @@ local validate = vim.validate --- --- @field attached_buffers table<integer,true> --- ---- Buffers that should be attached to upon initialize() ---- @field package _buffers_to_attach table<integer,true> ---- --- @field private _log_prefix string --- --- Track this so that we can escalate automatically if we've already tried a @@ -207,7 +211,7 @@ local validate = vim.validate --- Map with language server specific settings. These are returned to the --- language server if requested via `workspace/configuration`. Keys are --- case-sensitive. ---- @field settings table +--- @field settings lsp.LSPObject --- --- A table with flags for the client. The current (experimental) flags are: --- @field flags vim.lsp.Client.Flags @@ -219,70 +223,28 @@ local validate = vim.validate --- @field private registrations table<string,lsp.Registration[]> --- @field dynamic_capabilities lsp.DynamicCapabilities --- ---- Sends a request to the server. ---- This is a thin wrapper around {client.rpc.request} with some additional ---- checking. ---- If {handler} is not specified and if there's no respective global ---- handler, then an error will occur. ---- Returns: {status}, {client_id}?. {status} is a boolean indicating if ---- the notification was successful. If it is `false`, then it will always ---- be `false` (the client has shutdown). ---- If {status} is `true`, the function returns {request_id} as the second ---- result. You can use this with `client.cancel_request(request_id)` to cancel ---- the request. ---- @field request fun(method: string, params: table?, handler: lsp.Handler?, bufnr: integer?): boolean, integer? ---- ---- Sends a request to the server and synchronously waits for the response. ---- This is a wrapper around {client.request} ---- Returns: { err=err, result=result }, a dict, where `err` and `result` ---- come from the |lsp-handler|. On timeout, cancel or error, returns `(nil, ---- err)` where `err` is a string describing the failure reason. If the request ---- was unsuccessful returns `nil`. ---- @field request_sync fun(method: string, params: table?, timeout_ms: integer?, bufnr: integer): {err: lsp.ResponseError|nil, result:any}|nil, string|nil err # a dict ---- ---- Sends a notification to an LSP server. ---- Returns: a boolean to indicate if the notification was successful. If ---- it is false, then it will always be false (the client has shutdown). ---- @field notify fun(method: string, params: table?): boolean ---- ---- Cancels a request with a given request id. ---- Returns: same as `notify()`. ---- @field cancel_request fun(id: integer): boolean ---- ---- Stops a client, optionally with force. ---- By default, it will just ask the server to shutdown without force. ---- If you request to stop a client which has previously been requested to ---- shutdown, it will automatically escalate and force shutdown. ---- @field stop fun(force?: boolean) ---- ---- Runs the on_attach function from the client's config if it was defined. ---- Useful for buffer-local setup. ---- @field on_attach fun(bufnr: integer) ---- --- @field private _before_init_cb? vim.lsp.client.before_init_cb --- @field private _on_attach_cbs vim.lsp.client.on_attach_cb[] --- @field private _on_init_cbs vim.lsp.client.on_init_cb[] --- @field private _on_exit_cbs vim.lsp.client.on_exit_cb[] --- @field private _on_error_cb? fun(code: integer, err: string) ---- ---- Checks if a client supports a given method. ---- Always returns true for unknown off-spec methods. ---- {opts} is a optional `{bufnr?: integer}` table. ---- Some language server capabilities can be file specific. ---- @field supports_method fun(method: string, opts?: {bufnr: integer?}): boolean ---- ---- Checks whether a client is stopped. ---- Returns: true if the client is fully stopped. ---- @field is_stopped fun(): boolean local Client = {} Client.__index = Client ---- @param cls table ---- @param meth any ---- @return function -local function method_wrapper(cls, meth) - return function(...) - return meth(cls, ...) +--- @param obj table<string,any> +--- @param cls table<string,function> +--- @param name string +local function method_wrapper(obj, cls, name) + local meth = assert(cls[name]) + obj[name] = function(...) + local arg = select(1, ...) + if arg and getmetatable(arg) == cls then + -- First argument is self, call meth directly + return meth(...) + end + vim.deprecate('client.' .. name, 'client:' .. name, '0.13') + -- First argument is not self, insert it + return meth(obj, ...) end end @@ -304,9 +266,6 @@ local valid_encodings = { ['utf8'] = 'utf-8', ['utf16'] = 'utf-16', ['utf32'] = 'utf-32', - UTF8 = 'utf-8', - UTF16 = 'utf-16', - UTF32 = 'utf-32', } --- Normalizes {encoding} to valid LSP encoding names. @@ -315,12 +274,12 @@ local valid_encodings = { local function validate_encoding(encoding) validate('encoding', encoding, 'string', true) if not encoding then - return valid_encodings.UTF16 + return valid_encodings.utf16 end return valid_encodings[encoding:lower()] or error( string.format( - "Invalid offset encoding %q. Must be one of: 'utf-8', 'utf-16', 'utf-32'", + "Invalid position encoding %q. Must be one of: 'utf-8', 'utf-16', 'utf-32'", encoding ) ) @@ -346,7 +305,7 @@ local function default_get_language_id(_bufnr, filetype) return filetype end ---- Validates a client configuration as given to |vim.lsp.start_client()|. +--- Validates a client configuration as given to |vim.lsp.start()|. --- @param config vim.lsp.ClientConfig local function validate_config(config) validate('config', config, 'table') @@ -404,31 +363,6 @@ local function get_name(id, config) return tostring(id) end ---- @param workspace_folders string|lsp.WorkspaceFolder[]? ---- @return lsp.WorkspaceFolder[]? -local function get_workspace_folders(workspace_folders) - if type(workspace_folders) == 'table' then - return workspace_folders - elseif type(workspace_folders) == 'string' then - return { - { - uri = vim.uri_from_fname(workspace_folders), - name = workspace_folders, - }, - } - end -end - ---- @generic T ---- @param x elem_or_list<T>? ---- @return T[] -local function ensure_list(x) - if type(x) == 'table' then - return x - end - return { x } -end - --- @nodoc --- @param config vim.lsp.ClientConfig --- @return vim.lsp.Client? @@ -455,13 +389,13 @@ function Client.create(config) settings = config.settings or {}, flags = config.flags or {}, get_language_id = config.get_language_id or default_get_language_id, - capabilities = config.capabilities or lsp.protocol.make_client_capabilities(), - workspace_folders = get_workspace_folders(config.workspace_folders or config.root_dir), + capabilities = config.capabilities, + workspace_folders = lsp._get_workspace_folders(config.workspace_folders or config.root_dir), root_dir = config.root_dir, _before_init_cb = config.before_init, - _on_init_cbs = ensure_list(config.on_init), - _on_exit_cbs = ensure_list(config.on_exit), - _on_attach_cbs = ensure_list(config.on_attach), + _on_init_cbs = vim._ensure_list(config.on_init), + _on_exit_cbs = vim._ensure_list(config.on_exit), + _on_attach_cbs = vim._ensure_list(config.on_attach), _on_error_cb = config.on_error, _trace = get_trace(config.trace), @@ -477,6 +411,9 @@ function Client.create(config) messages = { name = name, messages = {}, progress = {}, status = {} }, } + self.capabilities = + vim.tbl_deep_extend('force', lsp.protocol.make_client_capabilities(), self.capabilities or {}) + --- @class lsp.DynamicCapabilities --- @nodoc self.dynamic_capabilities = { @@ -499,24 +436,23 @@ function Client.create(config) end, } - self.request = method_wrapper(self, Client._request) - self.request_sync = method_wrapper(self, Client._request_sync) - self.notify = method_wrapper(self, Client._notify) - self.cancel_request = method_wrapper(self, Client._cancel_request) - self.stop = method_wrapper(self, Client._stop) - self.is_stopped = method_wrapper(self, Client._is_stopped) - self.on_attach = method_wrapper(self, Client._on_attach) - self.supports_method = method_wrapper(self, Client._supports_method) - --- @type table<string|integer, string> title of unfinished progress sequences by token self.progress.pending = {} --- @type vim.lsp.rpc.Dispatchers local dispatchers = { - notification = method_wrapper(self, Client._notification), - server_request = method_wrapper(self, Client._server_request), - on_error = method_wrapper(self, Client._on_error), - on_exit = method_wrapper(self, Client._on_exit), + notification = function(...) + return self:_notification(...) + end, + server_request = function(...) + return self:_server_request(...) + end, + on_error = function(...) + return self:_on_error(...) + end, + on_exit = function(...) + return self:_on_exit(...) + end, } -- Start the RPC client. @@ -533,6 +469,15 @@ function Client.create(config) setmetatable(self, Client) + method_wrapper(self, Client, 'request') + method_wrapper(self, Client, 'request_sync') + method_wrapper(self, Client, 'notify') + method_wrapper(self, Client, 'cancel_request') + method_wrapper(self, Client, 'stop') + method_wrapper(self, Client, 'is_stopped') + method_wrapper(self, Client, 'on_attach') + method_wrapper(self, Client, 'supports_method') + return self end @@ -615,8 +560,10 @@ function Client:initialize() self.offset_encoding = self.server_capabilities.positionEncoding end + self.server_info = result.serverInfo + if next(self.settings) then - self:_notify(ms.workspace_didChangeConfiguration, { settings = self.settings }) + self:notify(ms.workspace_didChangeConfiguration, { settings = self.settings }) end -- If server is being restarted, make sure to re-attach to any previously attached buffers. @@ -628,7 +575,7 @@ function Client:initialize() for buf in pairs(reattach_bufs) do -- The buffer may have been detached in the on_init callback. if self.attached_buffers[buf] then - self:_on_attach(buf) + self:on_attach(buf) end end @@ -645,24 +592,62 @@ end --- Returns the default handler if the user hasn't set a custom one. --- --- @param method (string) LSP method name ---- @return lsp.Handler|nil handler for the given method, if defined, or the default from |vim.lsp.handlers| +--- @return lsp.Handler? handler for the given method, if defined, or the default from |vim.lsp.handlers| function Client:_resolve_handler(method) return self.handlers[method] or lsp.handlers[method] end ---- Returns the buffer number for the given {bufnr}. ---- ---- @param bufnr (integer|nil) Buffer number to resolve. Defaults to current buffer ---- @return integer bufnr -local function resolve_bufnr(bufnr) - validate('bufnr', bufnr, 'number', true) - if bufnr == nil or bufnr == 0 then - return api.nvim_get_current_buf() +--- @private +--- @param id integer +--- @param req_type 'pending'|'complete'|'cancel'| +--- @param bufnr? integer (only required for req_type='pending') +--- @param method? string (only required for req_type='pending') +function Client:_process_request(id, req_type, bufnr, method) + local pending = req_type == 'pending' + + validate('id', id, 'number') + if pending then + validate('bufnr', bufnr, 'number') + validate('method', method, 'string') + end + + local cur_request = self.requests[id] + + if pending and cur_request then + log.error( + self._log_prefix, + ('Cannot create request with id %d as one already exists'):format(id) + ) + return + elseif not pending and not cur_request then + log.error( + self._log_prefix, + ('Cannot find request with id %d whilst attempting to %s'):format(id, req_type) + ) + return + end + + if cur_request then + bufnr = cur_request.bufnr + method = cur_request.method end - return bufnr + + assert(bufnr and method) + + local request = { type = req_type, bufnr = bufnr, method = method } + + -- Clear 'complete' requests + -- Note 'pending' and 'cancelled' requests are cleared when the server sends a response + -- which is processed via the notify_reply_callback argument to rpc.request. + self.requests[id] = req_type ~= 'complete' and request or nil + + api.nvim_exec_autocmds('LspRequest', { + buffer = api.nvim_buf_is_valid(bufnr) and bufnr or nil, + modeline = false, + data = { client_id = self.id, request_id = id, request = request }, + }) end ---- @private --- Sends a request to the server. --- --- This is a thin wrapper around {client.rpc.request} with some additional @@ -671,15 +656,14 @@ end --- @param method string LSP method name. --- @param params? table LSP request params. --- @param handler? lsp.Handler Response |lsp-handler| for this method. ---- @param bufnr integer Buffer handle (0 for current). ---- @return boolean status, integer? request_id {status} is a bool indicating ---- whether the request was successful. If it is `false`, then it will ---- always be `false` (the client has shutdown). If it was ---- successful, then it will return {request_id} as the ---- second result. You can use this with `client.cancel_request(request_id)` +--- @param bufnr? integer (default: 0) Buffer handle, or 0 for current. +--- @return boolean status indicates whether the request was successful. +--- If it is `false`, then it will always be `false` (the client has shutdown). +--- @return integer? request_id Can be used with |Client:cancel_request()|. +--- `nil` is request failed. --- to cancel the-request. --- @see |vim.lsp.buf_request_all()| -function Client:_request(method, params, handler, bufnr) +function Client:request(method, params, handler, bufnr) if not handler then handler = assert( self:_resolve_handler(method), @@ -688,37 +672,24 @@ function Client:_request(method, params, handler, bufnr) end -- Ensure pending didChange notifications are sent so that the server doesn't operate on a stale state changetracking.flush(self, bufnr) + bufnr = vim._resolve_bufnr(bufnr) local version = lsp.util.buf_versions[bufnr] - bufnr = resolve_bufnr(bufnr) log.debug(self._log_prefix, 'client.request', self.id, method, params, handler, bufnr) local success, request_id = self.rpc.request(method, params, function(err, result) - local context = { + handler(err, result, { method = method, client_id = self.id, bufnr = bufnr, params = params, version = version, - } - handler(err, result, context) - end, function(request_id) - local request = self.requests[request_id] - request.type = 'complete' - api.nvim_exec_autocmds('LspRequest', { - buffer = api.nvim_buf_is_valid(bufnr) and bufnr or nil, - modeline = false, - data = { client_id = self.id, request_id = request_id, request = request }, }) - self.requests[request_id] = nil + end, function(request_id) + -- Called when the server sends a response to the request (including cancelled acknowledgment). + self:_process_request(request_id, 'complete') end) if success and request_id then - local request = { type = 'pending', bufnr = bufnr, method = method } - self.requests[request_id] = request - api.nvim_exec_autocmds('LspRequest', { - buffer = api.nvim_buf_is_valid(bufnr) and bufnr or nil, - modeline = false, - data = { client_id = self.id, request_id = request_id, request = request }, - }) + self:_process_request(request_id, 'pending', bufnr, method) end return success, request_id @@ -731,41 +702,39 @@ local wait_result_reason = { [-1] = 'timeout', [-2] = 'interrupted', [-3] = 'err --- --- @param ... string List to write to the buffer local function err_message(...) - local message = table.concat(vim.iter({ ... }):flatten():totable()) + local chunks = { { table.concat(vim.iter({ ... }):flatten():totable()) } } if vim.in_fast_event() then vim.schedule(function() - api.nvim_err_writeln(message) + api.nvim_echo(chunks, true, { err = true }) api.nvim_command('redraw') end) else - api.nvim_err_writeln(message) + api.nvim_echo(chunks, true, { err = true }) api.nvim_command('redraw') end end ---- @private --- Sends a request to the server and synchronously waits for the response. --- ---- This is a wrapper around {client.request} +--- This is a wrapper around |Client:request()| --- ---- @param method (string) LSP method name. ---- @param params (table) LSP request params. ---- @param timeout_ms (integer|nil) Maximum time in milliseconds to wait for +--- @param method string LSP method name. +--- @param params table LSP request params. +--- @param timeout_ms integer? Maximum time in milliseconds to wait for --- a result. Defaults to 1000 ---- @param bufnr (integer) Buffer handle (0 for current). ---- @return {err: lsp.ResponseError|nil, result:any}|nil, string|nil err # a dict, where ---- `err` and `result` come from the |lsp-handler|. ---- On timeout, cancel or error, returns `(nil, err)` where `err` is a ---- string describing the failure reason. If the request was unsuccessful ---- returns `nil`. +--- @param bufnr? integer (default: 0) Buffer handle, or 0 for current. +--- @return {err: lsp.ResponseError?, result:any}? `result` and `err` from the |lsp-handler|. +--- `nil` is the request was unsuccessful +--- @return string? err On timeout, cancel or error, where `err` is a +--- string describing the failure reason. --- @see |vim.lsp.buf_request_sync()| -function Client:_request_sync(method, params, timeout_ms, bufnr) +function Client:request_sync(method, params, timeout_ms, bufnr) local request_result = nil local function _sync_handler(err, result) request_result = { err = err, result = result } end - local success, request_id = self:_request(method, params, _sync_handler, bufnr) + local success, request_id = self:request(method, params, _sync_handler, bufnr) if not success then return nil end @@ -776,22 +745,20 @@ function Client:_request_sync(method, params, timeout_ms, bufnr) if not wait_result then if request_id then - self:_cancel_request(request_id) + self:cancel_request(request_id) end return nil, wait_result_reason[reason] end return request_result end ---- @package --- Sends a notification to an LSP server. --- --- @param method string LSP method name. ---- @param params table|nil LSP request params. ---- @return boolean status true if the notification was successful. ---- If it is false, then it will always be false ---- (the client has shutdown). -function Client:_notify(method, params) +--- @param params table? LSP request params. +--- @return boolean status indicating if the notification was successful. +--- If it is false, then the client has shutdown. +function Client:notify(method, params) if method ~= ms.textDocument_didChange then changetracking.flush(self) end @@ -814,41 +781,32 @@ function Client:_notify(method, params) return client_active end ---- @private --- Cancels a request with a given request id. --- ---- @param id (integer) id of request to cancel ---- @return boolean status true if notification was successful. false otherwise ---- @see |vim.lsp.client.notify()| -function Client:_cancel_request(id) - validate('id', id, 'number') - local request = self.requests[id] - if request and request.type == 'pending' then - request.type = 'cancel' - api.nvim_exec_autocmds('LspRequest', { - buffer = api.nvim_buf_is_valid(request.bufnr) and request.bufnr or nil, - modeline = false, - data = { client_id = self.id, request_id = id, request = request }, - }) - end +--- @param id integer id of request to cancel +--- @return boolean status indicating if the notification was successful. +--- @see |Client:notify()| +function Client:cancel_request(id) + self:_process_request(id, 'cancel') return self.rpc.notify(ms.dollar_cancelRequest, { id = id }) end ---- @private --- Stops a client, optionally with force. --- ---- By default, it will just ask the - server to shutdown without force. If +--- By default, it will just request the server to shutdown without force. If --- you request to stop a client which has previously been requested to --- shutdown, it will automatically escalate and force shutdown. --- ---- @param force boolean|nil -function Client:_stop(force) +--- @param force? boolean +function Client:stop(force) local rpc = self.rpc if rpc.is_closing() then return end + vim.lsp._watchfiles.cancel(self.id) + if force or not self.initialized or self._graceful_shutdown_failed then rpc.terminate() return @@ -863,7 +821,6 @@ function Client:_stop(force) rpc.terminate() self._graceful_shutdown_failed = true end - vim.lsp._watchfiles.cancel(self.id) end) end @@ -945,20 +902,22 @@ end --- @param bufnr? integer --- @return lsp.Registration? function Client:_get_registration(method, bufnr) - bufnr = bufnr or vim.api.nvim_get_current_buf() + bufnr = vim._resolve_bufnr(bufnr) for _, reg in ipairs(self.registrations[method] or {}) do - if not reg.registerOptions or not reg.registerOptions.documentSelector then + local regoptions = reg.registerOptions --[[@as {documentSelector:lsp.TextDocumentFilter[]}]] + if not regoptions or not regoptions.documentSelector then return reg end - local documentSelector = reg.registerOptions.documentSelector + local documentSelector = regoptions.documentSelector local language = self:_get_language_id(bufnr) local uri = vim.uri_from_bufnr(bufnr) local fname = vim.uri_to_fname(uri) for _, filter in ipairs(documentSelector) do + local flang, fscheme, fpat = filter.language, filter.scheme, filter.pattern if - not (filter.language and language ~= filter.language) - and not (filter.scheme and not vim.startswith(uri, filter.scheme .. ':')) - and not (filter.pattern and not vim.glob.to_lpeg(filter.pattern):match(fname)) + not (flang and language ~= flang) + and not (fscheme and not vim.startswith(uri, fscheme .. ':')) + and not (type(fpat) == 'string' and not vim.glob.to_lpeg(fpat):match(fname)) then return reg end @@ -966,12 +925,11 @@ function Client:_get_registration(method, bufnr) end end ---- @private --- Checks whether a client is stopped. --- --- @return boolean # true if client is stopped or in the process of being --- stopped; false otherwise -function Client:_is_stopped() +function Client:is_stopped() return self.rpc.is_closing() end @@ -983,7 +941,7 @@ end --- @param handler? lsp.Handler only called if a server command function Client:exec_cmd(command, context, handler) context = vim.deepcopy(context or {}, true) --[[@as lsp.HandlerContext]] - context.bufnr = context.bufnr or api.nvim_get_current_buf() + context.bufnr = vim._resolve_bufnr(context.bufnr) context.client_id = self.id local cmdname = command.command local fn = self.commands[cmdname] or lsp.commands[cmdname] @@ -1013,7 +971,7 @@ function Client:exec_cmd(command, context, handler) command = cmdname, arguments = command.arguments, } - self.request(ms.workspace_executeCommand, params, handler, context.bufnr) + self:request(ms.workspace_executeCommand, params, handler, context.bufnr) end --- Default handler for the 'textDocument/didOpen' LSP notification. @@ -1021,14 +979,14 @@ end --- @param bufnr integer Number of the buffer, or 0 for current function Client:_text_document_did_open_handler(bufnr) changetracking.init(self, bufnr) - if not self.supports_method(ms.textDocument_didOpen) then + if not self:supports_method(ms.textDocument_didOpen) then return end if not api.nvim_buf_is_loaded(bufnr) then return end - self.notify(ms.textDocument_didOpen, { + self:notify(ms.textDocument_didOpen, { textDocument = { version = lsp.util.buf_versions[bufnr], uri = vim.uri_from_bufnr(bufnr), @@ -1049,8 +1007,9 @@ function Client:_text_document_did_open_handler(bufnr) end --- Runs the on_attach function from the client's config if it was defined. +--- Useful for buffer-local setup. --- @param bufnr integer Buffer number -function Client:_on_attach(bufnr) +function Client:on_attach(bufnr) self:_text_document_did_open_handler(bufnr) lsp._set_defaults(self, bufnr) @@ -1085,10 +1044,18 @@ function Client:write_error(code, err) err_message(self._log_prefix, ': Error ', client_error, ': ', vim.inspect(err)) end ---- @private +--- Checks if a client supports a given method. +--- Always returns true for unknown off-spec methods. +--- +--- Note: Some language server capabilities can be file specific. --- @param method string ---- @param opts? {bufnr: integer?} -function Client:_supports_method(method, opts) +--- @param bufnr? integer +function Client:supports_method(method, bufnr) + -- Deprecated form + if type(bufnr) == 'table' then + --- @diagnostic disable-next-line:no-unknown + bufnr = bufnr.bufnr + end local required_capability = lsp._request_name_to_capability[method] -- if we don't know about the method, assume that the client supports it. if not required_capability then @@ -1101,12 +1068,12 @@ function Client:_supports_method(method, opts) local rmethod = lsp._resolve_to_request[method] if rmethod then if self:_supports_registration(rmethod) then - local reg = self:_get_registration(rmethod, opts and opts.bufnr) + local reg = self:_get_registration(rmethod, bufnr) return vim.tbl_get(reg or {}, 'registerOptions', 'resolveProvider') or false end else if self:_supports_registration(method) then - return self:_get_registration(method, opts and opts.bufnr) ~= nil + return self:_get_registration(method, bufnr) ~= nil end end return false @@ -1205,9 +1172,9 @@ function Client:_add_workspace_folder(dir) end end - local wf = assert(get_workspace_folders(dir)) + local wf = assert(lsp._get_workspace_folders(dir)) - self:_notify(ms.workspace_didChangeWorkspaceFolders, { + self:notify(ms.workspace_didChangeWorkspaceFolders, { event = { added = wf, removed = {} }, }) @@ -1220,9 +1187,9 @@ end --- Remove a directory to the workspace folders. --- @param dir string? function Client:_remove_workspace_folder(dir) - local wf = assert(get_workspace_folders(dir)) + local wf = assert(lsp._get_workspace_folders(dir)) - self:_notify(ms.workspace_didChangeWorkspaceFolders, { + self:notify(ms.workspace_didChangeWorkspaceFolders, { event = { added = {}, removed = wf }, }) diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index fdbdda695a..e36d8fee27 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -21,7 +21,7 @@ local lens_cache_by_buf = setmetatable({}, { ---client_id -> namespace local namespaces = setmetatable({}, { __index = function(t, key) - local value = api.nvim_create_namespace('vim_lsp_codelens:' .. key) + local value = api.nvim_create_namespace('nvim.lsp.codelens:' .. key) rawset(t, key, value) return value end, @@ -30,7 +30,7 @@ local namespaces = setmetatable({}, { ---@private M.__namespaces = namespaces -local augroup = api.nvim_create_augroup('vim_lsp_codelens', {}) +local augroup = api.nvim_create_augroup('nvim.lsp.codelens', {}) api.nvim_create_autocmd('LspDetach', { group = augroup, @@ -104,16 +104,12 @@ function M.run() end end -local function resolve_bufnr(bufnr) - return bufnr == 0 and api.nvim_get_current_buf() or bufnr -end - --- Clear the lenses --- ---@param client_id integer|nil filter by client_id. All clients if nil ---@param bufnr integer|nil filter by buffer. All buffers if nil, 0 for current buffer function M.clear(client_id, bufnr) - bufnr = bufnr and resolve_bufnr(bufnr) + bufnr = bufnr and vim._resolve_bufnr(bufnr) local buffers = bufnr and { bufnr } or vim.tbl_filter(api.nvim_buf_is_loaded, api.nvim_list_bufs()) for _, iter_bufnr in pairs(buffers) do @@ -231,7 +227,7 @@ local function resolve_lenses(lenses, bufnr, client_id, callback) countdown() else assert(client) - client.request(ms.codeLens_resolve, lens, function(_, result) + client:request(ms.codeLens_resolve, lens, function(_, result) if api.nvim_buf_is_loaded(bufnr) and result and result.command then lens.command = result.command -- Eager display to have some sort of incremental feedback @@ -296,7 +292,7 @@ end --- @param opts? vim.lsp.codelens.refresh.Opts Optional fields function M.refresh(opts) opts = opts or {} - local bufnr = opts.bufnr and resolve_bufnr(opts.bufnr) + local bufnr = opts.bufnr and vim._resolve_bufnr(opts.bufnr) local buffers = bufnr and { bufnr } or vim.tbl_filter(api.nvim_buf_is_loaded, api.nvim_list_bufs()) diff --git a/runtime/lua/vim/lsp/completion.lua b/runtime/lua/vim/lsp/completion.lua index 92bc110a97..cf6d07745f 100644 --- a/runtime/lua/vim/lsp/completion.lua +++ b/runtime/lua/vim/lsp/completion.lua @@ -127,8 +127,10 @@ end --- See https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion --- --- @param item lsp.CompletionItem +--- @param prefix string +--- @param match fun(text: string, prefix: string):boolean --- @return string -local function get_completion_word(item) +local function get_completion_word(item, prefix, match) if item.insertTextFormat == protocol.InsertTextFormat.Snippet then if item.textEdit then -- Use label instead of text if text has different starting characters. @@ -146,7 +148,12 @@ local function get_completion_word(item) -- -- Typing `i` would remove the candidate because newText starts with `t`. local text = parse_snippet(item.insertText or item.textEdit.newText) - return #text < #item.label and vim.fn.matchstr(text, '\\k*') or item.label + local word = #text < #item.label and vim.fn.matchstr(text, '\\k*') or item.label + if item.filterText and not match(word, prefix) then + return item.filterText + else + return word + end elseif item.insertText and item.insertText ~= '' then return parse_snippet(item.insertText) else @@ -224,6 +231,9 @@ end ---@param prefix string ---@return boolean local function match_item_by_value(value, prefix) + if prefix == '' then + return true + end if vim.o.completeopt:find('fuzzy') ~= nil then return next(vim.fn.matchfuzzy({ value }, prefix)) ~= nil end @@ -276,7 +286,7 @@ function M._lsp_to_complete_items(result, prefix, client_id) local user_convert = vim.tbl_get(buf_handles, bufnr, 'convert') for _, item in ipairs(items) do if matches(item) then - local word = get_completion_word(item) + local word = get_completion_word(item, prefix, match_item_by_value) local hl_group = '' if item.deprecated @@ -404,7 +414,7 @@ local function request(clients, bufnr, win, callback) for _, client in pairs(clients) do local client_id = client.id local params = lsp.util.make_position_params(win, client.offset_encoding) - local ok, request_id = client.request(ms.textDocument_completion, params, function(err, result) + local ok, request_id = client:request(ms.textDocument_completion, params, function(err, result) responses[client_id] = { err = err, result = result } remaining_requests = remaining_requests - 1 if remaining_requests == 0 then @@ -421,7 +431,7 @@ local function request(clients, bufnr, win, callback) for client_id, request_id in pairs(request_ids) do local client = lsp.get_client_by_id(client_id) if client then - client.cancel_request(request_id) + client:cancel_request(request_id) end end end @@ -460,7 +470,7 @@ local function trigger(bufnr, clients) local server_start_boundary --- @type integer? for client_id, response in pairs(responses) do if response.err then - vim.notify_once(response.err.message, vim.log.levels.warn) + vim.notify_once(response.err.message, vim.log.levels.WARN) end local result = response.result @@ -550,7 +560,7 @@ local function on_complete_done() return end - local offset_encoding = client.offset_encoding or 'utf-16' + local position_encoding = client.offset_encoding or 'utf-16' local resolve_provider = (client.server_capabilities.completionProvider or {}).resolveProvider local function clear_word() @@ -576,13 +586,13 @@ local function on_complete_done() if completion_item.additionalTextEdits and next(completion_item.additionalTextEdits) then clear_word() - lsp.util.apply_text_edits(completion_item.additionalTextEdits, bufnr, offset_encoding) + lsp.util.apply_text_edits(completion_item.additionalTextEdits, bufnr, position_encoding) apply_snippet_and_command() elseif resolve_provider and type(completion_item) == 'table' then local changedtick = vim.b[bufnr].changedtick --- @param result lsp.CompletionItem - client.request(ms.completionItem_resolve, completion_item, function(err, result) + client:request(ms.completionItem_resolve, completion_item, function(err, result) if changedtick ~= vim.b[bufnr].changedtick then return end @@ -591,7 +601,7 @@ local function on_complete_done() if err then vim.notify_once(err.message, vim.log.levels.WARN) elseif result and result.additionalTextEdits then - lsp.util.apply_text_edits(result.additionalTextEdits, bufnr, offset_encoding) + lsp.util.apply_text_edits(result.additionalTextEdits, bufnr, position_encoding) if result.command then completion_item.command = result.command end @@ -605,6 +615,12 @@ local function on_complete_done() end end +---@param bufnr integer +---@return string +local function get_augroup(bufnr) + return string.format('nvim.lsp.completion_%d', bufnr) +end + --- @class vim.lsp.completion.BufferOpts --- @field autotrigger? boolean Default: false When true, completion triggers automatically based on the server's `triggerCharacters`. --- @field convert? fun(item: lsp.CompletionItem): table Transforms an LSP CompletionItem to |complete-items|. @@ -629,8 +645,7 @@ local function enable_completions(client_id, bufnr, opts) }) -- Set up autocommands. - local group = - api.nvim_create_augroup(string.format('vim/lsp/completion-%d', bufnr), { clear = true }) + local group = api.nvim_create_augroup(get_augroup(bufnr), { clear = true }) api.nvim_create_autocmd('CompleteDone', { group = group, buffer = bufnr, @@ -698,7 +713,7 @@ local function disable_completions(client_id, bufnr) handle.clients[client_id] = nil if not next(handle.clients) then buf_handles[bufnr] = nil - api.nvim_del_augroup_by_name(string.format('vim/lsp/completion-%d', bufnr)) + api.nvim_del_augroup_by_name(get_augroup(bufnr)) else for char, clients in pairs(handle.triggers) do --- @param c vim.lsp.Client @@ -716,7 +731,7 @@ end --- @param bufnr integer Buffer handle, or 0 for the current buffer --- @param opts? vim.lsp.completion.BufferOpts function M.enable(enable, client_id, bufnr, opts) - bufnr = (bufnr == 0 and api.nvim_get_current_buf()) or bufnr + bufnr = vim._resolve_bufnr(bufnr) if enable then enable_completions(client_id, bufnr, opts or {}) diff --git a/runtime/lua/vim/lsp/diagnostic.lua b/runtime/lua/vim/lsp/diagnostic.lua index 8fd30c7668..fe24928a69 100644 --- a/runtime/lua/vim/lsp/diagnostic.lua +++ b/runtime/lua/vim/lsp/diagnostic.lua @@ -5,7 +5,7 @@ local api = vim.api local M = {} -local augroup = api.nvim_create_augroup('vim_lsp_diagnostic', {}) +local augroup = api.nvim_create_augroup('nvim.lsp.diagnostic', {}) local DEFAULT_CLIENT_ID = -1 @@ -20,7 +20,7 @@ end ---@return lsp.DiagnosticSeverity local function severity_vim_to_lsp(severity) if type(severity) == 'string' then - severity = vim.diagnostic.severity[severity] + severity = vim.diagnostic.severity[severity] --- @type integer end return severity end @@ -77,7 +77,7 @@ end local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id) local buf_lines = get_buf_lines(bufnr) local client = vim.lsp.get_client_by_id(client_id) - local offset_encoding = client and client.offset_encoding or 'utf-16' + local position_encoding = client and client.offset_encoding or 'utf-16' --- @param diagnostic lsp.Diagnostic --- @return vim.Diagnostic return vim.tbl_map(function(diagnostic) @@ -89,15 +89,16 @@ local function diagnostic_lsp_to_vim(diagnostics, bufnr, client_id) string.format('Unsupported Markup message from LSP client %d', client_id), vim.lsp.log_levels.ERROR ) + --- @diagnostic disable-next-line: undefined-field,no-unknown message = diagnostic.message.value end local line = buf_lines and buf_lines[start.line + 1] or '' --- @type vim.Diagnostic return { lnum = start.line, - col = vim.str_byteindex(line, offset_encoding, start.character, false), + col = vim.str_byteindex(line, position_encoding, start.character, false), end_lnum = _end.line, - end_col = vim.str_byteindex(line, offset_encoding, _end.character, false), + end_col = vim.str_byteindex(line, position_encoding, _end.character, false), severity = severity_lsp_to_vim(diagnostic.severity), message = message, source = diagnostic.source, @@ -208,7 +209,7 @@ end --- @param uri string --- @param client_id? integer ---- @param diagnostics vim.Diagnostic[] +--- @param diagnostics lsp.Diagnostic[] --- @param is_pull boolean local function handle_diagnostics(uri, client_id, diagnostics, is_pull) local fname = vim.uri_to_fname(uri) @@ -246,10 +247,18 @@ end --- --- See |vim.diagnostic.config()| for configuration options. --- ----@param _ lsp.ResponseError? +---@param error lsp.ResponseError? ---@param result lsp.DocumentDiagnosticReport ---@param ctx lsp.HandlerContext -function M.on_diagnostic(_, result, ctx) +function M.on_diagnostic(error, result, ctx) + if error ~= nil and error.code == protocol.ErrorCodes.ServerCancelled then + if error.data == nil or error.data.retriggerRequest ~= false then + local client = assert(vim.lsp.get_client_by_id(ctx.client_id)) + client:request(ctx.method, ctx.params) + end + return + end + if result == nil or result.kind == 'unchanged' then return end @@ -348,9 +357,7 @@ end ---@param bufnr (integer) Buffer handle, or 0 for current ---@private function M._enable(bufnr) - if bufnr == nil or bufnr == 0 then - bufnr = api.nvim_get_current_buf() - end + bufnr = vim._resolve_bufnr(bufnr) if not bufstates[bufnr] then bufstates[bufnr] = { enabled = true } diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 5c28d88b38..b35140dfad 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -47,7 +47,7 @@ RSC[ms.dollar_progress] = function(_, params, ctx) local value = params.value if type(value) == 'table' then - kind = value.kind + kind = value.kind --- @type string -- Carry over title of `begin` messages to `report` and `end` messages -- So that consumers always have it available, even if they consume a -- subset of the full sequence @@ -247,12 +247,12 @@ local function response_to_list(map_result, entity, title_fn) local items = map_result(result, ctx.bufnr) local list = { title = title, items = items, context = ctx } - if config.loclist then - vim.fn.setloclist(0, {}, ' ', list) - vim.cmd.lopen() - elseif config.on_list then + if config.on_list then assert(vim.is_callable(config.on_list), 'on_list is not a function') config.on_list(list) + elseif config.loclist then + vim.fn.setloclist(0, {}, ' ', list) + vim.cmd.lopen() else vim.fn.setqflist({}, ' ', list) vim.cmd('botright copen') @@ -382,7 +382,7 @@ end --- @diagnostic disable-next-line: deprecated RCS[ms.textDocument_hover] = M.hover -local sig_help_ns = api.nvim_create_namespace('vim_lsp_signature_help') +local sig_help_ns = api.nvim_create_namespace('nvim.lsp.signature_help') --- @deprecated remove in 0.13 --- |lsp-handler| for the method "textDocument/signatureHelp". @@ -582,9 +582,8 @@ NSC['window/showMessage'] = function(_, params, ctx) if message_type == protocol.MessageType.Error then err_message('LSP[', client_name, '] ', message) else - --- @type string - local message_type_name = protocol.MessageType[message_type] - api.nvim_out_write(string.format('LSP[%s][%s] %s\n', client_name, message_type_name, message)) + message = ('LSP[%s][%s] %s\n'):format(client_name, protocol.MessageType[message_type], message) + api.nvim_echo({ { message } }, true, {}) end return params end @@ -659,7 +658,8 @@ for k, fn in pairs(M) do }) end - if err then + -- ServerCancelled errors should be propagated to the request handler + if err and err.code ~= protocol.ErrorCodes.ServerCancelled then -- LSP spec: -- interface ResponseError: -- code: integer; diff --git a/runtime/lua/vim/lsp/health.lua b/runtime/lua/vim/lsp/health.lua index 0d314108fe..8af9f2f791 100644 --- a/runtime/lua/vim/lsp/health.lua +++ b/runtime/lua/vim/lsp/health.lua @@ -28,42 +28,48 @@ local function check_log() report_fn(string.format('Log size: %d KB', log_size / 1000)) end +--- @param f function +--- @return string +local function func_tostring(f) + local info = debug.getinfo(f, 'S') + return ('<function %s:%s>'):format(info.source, info.linedefined) +end + local function check_active_clients() vim.health.start('vim.lsp: Active Clients') local clients = vim.lsp.get_clients() if next(clients) then for _, client in pairs(clients) do + local server_version = vim.tbl_get(client, 'server_info', 'version') + or '? (no serverInfo.version response)' local cmd ---@type string - if type(client.config.cmd) == 'table' then - cmd = table.concat(client.config.cmd --[[@as table]], ' ') - elseif type(client.config.cmd) == 'function' then - cmd = tostring(client.config.cmd) + local ccmd = client.config.cmd + if type(ccmd) == 'table' then + cmd = vim.inspect(ccmd) + elseif type(ccmd) == 'function' then + cmd = func_tostring(ccmd) end local dirs_info ---@type string if client.workspace_folders and #client.workspace_folders > 1 then - dirs_info = string.format( - ' Workspace folders:\n %s', - vim - .iter(client.workspace_folders) - ---@param folder lsp.WorkspaceFolder - :map(function(folder) - return folder.name - end) - :join('\n ') - ) + local wfolders = {} --- @type string[] + for _, dir in ipairs(client.workspace_folders) do + wfolders[#wfolders + 1] = dir.name + end + dirs_info = ('- Workspace folders:\n %s'):format(table.concat(wfolders, '\n ')) else dirs_info = string.format( - ' Root directory: %s', + '- Root directory: %s', client.root_dir and vim.fn.fnamemodify(client.root_dir, ':~') ) or nil end report_info(table.concat({ string.format('%s (id: %d)', client.name, client.id), + string.format('- Version: %s', server_version), dirs_info, - string.format(' Command: %s', cmd), - string.format(' Settings: %s', vim.inspect(client.settings, { newline = '\n ' })), + string.format('- Command: %s', cmd), + string.format('- Settings: %s', vim.inspect(client.settings, { newline = '\n ' })), string.format( - ' Attached buffers: %s', + '- Attached buffers: %s', vim.iter(pairs(client.attached_buffers)):map(tostring):join(', ') ), }, '\n')) @@ -174,10 +180,45 @@ local function check_position_encodings() end end +local function check_enabled_configs() + vim.health.start('vim.lsp: Enabled Configurations') + + for name in vim.spairs(vim.lsp._enabled_configs) do + local config = vim.lsp.config[name] + local text = {} --- @type string[] + text[#text + 1] = ('%s:'):format(name) + for k, v in + vim.spairs(config --[[@as table<string,any>]]) + do + local v_str --- @type string? + if k == 'name' then + v_str = nil + elseif k == 'filetypes' or k == 'root_markers' then + v_str = table.concat(v, ', ') + elseif type(v) == 'function' then + v_str = func_tostring(v) + else + v_str = vim.inspect(v, { newline = '\n ' }) + end + + if k == 'cmd' and type(v) == 'table' and vim.fn.executable(v[1]) == 0 then + report_warn(("'%s' is not executable. Configuration will not be used."):format(v[1])) + end + + if v_str then + text[#text + 1] = ('- %s: %s'):format(k, v_str) + end + end + text[#text + 1] = '' + report_info(table.concat(text, '\n')) + end +end + --- Performs a healthcheck for LSP function M.check() check_log() check_active_clients() + check_enabled_configs() check_watcher() check_position_encodings() end diff --git a/runtime/lua/vim/lsp/inlay_hint.lua b/runtime/lua/vim/lsp/inlay_hint.lua index f1ae9a8e9e..37e1202d1d 100644 --- a/runtime/lua/vim/lsp/inlay_hint.lua +++ b/runtime/lua/vim/lsp/inlay_hint.lua @@ -29,8 +29,8 @@ local bufstates = vim.defaulttable(function(_) }) end) -local namespace = api.nvim_create_namespace('vim_lsp_inlayhint') -local augroup = api.nvim_create_augroup('vim_lsp_inlayhint', {}) +local namespace = api.nvim_create_namespace('nvim.lsp.inlayhint') +local augroup = api.nvim_create_augroup('nvim.lsp.inlayhint', {}) --- |lsp-handler| for the method `textDocument/inlayHint` --- Store hints for a specific buffer and client @@ -122,12 +122,12 @@ end --- local hint = vim.lsp.inlay_hint.get({ bufnr = 0 })[1] -- 0 for current buffer --- --- local client = vim.lsp.get_client_by_id(hint.client_id) ---- local resp = client.request_sync('inlayHint/resolve', hint.inlay_hint, 100, 0) +--- local resp = client:request_sync('inlayHint/resolve', hint.inlay_hint, 100, 0) --- local resolved_hint = assert(resp and resp.result, resp.err) --- vim.lsp.util.apply_text_edits(resolved_hint.textEdits, 0, client.encoding) --- --- location = resolved_hint.label[1].location ---- client.request('textDocument/hover', { +--- client:request('textDocument/hover', { --- textDocument = { uri = location.uri }, --- position = location.range.start, --- }) @@ -149,8 +149,8 @@ function M.get(filter) vim.list_extend(hints, M.get(vim.tbl_extend('keep', { bufnr = buf }, filter))) end, vim.api.nvim_list_bufs()) return hints - elseif bufnr == 0 then - bufnr = api.nvim_get_current_buf() + else + bufnr = vim._resolve_bufnr(bufnr) end local bufstate = bufstates[bufnr] @@ -203,9 +203,7 @@ end --- Clear inlay hints ---@param bufnr (integer) Buffer handle, or 0 for current local function clear(bufnr) - if bufnr == 0 then - bufnr = api.nvim_get_current_buf() - end + bufnr = vim._resolve_bufnr(bufnr) local bufstate = bufstates[bufnr] local client_lens = (bufstate or {}).client_hints or {} local client_ids = vim.tbl_keys(client_lens) --- @type integer[] @@ -221,9 +219,7 @@ end --- Disable inlay hints for a buffer ---@param bufnr (integer) Buffer handle, or 0 for current local function _disable(bufnr) - if bufnr == 0 then - bufnr = api.nvim_get_current_buf() - end + bufnr = vim._resolve_bufnr(bufnr) clear(bufnr) bufstates[bufnr] = nil bufstates[bufnr].enabled = false @@ -242,9 +238,7 @@ end --- Enable inlay hints for a buffer ---@param bufnr (integer) Buffer handle, or 0 for current local function _enable(bufnr) - if bufnr == 0 then - bufnr = api.nvim_get_current_buf() - end + bufnr = vim._resolve_bufnr(bufnr) bufstates[bufnr] = nil bufstates[bufnr].enabled = true _refresh(bufnr) @@ -371,13 +365,10 @@ function M.is_enabled(filter) filter = filter or {} local bufnr = filter.bufnr - vim.validate('bufnr', bufnr, 'number', true) if bufnr == nil then return globalstate.enabled - elseif bufnr == 0 then - bufnr = api.nvim_get_current_buf() end - return bufstates[bufnr].enabled + return bufstates[vim._resolve_bufnr(bufnr)].enabled end --- Optional filters |kwargs|, or `nil` for all. diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 7db48b0c06..fbfd0cd6b0 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -15,7 +15,6 @@ local sysname = vim.uv.os_uname().sysname --- @class vim.lsp.protocol.constants --- @nodoc local constants = { - --- @enum lsp.DiagnosticSeverity DiagnosticSeverity = { -- Reports an error. Error = 1, @@ -27,7 +26,6 @@ local constants = { Hint = 4, }, - --- @enum lsp.DiagnosticTag DiagnosticTag = { -- Unused or unnecessary code Unnecessary = 1, @@ -35,7 +33,6 @@ local constants = { Deprecated = 2, }, - ---@enum lsp.MessageType MessageType = { -- An error message. Error = 1, @@ -50,7 +47,6 @@ local constants = { }, -- The file event type. - ---@enum lsp.FileChangeType FileChangeType = { -- The file got created. Created = 1, @@ -149,7 +145,6 @@ local constants = { }, -- Represents reasons why a text document is saved. - ---@enum lsp.TextDocumentSaveReason TextDocumentSaveReason = { -- Manually triggered, e.g. by the user pressing save, by starting debugging, -- or by an API call. @@ -174,6 +169,7 @@ local constants = { -- Defined by the protocol. RequestCancelled = -32800, ContentModified = -32801, + ServerCancelled = -32802, }, -- Describes the content type that a client supports in various @@ -245,7 +241,6 @@ local constants = { -- Defines whether the insert text in a completion item should be interpreted as -- plain text or a snippet. - --- @enum lsp.InsertTextFormat InsertTextFormat = { -- The primary text to be inserted is treated as a plain string. PlainText = 1, @@ -304,7 +299,6 @@ local constants = { SourceOrganizeImports = 'source.organizeImports', }, -- The reason why code actions were requested. - ---@enum lsp.CodeActionTriggerKind CodeActionTriggerKind = { -- Code actions were explicitly requested by the user or by an extension. Invoked = 1, @@ -439,6 +433,13 @@ function protocol.make_client_capabilities() properties = { 'command' }, }, }, + foldingRange = { + dynamicRegistration = false, + lineFoldingOnly = true, + foldingRange = { + collapsedText = true, + }, + }, formatting = { dynamicRegistration = true, }, diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 6c8564845f..a0d1fe776b 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -1,18 +1,8 @@ -local uv = vim.uv local log = require('vim.lsp.log') local protocol = require('vim.lsp.protocol') +local lsp_transport = require('vim.lsp._transport') local validate, schedule_wrap = vim.validate, vim.schedule_wrap -local is_win = vim.fn.has('win32') == 1 - ---- Checks whether a given path exists and is a directory. ----@param filename string path to check ----@return boolean -local function is_dir(filename) - local stat = uv.fs_stat(filename) - return stat and stat.type == 'directory' or false -end - --- Embeds the given string into a table and correctly computes `Content-Length`. --- ---@param message string @@ -242,8 +232,11 @@ local default_dispatchers = { end, } ----@private -function M.create_read_loop(handle_body, on_no_chunk, on_error) +--- @private +--- @param handle_body fun(body: string) +--- @param on_exit? fun() +--- @param on_error fun(err: any) +function M.create_read_loop(handle_body, on_exit, on_error) local parse_chunk = coroutine.wrap(request_parser_loop) --[[@as fun(chunk: string?): vim.lsp.rpc.Headers?, string?]] parse_chunk() return function(err, chunk) @@ -253,8 +246,8 @@ function M.create_read_loop(handle_body, on_no_chunk, on_error) end if not chunk then - if on_no_chunk then - on_no_chunk() + if on_exit then + on_exit() end return end @@ -262,7 +255,7 @@ function M.create_read_loop(handle_body, on_no_chunk, on_error) while true do local headers, body = parse_chunk(chunk) if headers then - handle_body(body) + handle_body(assert(body)) chunk = '' else break @@ -282,14 +275,14 @@ local Client = {} ---@private function Client:encode_and_send(payload) log.debug('rpc.send', payload) - if self.transport.is_closing() then + if self.transport:is_closing() then return false end local jsonstr = assert( vim.json.encode(payload), string.format("Couldn't encode payload '%s'", vim.inspect(payload)) ) - self.transport.write(format_message_with_content_length(jsonstr)) + self.transport:write(format_message_with_content_length(jsonstr)) return true end @@ -323,7 +316,7 @@ end ---@param method string The invoked LSP method ---@param params table? Parameters for the invoked LSP method ---@param callback fun(err?: lsp.ResponseError, result: any) Callback to invoke ----@param notify_reply_callback fun(message_id: integer)|nil Callback to invoke as soon as a request is no longer pending +---@param notify_reply_callback? fun(message_id: integer) Callback to invoke as soon as a request is no longer pending ---@return boolean success `true` if request could be sent, `false` if not ---@return integer? message_id if request could be sent, `nil` if not function Client:request(method, params, callback, notify_reply_callback) @@ -337,21 +330,16 @@ function Client:request(method, params, callback, notify_reply_callback) method = method, params = params, }) - local message_callbacks = self.message_callbacks - local notify_reply_callbacks = self.notify_reply_callbacks - if result then - if message_callbacks then - message_callbacks[message_id] = schedule_wrap(callback) - else - return false, nil - end - if notify_reply_callback and notify_reply_callbacks then - notify_reply_callbacks[message_id] = schedule_wrap(notify_reply_callback) - end - return result, message_id - else - return false, nil + + if not result then + return false + end + + self.message_callbacks[message_id] = schedule_wrap(callback) + if notify_reply_callback then + self.notify_reply_callbacks[message_id] = schedule_wrap(notify_reply_callback) end + return result, message_id end ---@package @@ -370,7 +358,7 @@ end ---@param ... any ---@return boolean status ---@return any head ----@return any|nil ... +---@return any? ... function Client:pcall_handler(errkind, status, head, ...) if not status then self:on_error(errkind, head, ...) @@ -385,7 +373,7 @@ end ---@param ... any ---@return boolean status ---@return any head ----@return any|nil ... +---@return any? ... function Client:try_call(errkind, fn, ...) return self:pcall_handler(errkind, pcall(fn, ...)) end @@ -394,7 +382,8 @@ end -- time and log them. This would require storing the timestamp. I could call -- them with an error then, perhaps. ----@package +--- @package +--- @param body string function Client:handle_body(body) local ok, decoded = pcall(vim.json.decode, body, { luanil = { object = true } }) if not ok then @@ -406,7 +395,7 @@ function Client:handle_body(body) if type(decoded) ~= 'table' then self:on_error(M.client_errors.INVALID_SERVER_MESSAGE, decoded) elseif type(decoded.method) == 'string' and decoded.id then - local err --- @type lsp.ResponseError|nil + local err --- @type lsp.ResponseError? -- Schedule here so that the users functions don't trigger an error and -- we can still use the result. vim.schedule(coroutine.wrap(function() @@ -453,45 +442,36 @@ function Client:handle_body(body) local result_id = assert(tonumber(decoded.id), 'response id must be a number') -- Notify the user that a response was received for the request - local notify_reply_callbacks = self.notify_reply_callbacks - local notify_reply_callback = notify_reply_callbacks and notify_reply_callbacks[result_id] + local notify_reply_callback = self.notify_reply_callbacks[result_id] if notify_reply_callback then validate('notify_reply_callback', notify_reply_callback, 'function') notify_reply_callback(result_id) - notify_reply_callbacks[result_id] = nil + self.notify_reply_callbacks[result_id] = nil end - local message_callbacks = self.message_callbacks - -- Do not surface RequestCancelled to users, it is RPC-internal. if decoded.error then - local mute_error = false + assert(type(decoded.error) == 'table') if decoded.error.code == protocol.ErrorCodes.RequestCancelled then log.debug('Received cancellation ack', decoded) - mute_error = true - end - - if mute_error then -- Clear any callback since this is cancelled now. -- This is safe to do assuming that these conditions hold: -- - The server will not send a result callback after this cancellation. -- - If the server sent this cancellation ACK after sending the result, the user of this RPC -- client will ignore the result themselves. - if result_id and message_callbacks then - message_callbacks[result_id] = nil + if result_id then + self.message_callbacks[result_id] = nil end return end end - local callback = message_callbacks and message_callbacks[result_id] + local callback = self.message_callbacks[result_id] if callback then - message_callbacks[result_id] = nil + self.message_callbacks[result_id] = nil validate('callback', callback, 'function') if decoded.error then - decoded.error = setmetatable(decoded.error, { - __tostring = M.format_rpc_error, - }) + setmetatable(decoded.error, { __tostring = M.format_rpc_error }) end self:try_call( M.client_errors.SERVER_RESULT_CALLBACK_ERROR, @@ -517,11 +497,6 @@ function Client:handle_body(body) end end ----@class (private) vim.lsp.rpc.Transport ----@field write fun(msg: string) ----@field is_closing fun(): boolean ----@field terminate fun() - ---@param dispatchers vim.lsp.rpc.Dispatchers ---@param transport vim.lsp.rpc.Transport ---@return vim.lsp.rpc.Client @@ -536,11 +511,20 @@ local function new_client(dispatchers, transport) return setmetatable(state, { __index = Client }) end ----@class vim.lsp.rpc.PublicClient ----@field request fun(method: string, params: table?, callback: fun(err: lsp.ResponseError|nil, result: any), notify_reply_callback: fun(message_id: integer)|nil):boolean,integer? see |vim.lsp.rpc.request()| ----@field notify fun(method: string, params: any):boolean see |vim.lsp.rpc.notify()| ----@field is_closing fun(): boolean ----@field terminate fun() +--- Client RPC object +--- @class vim.lsp.rpc.PublicClient +--- +--- See [vim.lsp.rpc.request()] +--- @field request fun(method: string, params: table?, callback: fun(err?: lsp.ResponseError, result: any), notify_reply_callback?: fun(message_id: integer)):boolean,integer? +--- +--- See [vim.lsp.rpc.notify()] +--- @field notify fun(method: string, params: any): boolean +--- +--- Indicates if the RPC is closing. +--- @field is_closing fun(): boolean +--- +--- Terminates the RPC client. +--- @field terminate fun() ---@param client vim.lsp.rpc.Client ---@return vim.lsp.rpc.PublicClient @@ -551,20 +535,20 @@ local function public_client(client) ---@private function result.is_closing() - return client.transport.is_closing() + return client.transport:is_closing() end ---@private function result.terminate() - client.transport.terminate() + client.transport:terminate() end --- Sends a request to the LSP server and runs {callback} upon response. --- ---@param method (string) The invoked LSP method ---@param params (table?) Parameters for the invoked LSP method - ---@param callback fun(err: lsp.ResponseError|nil, result: any) Callback to invoke - ---@param notify_reply_callback fun(message_id: integer)|nil Callback to invoke as soon as a request is no longer pending + ---@param callback fun(err: lsp.ResponseError?, result: any) Callback to invoke + ---@param notify_reply_callback? fun(message_id: integer) Callback to invoke as soon as a request is no longer pending ---@return boolean success `true` if request could be sent, `false` if not ---@return integer? message_id if request could be sent, `nil` if not function result.request(method, params, callback, notify_reply_callback) @@ -610,6 +594,21 @@ local function merge_dispatchers(dispatchers) return merged end +--- @param client vim.lsp.rpc.Client +--- @param on_exit? fun() +local function create_client_read_loop(client, on_exit) + --- @param body string + local function handle_body(body) + client:handle_body(body) + end + + local function on_error(err) + client:on_error(M.client_errors.READ_ERROR, err) + end + + return M.create_read_loop(handle_body, on_exit, on_error) +end + --- Create a LSP RPC client factory that connects to either: --- --- - a named pipe (windows) @@ -617,83 +616,26 @@ end --- - a host and port via TCP --- --- Return a function that can be passed to the `cmd` field for ---- |vim.lsp.start_client()| or |vim.lsp.start()|. +--- |vim.lsp.start()|. --- ---@param host_or_path string host to connect to or path to a pipe/domain socket ---@param port integer? TCP port to connect to. If absent the first argument must be a pipe ---@return fun(dispatchers: vim.lsp.rpc.Dispatchers): vim.lsp.rpc.PublicClient function M.connect(host_or_path, port) + validate('host_or_path', host_or_path, 'string') + validate('port', port, 'number', true) + return function(dispatchers) + validate('dispatchers', dispatchers, 'table', true) + dispatchers = merge_dispatchers(dispatchers) - local handle = ( - port == nil - and assert( - uv.new_pipe(false), - string.format('Pipe with name %s could not be opened.', host_or_path) - ) - or assert(uv.new_tcp(), 'Could not create new TCP socket') - ) - local closing = false - -- Connect returns a PublicClient synchronously so the caller - -- can immediately send messages before the connection is established - -- -> Need to buffer them until that happens - local connected = false - -- size should be enough because the client can't really do anything until initialization is done - -- which required a response from the server - implying the connection got established - local msgbuf = vim.ringbuf(10) - local transport = { - write = function(msg) - if connected then - local _, err = handle:write(msg) - if err and not closing then - log.error('Error on handle:write: %q', err) - end - else - msgbuf:push(msg) - end - end, - is_closing = function() - return closing - end, - terminate = function() - if not closing then - closing = true - handle:shutdown() - handle:close() - dispatchers.on_exit(0, 0) - end - end, - } + + local transport = lsp_transport.TransportConnect.new() local client = new_client(dispatchers, transport) - local function on_connect(err) - if err then - local address = port == nil and host_or_path or (host_or_path .. ':' .. port) - vim.schedule(function() - vim.notify( - string.format('Could not connect to %s, reason: %s', address, vim.inspect(err)), - vim.log.levels.WARN - ) - end) - return - end - local handle_body = function(body) - client:handle_body(body) - end - handle:read_start(M.create_read_loop(handle_body, transport.terminate, function(read_err) - client:on_error(M.client_errors.READ_ERROR, read_err) - end)) - connected = true - for msg in msgbuf do - handle:write(msg) - end - end - if port == nil then - handle:connect(host_or_path, on_connect) - else - local info = uv.getaddrinfo(host_or_path, nil) - local resolved_host = info and info[1] and info[1].addr or host_or_path - handle:connect(resolved_host, port, on_connect) - end + local on_read = create_client_read_loop(client, function() + transport:terminate() + end) + transport:connect(host_or_path, port, on_read, dispatchers.on_exit) return public_client(client) end @@ -713,83 +655,19 @@ end --- @param cmd string[] Command to start the LSP server. --- @param dispatchers? vim.lsp.rpc.Dispatchers --- @param extra_spawn_params? vim.lsp.rpc.ExtraSpawnParams ---- @return vim.lsp.rpc.PublicClient : Client RPC object, with these methods: ---- - `notify()` |vim.lsp.rpc.notify()| ---- - `request()` |vim.lsp.rpc.request()| ---- - `is_closing()` returns a boolean indicating if the RPC is closing. ---- - `terminate()` terminates the RPC client. +--- @return vim.lsp.rpc.PublicClient function M.start(cmd, dispatchers, extra_spawn_params) log.info('Starting RPC client', { cmd = cmd, extra = extra_spawn_params }) validate('cmd', cmd, 'table') validate('dispatchers', dispatchers, 'table', true) - extra_spawn_params = extra_spawn_params or {} - - if extra_spawn_params.cwd then - assert(is_dir(extra_spawn_params.cwd), 'cwd must be a directory') - end - dispatchers = merge_dispatchers(dispatchers) - local sysobj ---@type vim.SystemObj - - local client = new_client(dispatchers, { - write = function(msg) - sysobj:write(msg) - end, - is_closing = function() - return sysobj == nil or sysobj:is_closing() - end, - terminate = function() - sysobj:kill(15) - end, - }) - - local handle_body = function(body) - client:handle_body(body) - end - - local stdout_handler = M.create_read_loop(handle_body, nil, function(err) - client:on_error(M.client_errors.READ_ERROR, err) - end) - - local stderr_handler = function(_, chunk) - if chunk then - log.error('rpc', cmd[1], 'stderr', chunk) - end - end - - local detached = not is_win - if extra_spawn_params.detached ~= nil then - detached = extra_spawn_params.detached - end - - local ok, sysobj_or_err = pcall(vim.system, cmd, { - stdin = true, - stdout = stdout_handler, - stderr = stderr_handler, - cwd = extra_spawn_params.cwd, - env = extra_spawn_params.env, - detach = detached, - }, function(obj) - dispatchers.on_exit(obj.code, obj.signal) - end) - - if not ok then - local err = sysobj_or_err --[[@as string]] - local sfx --- @type string - if string.match(err, 'ENOENT') then - sfx = '. The language server is either not installed, missing from PATH, or not executable.' - else - sfx = string.format(' with error message: %s', err) - end - local msg = - string.format('Spawning language server with cmd: `%s` failed%s', vim.inspect(cmd), sfx) - error(msg) - end - - sysobj = sysobj_or_err --[[@as vim.SystemObj]] + local transport = lsp_transport.TransportRun.new() + local client = new_client(dispatchers, transport) + local on_read = create_client_read_loop(client) + transport:run(cmd, extra_spawn_params, on_read, dispatchers.on_exit) return public_client(client) end diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index 215e5f41aa..dd8b654856 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -139,7 +139,7 @@ local function tokens_to_ranges(data, bufnr, client, request) if token_type then local modifiers = modifiers_from_number(data[i + 4], token_modifiers) - local end_char = start_char + data[i + 2] + local end_char = start_char + data[i + 2] --- @type integer LuaLS bug local buf_line = lines and lines[line + 1] or '' local start_col = vim.str_byteindex(buf_line, encoding, start_char, false) local end_col = vim.str_byteindex(buf_line, encoding, end_char, false) @@ -166,7 +166,7 @@ function STHighlighter.new(bufnr) local self = setmetatable({}, { __index = STHighlighter }) self.bufnr = bufnr - self.augroup = api.nvim_create_augroup('vim_lsp_semantic_tokens:' .. bufnr, { clear = true }) + self.augroup = api.nvim_create_augroup('nvim.lsp.semantic_tokens:' .. bufnr, { clear = true }) self.client_state = {} STHighlighter.active[bufnr] = self @@ -225,7 +225,7 @@ function STHighlighter:attach(client_id) local state = self.client_state[client_id] if not state then state = { - namespace = api.nvim_create_namespace('vim_lsp_semantic_tokens:' .. client_id), + namespace = api.nvim_create_namespace('nvim.lsp.semantic_tokens:' .. client_id), active_request = {}, current_result = {}, } @@ -273,7 +273,7 @@ function STHighlighter:send_request() if client and current_result.version ~= version and active_request.version ~= version then -- cancel stale in-flight request if active_request.request_id then - client.cancel_request(active_request.request_id) + client:cancel_request(active_request.request_id) active_request = {} state.active_request = active_request end @@ -288,7 +288,7 @@ function STHighlighter:send_request() method = method .. '/delta' params.previousResultId = current_result.result_id end - local success, request_id = client.request(method, params, function(err, response, ctx) + local success, request_id = client:request(method, params, function(err, response, ctx) -- look client up again using ctx.client_id instead of using a captured -- client object local c = vim.lsp.get_client_by_id(ctx.client_id) @@ -519,7 +519,7 @@ function STHighlighter:reset() if state.active_request.request_id then local client = vim.lsp.get_client_by_id(client_id) assert(client) - client.cancel_request(state.active_request.request_id) + client:cancel_request(state.active_request.request_id) state.active_request = {} end end @@ -547,7 +547,7 @@ function STHighlighter:mark_dirty(client_id) if state.active_request.request_id then local client = vim.lsp.get_client_by_id(client_id) assert(client) - client.cancel_request(state.active_request.request_id) + client:cancel_request(state.active_request.request_id) state.active_request = {} end end @@ -600,9 +600,7 @@ function M.start(bufnr, client_id, opts) vim.validate('bufnr', bufnr, 'number') vim.validate('client_id', client_id, 'number') - if bufnr == 0 then - bufnr = api.nvim_get_current_buf() - end + bufnr = vim._resolve_bufnr(bufnr) opts = opts or {} assert( @@ -655,9 +653,7 @@ function M.stop(bufnr, client_id) vim.validate('bufnr', bufnr, 'number') vim.validate('client_id', client_id, 'number') - if bufnr == 0 then - bufnr = api.nvim_get_current_buf() - end + bufnr = vim._resolve_bufnr(bufnr) local highlighter = STHighlighter.active[bufnr] if not highlighter then @@ -691,9 +687,7 @@ end --- - modifiers (table) token modifiers as a set. E.g., { static = true, readonly = true } --- - client_id (integer) function M.get_at_pos(bufnr, row, col) - if bufnr == nil or bufnr == 0 then - bufnr = api.nvim_get_current_buf() - end + bufnr = vim._resolve_bufnr(bufnr) local highlighter = STHighlighter.active[bufnr] if not highlighter then @@ -739,8 +733,7 @@ function M.force_refresh(bufnr) vim.validate('bufnr', bufnr, 'number', true) local buffers = bufnr == nil and vim.tbl_keys(STHighlighter.active) - or bufnr == 0 and { api.nvim_get_current_buf() } - or { bufnr } + or { vim._resolve_bufnr(bufnr) } for _, buffer in ipairs(buffers) do local highlighter = STHighlighter.active[buffer] @@ -770,9 +763,7 @@ end ---@param hl_group (string) Highlight group name ---@param opts? vim.lsp.semantic_tokens.highlight_token.Opts Optional parameters: function M.highlight_token(token, bufnr, client_id, hl_group, opts) - if bufnr == 0 then - bufnr = api.nvim_get_current_buf() - end + bufnr = vim._resolve_bufnr(bufnr) local highlighter = STHighlighter.active[bufnr] if not highlighter then return @@ -814,7 +805,7 @@ function M._refresh(err, _, ctx) return vim.NIL end -local namespace = api.nvim_create_namespace('vim_lsp_semantic_tokens') +local namespace = api.nvim_create_namespace('nvim.lsp.semantic_tokens') api.nvim_set_decoration_provider(namespace, { on_win = function(_, _, bufnr, topline, botline) local highlighter = STHighlighter.active[bufnr] diff --git a/runtime/lua/vim/lsp/sync.lua b/runtime/lua/vim/lsp/sync.lua index 3df45ebff0..621f63b25f 100644 --- a/runtime/lua/vim/lsp/sync.lua +++ b/runtime/lua/vim/lsp/sync.lua @@ -48,21 +48,21 @@ local str_utfindex = vim.str_utfindex local str_utf_start = vim.str_utf_start local str_utf_end = vim.str_utf_end --- Given a line, byte idx, alignment, and offset_encoding convert to the aligned +-- Given a line, byte idx, alignment, and position_encoding convert to the aligned -- utf-8 index and either the utf-16, or utf-32 index. ---@param line string the line to index into ---@param byte integer the byte idx ----@param offset_encoding string utf-8|utf-16|utf-32|nil (default: utf-8) +---@param position_encoding string utf-8|utf-16|utf-32|nil (default: utf-8) ---@return integer byte_idx of first change position ---@return integer char_idx of first change position -local function align_end_position(line, byte, offset_encoding) +local function align_end_position(line, byte, position_encoding) local char --- @type integer -- If on the first byte, or an empty string: the trivial case if byte == 1 or #line == 0 then char = byte -- Called in the case of extending an empty line "" -> "a" elseif byte == #line + 1 then - char = str_utfindex(line, offset_encoding) + 1 + char = str_utfindex(line, position_encoding) + 1 else -- Modifying line, find the nearest utf codepoint local offset = str_utf_start(line, byte) @@ -73,9 +73,9 @@ local function align_end_position(line, byte, offset_encoding) end if byte <= #line then --- Convert to 0 based for input, and from 0 based for output - char = str_utfindex(line, offset_encoding, byte - 1) + 1 + char = str_utfindex(line, position_encoding, byte - 1) + 1 else - char = str_utfindex(line, offset_encoding) + 1 + char = str_utfindex(line, position_encoding) + 1 end -- Extending line, find the nearest utf codepoint for the last valid character end @@ -93,7 +93,7 @@ end ---@param firstline integer firstline from on_lines, adjusted to 1-index ---@param lastline integer lastline from on_lines, adjusted to 1-index ---@param new_lastline integer new_lastline from on_lines, adjusted to 1-index ----@param offset_encoding string utf-8|utf-16|utf-32|nil (fallback to utf-8) +---@param position_encoding string utf-8|utf-16|utf-32|nil (fallback to utf-8) ---@return vim.lsp.sync.Range result table include line_idx, byte_idx, and char_idx of first change position local function compute_start_range( prev_lines, @@ -101,7 +101,7 @@ local function compute_start_range( firstline, lastline, new_lastline, - offset_encoding + position_encoding ) local char_idx --- @type integer? local byte_idx --- @type integer? @@ -115,7 +115,7 @@ local function compute_start_range( if line then line_idx = firstline - 1 byte_idx = #line + 1 - char_idx = str_utfindex(line, offset_encoding) + 1 + char_idx = str_utfindex(line, position_encoding) + 1 else line_idx = firstline byte_idx = 1 @@ -152,11 +152,11 @@ local function compute_start_range( char_idx = 1 elseif start_byte_idx == #prev_line + 1 then byte_idx = start_byte_idx - char_idx = str_utfindex(prev_line, offset_encoding) + 1 + char_idx = str_utfindex(prev_line, position_encoding) + 1 else byte_idx = start_byte_idx + str_utf_start(prev_line, start_byte_idx) --- Convert to 0 based for input, and from 0 based for output - char_idx = vim.str_utfindex(prev_line, offset_encoding, byte_idx - 1) + 1 + char_idx = vim.str_utfindex(prev_line, position_encoding, byte_idx - 1) + 1 end -- Return the start difference (shared for new and prev lines) @@ -174,7 +174,7 @@ end ---@param firstline integer ---@param lastline integer ---@param new_lastline integer ----@param offset_encoding string +---@param position_encoding string ---@return vim.lsp.sync.Range prev_end_range ---@return vim.lsp.sync.Range curr_end_range local function compute_end_range( @@ -184,7 +184,7 @@ local function compute_end_range( firstline, lastline, new_lastline, - offset_encoding + position_encoding ) -- A special case for the following `firstline == new_lastline` case where lines are deleted. -- Even if the buffer has become empty, nvim behaves as if it has an empty line with eol. @@ -193,7 +193,7 @@ local function compute_end_range( return { line_idx = lastline - 1, byte_idx = #prev_line + 1, - char_idx = str_utfindex(prev_line, offset_encoding) + 1, + char_idx = str_utfindex(prev_line, position_encoding) + 1, }, { line_idx = 1, byte_idx = 1, char_idx = 1 } end -- If firstline == new_lastline, the first change occurred on a line that was deleted. @@ -259,7 +259,7 @@ local function compute_end_range( prev_end_byte_idx = 1 end local prev_byte_idx, prev_char_idx = - align_end_position(prev_line, prev_end_byte_idx, offset_encoding) + align_end_position(prev_line, prev_end_byte_idx, position_encoding) local prev_end_range = { line_idx = prev_line_idx, byte_idx = prev_byte_idx, char_idx = prev_char_idx } @@ -274,7 +274,7 @@ local function compute_end_range( curr_end_byte_idx = 1 end local curr_byte_idx, curr_char_idx = - align_end_position(curr_line, curr_end_byte_idx, offset_encoding) + align_end_position(curr_line, curr_end_byte_idx, position_encoding) curr_end_range = { line_idx = curr_line_idx, byte_idx = curr_byte_idx, char_idx = curr_char_idx } end @@ -317,7 +317,7 @@ local function extract_text(lines, start_range, end_range, line_ending) end end --- rangelength depends on the offset encoding +-- rangelength depends on the position encoding -- bytes for utf-8 (clangd with extension) -- codepoints for utf-16 -- codeunits for utf-32 @@ -326,10 +326,10 @@ end ---@param lines string[] ---@param start_range vim.lsp.sync.Range ---@param end_range vim.lsp.sync.Range ----@param offset_encoding string +---@param position_encoding string ---@param line_ending string ---@return integer -local function compute_range_length(lines, start_range, end_range, offset_encoding, line_ending) +local function compute_range_length(lines, start_range, end_range, position_encoding, line_ending) local line_ending_length = #line_ending -- Single line case if start_range.line_idx == end_range.line_idx then @@ -339,7 +339,7 @@ local function compute_range_length(lines, start_range, end_range, offset_encodi local start_line = lines[start_range.line_idx] local range_length --- @type integer if start_line and #start_line > 0 then - range_length = str_utfindex(start_line, offset_encoding) + range_length = str_utfindex(start_line, position_encoding) - start_range.char_idx + 1 + line_ending_length @@ -352,7 +352,7 @@ local function compute_range_length(lines, start_range, end_range, offset_encodi for idx = start_range.line_idx + 1, end_range.line_idx - 1 do -- Length full line plus newline character if #lines[idx] > 0 then - range_length = range_length + str_utfindex(lines[idx], offset_encoding) + #line_ending + range_length = range_length + str_utfindex(lines[idx], position_encoding) + #line_ending else range_length = range_length + line_ending_length end @@ -372,7 +372,7 @@ end ---@param firstline integer line to begin search for first difference ---@param lastline integer line to begin search in old_lines for last difference ---@param new_lastline integer line to begin search in new_lines for last difference ----@param offset_encoding string encoding requested by language server +---@param position_encoding string encoding requested by language server ---@param line_ending string ---@return lsp.TextDocumentContentChangeEvent : see https://microsoft.github.io/language-server-protocol/specification/#textDocumentContentChangeEvent function M.compute_diff( @@ -381,7 +381,7 @@ function M.compute_diff( firstline, lastline, new_lastline, - offset_encoding, + position_encoding, line_ending ) -- Find the start of changes between the previous and current buffer. Common between both. @@ -393,7 +393,7 @@ function M.compute_diff( firstline + 1, lastline + 1, new_lastline + 1, - offset_encoding + position_encoding ) -- Find the last position changed in the previous and current buffer. -- prev_end_range is sent to the server as as the end of the changed range. @@ -405,7 +405,7 @@ function M.compute_diff( firstline + 1, lastline + 1, new_lastline + 1, - offset_encoding + position_encoding ) -- Grab the changed text of from start_range to curr_end_range in the current buffer. @@ -414,7 +414,7 @@ function M.compute_diff( -- Compute the range of the replaced text. Deprecated but still required for certain language servers local range_length = - compute_range_length(prev_lines, start_range, prev_end_range, offset_encoding, line_ending) + compute_range_length(prev_lines, start_range, prev_end_range, position_encoding, line_ending) -- convert to 0 based indexing local result = { diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index 6eab0f3da4..e16a905c44 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -49,7 +49,8 @@ local function get_border_size(opts) if not border_size[border] then border_error(border) end - return unpack(border_size[border]) + local r = border_size[border] + return r[1], r[2] end if 8 % #border ~= 0 then @@ -192,9 +193,7 @@ local function get_lines(bufnr, rows) rows = type(rows) == 'table' and rows or { rows } -- This is needed for bufload and bufloaded - if bufnr == 0 then - bufnr = api.nvim_get_current_buf() - end + bufnr = vim._resolve_bufnr(bufnr) local function buf_lines() local lines = {} --- @type table<integer,string> @@ -277,9 +276,9 @@ end --- Position is a https://microsoft.github.io/language-server-protocol/specifications/specification-current/#position ---@param position lsp.Position ----@param offset_encoding 'utf-8'|'utf-16'|'utf-32' +---@param position_encoding 'utf-8'|'utf-16'|'utf-32' ---@return integer -local function get_line_byte_from_position(bufnr, position, offset_encoding) +local function get_line_byte_from_position(bufnr, position, position_encoding) -- LSP's line and characters are 0-indexed -- Vim's line and columns are 1-indexed local col = position.character @@ -287,7 +286,7 @@ local function get_line_byte_from_position(bufnr, position, offset_encoding) -- character if col > 0 then local line = get_line(bufnr, position.line) or '' - return vim.str_byteindex(line, offset_encoding, col, false) + return vim.str_byteindex(line, position_encoding, col, false) end return col end @@ -295,12 +294,12 @@ end --- Applies a list of text edits to a buffer. ---@param text_edits lsp.TextEdit[] ---@param bufnr integer Buffer id ----@param offset_encoding 'utf-8'|'utf-16'|'utf-32' +---@param position_encoding 'utf-8'|'utf-16'|'utf-32' ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textEdit -function M.apply_text_edits(text_edits, bufnr, offset_encoding) +function M.apply_text_edits(text_edits, bufnr, position_encoding) validate('text_edits', text_edits, 'table', false) validate('bufnr', bufnr, 'number', false) - validate('offset_encoding', offset_encoding, 'string', false) + validate('position_encoding', position_encoding, 'string', false) if not next(text_edits) then return @@ -359,9 +358,9 @@ function M.apply_text_edits(text_edits, bufnr, offset_encoding) -- Convert from LSP style ranges to Neovim style ranges. local start_row = text_edit.range.start.line - local start_col = get_line_byte_from_position(bufnr, text_edit.range.start, offset_encoding) + local start_col = get_line_byte_from_position(bufnr, text_edit.range.start, position_encoding) local end_row = text_edit.range['end'].line - local end_col = get_line_byte_from_position(bufnr, text_edit.range['end'], offset_encoding) + local end_col = get_line_byte_from_position(bufnr, text_edit.range['end'], position_encoding) local text = vim.split(text_edit.newText, '\n', { plain = true }) local max = api.nvim_buf_line_count(bufnr) @@ -430,14 +429,14 @@ end --- ---@param text_document_edit lsp.TextDocumentEdit ---@param index? integer: Optional index of the edit, if from a list of edits (or nil, if not from a list) ----@param offset_encoding? 'utf-8'|'utf-16'|'utf-32' +---@param position_encoding? 'utf-8'|'utf-16'|'utf-32' ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentEdit -function M.apply_text_document_edit(text_document_edit, index, offset_encoding) +function M.apply_text_document_edit(text_document_edit, index, position_encoding) local text_document = text_document_edit.textDocument local bufnr = vim.uri_to_bufnr(text_document.uri) - if offset_encoding == nil then + if position_encoding == nil then vim.notify_once( - 'apply_text_document_edit must be called with valid offset encoding', + 'apply_text_document_edit must be called with valid position encoding', vim.log.levels.WARN ) return @@ -459,7 +458,7 @@ function M.apply_text_document_edit(text_document_edit, index, offset_encoding) return end - M.apply_text_edits(text_document_edit.edits, bufnr, offset_encoding) + M.apply_text_edits(text_document_edit.edits, bufnr, position_encoding) end local function path_components(path) @@ -619,12 +618,12 @@ end --- Applies a `WorkspaceEdit`. --- ---@param workspace_edit lsp.WorkspaceEdit ----@param offset_encoding 'utf-8'|'utf-16'|'utf-32' (required) +---@param position_encoding 'utf-8'|'utf-16'|'utf-32' (required) ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit -function M.apply_workspace_edit(workspace_edit, offset_encoding) - if offset_encoding == nil then +function M.apply_workspace_edit(workspace_edit, position_encoding) + if position_encoding == nil then vim.notify_once( - 'apply_workspace_edit must be called with valid offset encoding', + 'apply_workspace_edit must be called with valid position encoding', vim.log.levels.WARN ) return @@ -641,7 +640,7 @@ function M.apply_workspace_edit(workspace_edit, offset_encoding) elseif change.kind then --- @diagnostic disable-line:undefined-field error(string.format('Unsupported change: %q', vim.inspect(change))) else - M.apply_text_document_edit(change, idx, offset_encoding) + M.apply_text_document_edit(change, idx, position_encoding) end end return @@ -654,7 +653,7 @@ function M.apply_workspace_edit(workspace_edit, offset_encoding) for uri, changes in pairs(all_changes) do local bufnr = vim.uri_to_bufnr(uri) - M.apply_text_edits(changes, bufnr, offset_encoding) + M.apply_text_edits(changes, bufnr, position_encoding) end end @@ -877,15 +876,16 @@ function M.make_floating_popup_options(width, height, opts) return { anchor = anchor, + row = row + (opts.offset_y or 0), col = col + (opts.offset_x or 0), height = height, focusable = opts.focusable, - relative = opts.relative == 'mouse' and 'mouse' or 'cursor', - row = row + (opts.offset_y or 0), + relative = (opts.relative == 'mouse' or opts.relative == 'editor') and opts.relative + or 'cursor', style = 'minimal', width = width, border = opts.border or default_border, - zindex = opts.zindex or 50, + zindex = opts.zindex or (api.nvim_win_get_config(0).zindex or 49) + 1, title = title, title_pos = title_pos, } @@ -904,17 +904,20 @@ end --- Shows document and optionally jumps to the location. --- ---@param location lsp.Location|lsp.LocationLink ----@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? +---@param position_encoding 'utf-8'|'utf-16'|'utf-32'? ---@param opts? vim.lsp.util.show_document.Opts ---@return boolean `true` if succeeded -function M.show_document(location, offset_encoding, opts) +function M.show_document(location, position_encoding, opts) -- location may be Location or LocationLink local uri = location.uri or location.targetUri if uri == nil then return false end - if offset_encoding == nil then - vim.notify_once('show_document must be called with valid offset encoding', vim.log.levels.WARN) + if position_encoding == nil then + vim.notify_once( + 'show_document must be called with valid position encoding', + vim.log.levels.WARN + ) return false end local bufnr = vim.uri_to_bufnr(uri) @@ -946,7 +949,7 @@ function M.show_document(location, offset_encoding, opts) if range then -- Jump to new location (adjusting for encoding of characters) local row = range.start.line - local col = get_line_byte_from_position(bufnr, range.start, offset_encoding) + local col = get_line_byte_from_position(bufnr, range.start, position_encoding) api.nvim_win_set_cursor(win, { row + 1, col }) vim._with({ win = win }, function() -- Open folds under the cursor @@ -961,12 +964,12 @@ end --- ---@deprecated use `vim.lsp.util.show_document` with `{focus=true}` instead ---@param location lsp.Location|lsp.LocationLink ----@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? +---@param position_encoding 'utf-8'|'utf-16'|'utf-32'? ---@param reuse_win boolean? Jump to existing window if buffer is already open. ---@return boolean `true` if the jump succeeded -function M.jump_to_location(location, offset_encoding, reuse_win) +function M.jump_to_location(location, position_encoding, reuse_win) vim.deprecate('vim.lsp.util.jump_to_location', nil, '0.12') - return M.show_document(location, offset_encoding, { reuse_win = reuse_win, focus = true }) + return M.show_document(location, position_encoding, { reuse_win = reuse_win, focus = true }) end --- Previews a location in a floating window @@ -1355,7 +1358,7 @@ end ---@param bufnrs table list of buffers where the preview window will remain visible ---@see autocmd-events local function close_preview_autocmd(events, winnr, bufnrs) - local augroup = api.nvim_create_augroup('preview_window_' .. winnr, { + local augroup = api.nvim_create_augroup('nvim.preview_window_' .. winnr, { clear = true, }) @@ -1430,7 +1433,7 @@ function M._make_floating_popup_size(contents, opts) if vim.tbl_isempty(line_widths) then for _, line in ipairs(contents) do local line_width = vim.fn.strdisplaywidth(line:gsub('%z', '\n')) - height = height + math.ceil(line_width / wrap_at) + height = height + math.max(1, math.ceil(line_width / wrap_at)) end else for i = 1, #contents do @@ -1493,7 +1496,7 @@ end --- @field title_pos? 'left'|'center'|'right' --- --- (default: `'cursor'`) ---- @field relative? 'mouse'|'cursor' +--- @field relative? 'mouse'|'cursor'|'editor' --- --- - "auto": place window based on which side of the cursor has more lines --- - "above": place the window above the cursor unless there are not enough lines @@ -1566,8 +1569,6 @@ function M.open_floating_preview(contents, syntax, opts) if do_stylize then local width = M._make_floating_popup_size(contents, opts) contents = M._normalize_markdown(contents, { width = width }) - vim.bo[floating_bufnr].filetype = 'markdown' - vim.treesitter.start(floating_bufnr) else -- Clean up input: trim empty lines contents = vim.split(table.concat(contents, '\n'), '\n', { trimempty = true }) @@ -1617,9 +1618,22 @@ function M.open_floating_preview(contents, syntax, opts) api.nvim_buf_set_var(bufnr, 'lsp_floating_preview', floating_winnr) end - if do_stylize then - vim.wo[floating_winnr].conceallevel = 2 + local augroup_name = ('nvim.closing_floating_preview_%d'):format(floating_winnr) + local ok = + pcall(api.nvim_get_autocmds, { group = augroup_name, pattern = tostring(floating_winnr) }) + if not ok then + api.nvim_create_autocmd('WinClosed', { + group = api.nvim_create_augroup(augroup_name, {}), + pattern = tostring(floating_winnr), + callback = function() + if api.nvim_buf_is_valid(bufnr) then + vim.b[bufnr].lsp_floating_preview = nil + end + api.nvim_del_augroup_by_name(augroup_name) + end, + }) end + vim.wo[floating_winnr].foldenable = false -- Disable folding. vim.wo[floating_winnr].wrap = opts.wrap -- Soft wrapping. vim.wo[floating_winnr].breakindent = true -- Slightly better list presentation. @@ -1628,11 +1642,17 @@ function M.open_floating_preview(contents, syntax, opts) vim.bo[floating_bufnr].modifiable = false vim.bo[floating_bufnr].bufhidden = 'wipe' + if do_stylize then + vim.wo[floating_winnr].conceallevel = 2 + vim.bo[floating_bufnr].filetype = 'markdown' + vim.treesitter.start(floating_bufnr) + end + return floating_bufnr, floating_winnr end do --[[ References ]] - local reference_ns = api.nvim_create_namespace('vim_lsp_references') + local reference_ns = api.nvim_create_namespace('nvim.lsp.references') --- Removes document highlights from a buffer. --- @@ -1645,18 +1665,18 @@ do --[[ References ]] --- ---@param bufnr integer Buffer id ---@param references lsp.DocumentHighlight[] objects to highlight - ---@param offset_encoding 'utf-8'|'utf-16'|'utf-32' + ---@param position_encoding 'utf-8'|'utf-16'|'utf-32' ---@see https://microsoft.github.io/language-server-protocol/specification/#textDocumentContentChangeEvent - function M.buf_highlight_references(bufnr, references, offset_encoding) + function M.buf_highlight_references(bufnr, references, position_encoding) validate('bufnr', bufnr, 'number', true) - validate('offset_encoding', offset_encoding, 'string', false) + validate('position_encoding', position_encoding, 'string', false) for _, reference in ipairs(references) do local range = reference.range local start_line = range.start.line local end_line = range['end'].line - local start_idx = get_line_byte_from_position(bufnr, range.start, offset_encoding) - local end_idx = get_line_byte_from_position(bufnr, range['end'], offset_encoding) + local start_idx = get_line_byte_from_position(bufnr, range.start, position_encoding) + local end_idx = get_line_byte_from_position(bufnr, range['end'], position_encoding) local document_highlight_kind = { [protocol.DocumentHighlightKind.Text] = 'LspReferenceText', @@ -1690,16 +1710,16 @@ end) --- |setloclist()|. --- ---@param locations lsp.Location[]|lsp.LocationLink[] ----@param offset_encoding? 'utf-8'|'utf-16'|'utf-32' +---@param position_encoding? 'utf-8'|'utf-16'|'utf-32' --- default to first client of buffer ---@return vim.quickfix.entry[] # See |setqflist()| for the format -function M.locations_to_items(locations, offset_encoding) - if offset_encoding == nil then +function M.locations_to_items(locations, position_encoding) + if position_encoding == nil then vim.notify_once( - 'locations_to_items must be called with valid offset encoding', + 'locations_to_items must be called with valid position encoding', vim.log.levels.WARN ) - offset_encoding = vim.lsp.get_clients({ bufnr = 0 })[1].offset_encoding + position_encoding = vim.lsp.get_clients({ bufnr = 0 })[1].offset_encoding end local items = {} --- @type vim.quickfix.entry[] @@ -1736,8 +1756,8 @@ function M.locations_to_items(locations, offset_encoding) local end_row = end_pos.line local line = lines[row] or '' local end_line = lines[end_row] or '' - local col = vim.str_byteindex(line, offset_encoding, pos.character, false) - local end_col = vim.str_byteindex(end_line, offset_encoding, end_pos.character, false) + local col = vim.str_byteindex(line, position_encoding, pos.character, false) + local end_col = vim.str_byteindex(end_line, position_encoding, end_pos.character, false) items[#items + 1] = { filename = filename, @@ -1848,19 +1868,18 @@ function M.try_trim_markdown_code_blocks(lines) end ---@param window integer?: window handle or 0 for current, defaults to current ----@param offset_encoding? 'utf-8'|'utf-16'|'utf-32'? defaults to `offset_encoding` of first client of buffer of `window` -local function make_position_param(window, offset_encoding) +---@param position_encoding 'utf-8'|'utf-16'|'utf-32' +local function make_position_param(window, position_encoding) window = window or 0 local buf = api.nvim_win_get_buf(window) local row, col = unpack(api.nvim_win_get_cursor(window)) - offset_encoding = offset_encoding or M._get_offset_encoding(buf) row = row - 1 local line = api.nvim_buf_get_lines(buf, row, row + 1, true)[1] if not line then return { line = 0, character = 0 } end - col = vim.str_utfindex(line, offset_encoding, col, false) + col = vim.str_utfindex(line, position_encoding, col, false) return { line = row, character = col } end @@ -1868,20 +1887,28 @@ end --- Creates a `TextDocumentPositionParams` object for the current buffer and cursor position. --- ---@param window integer?: window handle or 0 for current, defaults to current ----@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? defaults to `offset_encoding` of first client of buffer of `window` +---@param position_encoding 'utf-8'|'utf-16'|'utf-32' ---@return lsp.TextDocumentPositionParams ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocumentPositionParams -function M.make_position_params(window, offset_encoding) +function M.make_position_params(window, position_encoding) window = window or 0 local buf = api.nvim_win_get_buf(window) - offset_encoding = offset_encoding or M._get_offset_encoding(buf) + if position_encoding == nil then + vim.notify_once( + 'position_encoding param is required in vim.lsp.util.make_position_params. Defaulting to position encoding of the first client.', + vim.log.levels.WARN + ) + --- @diagnostic disable-next-line: deprecated + position_encoding = M._get_offset_encoding(buf) + end return { textDocument = M.make_text_document_params(buf), - position = make_position_param(window, offset_encoding), + position = make_position_param(window, position_encoding), } end --- Utility function for getting the encoding of the first LSP client on the given buffer. +---@deprecated ---@param bufnr integer buffer handle or 0 for current, defaults to current ---@return string encoding first client if there is one, nil otherwise function M._get_offset_encoding(bufnr) @@ -1904,7 +1931,7 @@ function M._get_offset_encoding(bufnr) offset_encoding = this_offset_encoding elseif offset_encoding ~= this_offset_encoding then vim.notify_once( - 'warning: multiple different client offset_encodings detected for buffer, this is not supported yet', + 'warning: multiple different client offset_encodings detected for buffer, vim.lsp.util._get_offset_encoding() uses the offset_encoding from the first client', vim.log.levels.WARN ) end @@ -1919,13 +1946,19 @@ end --- `textDocument/rangeFormatting`. --- ---@param window integer? window handle or 0 for current, defaults to current ----@param offset_encoding "utf-8"|"utf-16"|"utf-32"? defaults to `offset_encoding` of first client of buffer of `window` ----@return table { textDocument = { uri = `current_file_uri` }, range = { start = ----`current_position`, end = `current_position` } } -function M.make_range_params(window, offset_encoding) +---@param position_encoding "utf-8"|"utf-16"|"utf-32" +---@return { textDocument: { uri: lsp.DocumentUri }, range: lsp.Range } +function M.make_range_params(window, position_encoding) local buf = api.nvim_win_get_buf(window or 0) - offset_encoding = offset_encoding or M._get_offset_encoding(buf) - local position = make_position_param(window, offset_encoding) + if position_encoding == nil then + vim.notify_once( + 'position_encoding param is required in vim.lsp.util.make_range_params. Defaulting to position encoding of the first client.', + vim.log.levels.WARN + ) + --- @diagnostic disable-next-line: deprecated + position_encoding = M._get_offset_encoding(buf) + end + local position = make_position_param(window, position_encoding) return { textDocument = M.make_text_document_params(buf), range = { start = position, ['end'] = position }, @@ -1940,15 +1973,21 @@ end ---@param end_pos [integer,integer]? {row,col} mark-indexed position. --- Defaults to the end of the last visual selection. ---@param bufnr integer? buffer handle or 0 for current, defaults to current ----@param offset_encoding 'utf-8'|'utf-16'|'utf-32'? defaults to `offset_encoding` of first client of `bufnr` ----@return table { textDocument = { uri = `current_file_uri` }, range = { start = ----`start_position`, end = `end_position` } } -function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding) +---@param position_encoding 'utf-8'|'utf-16'|'utf-32' +---@return { textDocument: { uri: lsp.DocumentUri }, range: lsp.Range } +function M.make_given_range_params(start_pos, end_pos, bufnr, position_encoding) validate('start_pos', start_pos, 'table', true) validate('end_pos', end_pos, 'table', true) - validate('offset_encoding', offset_encoding, 'string', true) - bufnr = bufnr or api.nvim_get_current_buf() - offset_encoding = offset_encoding or M._get_offset_encoding(bufnr) + validate('position_encoding', position_encoding, 'string', true) + bufnr = vim._resolve_bufnr(bufnr) + if position_encoding == nil then + vim.notify_once( + 'position_encoding param is required in vim.lsp.util.make_given_range_params. Defaulting to position encoding of the first client.', + vim.log.levels.WARN + ) + --- @diagnostic disable-next-line: deprecated + position_encoding = M._get_offset_encoding(bufnr) + end --- @type [integer, integer] local A = { unpack(start_pos or api.nvim_buf_get_mark(bufnr, '<')) } --- @type [integer, integer] @@ -1956,12 +1995,12 @@ function M.make_given_range_params(start_pos, end_pos, bufnr, offset_encoding) -- convert to 0-index A[1] = A[1] - 1 B[1] = B[1] - 1 - -- account for offset_encoding. + -- account for position_encoding. if A[2] > 0 then - A[2] = M.character_offset(bufnr, A[1], A[2], offset_encoding) + A[2] = M.character_offset(bufnr, A[1], A[2], position_encoding) end if B[2] > 0 then - B[2] = M.character_offset(bufnr, B[1], B[2], offset_encoding) + B[2] = M.character_offset(bufnr, B[1], B[2], position_encoding) end -- we need to offset the end character position otherwise we loose the last -- character of the selection, as LSP end position is exclusive @@ -2068,9 +2107,9 @@ end ---@param bufnr integer ---@param start_line integer ---@param end_line integer ----@param offset_encoding 'utf-8'|'utf-16'|'utf-32' +---@param position_encoding 'utf-8'|'utf-16'|'utf-32' ---@return lsp.Range -local function make_line_range_params(bufnr, start_line, end_line, offset_encoding) +local function make_line_range_params(bufnr, start_line, end_line, position_encoding) local last_line = api.nvim_buf_line_count(bufnr) - 1 ---@type lsp.Position @@ -2079,7 +2118,12 @@ local function make_line_range_params(bufnr, start_line, end_line, offset_encodi if end_line == last_line and not vim.bo[bufnr].endofline then end_pos = { line = end_line, - character = M.character_offset(bufnr, end_line, #get_line(bufnr, end_line), offset_encoding), + character = M.character_offset( + bufnr, + end_line, + #get_line(bufnr, end_line), + position_encoding + ), } else end_pos = { line = end_line + 1, character = 0 } @@ -2103,10 +2147,7 @@ end ---@param opts? vim.lsp.util._refresh.Opts Options table function M._refresh(method, opts) opts = opts or {} - local bufnr = opts.bufnr - if bufnr == nil or bufnr == 0 then - bufnr = api.nvim_get_current_buf() - end + local bufnr = vim._resolve_bufnr(opts.bufnr) local clients = vim.lsp.get_clients({ bufnr = bufnr, method = method, id = opts.client_id }) @@ -2122,7 +2163,12 @@ function M._refresh(method, opts) local first = vim.fn.line('w0', window) local last = vim.fn.line('w$', window) for _, client in ipairs(clients) do - client.request(method, { + for rid, req in pairs(client.requests) do + if req.method == method and req.type == 'pending' and req.bufnr == bufnr then + client:cancel_request(rid) + end + end + client:request(method, { textDocument = textDocument, range = make_line_range_params(bufnr, first - 1, last - 1, client.offset_encoding), }, nil, bufnr) @@ -2131,7 +2177,7 @@ function M._refresh(method, opts) end else for _, client in ipairs(clients) do - client.request(method, { + client:request(method, { textDocument = textDocument, range = make_line_range_params( bufnr, diff --git a/runtime/lua/vim/provider/health.lua b/runtime/lua/vim/provider/health.lua index 5ecb00f49b..fa01951b02 100644 --- a/runtime/lua/vim/provider/health.lua +++ b/runtime/lua/vim/provider/health.lua @@ -10,22 +10,20 @@ end -- Attempts to construct a shell command from an args list. -- Only for display, to help users debug a failed command. +--- @param cmd string|string[] local function shellify(cmd) if type(cmd) ~= 'table' then return cmd end - local escaped = {} + local escaped = {} --- @type string[] for i, v in ipairs(cmd) do - if v:match('[^A-Za-z_/.-]') then - escaped[i] = vim.fn.shellescape(v) - else - escaped[i] = v - end + escaped[i] = v:match('[^A-Za-z_/.-]') and vim.fn.shellescape(v) or v end return table.concat(escaped, ' ') end -- Handler for s:system() function. +--- @param self {output: string, stderr: string, add_stderr_to_output: boolean} local function system_handler(self, _, data, event) if event == 'stderr' then if self.add_stderr_to_output then @@ -38,7 +36,7 @@ local function system_handler(self, _, data, event) end end ---- @param cmd table List of command arguments to execute +--- @param cmd string|string[] List of command arguments to execute --- @param args? table Optional arguments: --- - stdin (string): Data to write to the job's stdin --- - stderr (boolean): Append stderr to stdout @@ -47,8 +45,8 @@ end local function system(cmd, args) args = args or {} local stdin = args.stdin or '' - local stderr = vim.F.if_nil(args.stderr, false) - local ignore_error = vim.F.if_nil(args.ignore_error, false) + local stderr = args.stderr or false + local ignore_error = args.ignore_error or false local shell_error_code = 0 local opts = { @@ -530,13 +528,14 @@ local function version_info(python) if rc ~= 0 or nvim_version == '' then nvim_version = 'unable to find pynvim module version' local base = vim.fs.basename(nvim_path) - local metas = vim.fn.glob(base .. '-*/METADATA', true, 1) - vim.list_extend(metas, vim.fn.glob(base .. '-*/PKG-INFO', true, 1)) - vim.list_extend(metas, vim.fn.glob(base .. '.egg-info/PKG-INFO', true, 1)) + local metas = vim.fn.glob(base .. '-*/METADATA', true, true) + vim.list_extend(metas, vim.fn.glob(base .. '-*/PKG-INFO', true, true)) + vim.list_extend(metas, vim.fn.glob(base .. '.egg-info/PKG-INFO', true, true)) metas = table.sort(metas, compare) if metas and next(metas) ~= nil then for line in io.lines(metas[1]) do + --- @cast line string local version = line:match('^Version: (%S+)') if version then nvim_version = version @@ -762,6 +761,7 @@ local function python() -- subshells launched from Nvim. local bin_dir = iswin and 'Scripts' or 'bin' local venv_bins = vim.fn.glob(string.format('%s/%s/python*', virtual_env, bin_dir), true, true) + --- @param v string venv_bins = vim.tbl_filter(function(v) -- XXX: Remove irrelevant executables found in bin/. return not v:match('python.*%-config') @@ -809,6 +809,7 @@ local function python() msg, bin_dir, table.concat( + --- @param v string vim.tbl_map(function(v) return vim.fs.basename(v) end, venv_bins), @@ -817,12 +818,15 @@ local function python() ) end local conj = '\nBut ' + local msgs = {} --- @type string[] for _, err in ipairs(errors) do - msg = msg .. conj .. err + msgs[#msgs + 1] = msg + msgs[#msgs + 1] = conj + msgs[#msgs + 1] = err conj = '\nAnd ' end - msg = msg .. '\nSo invoking Python may lead to unexpected results.' - health.warn(msg, vim.tbl_keys(hints)) + msgs[#msgs + 1] = '\nSo invoking Python may lead to unexpected results.' + health.warn(table.concat(msgs), vim.tbl_keys(hints)) else health.info(msg) health.info( diff --git a/runtime/lua/vim/re.lua b/runtime/lua/vim/re.lua index 114f74eb80..e0a36703e3 100644 --- a/runtime/lua/vim/re.lua +++ b/runtime/lua/vim/re.lua @@ -1,3 +1,4 @@ +--- @diagnostic disable: no-unknown -- -- Copyright 2007-2023, Lua.org & PUC-Rio (see 'lpeg.html' for license) -- written by Roberto Ierusalimschy diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua index 4f2373b182..f19533f474 100644 --- a/runtime/lua/vim/shared.lua +++ b/runtime/lua/vim/shared.lua @@ -7,8 +7,7 @@ -- so this wouldn't be a separate case to consider) ---@nodoc ----@diagnostic disable-next-line: lowercase-global -vim = vim or {} +_G.vim = _G.vim or {} ---@generic T ---@param orig T @@ -737,6 +736,51 @@ function vim.list_slice(list, start, finish) return new_list end +--- Efficiently insert items into the middle of a list. +--- +--- Calling table.insert() in a loop will re-index the tail of the table on +--- every iteration, instead this function will re-index the table exactly +--- once. +--- +--- Based on https://stackoverflow.com/questions/12394841/safely-remove-items-from-an-array-table-while-iterating/53038524#53038524 +--- +---@param t any[] +---@param first integer +---@param last integer +---@param v any +function vim._list_insert(t, first, last, v) + local n = #t + + -- Shift table forward + for i = n - first, 0, -1 do + t[last + 1 + i] = t[first + i] + end + + -- Fill in new values + for i = first, last do + t[i] = v + end +end + +--- Efficiently remove items from middle of a list. +--- +--- Calling table.remove() in a loop will re-index the tail of the table on +--- every iteration, instead this function will re-index the table exactly +--- once. +--- +--- Based on https://stackoverflow.com/questions/12394841/safely-remove-items-from-an-array-table-while-iterating/53038524#53038524 +--- +---@param t any[] +---@param first integer +---@param last integer +function vim._list_remove(t, first, last) + local n = #t + for i = 0, n - first do + t[first + i] = t[last + 1 + i] + t[last + 1 + i] = nil + end +end + --- Trim whitespace (Lua pattern "%s") from both sides of a string. --- ---@see |lua-patterns| @@ -914,7 +958,7 @@ do --- function vim.startswith(s, prefix) --- vim.validate('s', s, 'string') --- vim.validate('prefix', prefix, 'string') - --- ... + --- -- ... --- end --- ``` --- @@ -934,7 +978,7 @@ do --- age={age, 'number'}, --- hobbies={hobbies, 'table'}, --- } - --- ... + --- -- ... --- end --- ``` --- @@ -968,7 +1012,7 @@ do --- best performance. --- --- @param name string Argument name - --- @param value string Argument value + --- @param value any Argument value --- @param validator vim.validate.Validator --- - (`string|string[]`): Any value that can be returned from |lua-type()| in addition to --- `'callable'`: `'boolean'`, `'callable'`, `'function'`, `'nil'`, `'number'`, `'string'`, `'table'`, @@ -1354,4 +1398,24 @@ function vim._with(context, f) return vim._with_c(context, callback) end +--- @param bufnr? integer +--- @return integer +function vim._resolve_bufnr(bufnr) + if bufnr == nil or bufnr == 0 then + return vim.api.nvim_get_current_buf() + end + vim.validate('bufnr', bufnr, 'number') + return bufnr +end + +--- @generic T +--- @param x elem_or_list<T>? +--- @return T[] +function vim._ensure_list(x) + if type(x) == 'table' then + return x + end + return { x } +end + return vim diff --git a/runtime/lua/vim/snippet.lua b/runtime/lua/vim/snippet.lua index af7e3c6d33..bfd439181e 100644 --- a/runtime/lua/vim/snippet.lua +++ b/runtime/lua/vim/snippet.lua @@ -1,6 +1,6 @@ local G = vim.lsp._snippet_grammar -local snippet_group = vim.api.nvim_create_augroup('vim/snippet', {}) -local snippet_ns = vim.api.nvim_create_namespace('vim/snippet') +local snippet_group = vim.api.nvim_create_augroup('nvim.snippet', {}) +local snippet_ns = vim.api.nvim_create_namespace('nvim.snippet') local hl_group = 'SnippetTabstop' local jump_forward_key = '<tab>' local jump_backward_key = '<s-tab>' diff --git a/runtime/lua/vim/text.lua b/runtime/lua/vim/text.lua index d45c8021c6..f910ab3a1d 100644 --- a/runtime/lua/vim/text.lua +++ b/runtime/lua/vim/text.lua @@ -2,6 +2,18 @@ local M = {} +local alphabet = '0123456789ABCDEF' +local atoi = {} ---@type table<string, integer> +local itoa = {} ---@type table<integer, string> +do + for i = 1, #alphabet do + local char = alphabet:sub(i, i) + itoa[i - 1] = char + atoi[char] = i - 1 + atoi[char:lower()] = i - 1 + end +end + --- Hex encode a string. --- --- @param str string String to encode @@ -9,7 +21,9 @@ local M = {} function M.hexencode(str) local enc = {} ---@type string[] for i = 1, #str do - enc[i] = string.format('%02X', str:byte(i, i + 1)) + local byte = str:byte(i) + enc[2 * i - 1] = itoa[math.floor(byte / 16)] + enc[2 * i] = itoa[byte % 16] end return table.concat(enc) end @@ -26,8 +40,12 @@ function M.hexdecode(enc) local str = {} ---@type string[] for i = 1, #enc, 2 do - local n = assert(tonumber(enc:sub(i, i + 1), 16)) - str[#str + 1] = string.char(n) + local u = atoi[enc:sub(i, i)] + local l = atoi[enc:sub(i + 1, i + 1)] + if not u or not l then + return nil, 'string must contain only hex characters' + end + str[(i + 1) / 2] = string.char(u * 16 + l) end return table.concat(str), nil end diff --git a/runtime/lua/vim/treesitter.lua b/runtime/lua/vim/treesitter.lua index dca89f413c..10638e10d8 100644 --- a/runtime/lua/vim/treesitter.lua +++ b/runtime/lua/vim/treesitter.lua @@ -32,9 +32,7 @@ M.minimum_language_version = vim._ts_get_minimum_language_version() --- ---@return vim.treesitter.LanguageTree object to use for parsing function M._create_parser(bufnr, lang, opts) - if bufnr == 0 then - bufnr = vim.api.nvim_get_current_buf() - end + bufnr = vim._resolve_bufnr(bufnr) vim.fn.bufload(bufnr) @@ -63,8 +61,6 @@ function M._create_parser(bufnr, lang, opts) { on_bytes = bytes_cb, on_detach = detach_cb, on_reload = reload_cb, preview = true } ) - self:parse() - return self end @@ -90,9 +86,7 @@ function M.get_parser(bufnr, lang, opts) opts = opts or {} local should_error = opts.error == nil or opts.error - if bufnr == nil or bufnr == 0 then - bufnr = api.nvim_get_current_buf() - end + bufnr = vim._resolve_bufnr(bufnr) if not valid_lang(lang) then lang = M.language.get_lang(vim.bo[bufnr].filetype) @@ -155,7 +149,7 @@ end --- Returns the node's range or an unpacked range table --- ----@param node_or_range (TSNode | table) Node or table of positions +---@param node_or_range TSNode|Range4 Node or table of positions --- ---@return integer start_row ---@return integer start_col @@ -163,7 +157,8 @@ end ---@return integer end_col function M.get_node_range(node_or_range) if type(node_or_range) == 'table' then - return unpack(node_or_range) + --- @cast node_or_range -TSNode LuaLS bug + return M._range.unpack4(node_or_range) else return node_or_range:range(false) end @@ -244,23 +239,24 @@ function M.node_contains(node, range) -- allow a table so nodes can be mocked vim.validate('node', node, { 'userdata', 'table' }) vim.validate('range', range, M._range.validate, 'integer list with 4 or 6 elements') - return M._range.contains({ node:range() }, range) + --- @diagnostic disable-next-line: missing-fields LuaLS bug + local nrange = { node:range() } --- @type Range4 + return M._range.contains(nrange, range) end --- Returns a list of highlight captures at the given position --- ---- Each capture is represented by a table containing the capture name as a string as ---- well as a table of metadata (`priority`, `conceal`, ...; empty if none are defined). +--- Each capture is represented by a table containing the capture name as a string, the capture's +--- language, a table of metadata (`priority`, `conceal`, ...; empty if none are defined), and the +--- id of the capture. --- ---@param bufnr integer Buffer number (0 for current buffer) ---@param row integer Position row ---@param col integer Position column --- ----@return {capture: string, lang: string, metadata: vim.treesitter.query.TSMetadata}[] +---@return {capture: string, lang: string, metadata: vim.treesitter.query.TSMetadata, id: integer}[] function M.get_captures_at_pos(bufnr, row, col) - if bufnr == 0 then - bufnr = api.nvim_get_current_buf() - end + bufnr = vim._resolve_bufnr(bufnr) local buf_highlighter = M.highlighter.active[bufnr] if not buf_highlighter then @@ -291,12 +287,15 @@ function M.get_captures_at_pos(bufnr, row, col) local iter = q:query():iter_captures(root, buf_highlighter.bufnr, row, row + 1) - for capture, node, metadata in iter do + for id, node, metadata in iter do if M.is_in_node_range(node, row, col) then ---@diagnostic disable-next-line: invisible - local c = q._query.captures[capture] -- name of the capture in the query - if c ~= nil then - table.insert(matches, { capture = c, metadata = metadata, lang = tree:lang() }) + local capture = q._query.captures[id] -- name of the capture in the query + if capture ~= nil then + table.insert( + matches, + { capture = capture, metadata = metadata, lang = tree:lang(), id = id } + ) end end end @@ -361,11 +360,7 @@ end function M.get_node(opts) opts = opts or {} - local bufnr = opts.bufnr - - if not bufnr or bufnr == 0 then - bufnr = api.nvim_get_current_buf() - end + local bufnr = vim._resolve_bufnr(opts.bufnr) local row, col --- @type integer, integer if opts.pos then @@ -403,6 +398,8 @@ end --- Note: By default, disables regex syntax highlighting, which may be required for some plugins. --- In this case, add `vim.bo.syntax = 'on'` after the call to `start`. --- +--- Note: By default, the highlighter parses code asynchronously, using a segment time of 3ms. +--- --- Example: --- --- ```lua @@ -414,10 +411,10 @@ end --- }) --- ``` --- ----@param bufnr (integer|nil) Buffer to be highlighted (default: current buffer) ----@param lang (string|nil) Language of the parser (default: from buffer filetype) +---@param bufnr integer? Buffer to be highlighted (default: current buffer) +---@param lang string? Language of the parser (default: from buffer filetype) function M.start(bufnr, lang) - bufnr = bufnr or api.nvim_get_current_buf() + bufnr = vim._resolve_bufnr(bufnr) local parser = assert(M.get_parser(bufnr, lang, { error = false })) M.highlighter.new(parser) end @@ -426,7 +423,7 @@ end --- ---@param bufnr (integer|nil) Buffer to stop highlighting (default: current buffer) function M.stop(bufnr) - bufnr = (bufnr and bufnr ~= 0) and bufnr or api.nvim_get_current_buf() + bufnr = vim._resolve_bufnr(bufnr) if M.highlighter.active[bufnr] then M.highlighter.active[bufnr]:destroy() diff --git a/runtime/lua/vim/treesitter/_fold.lua b/runtime/lua/vim/treesitter/_fold.lua index 7237d2e7d4..38318347a7 100644 --- a/runtime/lua/vim/treesitter/_fold.lua +++ b/runtime/lua/vim/treesitter/_fold.lua @@ -19,76 +19,36 @@ local api = vim.api ---The range on which to evaluate foldexpr. ---When in insert mode, the evaluation is deferred to InsertLeave. ---@field foldupdate_range? Range2 +--- +---The treesitter parser associated with this buffer. +---@field parser? vim.treesitter.LanguageTree local FoldInfo = {} FoldInfo.__index = FoldInfo ---@private -function FoldInfo.new() +---@param bufnr integer +function FoldInfo.new(bufnr) return setmetatable({ levels0 = {}, levels = {}, + parser = ts.get_parser(bufnr, nil, { error = false }), }, FoldInfo) end ---- Efficiently remove items from middle of a list a list. ---- ---- Calling table.remove() in a loop will re-index the tail of the table on ---- every iteration, instead this function will re-index the table exactly ---- once. ---- ---- Based on https://stackoverflow.com/questions/12394841/safely-remove-items-from-an-array-table-while-iterating/53038524#53038524 ---- ----@param t any[] ----@param first integer ----@param last integer -local function list_remove(t, first, last) - local n = #t - for i = 0, n - first do - t[first + i] = t[last + 1 + i] - t[last + 1 + i] = nil - end -end - ---@package ---@param srow integer ---@param erow integer 0-indexed, exclusive function FoldInfo:remove_range(srow, erow) - list_remove(self.levels, srow + 1, erow) - list_remove(self.levels0, srow + 1, erow) -end - ---- Efficiently insert items into the middle of a list. ---- ---- Calling table.insert() in a loop will re-index the tail of the table on ---- every iteration, instead this function will re-index the table exactly ---- once. ---- ---- Based on https://stackoverflow.com/questions/12394841/safely-remove-items-from-an-array-table-while-iterating/53038524#53038524 ---- ----@param t any[] ----@param first integer ----@param last integer ----@param v any -local function list_insert(t, first, last, v) - local n = #t - - -- Shift table forward - for i = n - first, 0, -1 do - t[last + 1 + i] = t[first + i] - end - - -- Fill in new values - for i = first, last do - t[i] = v - end + vim._list_remove(self.levels, srow + 1, erow) + vim._list_remove(self.levels0, srow + 1, erow) end ---@package ---@param srow integer ---@param erow integer 0-indexed, exclusive function FoldInfo:add_range(srow, erow) - list_insert(self.levels, srow + 1, erow, -1) - list_insert(self.levels0, srow + 1, erow, -1) + vim._list_insert(self.levels, srow + 1, erow, -1) + vim._list_insert(self.levels0, srow + 1, erow, -1) end ---@param range Range2 @@ -109,111 +69,122 @@ end ---@param info TS.FoldInfo ---@param srow integer? ---@param erow integer? 0-indexed, exclusive ----@param parse_injections? boolean -local function compute_folds_levels(bufnr, info, srow, erow, parse_injections) +---@param callback function? +local function compute_folds_levels(bufnr, info, srow, erow, callback) srow = srow or 0 erow = erow or api.nvim_buf_line_count(bufnr) - local parser = assert(ts.get_parser(bufnr, nil, { error = false })) - - parser:parse(parse_injections and { srow, erow } or nil) - - local enter_counts = {} ---@type table<integer, integer> - local leave_counts = {} ---@type table<integer, integer> - local prev_start = -1 - local prev_stop = -1 + local parser = info.parser + if not parser then + return + end - parser:for_each_tree(function(tree, ltree) - local query = ts.query.get(ltree:lang(), 'folds') - if not query then + parser:parse(nil, function(_, trees) + if not trees then return end - -- Collect folds starting from srow - 1, because we should first subtract the folds that end at - -- srow - 1 from the level of srow - 1 to get accurate level of srow. - for _, match, metadata in query:iter_matches(tree:root(), bufnr, math.max(srow - 1, 0), erow) do - for id, nodes in pairs(match) do - if query.captures[id] == 'fold' then - local range = ts.get_range(nodes[1], bufnr, metadata[id]) - local start, _, stop, stop_col = Range.unpack4(range) - - if #nodes > 1 then - -- assumes nodes are ordered by range - local end_range = ts.get_range(nodes[#nodes], bufnr, metadata[id]) - local _, _, end_stop, end_stop_col = Range.unpack4(end_range) - stop = end_stop - stop_col = end_stop_col - end + local enter_counts = {} ---@type table<integer, integer> + local leave_counts = {} ---@type table<integer, integer> + local prev_start = -1 + local prev_stop = -1 - if stop_col == 0 then - stop = stop - 1 - end + parser:for_each_tree(function(tree, ltree) + local query = ts.query.get(ltree:lang(), 'folds') + if not query then + return + end - local fold_length = stop - start + 1 - - -- Fold only multiline nodes that are not exactly the same as previously met folds - -- Checking against just the previously found fold is sufficient if nodes - -- are returned in preorder or postorder when traversing tree - if - fold_length > vim.wo.foldminlines and not (start == prev_start and stop == prev_stop) - then - enter_counts[start + 1] = (enter_counts[start + 1] or 0) + 1 - leave_counts[stop + 1] = (leave_counts[stop + 1] or 0) + 1 - prev_start = start - prev_stop = stop + -- Collect folds starting from srow - 1, because we should first subtract the folds that end at + -- srow - 1 from the level of srow - 1 to get accurate level of srow. + for _, match, metadata in query:iter_matches(tree:root(), bufnr, math.max(srow - 1, 0), erow) do + for id, nodes in pairs(match) do + if query.captures[id] == 'fold' then + local range = ts.get_range(nodes[1], bufnr, metadata[id]) + local start, _, stop, stop_col = Range.unpack4(range) + + if #nodes > 1 then + -- assumes nodes are ordered by range + local end_range = ts.get_range(nodes[#nodes], bufnr, metadata[id]) + local _, _, end_stop, end_stop_col = Range.unpack4(end_range) + stop = end_stop + stop_col = end_stop_col + end + + if stop_col == 0 then + stop = stop - 1 + end + + local fold_length = stop - start + 1 + + -- Fold only multiline nodes that are not exactly the same as previously met folds + -- Checking against just the previously found fold is sufficient if nodes + -- are returned in preorder or postorder when traversing tree + if + fold_length > vim.wo.foldminlines and not (start == prev_start and stop == prev_stop) + then + enter_counts[start + 1] = (enter_counts[start + 1] or 0) + 1 + leave_counts[stop + 1] = (leave_counts[stop + 1] or 0) + 1 + prev_start = start + prev_stop = stop + end end end end - end - end) + end) - local nestmax = vim.wo.foldnestmax - local level0_prev = info.levels0[srow] or 0 - local leave_prev = leave_counts[srow] or 0 - - -- We now have the list of fold opening and closing, fill the gaps and mark where fold start - for lnum = srow + 1, erow do - local enter_line = enter_counts[lnum] or 0 - local leave_line = leave_counts[lnum] or 0 - local level0 = level0_prev - leave_prev + enter_line - - -- Determine if it's the start/end of a fold - -- NB: vim's fold-expr interface does not have a mechanism to indicate that - -- two (or more) folds start at this line, so it cannot distinguish between - -- ( \n ( \n )) \n (( \n ) \n ) - -- versus - -- ( \n ( \n ) \n ( \n ) \n ) - -- Both are represented by ['>1', '>2', '2', '>2', '2', '1'], and - -- vim interprets as the second case. - -- If it did have such a mechanism, (clamped - clamped_prev) - -- would be the correct number of starts to pass on. - local adjusted = level0 ---@type integer - local prefix = '' - if enter_line > 0 then - prefix = '>' - if leave_line > 0 then - -- If this line ends a fold f1 and starts a fold f2, then move f1's end to the previous line - -- so that f2 gets the correct level on this line. This may reduce the size of f1 below - -- foldminlines, but we don't handle it for simplicity. - adjusted = level0 - leave_line - leave_line = 0 + local nestmax = vim.wo.foldnestmax + local level0_prev = info.levels0[srow] or 0 + local leave_prev = leave_counts[srow] or 0 + + -- We now have the list of fold opening and closing, fill the gaps and mark where fold start + for lnum = srow + 1, erow do + local enter_line = enter_counts[lnum] or 0 + local leave_line = leave_counts[lnum] or 0 + local level0 = level0_prev - leave_prev + enter_line + + -- Determine if it's the start/end of a fold + -- NB: vim's fold-expr interface does not have a mechanism to indicate that + -- two (or more) folds start at this line, so it cannot distinguish between + -- ( \n ( \n )) \n (( \n ) \n ) + -- versus + -- ( \n ( \n ) \n ( \n ) \n ) + -- Both are represented by ['>1', '>2', '2', '>2', '2', '1'], and + -- vim interprets as the second case. + -- If it did have such a mechanism, (clamped - clamped_prev) + -- would be the correct number of starts to pass on. + local adjusted = level0 ---@type integer + local prefix = '' + if enter_line > 0 then + prefix = '>' + if leave_line > 0 then + -- If this line ends a fold f1 and starts a fold f2, then move f1's end to the previous line + -- so that f2 gets the correct level on this line. This may reduce the size of f1 below + -- foldminlines, but we don't handle it for simplicity. + adjusted = level0 - leave_line + leave_line = 0 + end end - end - -- Clamp at foldnestmax. - local clamped = adjusted - if adjusted > nestmax then - prefix = '' - clamped = nestmax - end + -- Clamp at foldnestmax. + local clamped = adjusted + if adjusted > nestmax then + prefix = '' + clamped = nestmax + end - -- Record the "real" level, so that it can be used as "base" of later compute_folds_levels(). - info.levels0[lnum] = adjusted - info.levels[lnum] = prefix .. tostring(clamped) + -- Record the "real" level, so that it can be used as "base" of later compute_folds_levels(). + info.levels0[lnum] = adjusted + info.levels[lnum] = prefix .. tostring(clamped) - leave_prev = leave_line - level0_prev = adjusted - end + leave_prev = leave_line + level0_prev = adjusted + end + + if callback then + callback() + end + end) end local M = {} @@ -221,7 +192,7 @@ local M = {} ---@type table<integer,TS.FoldInfo> local foldinfos = {} -local group = api.nvim_create_augroup('treesitter/fold', {}) +local group = api.nvim_create_augroup('nvim.treesitter.fold', {}) --- Update the folds in the windows that contain the buffer and use expr foldmethod (assuming that --- the user doesn't use different foldexpr for the same buffer). @@ -298,12 +269,19 @@ local function schedule_if_loaded(bufnr, fn) end ---@param bufnr integer ----@param foldinfo TS.FoldInfo ---@param tree_changes Range4[] -local function on_changedtree(bufnr, foldinfo, tree_changes) +local function on_changedtree(bufnr, tree_changes) schedule_if_loaded(bufnr, function() + -- Buffer reload clears `foldinfos[bufnr]`, which may still be nil when callback is invoked. + local foldinfo = foldinfos[bufnr] + if not foldinfo then + return + end + local srow_upd, erow_upd ---@type integer?, integer? local max_erow = api.nvim_buf_line_count(bufnr) + -- TODO(ribru17): Replace this with a proper .all() awaiter once #19624 is resolved + local iterations = 0 for _, change in ipairs(tree_changes) do local srow, _, erow, ecol = Range.unpack4(change) -- If a parser doesn't have any ranges explicitly set, treesitter will @@ -317,24 +295,31 @@ local function on_changedtree(bufnr, foldinfo, tree_changes) end -- Start from `srow - foldminlines`, because this edit may have shrunken the fold below limit. srow = math.max(srow - vim.wo.foldminlines, 0) - compute_folds_levels(bufnr, foldinfo, srow, erow) srow_upd = srow_upd and math.min(srow_upd, srow) or srow erow_upd = erow_upd and math.max(erow_upd, erow) or erow - end - if #tree_changes > 0 then - foldinfo:foldupdate(bufnr, srow_upd, erow_upd) + compute_folds_levels(bufnr, foldinfo, srow, erow, function() + iterations = iterations + 1 + if iterations == #tree_changes then + foldinfo:foldupdate(bufnr, srow_upd, erow_upd) + end + end) end end) end ---@param bufnr integer ----@param foldinfo TS.FoldInfo ---@param start_row integer ---@param old_row integer ---@param old_col integer ---@param new_row integer ---@param new_col integer -local function on_bytes(bufnr, foldinfo, start_row, start_col, old_row, old_col, new_row, new_col) +local function on_bytes(bufnr, start_row, start_col, old_row, old_col, new_row, new_col) + -- Buffer reload clears `foldinfos[bufnr]`, which may still be nil when callback is invoked. + local foldinfo = foldinfos[bufnr] + if not foldinfo then + return + end + -- extend the end to fully include the range local end_row_old = start_row + old_row + 1 local end_row_new = start_row + new_row + 1 @@ -373,15 +358,16 @@ local function on_bytes(bufnr, foldinfo, start_row, start_col, old_row, old_col, -- is invoked. For example, `J` with non-zero count triggers multiple on_bytes before executing -- the scheduled callback. So we accumulate the edited ranges in `on_bytes_range`. schedule_if_loaded(bufnr, function() - if not foldinfo.on_bytes_range then + if not (foldinfo.on_bytes_range and foldinfos[bufnr]) then return end local srow, erow = foldinfo.on_bytes_range[1], foldinfo.on_bytes_range[2] foldinfo.on_bytes_range = nil -- Start from `srow - foldminlines`, because this edit may have shrunken the fold below limit. srow = math.max(srow - vim.wo.foldminlines, 0) - compute_folds_levels(bufnr, foldinfo, srow, erow) - foldinfo:foldupdate(bufnr, srow, erow) + compute_folds_levels(bufnr, foldinfo, srow, erow, function() + foldinfo:foldupdate(bufnr, srow, erow) + end) end) end end @@ -392,22 +378,30 @@ function M.foldexpr(lnum) lnum = lnum or vim.v.lnum local bufnr = api.nvim_get_current_buf() - local parser = ts.get_parser(bufnr, nil, { error = false }) - if not parser then - return '0' - end - if not foldinfos[bufnr] then - foldinfos[bufnr] = FoldInfo.new() + foldinfos[bufnr] = FoldInfo.new(bufnr) + api.nvim_create_autocmd({ 'BufUnload', 'VimEnter' }, { + buffer = bufnr, + once = true, + callback = function() + foldinfos[bufnr] = nil + end, + }) + + local parser = foldinfos[bufnr].parser + if not parser then + return '0' + end + compute_folds_levels(bufnr, foldinfos[bufnr]) parser:register_cbs({ on_changedtree = function(tree_changes) - on_changedtree(bufnr, foldinfos[bufnr], tree_changes) + on_changedtree(bufnr, tree_changes) end, on_bytes = function(_, _, start_row, start_col, _, old_row, old_col, _, new_row, new_col, _) - on_bytes(bufnr, foldinfos[bufnr], start_row, start_col, old_row, old_col, new_row, new_col) + on_bytes(bufnr, start_row, start_col, old_row, old_col, new_row, new_col) end, on_detach = function() @@ -423,10 +417,17 @@ api.nvim_create_autocmd('OptionSet', { pattern = { 'foldminlines', 'foldnestmax' }, desc = 'Refresh treesitter folds', callback = function() - for bufnr, _ in pairs(foldinfos) do - foldinfos[bufnr] = FoldInfo.new() - compute_folds_levels(bufnr, foldinfos[bufnr]) - foldinfos[bufnr]:foldupdate(bufnr, 0, api.nvim_buf_line_count(bufnr)) + local buf = api.nvim_get_current_buf() + local bufs = vim.v.option_type == 'global' and vim.tbl_keys(foldinfos) + or foldinfos[buf] and { buf } + or {} + for _, bufnr in ipairs(bufs) do + foldinfos[bufnr] = FoldInfo.new(bufnr) + api.nvim_buf_call(bufnr, function() + compute_folds_levels(bufnr, foldinfos[bufnr], nil, nil, function() + foldinfos[bufnr]:foldupdate(bufnr, 0, api.nvim_buf_line_count(bufnr)) + end) + end) end end, }) diff --git a/runtime/lua/vim/treesitter/_meta/misc.lua b/runtime/lua/vim/treesitter/_meta/misc.lua index 33701ef254..c532257f49 100644 --- a/runtime/lua/vim/treesitter/_meta/misc.lua +++ b/runtime/lua/vim/treesitter/_meta/misc.lua @@ -20,9 +20,15 @@ error('Cannot require a meta file') ---@class (exact) TSQueryInfo ---@field captures string[] ---@field patterns table<integer, (integer|string)[][]> +--- +---@class TSLangInfo +---@field fields string[] +---@field symbols table<string,boolean> +---@field _wasm boolean +---@field _abi_version integer --- @param lang string ---- @return table +--- @return TSLangInfo vim._ts_inspect_language = function(lang) end ---@return integer diff --git a/runtime/lua/vim/treesitter/_meta/tsnode.lua b/runtime/lua/vim/treesitter/_meta/tsnode.lua index d982b6a505..552905c3f0 100644 --- a/runtime/lua/vim/treesitter/_meta/tsnode.lua +++ b/runtime/lua/vim/treesitter/_meta/tsnode.lua @@ -68,12 +68,6 @@ function TSNode:named_child_count() end --- @return TSNode? function TSNode:named_child(index) end ---- Get the node's child that contains {descendant}. ---- @param descendant TSNode ---- @return TSNode? ---- @deprecated -function TSNode:child_containing_descendant(descendant) end - --- Get the node's child that contains {descendant} (includes {descendant}). --- --- For example, with the following node hierarchy: @@ -109,17 +103,9 @@ function TSNode:end_() end --- - end row --- - end column --- - end byte (if {include_bytes} is `true`) ---- @param include_bytes boolean? -function TSNode:range(include_bytes) end - ---- @nodoc --- @param include_bytes false? --- @return integer, integer, integer, integer -function TSNode:range(include_bytes) end - ---- @nodoc ---- @param include_bytes true ---- @return integer, integer, integer, integer, integer, integer +--- @overload fun(self: TSNode, include_bytes: true): integer, integer, integer, integer, integer, integer function TSNode:range(include_bytes) end --- Get the node's type as a string. diff --git a/runtime/lua/vim/treesitter/_query_linter.lua b/runtime/lua/vim/treesitter/_query_linter.lua index a825505378..3dfc6b0cfe 100644 --- a/runtime/lua/vim/treesitter/_query_linter.lua +++ b/runtime/lua/vim/treesitter/_query_linter.lua @@ -1,6 +1,6 @@ local api = vim.api -local namespace = api.nvim_create_namespace('vim.treesitter.query_linter') +local namespace = api.nvim_create_namespace('nvim.treesitter.query_linter') local M = {} @@ -138,7 +138,9 @@ local function lint_match(buf, match, query, lang_context, diagnostics) -- perform language-independent checks only for first lang if lang_context.is_first_lang and cap_id == 'error' then local node_text = vim.treesitter.get_node_text(node, buf):gsub('\n', ' ') - add_lint_for_node(diagnostics, { node:range() }, 'Syntax error: ' .. node_text) + ---@diagnostic disable-next-line: missing-fields LuaLS varargs bug + local range = { node:range() } --- @type Range4 + add_lint_for_node(diagnostics, range, 'Syntax error: ' .. node_text) end -- other checks rely on Neovim parser introspection diff --git a/runtime/lua/vim/treesitter/dev.lua b/runtime/lua/vim/treesitter/dev.lua index 26817cdba5..24dd8243db 100644 --- a/runtime/lua/vim/treesitter/dev.lua +++ b/runtime/lua/vim/treesitter/dev.lua @@ -119,7 +119,7 @@ function TSTreeView:new(bufnr, lang) end local t = { - ns = api.nvim_create_namespace('treesitter/dev-inspect'), + ns = api.nvim_create_namespace('nvim.treesitter.dev_inspect'), nodes = nodes, named = named, ---@type vim.treesitter.dev.TSTreeViewOpts @@ -135,15 +135,7 @@ function TSTreeView:new(bufnr, lang) return t end -local decor_ns = api.nvim_create_namespace('ts.dev') - ----@param range Range4 ----@return string -local function range_to_string(range) - ---@type integer, integer, integer, integer - local row, col, end_row, end_col = unpack(range) - return string.format('[%d, %d] - [%d, %d]', row, col, end_row, end_col) -end +local decor_ns = api.nvim_create_namespace('nvim.treesitter.dev') ---@param w integer ---@return boolean closed Whether the window was closed. @@ -227,14 +219,17 @@ function TSTreeView:draw(bufnr) local lang_hl_marks = {} ---@type table[] for i, item in self:iter() do - local range_str = range_to_string({ item.node:range() }) + local range_str = ('[%d, %d] - [%d, %d]'):format(item.node:range()) local lang_str = self.opts.lang and string.format(' %s', item.lang) or '' local text ---@type string if item.node:named() then - text = string.format('(%s', item.node:type()) + text = string.format('(%s%s', item.node:missing() and 'MISSING ' or '', item.node:type()) else text = string.format('%q', item.node:type()):gsub('\n', 'n') + if item.node:missing() then + text = string.format('(MISSING %s)', text) + end end if item.field then text = string.format('%s: %s', item.field, text) @@ -442,7 +437,7 @@ function M.inspect_tree(opts) end, }) - local group = api.nvim_create_augroup('treesitter/dev', {}) + local group = api.nvim_create_augroup('nvim.treesitter.dev', {}) api.nvim_create_autocmd('CursorMoved', { group = group, @@ -547,7 +542,7 @@ function M.inspect_tree(opts) }) end -local edit_ns = api.nvim_create_namespace('treesitter/dev-edit') +local edit_ns = api.nvim_create_namespace('nvim.treesitter.dev_edit') ---@param query_win integer ---@param base_win integer @@ -633,7 +628,7 @@ function M.edit_query(lang) -- can infer the language later. api.nvim_buf_set_name(query_buf, string.format('%s/query_editor.scm', lang)) - local group = api.nvim_create_augroup('treesitter/dev-edit', {}) + local group = api.nvim_create_augroup('nvim.treesitter.dev_edit', {}) api.nvim_create_autocmd({ 'TextChanged', 'InsertLeave' }, { group = group, buffer = query_buf, diff --git a/runtime/lua/vim/treesitter/highlighter.lua b/runtime/lua/vim/treesitter/highlighter.lua index 8ce8652f7d..6dd47811bd 100644 --- a/runtime/lua/vim/treesitter/highlighter.lua +++ b/runtime/lua/vim/treesitter/highlighter.lua @@ -2,7 +2,7 @@ local api = vim.api local query = vim.treesitter.query local Range = require('vim.treesitter._range') -local ns = api.nvim_create_namespace('treesitter/highlighter') +local ns = api.nvim_create_namespace('nvim.treesitter.highlighter') ---@alias vim.treesitter.highlighter.Iter fun(end_line: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch @@ -69,6 +69,7 @@ end ---@field private _queries table<string,vim.treesitter.highlighter.Query> ---@field tree vim.treesitter.LanguageTree ---@field private redraw_count integer +---@field parsing boolean true if we are parsing asynchronously local TSHighlighter = { active = {}, } @@ -147,8 +148,6 @@ function TSHighlighter.new(tree, opts) vim.opt_local.spelloptions:append('noplainbuffer') end) - self.tree:parse() - return self end @@ -161,7 +160,10 @@ function TSHighlighter:destroy() vim.bo[self.bufnr].spelloptions = self.orig_spelloptions vim.b[self.bufnr].ts_highlight = nil if vim.g.syntax_on == 1 then - api.nvim_exec_autocmds('FileType', { group = 'syntaxset', buffer = self.bufnr }) + api.nvim_exec_autocmds( + 'FileType', + { group = 'syntaxset', buffer = self.bufnr, modeline = false } + ) end end end @@ -299,6 +301,8 @@ local function on_line_impl(self, buf, line, is_spell_nav) state.highlighter_query:query():iter_captures(root_node, self.bufnr, line, root_end_row + 1) end + local captures = state.highlighter_query:query().captures + while line >= state.next_row do local capture, node, metadata, match = state.iter(line) @@ -311,7 +315,7 @@ local function on_line_impl(self, buf, line, is_spell_nav) if capture then local hl = state.highlighter_query:get_hl_from_capture(capture) - local capture_name = state.highlighter_query:query().captures[capture] + local capture_name = captures[capture] local spell, spell_pri_offset = get_spell(capture_name) @@ -382,19 +386,23 @@ function TSHighlighter._on_spell_nav(_, _, buf, srow, _, erow, _) end ---@private ----@param _win integer ---@param buf integer ---@param topline integer ---@param botline integer -function TSHighlighter._on_win(_, _win, buf, topline, botline) +function TSHighlighter._on_win(_, _, buf, topline, botline) local self = TSHighlighter.active[buf] - if not self then + if not self or self.parsing then return false end - self.tree:parse({ topline, botline + 1 }) - self:prepare_highlight_states(topline, botline + 1) + self.parsing = self.tree:parse({ topline, botline + 1 }, function(_, trees) + if trees and self.parsing then + self.parsing = false + api.nvim__redraw({ buf = buf, valid = false, flush = false }) + end + end) == nil self.redraw_count = self.redraw_count + 1 - return true + self:prepare_highlight_states(topline, botline) + return #self._highlight_states > 0 end api.nvim_set_decoration_provider(ns, { diff --git a/runtime/lua/vim/treesitter/language.lua b/runtime/lua/vim/treesitter/language.lua index 446051dfd7..16d19bfc5a 100644 --- a/runtime/lua/vim/treesitter/language.lua +++ b/runtime/lua/vim/treesitter/language.lua @@ -133,8 +133,9 @@ function M.add(lang, opts) path = paths[1] end - return loadparser(path, lang, symbol_name) or nil, - string.format('Cannot load parser %s for language "%s"', path, lang) + local res = loadparser(path, lang, symbol_name) + return res, + res == nil and string.format('Cannot load parser %s for language "%s"', path, lang) or nil end --- @param x string|string[] @@ -174,7 +175,7 @@ end --- (`"`). --- ---@param lang string Language ----@return table +---@return TSLangInfo function M.inspect(lang) M.add(lang) return vim._ts_inspect_language(lang) diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index 4b42164dc8..ea745c4deb 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -44,6 +44,8 @@ local query = require('vim.treesitter.query') local language = require('vim.treesitter.language') local Range = require('vim.treesitter._range') +local default_parse_timeout_ms = 3 + ---@alias TSCallbackName ---| 'changedtree' ---| 'bytes' @@ -58,6 +60,8 @@ local Range = require('vim.treesitter._range') ---| 'on_child_added' ---| 'on_child_removed' +---@alias ParserThreadState { timeout: integer? } + --- @type table<TSCallbackNameOn,TSCallbackName> local TSCallbackNames = { on_changedtree = 'changedtree', @@ -76,8 +80,13 @@ local TSCallbackNames = { ---@field private _injections_processed boolean ---@field private _opts table Options ---@field private _parser TSParser Parser for language ----@field private _has_regions boolean +---Table of regions for which the tree is currently running an async parse +---@field private _ranges_being_parsed table<string, boolean> +---Table of callback queues, keyed by each region for which the callbacks should be run +---@field private _cb_queues table<string, fun(err?: string, trees?: table<integer, TSTree>)[]> ---@field private _regions table<integer, Range6[]>? +---The total number of regions. Since _regions can have holes, we cannot simply read this value from #_regions. +---@field private _num_regions integer ---List of regions this tree should manage and parse. If nil then regions are ---taken from _trees. This is mostly a short-lived cache for included_regions() ---@field private _lang string Language name @@ -85,7 +94,8 @@ local TSCallbackNames = { ---@field private _source (integer|string) Buffer or string to parse ---@field private _trees table<integer, TSTree> Reference to parsed tree (one for each language). ---Each key is the index of region, which is synced with _regions and _valid. ----@field private _valid boolean|table<integer,boolean> If the parsed tree is valid +---@field private _valid_regions table<integer,true> Set of valid region IDs. +---@field private _is_entirely_valid boolean Whether the entire tree (excluding children) is valid. ---@field private _logger? fun(logtype: string, msg: string) ---@field private _logfile? file* local LanguageTree = {} @@ -117,7 +127,7 @@ function LanguageTree.new(source, lang, opts) local injections = opts.injections or {} - --- @type vim.treesitter.LanguageTree + --- @class vim.treesitter.LanguageTree local self = { _source = source, _lang = lang, @@ -126,10 +136,13 @@ function LanguageTree.new(source, lang, opts) _opts = opts, _injection_query = injections[lang] and query.parse(lang, injections[lang]) or query.get(lang, 'injections'), - _has_regions = false, _injections_processed = false, - _valid = false, + _valid_regions = {}, + _num_regions = 1, + _is_entirely_valid = false, _parser = vim._create_ts_parser(lang), + _ranges_being_parsed = {}, + _cb_queues = {}, _callbacks = {}, _callbacks_rec = {}, } @@ -182,7 +195,7 @@ end ---Measure execution time of a function ---@generic R1, R2, R3 ----@param f fun(): R1, R2, R2 +---@param f fun(): R1, R2, R3 ---@return number, R1, R2, R3 local function tcall(f, ...) local start = vim.uv.hrtime() @@ -190,6 +203,7 @@ local function tcall(f, ...) local r = { f(...) } --- @type number local duration = (vim.uv.hrtime() - start) / 1000000 + --- @diagnostic disable-next-line: redundant-return-value return duration, unpack(r) end @@ -231,7 +245,9 @@ end --- tree in treesitter. Doesn't clear filesystem cache. Called often, so needs to be fast. ---@param reload boolean|nil function LanguageTree:invalidate(reload) - self._valid = false + self._valid_regions = {} + self._is_entirely_valid = false + self._parser:reset() -- buffer was reloaded, reparse all trees if reload then @@ -258,20 +274,51 @@ function LanguageTree:trees() end --- Gets the language of this tree node. +--- @return string function LanguageTree:lang() return self._lang end +--- @param region Range6[] +--- @param range? boolean|Range +--- @return boolean +local function intercepts_region(region, range) + if #region == 0 then + return true + end + + if range == nil then + return false + end + + if type(range) == 'boolean' then + return range + end + + for _, r in ipairs(region) do + if Range.intercepts(r, range) then + return true + end + end + + return false +end + --- Returns whether this LanguageTree is valid, i.e., |LanguageTree:trees()| reflects the latest --- state of the source. If invalid, user should call |LanguageTree:parse()|. ----@param exclude_children boolean|nil whether to ignore the validity of children (default `false`) +---@param exclude_children boolean? whether to ignore the validity of children (default `false`) +---@param range Range? range to check for validity ---@return boolean -function LanguageTree:is_valid(exclude_children) - local valid = self._valid +function LanguageTree:is_valid(exclude_children, range) + local valid_regions = self._valid_regions - if type(valid) == 'table' then - for i, _ in pairs(self:included_regions()) do - if not valid[i] then + if not self._is_entirely_valid then + if not range then + return false + end + -- TODO: Efficiently search for possibly intersecting regions using a binary search + for i, region in pairs(self:included_regions()) do + if not valid_regions[i] and intercepts_region(region, range) then return false end end @@ -283,97 +330,81 @@ function LanguageTree:is_valid(exclude_children) end for _, child in pairs(self._children) do - if not child:is_valid(exclude_children) then + if not child:is_valid(exclude_children, range) then return false end end end - if type(valid) == 'boolean' then - return valid - end - - self._valid = true return true end --- Returns a map of language to child tree. +--- @return table<string,vim.treesitter.LanguageTree> function LanguageTree:children() return self._children end --- Returns the source content of the language tree (bufnr or string). +--- @return integer|string function LanguageTree:source() return self._source end ---- @param region Range6[] ---- @param range? boolean|Range ---- @return boolean -local function intercepts_region(region, range) - if #region == 0 then - return true - end - - if range == nil then - return false - end - - if type(range) == 'boolean' then - return range - end - - for _, r in ipairs(region) do - if Range.intercepts(r, range) then - return true - end - end - - return false -end - --- @private --- @param range boolean|Range? +--- @param thread_state ParserThreadState --- @return Range6[] changes --- @return integer no_regions_parsed --- @return number total_parse_time -function LanguageTree:_parse_regions(range) +--- @return boolean finished whether async parsing still needs time +function LanguageTree:_parse_regions(range, thread_state) local changes = {} local no_regions_parsed = 0 local total_parse_time = 0 - if type(self._valid) ~= 'table' then - self._valid = {} - end - -- If there are no ranges, set to an empty list -- so the included ranges in the parser are cleared. for i, ranges in pairs(self:included_regions()) do if - not self._valid[i] + not self._valid_regions[i] and ( intercepts_region(ranges, range) or (self._trees[i] and intercepts_region(self._trees[i]:included_ranges(false), range)) ) then self._parser:set_included_ranges(ranges) + self._parser:set_timeout(thread_state.timeout and thread_state.timeout * 1000 or 0) -- ms -> micros + local parse_time, tree, tree_changes = tcall(self._parser.parse, self._parser, self._trees[i], self._source, true) + while true do + if tree then + break + end + coroutine.yield(changes, no_regions_parsed, total_parse_time, false) - -- Pass ranges if this is an initial parse - local cb_changes = self._trees[i] and tree_changes or tree:included_ranges(true) + parse_time, tree, tree_changes = + tcall(self._parser.parse, self._parser, self._trees[i], self._source, true) + end - self:_do_callback('changedtree', cb_changes, tree) + self:_do_callback('changedtree', tree_changes, tree) self._trees[i] = tree vim.list_extend(changes, tree_changes) total_parse_time = total_parse_time + parse_time no_regions_parsed = no_regions_parsed + 1 - self._valid[i] = true + self._valid_regions[i] = true + + -- _valid_regions can have holes, but that is okay because this equality is only true when it + -- has no holes (meaning all regions are valid) + if #self._valid_regions == self._num_regions then + self._is_entirely_valid = true + end end end - return changes, no_regions_parsed, total_parse_time + return changes, no_regions_parsed, total_parse_time, true end --- @private @@ -409,6 +440,98 @@ function LanguageTree:_add_injections() return query_time end +--- @param range boolean|Range? +--- @return string +local function range_to_string(range) + return type(range) == 'table' and table.concat(range, ',') or tostring(range) +end + +--- @private +--- @param range boolean|Range? +--- @param callback fun(err?: string, trees?: table<integer, TSTree>) +function LanguageTree:_push_async_callback(range, callback) + local key = range_to_string(range) + self._cb_queues[key] = self._cb_queues[key] or {} + local queue = self._cb_queues[key] + queue[#queue + 1] = callback +end + +--- @private +--- @param range boolean|Range? +--- @param err? string +--- @param trees? table<integer, TSTree> +function LanguageTree:_run_async_callbacks(range, err, trees) + local key = range_to_string(range) + for _, cb in ipairs(self._cb_queues[key]) do + cb(err, trees) + end + self._ranges_being_parsed[key] = nil + self._cb_queues[key] = nil +end + +--- Run an asynchronous parse, calling {on_parse} when complete. +--- +--- @private +--- @param range boolean|Range? +--- @param on_parse fun(err?: string, trees?: table<integer, TSTree>) +--- @return table<integer, TSTree>? trees the list of parsed trees, if parsing completed synchronously +function LanguageTree:_async_parse(range, on_parse) + self:_push_async_callback(range, on_parse) + + -- If we are already running an async parse, just queue the callback. + local range_string = range_to_string(range) + if not self._ranges_being_parsed[range_string] then + self._ranges_being_parsed[range_string] = true + else + return + end + + local source = self._source + local is_buffer_parser = type(source) == 'number' + local buf = is_buffer_parser and vim.b[source] or nil + local ct = is_buffer_parser and buf.changedtick or nil + local total_parse_time = 0 + local redrawtime = vim.o.redrawtime + + local thread_state = {} ---@type ParserThreadState + + ---@type fun(): table<integer, TSTree>, boolean + local parse = coroutine.wrap(self._parse) + + local function step() + if is_buffer_parser then + if + not vim.api.nvim_buf_is_valid(source --[[@as number]]) + then + return nil + end + + -- If buffer was changed in the middle of parsing, reset parse state + if buf.changedtick ~= ct then + ct = buf.changedtick + total_parse_time = 0 + parse = coroutine.wrap(self._parse) + end + end + + thread_state.timeout = not vim.g._ts_force_sync_parsing and default_parse_timeout_ms or nil + local parse_time, trees, finished = tcall(parse, self, range, thread_state) + total_parse_time = total_parse_time + parse_time + + if finished then + self:_run_async_callbacks(range, nil, trees) + return trees + elseif total_parse_time > redrawtime then + self:_run_async_callbacks(range, 'TIMEOUT', nil) + return nil + else + vim.schedule(step) + end + end + + return step() +end + --- Recursively parse all regions in the language tree using |treesitter-parsers| --- for the corresponding languages and run injection queries on the parsed trees --- to determine whether child trees should be created and parsed. @@ -420,11 +543,33 @@ end --- Set to `true` to run a complete parse of the source (Note: Can be slow!) --- Set to `false|nil` to only parse regions with empty ranges (typically --- only the root tree without injections). ---- @return table<integer, TSTree> -function LanguageTree:parse(range) - if self:is_valid() then +--- @param on_parse fun(err?: string, trees?: table<integer, TSTree>)? Function invoked when parsing completes. +--- When provided and `vim.g._ts_force_sync_parsing` is not set, parsing will run +--- asynchronously. The first argument to the function is a string representing the error type, +--- in case of a failure (currently only possible for timeouts). The second argument is the list +--- of trees returned by the parse (upon success), or `nil` if the parse timed out (determined +--- by 'redrawtime'). +--- +--- If parsing was still able to finish synchronously (within 3ms), `parse()` returns the list +--- of trees. Otherwise, it returns `nil`. +--- @return table<integer, TSTree>? +function LanguageTree:parse(range, on_parse) + if on_parse then + return self:_async_parse(range, on_parse) + end + local trees, _ = self:_parse(range, {}) + return trees +end + +--- @private +--- @param range boolean|Range|nil +--- @param thread_state ParserThreadState +--- @return table<integer, TSTree> trees +--- @return boolean finished +function LanguageTree:_parse(range, thread_state) + if self:is_valid(nil, type(range) == 'table' and range or nil) then self:_log('valid') - return self._trees + return self._trees, true end local changes --- @type Range6[]? @@ -435,15 +580,27 @@ function LanguageTree:parse(range) local total_parse_time = 0 -- At least 1 region is invalid - if not self:is_valid(true) then - changes, no_regions_parsed, total_parse_time = self:_parse_regions(range) + if not self:is_valid(true, type(range) == 'table' and range or nil) then + ---@type fun(self: vim.treesitter.LanguageTree, range: boolean|Range?, thread_state: ParserThreadState): Range6[], integer, number, boolean + local parse_regions = coroutine.wrap(self._parse_regions) + while true do + local is_finished + changes, no_regions_parsed, total_parse_time, is_finished = + parse_regions(self, range, thread_state) + thread_state.timeout = thread_state.timeout + and math.max(thread_state.timeout - total_parse_time, 0) + if is_finished then + break + end + coroutine.yield(self._trees, false) + end -- Need to run injections when we parsed something if no_regions_parsed > 0 then self._injections_processed = false end end - if not self._injections_processed and range ~= false and range ~= nil then + if not self._injections_processed and range then query_time = self:_add_injections() self._injections_processed = true end @@ -457,10 +614,24 @@ function LanguageTree:parse(range) }) for _, child in pairs(self._children) do - child:parse(range) + if thread_state.timeout == 0 then + coroutine.yield(self._trees, false) + end + + ---@type fun(): table<integer, TSTree>, boolean + local parse = coroutine.wrap(child._parse) + + while true do + local ctime, _, child_finished = tcall(parse, child, range, thread_state) + if child_finished then + thread_state.timeout = thread_state.timeout and math.max(thread_state.timeout - ctime, 0) + break + end + coroutine.yield(self._trees, child_finished) + end end - return self._trees + return self._trees, true end --- Invokes the callback for each |LanguageTree| recursively. @@ -504,7 +675,8 @@ function LanguageTree:add_child(lang) return self._children[lang] end ---- @package +---Returns the parent tree. `nil` for the root tree. +---@return vim.treesitter.LanguageTree? function LanguageTree:parent() return self._parent end @@ -551,38 +723,34 @@ end ---region is valid or not. ---@param fn fun(index: integer, region: Range6[]): boolean function LanguageTree:_iter_regions(fn) - if not self._valid then + if vim.deep_equal(self._valid_regions, {}) then return end - local was_valid = type(self._valid) ~= 'table' - - if was_valid then - self:_log('was valid', self._valid) - self._valid = {} + if self._is_entirely_valid then + self:_log('was valid') end local all_valid = true for i, region in pairs(self:included_regions()) do - if was_valid or self._valid[i] then - self._valid[i] = fn(i, region) - if not self._valid[i] then + if self._valid_regions[i] then + -- Setting this to nil rather than false allows us to determine if all regions were parsed + -- just by checking the length of _valid_regions. + self._valid_regions[i] = fn(i, region) and true or nil + if not self._valid_regions[i] then self:_log(function() return 'invalidating region', i, region_tostr(region) end) end end - if not self._valid[i] then + if not self._valid_regions[i] then all_valid = false end end - -- Compress the valid value to 'true' if there are no invalid regions - if all_valid then - self._valid = all_valid - end + self._is_entirely_valid = all_valid end --- Sets the included regions that should be parsed by this |LanguageTree|. @@ -602,14 +770,13 @@ end ---@private ---@param new_regions (Range4|Range6|TSNode)[][] List of regions this tree should manage and parse. function LanguageTree:set_included_regions(new_regions) - self._has_regions = true - -- Transform the tables from 4 element long to 6 element long (with byte offset) for _, region in ipairs(new_regions) do for i, range in ipairs(region) do if type(range) == 'table' and #range == 4 then region[i] = Range.add_bytes(self._source, range --[[@as Range4]]) elseif type(range) == 'userdata' then + --- @diagnostic disable-next-line: missing-fields LuaLS varargs bug region[i] = { range:range(true) } end end @@ -633,6 +800,7 @@ function LanguageTree:set_included_regions(new_regions) end self._regions = new_regions + self._num_regions = #new_regions end ---Gets the set of included regions managed by this LanguageTree. This can be different from the @@ -646,18 +814,8 @@ function LanguageTree:included_regions() return self._regions end - if not self._has_regions then - -- treesitter.c will default empty ranges to { -1, -1, -1, -1, -1, -1} (the full range) - return { {} } - end - - local regions = {} ---@type Range6[][] - for i, _ in pairs(self._trees) do - regions[i] = self._trees[i]:included_ranges(true) - end - - self._regions = regions - return regions + -- treesitter.c will default empty ranges to { -1, -1, -1, -1, -1, -1} (the full range) + return { {} } end ---@param node TSNode @@ -821,7 +979,7 @@ end --- @private --- @return table<string, Range6[][]> function LanguageTree:_get_injections() - if not self._injection_query then + if not self._injection_query or #self._injection_query.captures == 0 then return {} end @@ -907,7 +1065,15 @@ function LanguageTree:_edit( ) end - self._regions = nil + self._parser:reset() + + if self._regions then + local regions = {} ---@type table<integer, Range6[]> + for i, tree in pairs(self._trees) do + regions[i] = tree:included_ranges(true) + end + self._regions = regions + end local changed_range = { start_row, diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 1677e8d364..10fb82e533 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -1,17 +1,77 @@ +--- @brief This Lua |treesitter-query| interface allows you to create queries and use them to parse +--- text. See |vim.treesitter.query.parse()| for a working example. + local api = vim.api local language = require('vim.treesitter.language') local memoize = vim.func._memoize +local MODELINE_FORMAT = '^;+%s*inherits%s*:?%s*([a-z_,()]+)%s*$' +local EXTENDS_FORMAT = '^;+%s*extends%s*$' + local M = {} +local function is_directive(name) + return string.sub(name, -1) == '!' +end + +---@nodoc +---@class vim.treesitter.query.ProcessedPredicate +---@field [1] string predicate name +---@field [2] boolean should match +---@field [3] (integer|string)[] the original predicate + +---@alias vim.treesitter.query.ProcessedDirective (integer|string)[] + +---@nodoc +---@class vim.treesitter.query.ProcessedPattern { +---@field predicates vim.treesitter.query.ProcessedPredicate[] +---@field directives vim.treesitter.query.ProcessedDirective[] + +--- Splits the query patterns into predicates and directives. +---@param patterns table<integer, (integer|string)[][]> +---@return table<integer, vim.treesitter.query.ProcessedPattern> +local function process_patterns(patterns) + ---@type table<integer, vim.treesitter.query.ProcessedPattern> + local processed_patterns = {} + + for k, pattern_list in pairs(patterns) do + ---@type vim.treesitter.query.ProcessedPredicate[] + local predicates = {} + ---@type vim.treesitter.query.ProcessedDirective[] + local directives = {} + + for _, pattern in ipairs(pattern_list) do + -- Note: tree-sitter strips the leading # from predicates for us. + local pred_name = pattern[1] + ---@cast pred_name string + + if is_directive(pred_name) then + table.insert(directives, pattern) + else + local should_match = true + if pred_name:match('^not%-') then + pred_name = pred_name:sub(5) + should_match = false + end + table.insert(predicates, { pred_name, should_match, pattern }) + end + end + + processed_patterns[k] = { predicates = predicates, directives = directives } + end + + return processed_patterns +end + ---@nodoc ---Parsed query, see |vim.treesitter.query.parse()| --- ---@class vim.treesitter.Query ----@field lang string name of the language for this parser +---@field lang string parser language name ---@field captures string[] list of (unique) capture names defined in query ----@field info vim.treesitter.QueryInfo contains information used in the query (e.g. captures, predicates, directives) +---@field info vim.treesitter.QueryInfo query context (e.g. captures, predicates, directives) ---@field query TSQuery userdata query object +---@field private _processed_patterns table<integer, vim.treesitter.query.ProcessedPattern> local Query = {} Query.__index = Query @@ -30,6 +90,7 @@ function Query.new(lang, ts_query) patterns = query_info.patterns, } self.captures = self.info.captures + self._processed_patterns = process_patterns(self.info.patterns) return self end @@ -109,9 +170,6 @@ function M.get_files(lang, query_name, is_included) -- ;+ inherits: ({language},)*{language} -- -- {language} ::= {lang} | ({lang}) - local MODELINE_FORMAT = '^;+%s*inherits%s*:?%s*([a-z_,()]+)%s*$' - local EXTENDS_FORMAT = '^;+%s*extends%s*$' - for _, filename in ipairs(lang_files) do local file, err = io.open(filename, 'r') if not file then @@ -184,8 +242,8 @@ local function read_query_files(filenames) return table.concat(contents, '') end --- The explicitly set queries from |vim.treesitter.query.set()| ----@type table<string,table<string,vim.treesitter.Query>> +-- The explicitly set query strings from |vim.treesitter.query.set()| +---@type table<string,table<string,string>> local explicit_queries = setmetatable({}, { __index = function(t, k) local lang_queries = {} @@ -197,14 +255,27 @@ local explicit_queries = setmetatable({}, { --- Sets the runtime query named {query_name} for {lang} --- ---- This allows users to override any runtime files and/or configuration +--- This allows users to override or extend any runtime files and/or configuration --- set by plugins. --- +--- For example, you could enable spellchecking of `C` identifiers with the +--- following code: +--- ```lua +--- vim.treesitter.query.set( +--- 'c', +--- 'highlights', +--- [[;inherits c +--- (identifier) @spell]]) +--- ]]) +--- ``` +--- ---@param lang string Language to use for the query ---@param query_name string Name of the query (e.g., "highlights") ---@param text string Query text (unparsed). function M.set(lang, query_name, text) - explicit_queries[lang][query_name] = M.parse(lang, text) + --- @diagnostic disable-next-line: undefined-field LuaLS bad at generics + M.get:clear(lang, query_name) + explicit_queries[lang][query_name] = text end --- Returns the runtime query {query_name} for {lang}. @@ -214,34 +285,82 @@ end --- ---@return vim.treesitter.Query? : Parsed query. `nil` if no query files are found. M.get = memoize('concat-2', function(lang, query_name) + local query_string ---@type string + if explicit_queries[lang][query_name] then - return explicit_queries[lang][query_name] - end + local query_files = {} + local base_langs = {} ---@type string[] - local query_files = M.get_files(lang, query_name) - local query_string = read_query_files(query_files) + for line in explicit_queries[lang][query_name]:gmatch('([^\n]*)\n?') do + if not vim.startswith(line, ';') then + break + end + + local lang_list = line:match(MODELINE_FORMAT) + if lang_list then + for _, incl_lang in ipairs(vim.split(lang_list, ',')) do + local is_optional = incl_lang:match('%(.*%)') + + if is_optional then + add_included_lang(base_langs, lang, incl_lang:sub(2, #incl_lang - 1)) + else + add_included_lang(base_langs, lang, incl_lang) + end + end + elseif line:match(EXTENDS_FORMAT) then + table.insert(base_langs, lang) + end + end + + for _, base_lang in ipairs(base_langs) do + local base_files = M.get_files(base_lang, query_name, true) + vim.list_extend(query_files, base_files) + end + + query_string = read_query_files(query_files) .. explicit_queries[lang][query_name] + else + local query_files = M.get_files(lang, query_name) + query_string = read_query_files(query_files) + end if #query_string == 0 then return nil end return M.parse(lang, query_string) -end) +end, false) + +api.nvim_create_autocmd('OptionSet', { + pattern = { 'runtimepath' }, + group = api.nvim_create_augroup('nvim.treesitter.query_cache_reset', { clear = true }), + callback = function() + --- @diagnostic disable-next-line: undefined-field LuaLS bad at generics + M.get:clear() + end, +}) ---- Parse {query} as a string. (If the query is in a file, the caller ---- should read the contents into a string before calling). ---- ---- Returns a `Query` (see |lua-treesitter-query|) object which can be used to ---- search nodes in the syntax tree for the patterns defined in {query} ---- using the `iter_captures` and `iter_matches` methods. +--- Parses a {query} string and returns a `Query` object (|lua-treesitter-query|), which can be used +--- to search the tree for the query patterns (via |Query:iter_captures()|, |Query:iter_matches()|), +--- or inspect the query via these fields: +--- - `captures`: a list of unique capture names defined in the query (alias: `info.captures`). +--- - `info.patterns`: information about predicates. --- ---- Exposes `info` and `captures` with additional context about {query}. ---- - `captures` contains the list of unique capture names defined in {query}. ---- - `info.captures` also points to `captures`. ---- - `info.patterns` contains information about predicates. +--- Example: +--- ```lua +--- local query = vim.treesitter.query.parse('vimdoc', [[ +--- ; query +--- ((h1) @str +--- (#trim! @str 1 1 1 1)) +--- ]]) +--- local tree = vim.treesitter.get_parser():parse()[1] +--- for id, node, metadata in query:iter_captures(tree:root(), 0) do +--- -- Print the node name and source text. +--- vim.print({node:type(), vim.treesitter.get_node_text(node, vim.api.nvim_get_current_buf())}) +--- end +--- ``` --- ---@param lang string Language to use for the query ----@param query string Query in s-expr syntax +---@param query string Query text, in s-expr syntax --- ---@return vim.treesitter.Query : Parsed query --- @@ -250,7 +369,7 @@ M.parse = memoize('concat-2', function(lang, query) assert(language.add(lang)) local ts_query = vim._ts_parse_query(lang, query) return Query.new(lang, ts_query) -end) +end, false) --- Implementations of predicates that can optionally be prefixed with "any-". --- @@ -572,13 +691,17 @@ local directive_handlers = { metadata[id].text = text:gsub(pattern, replacement) end, - -- Trim blank lines from end of the node - -- Example: (#trim! @fold) - -- TODO(clason): generalize to arbitrary whitespace removal + -- Trim whitespace from both sides of the node + -- Example: (#trim! @fold 1 1 1 1) ['trim!'] = function(match, _, bufnr, pred, metadata) local capture_id = pred[2] assert(type(capture_id) == 'number') + local trim_start_lines = pred[3] == '1' + local trim_start_cols = pred[4] == '1' + local trim_end_lines = pred[5] == '1' or not pred[3] -- default true for backwards compatibility + local trim_end_cols = pred[6] == '1' + local nodes = match[capture_id] if not nodes or #nodes == 0 then return @@ -588,20 +711,45 @@ local directive_handlers = { local start_row, start_col, end_row, end_col = node:range() - -- Don't trim if region ends in middle of a line - if end_col ~= 0 then - return + local node_text = vim.split(vim.treesitter.get_node_text(node, bufnr), '\n') + if end_col == 0 then + -- get_node_text() will ignore the last line if the node ends at column 0 + node_text[#node_text + 1] = '' end - while end_row >= start_row do - -- As we only care when end_col == 0, always inspect one line above end_row. - local end_line = api.nvim_buf_get_lines(bufnr, end_row - 1, end_row, true)[1] + local end_idx = #node_text + local start_idx = 1 - if end_line ~= '' then - break + if trim_end_lines then + while end_idx > 0 and node_text[end_idx]:find('^%s*$') do + end_idx = end_idx - 1 + end_row = end_row - 1 + -- set the end position to the last column of the next line, or 0 if we just trimmed the + -- last line + end_col = end_idx > 0 and #node_text[end_idx] or 0 end + end + if trim_end_cols then + if end_idx == 0 then + end_row = start_row + end_col = start_col + else + local whitespace_start = node_text[end_idx]:find('(%s*)$') + end_col = (whitespace_start - 1) + (end_idx == 1 and start_col or 0) + end + end - end_row = end_row - 1 + if trim_start_lines then + while start_idx <= end_idx and node_text[start_idx]:find('^%s*$') do + start_idx = start_idx + 1 + start_row = start_row + 1 + start_col = 0 + end + end + if trim_start_cols and node_text[start_idx] then + local _, whitespace_end = node_text[start_idx]:find('^(%s*)') + whitespace_end = whitespace_end or 0 + start_col = (start_idx == 1 and start_col or 0) + whitespace_end end -- If this produces an invalid range, we just skip it. @@ -711,84 +859,50 @@ function M.list_predicates() return vim.tbl_keys(predicate_handlers) end -local function xor(x, y) - return (x or y) and not (x and y) -end - -local function is_directive(name) - return string.sub(name, -1) == '!' -end - ---@private ----@param match TSQueryMatch +---@param pattern_i integer +---@param predicates vim.treesitter.query.ProcessedPredicate[] +---@param captures table<integer, TSNode[]> ---@param source integer|string -function Query:match_preds(match, source) - local _, pattern = match:info() - local preds = self.info.patterns[pattern] - - if not preds then - return true - end - - local captures = match:captures() - - for _, pred in pairs(preds) do - -- Here we only want to return if a predicate DOES NOT match, and - -- continue on the other case. This way unknown predicates will not be considered, - -- which allows some testing and easier user extensibility (#12173). - -- Also, tree-sitter strips the leading # from predicates for us. - local is_not = false - - -- Skip over directives... they will get processed after all the predicates. - if not is_directive(pred[1]) then - local pred_name = pred[1] - if pred_name:match('^not%-') then - pred_name = pred_name:sub(5) - is_not = true - end - - local handler = predicate_handlers[pred_name] - - if not handler then - error(string.format('No handler for %s', pred[1])) - return false - end - - local pred_matches = handler(captures, pattern, source, pred) +---@return boolean whether the predicates match +function Query:_match_predicates(predicates, pattern_i, captures, source) + for _, predicate in ipairs(predicates) do + local processed_name = predicate[1] + local should_match = predicate[2] + local orig_predicate = predicate[3] + + local handler = predicate_handlers[processed_name] + if not handler then + error(string.format('No handler for %s', orig_predicate[1])) + return false + end - if not xor(is_not, pred_matches) then - return false - end + local does_match = handler(captures, pattern_i, source, orig_predicate) + if does_match ~= should_match then + return false end end return true end ---@private ----@param match TSQueryMatch +---@param pattern_i integer +---@param directives vim.treesitter.query.ProcessedDirective[] +---@param source integer|string +---@param captures table<integer, TSNode[]> ---@return vim.treesitter.query.TSMetadata metadata -function Query:apply_directives(match, source) +function Query:_apply_directives(directives, pattern_i, captures, source) ---@type vim.treesitter.query.TSMetadata local metadata = {} - local _, pattern = match:info() - local preds = self.info.patterns[pattern] - - if not preds then - return metadata - end - local captures = match:captures() - - for _, pred in pairs(preds) do - if is_directive(pred[1]) then - local handler = directive_handlers[pred[1]] - - if not handler then - error(string.format('No handler for %s', pred[1])) - end + for _, directive in pairs(directives) do + local handler = directive_handlers[directive[1]] - handler(captures, pattern, source, pred, metadata) + if not handler then + error(string.format('No handler for %s', directive[1])) end + + handler(captures, pattern_i, source, directive, metadata) end return metadata @@ -812,26 +926,22 @@ local function value_or_node_range(start, stop, node) return start, stop end ---- @param match TSQueryMatch ---- @return integer -local function match_id_hash(_, match) - return (match:info()) -end - ---- Iterate over all captures from all matches inside {node} +--- Iterates over all captures from all matches in {node}. --- ---- {source} is needed if the query contains predicates; then the caller +--- {source} is required if the query contains predicates; then the caller --- must ensure to use a freshly parsed tree consistent with the current --- text of the buffer (if relevant). {start} and {stop} can be used to limit --- matches inside a row range (this is typically used with root node --- as the {node}, i.e., to get syntax highlight matches in the current --- viewport). When omitted, the {start} and {stop} row values are used from the given node. --- ---- The iterator returns four values: a numeric id identifying the capture, ---- the captured node, metadata from any directives processing the match, ---- and the match itself. ---- The following example shows how to get captures by name: +--- The iterator returns four values: +--- 1. the numeric id identifying the capture +--- 2. the captured node +--- 3. metadata from any directives processing the match +--- 4. the match itself --- +--- Example: how to get captures by name: --- ```lua --- for id, node, metadata, match in query:iter_captures(tree:root(), bufnr, first, last) do --- local name = query.captures[id] -- name of the capture in the query @@ -847,8 +957,8 @@ end ---@param start? integer Starting line for the search. Defaults to `node:start()`. ---@param stop? integer Stopping line for the search (end-exclusive). Defaults to `node:end_()`. --- ----@return (fun(end_line: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch): ---- capture id, capture node, metadata, match +---@return (fun(end_line: integer|nil): integer, TSNode, vim.treesitter.query.TSMetadata, TSQueryMatch, TSTree): +--- capture id, capture node, metadata, match, tree --- ---@note Captures are only returned if the query pattern of a specific capture contained predicates. function Query:iter_captures(node, source, start, stop) @@ -858,10 +968,14 @@ function Query:iter_captures(node, source, start, stop) start, stop = value_or_node_range(start, stop, node) + -- Copy the tree to ensure it is valid during the entire lifetime of the iterator + local tree = node:tree():copy() local cursor = vim._create_ts_querycursor(node, self.query, start, stop, { match_limit = 256 }) - local apply_directives = memoize(match_id_hash, self.apply_directives, true) - local match_preds = memoize(match_id_hash, self.match_preds, true) + -- For faster checks that a match is not in the cache. + local highest_cached_match_id = -1 + ---@type table<integer, vim.treesitter.query.TSMetadata> + local match_cache = {} local function iter(end_line) local capture, captured_node, match = cursor:next_capture() @@ -870,18 +984,39 @@ function Query:iter_captures(node, source, start, stop) return end - if not match_preds(self, match, source) then - local match_id = match:info() - cursor:remove_match(match_id) - if end_line and captured_node:range() > end_line then - return nil, captured_node, nil, nil - end - return iter(end_line) -- tail call: try next match + local match_id, pattern_i = match:info() + + --- @type vim.treesitter.query.TSMetadata + local metadata + if match_id <= highest_cached_match_id then + metadata = match_cache[match_id] end - local metadata = apply_directives(self, match, source) + if not metadata then + metadata = {} + + local processed_pattern = self._processed_patterns[pattern_i] + if processed_pattern then + local captures = match:captures() - return capture, captured_node, metadata, match + local predicates = processed_pattern.predicates + if not self:_match_predicates(predicates, pattern_i, captures, source) then + cursor:remove_match(match_id) + if end_line and captured_node:range() > end_line then + return nil, captured_node, nil, nil + end + return iter(end_line) -- tail call: try next match + end + + local directives = processed_pattern.directives + metadata = self:_apply_directives(directives, pattern_i, captures, source) + end + + highest_cached_match_id = math.max(highest_cached_match_id, match_id) + match_cache[match_id] = metadata + end + + return capture, captured_node, metadata, match, tree end return iter end @@ -903,7 +1038,7 @@ end --- -- `node` was captured by the `name` capture in the match --- --- local node_data = metadata[id] -- Node level metadata ---- ... use the info here ... +--- -- ... use the info here ... --- end --- end --- end @@ -922,7 +1057,7 @@ end --- (last) node instead of the full list of matching nodes. This option is only for backward --- compatibility and will be removed in a future release. --- ----@return (fun(): integer, table<integer, TSNode[]>, vim.treesitter.query.TSMetadata): pattern id, match, metadata +---@return (fun(): integer, table<integer, TSNode[]>, vim.treesitter.query.TSMetadata, TSTree): pattern id, match, metadata, tree function Query:iter_matches(node, source, start, stop, opts) opts = opts or {} opts.match_limit = opts.match_limit or 256 @@ -933,6 +1068,8 @@ function Query:iter_matches(node, source, start, stop, opts) start, stop = value_or_node_range(start, stop, node) + -- Copy the tree to ensure it is valid during the entire lifetime of the iterator + local tree = node:tree():copy() local cursor = vim._create_ts_querycursor(node, self.query, start, stop, opts) local function iter() @@ -942,17 +1079,22 @@ function Query:iter_matches(node, source, start, stop, opts) return end - local match_id, pattern = match:info() + local match_id, pattern_i = match:info() + local processed_pattern = self._processed_patterns[pattern_i] + local captures = match:captures() - if not self:match_preds(match, source) then - cursor:remove_match(match_id) - return iter() -- tail call: try next match + --- @type vim.treesitter.query.TSMetadata + local metadata = {} + if processed_pattern then + local predicates = processed_pattern.predicates + if not self:_match_predicates(predicates, pattern_i, captures, source) then + cursor:remove_match(match_id) + return iter() -- tail call: try next match + end + local directives = processed_pattern.directives + metadata = self:_apply_directives(directives, pattern_i, captures, source) end - local metadata = self:apply_directives(match, source) - - local captures = match:captures() - if opts.all == false then -- Convert the match table into the old buggy version for backward -- compatibility. This is slow, but we only do it when the caller explicitly opted into it by @@ -961,11 +1103,11 @@ function Query:iter_matches(node, source, start, stop, opts) for k, v in pairs(captures or {}) do old_match[k] = v[#v] end - return pattern, old_match, metadata + return pattern_i, old_match, metadata end -- TODO(lewis6991): create a new function that returns {match, metadata} - return pattern, captures, metadata + return pattern_i, captures, metadata, tree end return iter end diff --git a/runtime/lua/vim/uri.lua b/runtime/lua/vim/uri.lua index b4e4098b91..8b6f1a61ee 100644 --- a/runtime/lua/vim/uri.lua +++ b/runtime/lua/vim/uri.lua @@ -15,7 +15,7 @@ local PATTERNS = { rfc2396 = "^A-Za-z0-9%-_.!~*'()", -- RFC 2732 -- https://tools.ietf.org/html/rfc2732 - rfc2732 = "^A-Za-z0-9%-_.!~*'()[]", + rfc2732 = "^A-Za-z0-9%-_.!~*'()%[%]", -- RFC 3986 -- https://tools.ietf.org/html/rfc3986#section-2.2 rfc3986 = "^A-Za-z0-9%-._~!$&'()*+,;=:@/", @@ -60,9 +60,10 @@ end ---@param path string Path to file ---@return string URI function M.uri_from_fname(path) - local volume_path, fname = path:match('^([a-zA-Z]:)(.*)') ---@type string? + local volume_path, fname = path:match('^([a-zA-Z]:)(.*)') ---@type string?, string? local is_windows = volume_path ~= nil if is_windows then + assert(fname) path = volume_path .. M.uri_encode(fname:gsub('\\', '/')) else path = M.uri_encode(path) @@ -111,7 +112,7 @@ function M.uri_to_fname(uri) uri = M.uri_decode(uri) --TODO improve this. if is_windows_file_uri(uri) then - uri = uri:gsub('^file:/+', ''):gsub('/', '\\') + uri = uri:gsub('^file:/+', ''):gsub('/', '\\') --- @type string else uri = uri:gsub('^file:/+', '/') ---@type string end diff --git a/runtime/lua/vim/version.lua b/runtime/lua/vim/version.lua index d64ef98d2d..06c54ac033 100644 --- a/runtime/lua/vim/version.lua +++ b/runtime/lua/vim/version.lua @@ -227,8 +227,7 @@ end ---@field to? vim.Version local VersionRange = {} ---- @private ---- +---@nodoc ---@param version string|vim.Version function VersionRange:has(version) if type(version) == 'string' then diff --git a/runtime/lua/vim/vimhelp.lua b/runtime/lua/vim/vimhelp.lua index 5579cc0174..a494d311b1 100644 --- a/runtime/lua/vim/vimhelp.lua +++ b/runtime/lua/vim/vimhelp.lua @@ -7,7 +7,7 @@ local M = {} --- Note: {patterns} is assumed to be sorted by occurrence in the file. --- @param patterns {start:string,stop:string,match:string}[] function M.highlight_groups(patterns) - local ns = vim.api.nvim_create_namespace('vimhelp') + local ns = vim.api.nvim_create_namespace('nvim.vimhelp') vim.api.nvim_buf_clear_namespace(0, ns, 0, -1) local save_cursor = vim.fn.getcurpos() diff --git a/runtime/optwin.vim b/runtime/optwin.vim index 48a4bd2816..6866d46d51 100644 --- a/runtime/optwin.vim +++ b/runtime/optwin.vim @@ -1,7 +1,7 @@ " These commands create the option window. " " Maintainer: The Vim Project <https://github.com/vim/vim> -" Last Change: 2024 Jul 12 +" Last Change: 2024 Dec 07 " Former Maintainer: Bram Moolenaar <Bram@vim.org> " If there already is an option window, jump to that one. @@ -626,8 +626,8 @@ call <SID>AddOption("terse", gettext("add 's' flag in 'shortmess' (don't show se call <SID>BinOptionG("terse", &terse) call <SID>AddOption("shortmess", gettext("list of flags to make messages shorter")) call <SID>OptionG("shm", &shm) -call <SID>AddOption("msghistory", gettext("how many messages are remembered")) -call append("$", " \tset mhi=" . &mhi) +call <SID>AddOption("messagesopt", gettext("options for outputting messages")) +call <SID>OptionG("mopt", &mopt) call <SID>AddOption("showcmd", gettext("show (partial) command keys in location given by 'showcmdloc'")) let &sc = s:old_sc call <SID>BinOptionG("sc", &sc) diff --git a/runtime/pack/dist/opt/matchit/autoload/matchit.vim b/runtime/pack/dist/opt/matchit/autoload/matchit.vim index aa977488e5..8a220eeb9b 100644 --- a/runtime/pack/dist/opt/matchit/autoload/matchit.vim +++ b/runtime/pack/dist/opt/matchit/autoload/matchit.vim @@ -222,6 +222,7 @@ function matchit#Match_wrapper(word, forward, mode) range let view = winsaveview() call cursor(0, curcol + 1) if skip =~ 'synID' && !(has("syntax") && exists("g:syntax_on")) + \ || skip =~ 'v:lua.vim.treesitter' && !exists('b:ts_highlight') let skip = "0" else execute "if " .. skip .. "| let skip = '0' | endif" @@ -678,6 +679,7 @@ fun! matchit#MultiMatch(spflag, mode) let middlepat = substitute(middlepat, ',', '\\|', 'g') if skip =~ 'synID' && !(has("syntax") && exists("g:syntax_on")) + \ || skip =~ 'v:lua.vim.treesitter' && !exists('b:ts_highlight') let skip = '0' else try @@ -760,10 +762,16 @@ endfun " S:foo becomes (current syntax item) !~ foo " r:foo becomes (line before cursor) =~ foo " R:foo becomes (line before cursor) !~ foo +" t:foo becomes (current treesitter captures) =~ foo +" T:foo becomes (current treesitter captures) !~ foo fun! s:ParseSkip(str) let skip = a:str if skip[1] == ":" - if skip[0] ==# "s" + if skip[0] ==# "t" || skip[0] ==# "s" && &syntax != 'on' && exists("b:ts_highlight") + let skip = "match(v:lua.vim.treesitter.get_captures_at_cursor(), '" .. strpart(skip,2) .. "') != -1" + elseif skip[0] ==# "T" || skip[0] ==# "S" && &syntax != 'on' && exists("b:ts_highlight") + let skip = "match(v:lua.vim.treesitter.get_captures_at_cursor(), '" .. strpart(skip,2) .. "') == -1" + elseif skip[0] ==# "s" let skip = "synIDattr(synID(line('.'),col('.'),1),'name') =~? '" .. \ strpart(skip,2) .. "'" elseif skip[0] ==# "S" diff --git a/runtime/pack/dist/opt/matchit/doc/matchit.txt b/runtime/pack/dist/opt/matchit/doc/matchit.txt index 8d56df6ddc..58031d8a8a 100644 --- a/runtime/pack/dist/opt/matchit/doc/matchit.txt +++ b/runtime/pack/dist/opt/matchit/doc/matchit.txt @@ -237,6 +237,8 @@ supported by matchit.vim: S:foo becomes (current syntax item) !~ foo r:foo becomes (line before cursor) =~ foo R:foo becomes (line before cursor) !~ foo + t:foo becomes (current treesitter captures) =~ foo + T:foo becomes (current treesitter captures) !~ foo (The "s" is meant to suggest "syntax", and the "r" is meant to suggest "regular expression".) diff --git a/runtime/pack/dist/opt/netrw/LICENSE.txt b/runtime/pack/dist/opt/netrw/LICENSE.txt new file mode 100644 index 0000000000..702c2386ac --- /dev/null +++ b/runtime/pack/dist/opt/netrw/LICENSE.txt @@ -0,0 +1,16 @@ +Unless otherwise stated, all files in this directory are distributed under the +Zero-Clause BSD license. + +Zero-Clause BSD +=============== + +Permission to use, copy, modify, and/or distribute this software for +any purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL +WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE +FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY +DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN +AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT +OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/runtime/pack/dist/opt/netrw/README.md b/runtime/pack/dist/opt/netrw/README.md new file mode 100644 index 0000000000..ecd97f1e9a --- /dev/null +++ b/runtime/pack/dist/opt/netrw/README.md @@ -0,0 +1,544 @@ +# Netrw.vim + +netrw.vim plugin from vim (upstream repository) + +The upstream maintained netrw plugin. The original has been created and +maintained by Charles E Campbell and maintained by the vim project until +v9.1.0988. + +Every major version a snapshot from here will be sent to the main [Vim][1] +upstream for distribution with Vim. + +# License + +To see License informations see the LICENSE.txt file included in this +repository. + +# Credits + +Below are stated the contribution made in the past to netrw. + +Changes made to `autoload/netrw.vim`: +- 2023 Nov 21 by Vim Project: ignore wildignore when expanding $COMSPEC (v173a) +- 2023 Nov 22 by Vim Project: fix handling of very long filename on longlist style (v173a) +- 2024 Feb 19 by Vim Project: (announce adoption) +- 2024 Feb 29 by Vim Project: handle symlinks in tree mode correctly +- 2024 Apr 03 by Vim Project: detect filetypes for remote edited files +- 2024 May 08 by Vim Project: cleanup legacy Win9X checks +- 2024 May 09 by Vim Project: remove hard-coded private.ppk +- 2024 May 10 by Vim Project: recursively delete directories by default +- 2024 May 13 by Vim Project: prefer scp over pscp +- 2024 Jun 04 by Vim Project: set bufhidden if buffer changed, nohidden is set and buffer shall be switched (#14915) +- 2024 Jun 13 by Vim Project: glob() on Windows fails when a directory name contains [] (#14952) +- 2024 Jun 23 by Vim Project: save ad restore registers when liststyle = WIDELIST (#15077, #15114) +- 2024 Jul 22 by Vim Project: avoid endless recursion (#15318) +- 2024 Jul 23 by Vim Project: escape filename before trying to delete it (#15330) +- 2024 Jul 30 by Vim Project: handle mark-copy to same target directory (#12112) +- 2024 Aug 02 by Vim Project: honor g:netrw_alt{o,v} for :{S,H,V}explore (#15417) +- 2024 Aug 15 by Vim Project: style changes, prevent E121 (#15501) +- 2024 Aug 22 by Vim Project: fix mf-selection highlight (#15551) +- 2024 Aug 22 by Vim Project: adjust echo output of mx command (#15550) +- 2024 Sep 15 by Vim Project: more strict confirmation dialog (#15680) +- 2024 Sep 19 by Vim Project: mf-selection highlight uses wrong pattern (#15700) +- 2024 Sep 21 by Vim Project: remove extraneous closing bracket (#15718) +- 2024 Oct 21 by Vim Project: remove netrwFileHandlers (#15895) +- 2024 Oct 27 by Vim Project: clean up gx mapping (#15721) +- 2024 Oct 30 by Vim Project: fix filetype detection for remote files (#15961) +- 2024 Oct 30 by Vim Project: fix x mapping on cygwin (#13687) +- 2024 Oct 31 by Vim Project: add netrw#Launch() and netrw#Open() (#15962) +- 2024 Oct 31 by Vim Project: fix E874 when browsing remote dir (#15964) +- 2024 Nov 07 by Vim Project: use keeppatterns to prevent polluting the search history +- 2024 Nov 07 by Vim Project: fix a few issues with netrw tree listing (#15996) +- 2024 Nov 10 by Vim Project: directory symlink not resolved in tree view (#16020) +- 2024 Nov 14 by Vim Project: small fixes to netrw#BrowseX (#16056) +- 2024 Nov 23 by Vim Project: update decompress defaults (#16104) +- 2024 Nov 23 by Vim Project: fix powershell escaping issues (#16094) +- 2024 Dec 04 by Vim Project: do not detach for gvim (#16168) +- 2024 Dec 08 by Vim Project: check the first arg of netrw_browsex_viewer for being executable (#16185) +- 2024 Dec 12 by Vim Project: do not pollute the search history (#16206) +- 2024 Dec 19 by Vim Project: change style (#16248) +- 2024 Dec 20 by Vim Project: change style continued (#16266), fix escaping of # in :Open command (#16265) + +General changes made to netrw: + +``` + v172: Sep 02, 2021 * (Bram Moolenaar) Changed "l:go" to "go" + * (Bram Moolenaar) no need for "b" in + netrw-safe guioptions + Nov 15, 2021 * removed netrw_localrm and netrw_localrmdir + references + Aug 18, 2022 * (Miguel Barro) improving compatibility with + powershell + v171: Oct 09, 2020 * included code in s:NetrwOptionsSafe() + to allow |'bh'| to be set to delete when + rather than hide when g:netrw_fastbrowse + was zero. + * Installed |g:netrw_clipboard| setting + * Installed option bypass for |'guioptions'| + a/A settings + * Changed popup_beval() to |popup_atcursor()| + in netrw#ErrorMsg (lacygoill). Apparently + popup_beval doesn't reliably close the + popup when the mouse is moved. + * VimEnter() now using win_execute to examine + buffers for an attempt to open a directory. + Avoids issues with popups/terminal from + command line. (lacygoill) + Jun 28, 2021 * (zeertzjq) provided a patch for use of + xmap,xno instead of vmap,vno in + netrwPlugin.vim. Avoids entanglement with + select mode. + Jul 14, 2021 * Fixed problem addressed by tst976; opening + a file using tree mode, going up a + directory, and opening a file there was + opening the file in the wrong directory. + Jul 28, 2021 * (Ingo Karkat) provided a patch fixing an + E488 error with netrwPlugin.vim + (occurred for vim versions < 8.02) + v170: Mar 11, 2020 * (reported by Reiner Herrmann) netrw+tree + would not hide with the ^\..* pattern + correctly. + * (Marcin Szamotulski) NetrwOptionRestore + did not restore options correctly that + had a single quote in the option string. + Apr 13, 2020 * implemented error handling via popup + windows (see |popup_beval()|) + Apr 30, 2020 * (reported by Manatsu Takahashi) while + using Lexplore, a modified file could + be overwritten. Sol'n: will not overwrite, + but will emit an |E37| (although one cannot + add an ! to override) + Jun 07, 2020 * (reported by Jo Totland) repeatedly invoking + :Lexplore and quitting it left unused + hidden buffers. Netrw will now set netrw + buffers created by :Lexplore to |'bh'|=wipe. + v169: Dec 20, 2019 * (reported by amkarthik) that netrw's x + (|netrw-x|) would throw an error when + attempting to open a local directory. + v168: Dec 12, 2019 * scp timeout error message not reported, + hopefully now fixed (Shane Xb Qian) + v167: Nov 29, 2019 * netrw does a save&restore on @* and @+. + That causes problems with the clipboard. + Now restores occurs only if @* or @+ have + been changed. + * netrw will change @* or @+ less often. + Never if I happen to have caught all the + operations that modify the unnamed + register (which also writes @*). + * Modified hiding behavior so that "s" + will not ignore hiding. + v166: Nov 06, 2019 * Removed a space from a nmap for "-" + * Numerous debugging statement changes + v163: Dec 05, 2017 * (Cristi Balan) reported that a setting ('sel') + was left changed + * (Holger Mitschke) reported a problem with + saving and restoring history. Fixed. + * Hopefully I fixed a nasty bug that caused a + file rename to wipe out a buffer that it + should not have wiped out. + * (Holger Mitschke) amended this help file + with additional |g:netrw_special_syntax| + items + * Prioritized wget over curl for + g:netrw_http_cmd + v162: Sep 19, 2016 * (haya14busa) pointed out two syntax errors + with a patch; these are now fixed. + Oct 26, 2016 * I started using mate-terminal and found that + x and gx (|netrw-x| and |netrw-gx|) were no + longer working. Fixed (using atril when + $DESKTOP_SESSION is "mate"). + Nov 04, 2016 * (Martin Vuille) pointed out that @+ was + being restored with keepregstar rather than + keepregplus. + Nov 09, 2016 * Broke apart the command from the options, + mostly for Windows. Introduced new netrw + settings: |g:netrw_localcopycmdopt| + |g:netrw_localcopydircmdopt| + |g:netrw_localmkdiropt| + |g:netrw_localmovecmdopt| + Nov 21, 2016 * (mattn) provided a patch for preview; swapped + winwidth() with winheight() + Nov 22, 2016 * (glacambre) reported that files containing + spaces weren't being obtained properly via + scp. Fix: apparently using single quotes + such as with 'file name' wasn't enough; the + spaces inside the quotes also had to be + escaped (ie. 'file\ name'). + * Also fixed obtain (|netrw-O|) to be able to + obtain files with spaces in their names + Dec 20, 2016 * (xc1427) Reported that using "I" (|netrw-I|) + when atop "Hiding" in the banner also caused + the active-banner hiding control to occur + Jan 03, 2017 * (Enno Nagel) reported that attempting to + apply netrw to a directory that was without + read permission caused a syntax error. + Jan 13, 2017 * (Ingo Karkat) provided a patch which makes + using netrw#Call() better. Now returns + value of internal routines return, for example. + Jan 13, 2017 * (Ingo Karkat) changed netrw#FileUrlRead to + use |:edit| instead of |:read|. I also + changed the routine name to netrw#FileUrlEdit. + Jan 16, 2017 * (Sayem) reported a problem where :Lexplore + could generate a new listing buffer and + window instead of toggling the netrw display. + Unfortunately, the directions for eliciting + the problem weren't complete, so I may or + may not have fixed that issue. + Feb 06, 2017 * Implemented cb and cB. Changed "c" to "cd". + (see |netrw-cb|, |netrw-cB|, and |netrw-cd|) + Mar 21, 2017 * previously, netrw would specify (safe) settings + even when the setting was already safe for + netrw. Netrw now attempts to leave such + already-netrw-safe settings alone. + (affects s:NetrwOptionRestore() and + s:NetrwSafeOptions(); also introduced + s:NetrwRestoreSetting()) + Jun 26, 2017 * (Christian Brabandt) provided a patch to + allow curl to follow redirects (ie. -L + option) + Jun 26, 2017 * (Callum Howard) reported a problem with + :Lexpore not removing the Lexplore window + after a change-directory + Aug 30, 2017 * (Ingo Karkat) one cannot switch to the + previously edited file (e.g. with CTRL-^) + after editing a file:// URL. Patch to + have a "keepalt" included. + Oct 17, 2017 * (Adam Faryna) reported that gn (|netrw-gn|) + did not work on directories in the current + tree + v157: Apr 20, 2016 * (Nicola) had set up a "nmap <expr> ..." with + a function that returned a 0 while silently + invoking a shell command. The shell command + activated a ShellCmdPost event which in turn + called s:LocalBrowseRefresh(). That looks + over all netrw buffers for changes needing + refreshes. However, inside a |:map-<expr>|, + tab and window changes are disallowed. Fixed. + (affects netrw's s:LocalBrowseRefresh()) + * g:netrw_localrmdir not used any more, but + the relevant patch that causes |delete()| to + take over was #1107 (not #1109). + * |expand()| is now used on |g:netrw_home|; + consequently, g:netrw_home may now use + environment variables + * s:NetrwLeftmouse and s:NetrwCLeftmouse will + return without doing anything if invoked + when inside a non-netrw window + Jun 15, 2016 * gx now calls netrw#GX() which returns + the word under the cursor. The new + wrinkle: if one is in a netrw buffer, + then netrw's s:NetrwGetWord(). + Jun 22, 2016 * Netrw was executing all its associated + Filetype commands silently; I'm going + to try doing that "noisily" and see if + folks have a problem with that. + Aug 12, 2016 * Changed order of tool selection for + handling http://... viewing. + (Nikolay Aleksandrovich Pavlov) + Aug 21, 2016 * Included hiding/showing/all for tree + listings + * Fixed refresh (^L) for tree listings + 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 + window. + Nov 05, 2015 * Improved s:TreeSqueezeDir() to use search() + instead of a loop + * NetrwBrowse() will return line to + w:netrw_bannercnt if cursor ended up in + banner + Nov 16, 2015 * Added a <Plug>NetrwTreeSqueeze (|netrw-s-cr|) + Nov 17, 2015 * Commented out imaps -- perhaps someone can + tell me how they're useful and should be + retained? + Nov 20, 2015 * Added |netrw-ma| and |netrw-mA| support + Nov 20, 2015 * gx (|netrw-gx|) on a URL downloaded the + file in addition to simply bringing up the + URL in a browser. Fixed. + Nov 23, 2015 * Added |g:netrw_sizestyle| support + Nov 27, 2015 * Inserted a lot of <c-u>s into various netrw + maps. + Jan 05, 2016 * |netrw-qL| implemented to mark files based + upon |location-list|s; similar to |netrw-qF|. + Jan 19, 2016 * using - call delete(directoryname,"d") - + instead of using g:netrw_localrmdir if + v7.4 + patch#1107 is available + Jan 28, 2016 * changed to using |winsaveview()| and + |winrestview()| + Jan 28, 2016 * s:NetrwTreePath() now does a save and + restore of view + Feb 08, 2016 * Fixed a tree-listing problem with remote + directories + v154: Feb 26, 2015 * (Yuri Kanivetsky) reported a situation where + a file was not treated properly as a file + due to g:netrw_keepdir == 1 + Mar 25, 2015 * (requested by Ben Friz) one may now sort by + extension + Mar 28, 2015 * (requested by Matt Brooks) netrw has a lot + of buffer-local mappings; however, some + plugins (such as vim-surround) set up + conflicting mappings that cause vim to wait. + The "<nowait>" modifier has been included + with most of netrw's mappings to avoid that + delay. + Jun 26, 2015 * |netrw-gn| mapping implemented + * :Ntree NotADir resulted in having + the tree listing expand in the error messages + window. Fixed. + Jun 29, 2015 * Attempting to delete a file remotely caused + an error with "keepsol" mentioned; fixed. + Jul 08, 2015 * Several changes to keep the |:jumps| table + correct when working with + |g:netrw_fastbrowse| set to 2 + * wide listing with accented characters fixed + (using %-S instead of %-s with a |printf()| + Jul 13, 2015 * (Daniel Hahler) CheckIfKde() could be true + but kfmclient not installed. Changed order + in netrw#BrowseX(): checks if kde and + kfmclient, then will use xdg-open on a unix + system (if xdg-open is executable) + Aug 11, 2015 * (McDonnell) tree listing mode wouldn't + select a file in a open subdirectory. + * (McDonnell) when multiple subdirectories + were concurrently open in tree listing + mode, a ctrl-L wouldn't refresh properly. + * The netrw:target menu showed duplicate + entries + Oct 13, 2015 * (mattn) provided an exception to handle + windows with shellslash set but no shell + Oct 23, 2015 * if g:netrw_usetab and <c-tab> now used + to control whether NetrwShrink is used + (see |netrw-c-tab|) + v153: May 13, 2014 * added another |g:netrw_ffkeep| usage {{{2 + May 14, 2014 * changed s:PerformListing() so that it + always sets ft=netrw for netrw buffers + (ie. even when syntax highlighting is + off, not available, etc) + May 16, 2014 * introduced the |netrw-ctrl-r| functionality + May 17, 2014 * introduced the |netrw-:NetrwMB| functionality + * mb and mB (|netrw-mb|, |netrw-mB|) will + add/remove marked files from bookmark list + May 20, 2014 * (Enno Nagel) reported that :Lex <dirname> + wasn't working. Fixed. + May 26, 2014 * restored test to prevent leftmouse window + resizing from causing refresh. + (see s:NetrwLeftmouse()) + * fixed problem where a refresh caused cursor + to go just under the banner instead of + staying put + May 28, 2014 * (László Bimba) provided a patch for opening + the |:Lexplore| window 100% high, optionally + on the right, and will work with remote + files. + May 29, 2014 * implemented :NetrwC (see |netrw-:NetrwC|) + Jun 01, 2014 * Removed some "silent"s from commands used + to implemented scp://... and pscp://... + directory listing. Permits request for + password to appear. + Jun 05, 2014 * (Enno Nagel) reported that user maps "/" + caused problems with "b" and "w", which + are mapped (for wide listings only) to + skip over files rather than just words. + Jun 10, 2014 * |g:netrw_gx| introduced to allow users to + override default "<cfile>" with the gx + (|netrw-gx|) map + Jun 11, 2014 * gx (|netrw-gx|), with |'autowrite'| set, + will write modified files. s:NetrwBrowseX() + will now save, turn off, and restore the + |'autowrite'| setting. + Jun 13, 2014 * added visual map for gx use + Jun 15, 2014 * (Enno Nagel) reported that with having hls + set and wide listing style in use, that the + b and w maps caused unwanted highlighting. + Jul 05, 2014 * |netrw-mv| and |netrw-mX| commands included + Jul 09, 2014 * |g:netrw_keepj| included, allowing optional + keepj + Jul 09, 2014 * fixing bugs due to previous update + Jul 21, 2014 * (Bruno Sutic) provided an updated + netrw_gitignore.vim + Jul 30, 2014 * (Yavuz Yetim) reported that editing two + remote files of the same name caused the + second instance to have a "temporary" + name. Fixed: now they use the same buffer. + Sep 18, 2014 * (Yasuhiro Matsumoto) provided a patch which + allows scp and windows local paths to work. + Oct 07, 2014 * gx (see |netrw-gx|) when atop a directory, + will now do |gf| instead + Nov 06, 2014 * For cygwin: cygstart will be available for + netrw#BrowseX() to use if its executable. + Nov 07, 2014 * Began support for file://... urls. Will use + |g:netrw_file_cmd| (typically elinks or links) + Dec 02, 2014 * began work on having mc (|netrw-mc|) copy + directories. Works for linux machines, + cygwin+vim, but not for windows+gvim. + Dec 02, 2014 * in tree mode, netrw was not opening + directories via symbolic links. + Dec 02, 2014 * added resolved link information to + thin and tree modes + Dec 30, 2014 * (issue#231) |:ls| was not showing + remote-file buffers reliably. Fixed. + v152: Apr 08, 2014 * uses the |'noswapfile'| option (requires {{{2 + vim 7.4 with patch 213) + * (Enno Nagel) turn |'rnu'| off in netrw + buffers. + * (Quinn Strahl) suggested that netrw + allow regular window splitting to occur, + thereby allowing |'equalalways'| to take + effect. + * (qingtian zhao) normally, netrw will + save and restore the |'fileformat'|; + however, sometimes that isn't wanted + Apr 14, 2014 * whenever netrw marks a buffer as ro, + it will also mark it as nomod. + Apr 16, 2014 * sftp protocol now supported by + netrw#Obtain(); this means that one + may use "mc" to copy a remote file + to a local file using sftp, and that + the |netrw-O| command can obtain remote + files via sftp. + * added [count]C support (see |netrw-C|) + Apr 18, 2014 * when |g:netrw_chgwin| is one more than + the last window, then vertically split + the last window and use it as the + chgwin window. + May 09, 2014 * SavePosn was "saving filename under cursor" + from a non-netrw window when using :Rex. + v151: Jan 22, 2014 * extended :Rexplore to return to buffer {{{2 + prior to Explore or editing a directory + * (Ken Takata) netrw gave error when + clipboard was disabled. Sol'n: Placed + several if has("clipboard") tests in. + * Fixed ftp://X@Y@Z// problem; X@Y now + part of user id, and only Z is part of + hostname. + * (A Loumiotis) reported that completion + using a directory name containing spaces + did not work. Fixed with a retry in + netrw#Explore() which removes the + backslashes vim inserted. + Feb 26, 2014 * :Rexplore now records the current file + using w:netrw_rexfile when returning via + |:Rexplore| + Mar 08, 2014 * (David Kotchan) provided some patches + allowing netrw to work properly with + windows shares. + * Multiple one-liner help messages available + by pressing <cr> while atop the "Quick + Help" line + * worked on ShellCmdPost, FocusGained event + handling. + * |:Lexplore| path: will be used to update + a left-side netrw browsing directory. + Mar 12, 2014 * |netrw-s-cr|: use <s-cr> to close + tree directory implemented + Mar 13, 2014 * (Tony Mechylynck) reported that using + the browser with ftp on a directory, + and selecting a gzipped txt file, that + an E19 occurred (which was issued by + gzip.vim). Fixed. + Mar 14, 2014 * Implemented :MF and :MT (see |netrw-:MF| + and |netrw-:MT|, respectively) + Mar 17, 2014 * |:Ntree| [dir] wasn't working properly; fixed + Mar 18, 2014 * Changed all uses of set to setl + Mar 18, 2014 * Commented the netrw_btkeep line in + s:NetrwOptionSave(); the effect is that + netrw buffers will remain as |'bt'|=nofile. + This should prevent swapfiles being created + for netrw buffers. + Mar 20, 2014 * Changed all uses of lcd to use s:NetrwLcd() + instead. Consistent error handling results + and it also handles Window's shares + * Fixed |netrw-d| command when applied with ftp + * https: support included for netrw#NetRead() + v150: Jul 12, 2013 * removed a "keepalt" to allow ":e #" to {{{2 + return to the netrw directory listing + Jul 13, 2013 * (Jonas Diemer) suggested changing + a <cWORD> to <cfile>. + Jul 21, 2013 * (Yuri Kanivetsky) reported that netrw's + use of mkdir did not produce directories + following the user's umask. + Aug 27, 2013 * introduced |g:netrw_altfile| option + Sep 05, 2013 * s:Strlen() now uses |strdisplaywidth()| + when available, by default + Sep 12, 2013 * (Selyano Baldo) reported that netrw wasn't + opening some directories properly from the + command line. + Nov 09, 2013 * |:Lexplore| introduced + * (Ondrej Platek) reported an issue with + netrw's trees (P15). Fixed. + * (Jorge Solis) reported that "t" in + tree mode caused netrw to forget its + line position. + Dec 05, 2013 * Added <s-leftmouse> file marking + (see |netrw-mf|) + Dec 05, 2013 * (Yasuhiro Matsumoto) Explore should use + strlen() instead s:Strlen() when handling + multibyte chars with strpart() + (ie. strpart() is byte oriented, not + display-width oriented). + Dec 09, 2013 * (Ken Takata) Provided a patch; File sizes + and a portion of timestamps were wrongly + highlighted with the directory color when + setting `:let g:netrw_liststyle=1` on Windows. + * (Paul Domaskis) noted that sometimes + cursorline was activating in non-netrw + windows. All but one setting of cursorline + was done via setl; there was one that was + overlooked. Fixed. + Dec 24, 2013 * (esquifit) asked that netrw allow the + /cygdrive prefix be a user-alterable + parameter. + Jan 02, 2014 * Fixed a problem with netrw-based ballon + evaluation (ie. netrw#NetrwBaloonHelp() + not having been loaded error messages) + Jan 03, 2014 * Fixed a problem with tree listings + * New command installed: |:Ntree| + Jan 06, 2014 * (Ivan Brennan) reported a problem with + |netrw-P|. Fixed. + Jan 06, 2014 * Fixed a problem with |netrw-P| when the + modified file was to be abandoned. + Jan 15, 2014 * (Matteo Cavalleri) reported that when the + banner is suppressed and tree listing is + used, a blank line was left at the top of + the display. Fixed. + Jan 20, 2014 * (Gideon Go) reported that, in tree listing + style, with a previous window open, that + the wrong directory was being used to open + a file. Fixed. (P21) + v149: Apr 18, 2013 * in wide listing format, now have maps for {{{2 + w and b to move to next/previous file + Apr 26, 2013 * one may now copy files in the same + directory; netrw will issue requests for + what names the files should be copied under + Apr 29, 2013 * Trying Benzinger's problem again. Seems + that commenting out the BufEnter and + installing VimEnter (only) works. Weird + problem! (tree listing, vim -O Dir1 Dir2) + May 01, 2013 * :Explore ftp://... wasn't working. Fixed. + May 02, 2013 * introduced |g:netrw_bannerbackslash| as + requested by Paul Domaskis. + Jul 03, 2013 * Explore now avoids splitting when a buffer + will be hidden. + v148: Apr 16, 2013 * changed Netrw's Style menu to allow direct {{{2 + choice of listing style, hiding style, and + sorting style +``` + +[1]: https://github.com/vim/vim diff --git a/runtime/pack/dist/opt/netrw/autoload/netrw.vim b/runtime/pack/dist/opt/netrw/autoload/netrw.vim new file mode 100644 index 0000000000..ae794954ce --- /dev/null +++ b/runtime/pack/dist/opt/netrw/autoload/netrw.vim @@ -0,0 +1,11927 @@ +" Maintainer: Luca Saccarola <github.e41mv@aleeas.com> +" Former Maintainer: Charles E Campbell +" Upstream: <https://github.com/saccarosium/netrw.vim> +" Copyright: Copyright (C) 2016 Charles E. Campbell {{{1 +" Permission is hereby granted to use and distribute this code, +" with or without modifications, provided that this copyright +" notice is copied with it. Like anything else that's free, +" netrw.vim, netrwPlugin.vim, and netrwSettings.vim are provided +" *as is* and come with no warranty of any kind, either +" expressed or implied. By using this plugin, you agree that +" in no event will the copyright holder be liable for any damages +" resulting from the use of this software. +" +" Note: the code here was started in 1999 under a much earlier version of vim. The directory browsing +" code was written using vim v6, which did not have Lists (Lists were first offered with vim-v7). + +" Load Once: {{{1 +if &cp || exists("g:loaded_netrw") + finish +endif + +" Check that vim has patches that netrw requires. +" Patches needed for v7.4: 1557, and 213. +" (netrw will benefit from vim's having patch#656, too) +let s:needspatches=[1557,213] +if exists("s:needspatches") + for ptch in s:needspatches + if v:version < 704 || (v:version == 704 && !has("patch".ptch)) + if !exists("s:needpatch{ptch}") + unsilent echomsg "***sorry*** this version of netrw requires vim v7.4 with patch#".ptch + endif + let s:needpatch{ptch}= 1 + finish + endif + endfor +endif + +let g:loaded_netrw = "v175" + +let s:keepcpo= &cpo +setl cpo&vim +"DechoFuncName 1 +"DechoRemOn +"call Decho("doing autoload/netrw.vim version ".g:loaded_netrw,'~'.expand("<slnum>")) + +" ====================== +" Netrw Variables: {{{1 +" ====================== + +" --------------------------------------------------------------------- +" netrw#ErrorMsg: {{{2 +" 0=note = s:NOTE +" 1=warning = s:WARNING +" 2=error = s:ERROR +" Usage: netrw#ErrorMsg(s:NOTE | s:WARNING | s:ERROR,"some message",error-number) +" netrw#ErrorMsg(s:NOTE | s:WARNING | s:ERROR,["message1","message2",...],error-number) +" (this function can optionally take a list of messages) +" Dec 2, 2019 : max errnum currently is 106 +fun! netrw#ErrorMsg(level,msg,errnum) + " call Dfunc("netrw#ErrorMsg(level=".a:level." msg<".a:msg."> errnum=".a:errnum.") g:netrw_use_errorwindow=".g:netrw_use_errorwindow) + + if a:level < g:netrw_errorlvl + " call Dret("netrw#ErrorMsg : suppressing level=".a:level." since g:netrw_errorlvl=".g:netrw_errorlvl) + return + endif + + if a:level == 1 + let level= "**warning** (netrw) " + elseif a:level == 2 + let level= "**error** (netrw) " + else + let level= "**note** (netrw) " + endif + " call Decho("level=".level,'~'.expand("<slnum>")) + + if g:netrw_use_errorwindow == 2 && exists("*popup_atcursor") + " use popup window + if type(a:msg) == 3 + let msg = [level]+a:msg + else + let msg= level.a:msg + endif + let s:popuperr_id = popup_atcursor(msg,{}) + let s:popuperr_text= "" + elseif g:netrw_use_errorwindow + " (default) netrw creates a one-line window to show error/warning + " messages (reliably displayed) + + " record current window number + let s:winBeforeErr= winnr() + " call Decho("s:winBeforeErr=".s:winBeforeErr,'~'.expand("<slnum>")) + + " getting messages out reliably is just plain difficult! + " This attempt splits the current window, creating a one line window. + if bufexists("NetrwMessage") && bufwinnr("NetrwMessage") > 0 + " call Decho("write to NetrwMessage buffer",'~'.expand("<slnum>")) + exe bufwinnr("NetrwMessage")."wincmd w" + " call Decho("setl ma noro",'~'.expand("<slnum>")) + setl ma noro + if type(a:msg) == 3 + for msg in a:msg + NetrwKeepj call setline(line("$")+1,level.msg) + endfor + else + NetrwKeepj call setline(line("$")+1,level.a:msg) + endif + NetrwKeepj $ + else + " call Decho("create a NetrwMessage buffer window",'~'.expand("<slnum>")) + bo 1split + sil! call s:NetrwEnew() + sil! NetrwKeepj call s:NetrwOptionsSafe(1) + setl bt=nofile + NetrwKeepj file NetrwMessage + " call Decho("setl ma noro",'~'.expand("<slnum>")) + setl ma noro + if type(a:msg) == 3 + for msg in a:msg + NetrwKeepj call setline(line("$")+1,level.msg) + endfor + else + NetrwKeepj call setline(line("$"),level.a:msg) + endif + NetrwKeepj $ + endif + " call Decho("wrote msg<".level.a:msg."> to NetrwMessage win#".winnr(),'~'.expand("<slnum>")) + if &fo !~ '[ta]' + syn clear + syn match netrwMesgNote "^\*\*note\*\*" + syn match netrwMesgWarning "^\*\*warning\*\*" + syn match netrwMesgError "^\*\*error\*\*" + hi link netrwMesgWarning WarningMsg + hi link netrwMesgError Error + endif + " call Decho("setl noma ro bh=wipe",'~'.expand("<slnum>")) + setl ro nomod noma bh=wipe + + else + " (optional) netrw will show messages using echomsg. Even if the + " message doesn't appear, at least it'll be recallable via :messages + " redraw! + if a:level == s:WARNING + echohl WarningMsg + elseif a:level == s:ERROR + echohl Error + endif + + if type(a:msg) == 3 + for msg in a:msg + unsilent echomsg level.msg + endfor + else + unsilent echomsg level.a:msg + endif + + " call Decho("echomsg ***netrw*** ".a:msg,'~'.expand("<slnum>")) + echohl None + endif + + " call Dret("netrw#ErrorMsg") +endfun + +" --------------------------------------------------------------------- +" s:NetrwInit: initializes variables if they haven't been defined {{{2 +" Loosely, varname = value. +fun s:NetrwInit(varname,value) + " call Decho("varname<".a:varname."> value=".a:value,'~'.expand("<slnum>")) + if !exists(a:varname) + if type(a:value) == 0 + exe "let ".a:varname."=".a:value + elseif type(a:value) == 1 && a:value =~ '^[{[]' + exe "let ".a:varname."=".a:value + elseif type(a:value) == 1 + exe "let ".a:varname."="."'".a:value."'" + else + exe "let ".a:varname."=".a:value + endif + endif +endfun + +" --------------------------------------------------------------------- +" Netrw Constants: {{{2 +call s:NetrwInit("g:netrw_dirhistcnt",0) +if !exists("s:LONGLIST") + call s:NetrwInit("s:THINLIST",0) + call s:NetrwInit("s:LONGLIST",1) + call s:NetrwInit("s:WIDELIST",2) + call s:NetrwInit("s:TREELIST",3) + call s:NetrwInit("s:MAXLIST" ,4) +endif + +let s:NOTE = 0 +let s:WARNING = 1 +let s:ERROR = 2 +call s:NetrwInit("g:netrw_errorlvl", s:NOTE) + +" --------------------------------------------------------------------- +" Default option values: {{{2 +let g:netrw_localcopycmdopt = "" +let g:netrw_localcopydircmdopt = "" +let g:netrw_localmkdiropt = "" +let g:netrw_localmovecmdopt = "" + +" --------------------------------------------------------------------- +" Default values for netrw's global protocol variables {{{2 +if !exists("g:netrw_use_errorwindow") + let g:netrw_use_errorwindow = 0 +endif + +if !exists("g:netrw_dav_cmd") + if executable("cadaver") + let g:netrw_dav_cmd = "cadaver" + elseif executable("curl") + let g:netrw_dav_cmd = "curl" + else + let g:netrw_dav_cmd = "" + endif +endif +if !exists("g:netrw_fetch_cmd") + if executable("fetch") + let g:netrw_fetch_cmd = "fetch -o" + else + let g:netrw_fetch_cmd = "" + endif +endif +if !exists("g:netrw_file_cmd") + if executable("elinks") + call s:NetrwInit("g:netrw_file_cmd","elinks") + elseif executable("links") + call s:NetrwInit("g:netrw_file_cmd","links") + endif +endif +if !exists("g:netrw_ftp_cmd") + let g:netrw_ftp_cmd = "ftp" +endif +let s:netrw_ftp_cmd= g:netrw_ftp_cmd +if !exists("g:netrw_ftp_options") + let g:netrw_ftp_options= "-i -n" +endif +if !exists("g:netrw_http_cmd") + if executable("wget") + let g:netrw_http_cmd = "wget" + call s:NetrwInit("g:netrw_http_xcmd","-q -O") + elseif executable("curl") + let g:netrw_http_cmd = "curl" + call s:NetrwInit("g:netrw_http_xcmd","-L -o") + elseif executable("elinks") + let g:netrw_http_cmd = "elinks" + call s:NetrwInit("g:netrw_http_xcmd","-source >") + elseif executable("fetch") + let g:netrw_http_cmd = "fetch" + call s:NetrwInit("g:netrw_http_xcmd","-o") + elseif executable("links") + let g:netrw_http_cmd = "links" + call s:NetrwInit("g:netrw_http_xcmd","-http.extra-header ".shellescape("Accept-Encoding: identity", 1)." -source >") + else + let g:netrw_http_cmd = "" + endif +endif +call s:NetrwInit("g:netrw_http_put_cmd","curl -T") +call s:NetrwInit("g:netrw_keepj","keepj") +call s:NetrwInit("g:netrw_rcp_cmd" , "rcp") +call s:NetrwInit("g:netrw_rsync_cmd", "rsync") +call s:NetrwInit("g:netrw_rsync_sep", "/") +if !exists("g:netrw_scp_cmd") + if executable("scp") + call s:NetrwInit("g:netrw_scp_cmd" , "scp -q") + elseif executable("pscp") + call s:NetrwInit("g:netrw_scp_cmd", 'pscp -q') + else + call s:NetrwInit("g:netrw_scp_cmd" , "scp -q") + endif +endif +call s:NetrwInit("g:netrw_sftp_cmd" , "sftp") +call s:NetrwInit("g:netrw_ssh_cmd" , "ssh") + +if has("win32") + \ && exists("g:netrw_use_nt_rcp") + \ && g:netrw_use_nt_rcp + \ && executable( $SystemRoot .'/system32/rcp.exe') + let s:netrw_has_nt_rcp = 1 + let s:netrw_rcpmode = '-b' +else + let s:netrw_has_nt_rcp = 0 + let s:netrw_rcpmode = '' +endif + +" --------------------------------------------------------------------- +" Default values for netrw's global variables {{{2 +" Cygwin Detection ------- {{{3 +if !exists("g:netrw_cygwin") + if has("win32unix") && &shell =~ '\%(\<bash\>\|\<zsh\>\)\%(\.exe\)\=$' + let g:netrw_cygwin= 1 + else + let g:netrw_cygwin= 0 + endif +endif +" Default values - a-c ---------- {{{3 +call s:NetrwInit("g:netrw_alto" , &sb) +call s:NetrwInit("g:netrw_altv" , &spr) +call s:NetrwInit("g:netrw_banner" , 1) +call s:NetrwInit("g:netrw_browse_split", 0) +call s:NetrwInit("g:netrw_bufsettings" , "noma nomod nonu nobl nowrap ro nornu") +call s:NetrwInit("g:netrw_chgwin" , -1) +call s:NetrwInit("g:netrw_clipboard" , 1) +call s:NetrwInit("g:netrw_compress" , "gzip") +call s:NetrwInit("g:netrw_ctags" , "ctags") +if exists("g:netrw_cursorline") && !exists("g:netrw_cursor") + call netrw#ErrorMsg(s:NOTE,'g:netrw_cursorline is deprecated; use g:netrw_cursor instead',77) + let g:netrw_cursor= g:netrw_cursorline +endif +call s:NetrwInit("g:netrw_cursor" , 2) +let s:netrw_usercul = &cursorline +let s:netrw_usercuc = &cursorcolumn +"call Decho("(netrw) COMBAK: cuc=".&l:cuc." cul=".&l:cul." initialization of s:netrw_cu[cl]") +call s:NetrwInit("g:netrw_cygdrive","/cygdrive") +" Default values - d-g ---------- {{{3 +call s:NetrwInit("s:didstarstar",0) +call s:NetrwInit("g:netrw_dirhistcnt" , 0) +let s:xz_opt = has('unix') ? "XZ_OPT=-T0" : + \ (has("win32") && &shell =~? '\vcmd(\.exe)?$' ? + \ "setx XZ_OPT=-T0 &&" : "") +call s:NetrwInit("g:netrw_decompress ", "{" + \ .."'.lz4': 'lz4 -d'," + \ .."'.lzo': 'lzop -d'," + \ .."'.lz': 'lzip -dk'," + \ .."'.7z': '7za x'," + \ .."'.001': '7za x'," + \ .."'.zip': 'unzip'," + \ .."'.bz': 'bunzip2 -k'," + \ .."'.bz2': 'bunzip2 -k'," + \ .."'.gz': 'gunzip -k'," + \ .."'.lzma': 'unlzma -T0 -k'," + \ .."'.xz': 'unxz -T0 -k'," + \ .."'.zst': 'zstd -T0 -d'," + \ .."'.Z': 'uncompress -k'," + \ .."'.tar': 'tar -xvf'," + \ .."'.tar.bz': 'tar -xvjf'," + \ .."'.tar.bz2': 'tar -xvjf'," + \ .."'.tbz': 'tar -xvjf'," + \ .."'.tbz2': 'tar -xvjf'," + \ .."'.tar.gz': 'tar -xvzf'," + \ .."'.tgz': 'tar -xvzf'," + \ .."'.tar.lzma': '"..s:xz_opt.." tar -xvf --lzma'," + \ .."'.tlz': '"..s:xz_opt.." tar -xvf --lzma'," + \ .."'.tar.xz': '"..s:xz_opt.." tar -xvfJ'," + \ .."'.txz': '"..s:xz_opt.." tar -xvfJ'," + \ .."'.tar.zst': '"..s:xz_opt.." tar -xvf --use-compress-program=unzstd'," + \ .."'.tzst': '"..s:xz_opt.." tar -xvf --use-compress-program=unzstd'," + \ .."'.rar': '"..(executable("unrar")?"unrar x -ad":"rar x -ad").."'" + \ .."}") +unlet s:xz_opt +call s:NetrwInit("g:netrw_dirhistmax" , 10) +call s:NetrwInit("g:netrw_fastbrowse" , 1) +call s:NetrwInit("g:netrw_ftp_browse_reject", '^total\s\+\d\+$\|^Trying\s\+\d\+.*$\|^KERBEROS_V\d rejected\|^Security extensions not\|No such file\|: connect to address [0-9a-fA-F:]*: No route to host$') +if !exists("g:netrw_ftp_list_cmd") + if has("unix") || (exists("g:netrw_cygwin") && g:netrw_cygwin) + let g:netrw_ftp_list_cmd = "ls -lF" + let g:netrw_ftp_timelist_cmd = "ls -tlF" + let g:netrw_ftp_sizelist_cmd = "ls -slF" + else + let g:netrw_ftp_list_cmd = "dir" + let g:netrw_ftp_timelist_cmd = "dir" + let g:netrw_ftp_sizelist_cmd = "dir" + endif +endif +call s:NetrwInit("g:netrw_ftpmode",'binary') +" Default values - h-lh ---------- {{{3 +call s:NetrwInit("g:netrw_hide",1) +if !exists("g:netrw_ignorenetrc") + if &shell =~ '\c\<\%(cmd\|4nt\)\.exe$' + let g:netrw_ignorenetrc= 1 + else + let g:netrw_ignorenetrc= 0 + endif +endif +call s:NetrwInit("g:netrw_keepdir",1) +if !exists("g:netrw_list_cmd") + if g:netrw_scp_cmd =~ '^pscp' && executable("pscp") + if exists("g:netrw_list_cmd_options") + let g:netrw_list_cmd= g:netrw_scp_cmd." -ls USEPORT HOSTNAME: ".g:netrw_list_cmd_options + else + let g:netrw_list_cmd= g:netrw_scp_cmd." -ls USEPORT HOSTNAME:" + endif + elseif executable(g:netrw_ssh_cmd) + " provide a scp-based default listing command + if exists("g:netrw_list_cmd_options") + let g:netrw_list_cmd= g:netrw_ssh_cmd." USEPORT HOSTNAME ls -FLa ".g:netrw_list_cmd_options + else + let g:netrw_list_cmd= g:netrw_ssh_cmd." USEPORT HOSTNAME ls -FLa" + endif + else + " call Decho(g:netrw_ssh_cmd." is not executable",'~'.expand("<slnum>")) + let g:netrw_list_cmd= "" + endif +endif +call s:NetrwInit("g:netrw_list_hide","") +" Default values - lh-lz ---------- {{{3 +if exists("g:netrw_local_copycmd") + let g:netrw_localcopycmd= g:netrw_local_copycmd + call netrw#ErrorMsg(s:NOTE,"g:netrw_local_copycmd is deprecated in favor of g:netrw_localcopycmd",84) +endif +if !exists("g:netrw_localcmdshell") + let g:netrw_localcmdshell= "" +endif +if !exists("g:netrw_localcopycmd") + if has("win32") + if g:netrw_cygwin + let g:netrw_localcopycmd= "cp" + else + let g:netrw_localcopycmd = expand("$COMSPEC", v:true) + let g:netrw_localcopycmdopt= " /c copy" + endif + elseif has("unix") || has("macunix") + let g:netrw_localcopycmd= "cp" + else + let g:netrw_localcopycmd= "" + endif +endif +if !exists("g:netrw_localcopydircmd") + if has("win32") + if g:netrw_cygwin + let g:netrw_localcopydircmd = "cp" + let g:netrw_localcopydircmdopt= " -R" + else + let g:netrw_localcopydircmd = expand("$COMSPEC", v:true) + let g:netrw_localcopydircmdopt= " /c xcopy /e /c /h /i /k" + endif + elseif has("unix") + let g:netrw_localcopydircmd = "cp" + let g:netrw_localcopydircmdopt= " -R" + elseif has("macunix") + let g:netrw_localcopydircmd = "cp" + let g:netrw_localcopydircmdopt= " -R" + else + let g:netrw_localcopydircmd= "" + endif +endif +if exists("g:netrw_local_mkdir") + let g:netrw_localmkdir= g:netrw_local_mkdir + call netrw#ErrorMsg(s:NOTE,"g:netrw_local_mkdir is deprecated in favor of g:netrw_localmkdir",87) +endif +if has("win32") + if g:netrw_cygwin + call s:NetrwInit("g:netrw_localmkdir","mkdir") + else + let g:netrw_localmkdir = expand("$COMSPEC", v:true) + let g:netrw_localmkdiropt= " /c mkdir" + endif +else + call s:NetrwInit("g:netrw_localmkdir","mkdir") +endif +call s:NetrwInit("g:netrw_remote_mkdir","mkdir") +if exists("g:netrw_local_movecmd") + let g:netrw_localmovecmd= g:netrw_local_movecmd + call netrw#ErrorMsg(s:NOTE,"g:netrw_local_movecmd is deprecated in favor of g:netrw_localmovecmd",88) +endif +if !exists("g:netrw_localmovecmd") + if has("win32") + if g:netrw_cygwin + let g:netrw_localmovecmd= "mv" + else + let g:netrw_localmovecmd = expand("$COMSPEC", v:true) + let g:netrw_localmovecmdopt= " /c move" + endif + elseif has("unix") || has("macunix") + let g:netrw_localmovecmd= "mv" + else + let g:netrw_localmovecmd= "" + endif +endif +" following serves as an example for how to insert a version&patch specific test +"if v:version < 704 || (v:version == 704 && !has("patch1107")) +"endif +call s:NetrwInit("g:netrw_liststyle" , s:THINLIST) +" sanity checks +if g:netrw_liststyle < 0 || g:netrw_liststyle >= s:MAXLIST + let g:netrw_liststyle= s:THINLIST +endif +if g:netrw_liststyle == s:LONGLIST && g:netrw_scp_cmd !~ '^pscp' + let g:netrw_list_cmd= g:netrw_list_cmd." -l" +endif +" Default values - m-r ---------- {{{3 +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_retmap" , 0) +if has("unix") || (exists("g:netrw_cygwin") && g:netrw_cygwin) + call s:NetrwInit("g:netrw_chgperm" , "chmod PERM FILENAME") +elseif has("win32") + call s:NetrwInit("g:netrw_chgperm" , "cacls FILENAME /e /p PERM") +else + call s:NetrwInit("g:netrw_chgperm" , "chmod PERM FILENAME") +endif +call s:NetrwInit("g:netrw_preview" , 0) +call s:NetrwInit("g:netrw_scpport" , "-P") +call s:NetrwInit("g:netrw_servername" , "NETRWSERVER") +call s:NetrwInit("g:netrw_sshport" , "-p") +call s:NetrwInit("g:netrw_rename_cmd" , g:netrw_ssh_cmd." USEPORT HOSTNAME mv") +call s:NetrwInit("g:netrw_rm_cmd" , g:netrw_ssh_cmd." USEPORT HOSTNAME rm") +call s:NetrwInit("g:netrw_rmdir_cmd" , g:netrw_ssh_cmd." USEPORT HOSTNAME rmdir") +call s:NetrwInit("g:netrw_rmf_cmd" , g:netrw_ssh_cmd." USEPORT HOSTNAME rm -f ") +" Default values - q-s ---------- {{{3 +call s:NetrwInit("g:netrw_quickhelp",0) +let s:QuickHelp= ["-:go up dir D:delete R:rename s:sort-by x:special", + \ "(create new) %:file d:directory", + \ "(windows split&open) o:horz v:vert p:preview", + \ "i:style qf:file info O:obtain r:reverse", + \ "(marks) mf:mark file mt:set target mm:move mc:copy", + \ "(bookmarks) mb:make mB:delete qb:list gb:go to", + \ "(history) qb:list u:go up U:go down", + \ "(targets) mt:target Tb:use bookmark Th:use history"] +" g:netrw_sepchr: picking a character that doesn't appear in filenames that can be used to separate priority from filename +call s:NetrwInit("g:netrw_sepchr" , (&enc == "euc-jp")? "\<Char-0x01>" : "\<Char-0xff>") +if !exists("g:netrw_keepj") || g:netrw_keepj == "keepj" + call s:NetrwInit("s:netrw_silentxfer" , (exists("g:netrw_silent") && g:netrw_silent != 0)? "sil keepj " : "keepj ") +else + call s:NetrwInit("s:netrw_silentxfer" , (exists("g:netrw_silent") && g:netrw_silent != 0)? "sil " : " ") +endif +call s:NetrwInit("g:netrw_sort_by" , "name") " alternatives: date , size +call s:NetrwInit("g:netrw_sort_options" , "") +call s:NetrwInit("g:netrw_sort_direction", "normal") " alternative: reverse (z y x ...) +if !exists("g:netrw_sort_sequence") + if has("unix") + let g:netrw_sort_sequence= '[\/]$,\<core\%(\.\d\+\)\=\>,\.h$,\.c$,\.cpp$,\~\=\*$,*,\.o$,\.obj$,\.info$,\.swp$,\.bak$,\~$' + else + let g:netrw_sort_sequence= '[\/]$,\.h$,\.c$,\.cpp$,*,\.o$,\.obj$,\.info$,\.swp$,\.bak$,\~$' + endif +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_use_noswf" , 1) +call s:NetrwInit("g:netrw_sizestyle" ,"b") +" Default values - t-w ---------- {{{3 +call s:NetrwInit("g:netrw_timefmt","%c") +if !exists("g:netrw_xstrlen") + if exists("g:Align_xstrlen") + let g:netrw_xstrlen= g:Align_xstrlen + elseif exists("g:drawit_xstrlen") + let g:netrw_xstrlen= g:drawit_xstrlen + elseif &enc == "latin1" || !has("multi_byte") + let g:netrw_xstrlen= 0 + else + let g:netrw_xstrlen= 1 + endif +endif +call s:NetrwInit("g:NetrwTopLvlMenu","Netrw.") +call s:NetrwInit("g:netrw_winsize",50) +call s:NetrwInit("g:netrw_wiw",1) +if g:netrw_winsize > 100|let g:netrw_winsize= 100|endif +" --------------------------------------------------------------------- +" Default values for netrw's script variables: {{{2 +call s:NetrwInit("g:netrw_fname_escape",' ?&;%') +if has("win32") + call s:NetrwInit("g:netrw_glob_escape",'*?`{[]$') +else + call s:NetrwInit("g:netrw_glob_escape",'*[]?`{~$\') +endif +call s:NetrwInit("g:netrw_menu_escape",'.&? \') +call s:NetrwInit("g:netrw_tmpfile_escape",' &;') +call s:NetrwInit("s:netrw_map_escape","<|\n\r\\\<C-V>\"") +if has("gui_running") && (&enc == 'utf-8' || &enc == 'utf-16' || &enc == 'ucs-4') + let s:treedepthstring= "│ " +else + let s:treedepthstring= "| " +endif +call s:NetrwInit("s:netrw_posn",'{}') + +" BufEnter event ignored by decho when following variable is true +" Has a side effect that doau BufReadPost doesn't work, so +" files read by network transfer aren't appropriately highlighted. +"let g:decho_bufenter = 1 "Decho + +" ====================== +" Netrw Initialization: {{{1 +" ====================== +if v:version >= 700 && has("balloon_eval") && !exists("s:initbeval") && !exists("g:netrw_nobeval") && has("syntax") && exists("g:syntax_on") + " call Decho("installed beval events",'~'.expand("<slnum>")) + let &l:bexpr = "netrw#BalloonHelp()" + " call Decho("&l:bexpr<".&l:bexpr."> buf#".bufnr()) + au FileType netrw setl beval + au WinLeave * if &ft == "netrw" && exists("s:initbeval")|let &beval= s:initbeval|endif + au VimEnter * let s:initbeval= &beval + "else " Decho + " if v:version < 700 | call Decho("did not install beval events: v:version=".v:version." < 700","~".expand("<slnum>")) | endif + " if !has("balloon_eval") | call Decho("did not install beval events: does not have balloon_eval","~".expand("<slnum>")) | endif + " if exists("s:initbeval") | call Decho("did not install beval events: s:initbeval exists","~".expand("<slnum>")) | endif + " if exists("g:netrw_nobeval") | call Decho("did not install beval events: g:netrw_nobeval exists","~".expand("<slnum>")) | endif + " if !has("syntax") | call Decho("did not install beval events: does not have syntax highlighting","~".expand("<slnum>")) | endif + " if exists("g:syntax_on") | call Decho("did not install beval events: g:syntax_on exists","~".expand("<slnum>")) | endif +endif +au WinEnter * if &ft == "netrw"|call s:NetrwInsureWinVars()|endif + +if g:netrw_keepj =~# "keepj" + com! -nargs=* NetrwKeepj keepj <args> +else + let g:netrw_keepj= "" + com! -nargs=* NetrwKeepj <args> +endif + +" ============================== +" Netrw Utility Functions: {{{1 +" ============================== + +" --------------------------------------------------------------------- +" netrw#BalloonHelp: {{{2 +if v:version >= 700 && has("balloon_eval") && has("syntax") && exists("g:syntax_on") && !exists("g:netrw_nobeval") + " call Decho("loading netrw#BalloonHelp()",'~'.expand("<slnum>")) + fun! netrw#BalloonHelp() + if &ft != "netrw" + return "" + endif + if exists("s:popuperr_id") && popup_getpos(s:popuperr_id) != {} + " popup error window is still showing + " s:pouperr_id and s:popuperr_text are set up in netrw#ErrorMsg() + if exists("s:popuperr_text") && s:popuperr_text != "" && v:beval_text != s:popuperr_text + " text under mouse hasn't changed; only close window when it changes + call popup_close(s:popuperr_id) + unlet s:popuperr_text + else + let s:popuperr_text= v:beval_text + endif + let mesg= "" + elseif !exists("w:netrw_bannercnt") || v:beval_lnum >= w:netrw_bannercnt || (exists("g:netrw_nobeval") && g:netrw_nobeval) + let mesg= "" + elseif v:beval_text == "Netrw" || v:beval_text == "Directory" || v:beval_text == "Listing" + let mesg = "i: thin-long-wide-tree gh: quick hide/unhide of dot-files qf: quick file info %:open new file" + elseif getline(v:beval_lnum) =~ '^"\s*/' + let mesg = "<cr>: edit/enter o: edit/enter in horiz window t: edit/enter in new tab v:edit/enter in vert window" + elseif v:beval_text == "Sorted" || v:beval_text == "by" + let mesg = 's: sort by name, time, file size, extension r: reverse sorting order mt: mark target' + elseif v:beval_text == "Sort" || v:beval_text == "sequence" + let mesg = "S: edit sorting sequence" + elseif v:beval_text == "Hiding" || v:beval_text == "Showing" + let mesg = "a: hiding-showing-all ctrl-h: editing hiding list mh: hide/show by suffix" + elseif v:beval_text == "Quick" || v:beval_text == "Help" + let mesg = "Help: press <F1>" + elseif v:beval_text == "Copy/Move" || v:beval_text == "Tgt" + let mesg = "mt: mark target mc: copy marked file to target mm: move marked file to target" + else + let mesg= "" + endif + return mesg + endfun + "else " Decho + " if v:version < 700 |call Decho("did not load netrw#BalloonHelp(): vim version ".v:version." < 700 -","~".expand("<slnum>"))|endif + " if !has("balloon_eval") |call Decho("did not load netrw#BalloonHelp(): does not have balloon eval","~".expand("<slnum>")) |endif + " if !has("syntax") |call Decho("did not load netrw#BalloonHelp(): syntax disabled","~".expand("<slnum>")) |endif + " if !exists("g:syntax_on") |call Decho("did not load netrw#BalloonHelp(): g:syntax_on n/a","~".expand("<slnum>")) |endif + " if exists("g:netrw_nobeval") |call Decho("did not load netrw#BalloonHelp(): g:netrw_nobeval exists","~".expand("<slnum>")) |endif +endif + +" ------------------------------------------------------------------------ +" netrw#Explore: launch the local browser in the directory of the current file {{{2 +" indx: == -1: Nexplore +" == -2: Pexplore +" == +: this is overloaded: +" * If Nexplore/Pexplore is in use, then this refers to the +" indx'th item in the w:netrw_explore_list[] of items which +" matched the */pattern **/pattern *//pattern **//pattern +" * If Hexplore or Vexplore, then this will override +" g:netrw_winsize to specify the qty of rows or columns the +" newly split window should have. +" dosplit==0: the window will be split iff the current file has been modified and hidden not set +" dosplit==1: the window will be split before running the local browser +" style == 0: Explore style == 1: Explore! +" == 2: Hexplore style == 3: Hexplore! +" == 4: Vexplore style == 5: Vexplore! +" == 6: Texplore +fun! netrw#Explore(indx,dosplit,style,...) + if !exists("b:netrw_curdir") + let b:netrw_curdir= getcwd() + endif + + " record current file for Rexplore's benefit + if &ft != "netrw" + let w:netrw_rexfile= expand("%:p") + endif + + " record current directory + let curdir = simplify(b:netrw_curdir) + let curfiledir = substitute(expand("%:p"),'^\(.*[/\\]\)[^/\\]*$','\1','e') + if !exists("g:netrw_cygwin") && has("win32") + let curdir= substitute(curdir,'\','/','g') + endif + + " using completion, directories with spaces in their names (thanks, Bill Gates, for a truly dumb idea) + " will end up with backslashes here. Solution: strip off backslashes that precede white space and + " try Explore again. + if a:0 > 0 + if a:1 =~ "\\\s" && !filereadable(s:NetrwFile(a:1)) && !isdirectory(s:NetrwFile(a:1)) + let a1 = substitute(a:1, '\\\(\s\)', '\1', 'g') + if a1 != a:1 + call netrw#Explore(a:indx, a:dosplit, a:style, a1) + return + endif + endif + endif + + " save registers + if !has('nvim') && has("clipboard") && g:netrw_clipboard + " call Decho("(netrw#Explore) save @* and @+",'~'.expand("<slnum>")) + sil! let keepregstar = @* + sil! let keepregplus = @+ + endif + sil! let keepregslash= @/ + + " if dosplit + " -or- file has been modified AND file not hidden when abandoned + " -or- Texplore used + if a:dosplit || (&modified && &hidden == 0 && &bufhidden != "hide") || a:style == 6 + call s:SaveWinVars() + let winsz= g:netrw_winsize + if a:indx > 0 + let winsz= a:indx + endif + + if a:style == 0 " Explore, Sexplore + let winsz= (winsz > 0)? (winsz*winheight(0))/100 : -winsz + if winsz == 0|let winsz= ""|endif + exe "noswapfile ".(g:netrw_alto ? "below " : "above ").winsz."wincmd s" + + elseif a:style == 1 " Explore!, Sexplore! + let winsz= (winsz > 0)? (winsz*winwidth(0))/100 : -winsz + if winsz == 0|let winsz= ""|endif + exe "keepalt noswapfile ".(g:netrw_altv ? "rightbelow " : "leftabove ").winsz."wincmd v" + + elseif a:style == 2 " Hexplore + let winsz= (winsz > 0)? (winsz*winheight(0))/100 : -winsz + if winsz == 0|let winsz= ""|endif + exe "keepalt noswapfile ".(g:netrw_alto ? "below " : "above ").winsz."wincmd s" + + elseif a:style == 3 " Hexplore! + let winsz= (winsz > 0)? (winsz*winheight(0))/100 : -winsz + if winsz == 0|let winsz= ""|endif + exe "keepalt noswapfile ".(!g:netrw_alto ? "below " : "above ").winsz."wincmd s" + + elseif a:style == 4 " Vexplore + let winsz= (winsz > 0)? (winsz*winwidth(0))/100 : -winsz + if winsz == 0|let winsz= ""|endif + exe "keepalt noswapfile ".(g:netrw_altv ? "rightbelow " : "leftabove ").winsz."wincmd v" + + elseif a:style == 5 " Vexplore! + let winsz= (winsz > 0)? (winsz*winwidth(0))/100 : -winsz + if winsz == 0|let winsz= ""|endif + exe "keepalt noswapfile ".(!g:netrw_altv ? "rightbelow " : "leftabove ").winsz."wincmd v" + + elseif a:style == 6 " Texplore + call s:SaveBufVars() + exe "keepalt tabnew ".fnameescape(curdir) + call s:RestoreBufVars() + endif + call s:RestoreWinVars() + endif + NetrwKeepj norm! 0 + + if a:0 > 0 + if a:1 =~ '^\~' && (has("unix") || (exists("g:netrw_cygwin") && g:netrw_cygwin)) + let dirname= simplify(substitute(a:1,'\~',expand("$HOME"),'')) + elseif a:1 == '.' + let dirname= simplify(exists("b:netrw_curdir")? b:netrw_curdir : getcwd()) + if dirname !~ '/$' + let dirname= dirname."/" + endif + elseif a:1 =~ '\$' + let dirname= simplify(expand(a:1)) + elseif a:1 !~ '^\*\{1,2}/' && a:1 !~ '^\a\{3,}://' + let dirname= simplify(a:1) + else + let dirname= a:1 + endif + else + " clear explore + call s:NetrwClearExplore() + return + endif + + if dirname =~ '\.\./\=$' + let dirname= simplify(fnamemodify(dirname,':p:h')) + elseif dirname =~ '\.\.' || dirname == '.' + let dirname= simplify(fnamemodify(dirname,':p')) + endif + + if dirname =~ '^\*//' + " starpat=1: Explore *//pattern (current directory only search for files containing pattern) + let pattern= substitute(dirname,'^\*//\(.*\)$','\1','') + let starpat= 1 + if &hls | let keepregslash= s:ExplorePatHls(pattern) | endif + + elseif dirname =~ '^\*\*//' + " starpat=2: Explore **//pattern (recursive descent search for files containing pattern) + let pattern= substitute(dirname,'^\*\*//','','') + let starpat= 2 + + elseif dirname =~ '/\*\*/' + " handle .../**/.../filepat + let prefixdir= substitute(dirname,'^\(.\{-}\)\*\*.*$','\1','') + if prefixdir =~ '^/' || (prefixdir =~ '^\a:/' && has("win32")) + let b:netrw_curdir = prefixdir + else + let b:netrw_curdir= getcwd().'/'.prefixdir + endif + let dirname= substitute(dirname,'^.\{-}\(\*\*/.*\)$','\1','') + let starpat= 4 + + elseif dirname =~ '^\*/' + " case starpat=3: Explore */filepat (search in current directory for filenames matching filepat) + let starpat= 3 + + elseif dirname=~ '^\*\*/' + " starpat=4: Explore **/filepat (recursive descent search for filenames matching filepat) + let starpat= 4 + + else + let starpat= 0 + endif + + if starpat == 0 && a:indx >= 0 + " [Explore Hexplore Vexplore Sexplore] [dirname] + if dirname == "" + let dirname= curfiledir + endif + if dirname =~# '^scp://' || dirname =~ '^ftp://' + call netrw#Nread(2,dirname) + else + if dirname == "" + let dirname= getcwd() + elseif has("win32") && !g:netrw_cygwin + " Windows : check for a drive specifier, or else for a remote share name ('\\Foo' or '//Foo', + " depending on whether backslashes have been converted to forward slashes by earlier code). + if dirname !~ '^[a-zA-Z]:' && dirname !~ '^\\\\\w\+' && dirname !~ '^//\w\+' + let dirname= b:netrw_curdir."/".dirname + endif + elseif dirname !~ '^/' + let dirname= b:netrw_curdir."/".dirname + endif + call netrw#LocalBrowseCheck(dirname) + endif + if exists("w:netrw_bannercnt") + " done to handle P08-Ingelrest. :Explore will _Always_ go to the line just after the banner. + " If one wants to return the same place in the netrw window, use :Rex instead. + exe w:netrw_bannercnt + endif + + + " starpat=1: Explore *//pattern (current directory only search for files containing pattern) + " starpat=2: Explore **//pattern (recursive descent search for files containing pattern) + " starpat=3: Explore */filepat (search in current directory for filenames matching filepat) + " starpat=4: Explore **/filepat (recursive descent search for filenames matching filepat) + elseif a:indx <= 0 + " Nexplore, Pexplore, Explore: handle starpat + if !mapcheck("<s-up>","n") && !mapcheck("<s-down>","n") && exists("b:netrw_curdir") + let s:didstarstar= 1 + nnoremap <buffer> <silent> <s-up> :Pexplore<cr> + nnoremap <buffer> <silent> <s-down> :Nexplore<cr> + endif + + if has("path_extra") + if !exists("w:netrw_explore_indx") + let w:netrw_explore_indx= 0 + endif + + let indx = a:indx + + if indx == -1 + " Nexplore + if !exists("w:netrw_explore_list") " sanity check + NetrwKeepj call netrw#ErrorMsg(s:WARNING,"using Nexplore or <s-down> improperly; see help for netrw-starstar",40) + if !has('nvim') && has("clipboard") && g:netrw_clipboard + if @* != keepregstar | sil! let @* = keepregstar | endif + if @+ != keepregplus | sil! let @+ = keepregplus | endif + endif + sil! let @/ = keepregslash + return + endif + let indx= w:netrw_explore_indx + if indx < 0 | let indx= 0 | endif + if indx >= w:netrw_explore_listlen | let indx= w:netrw_explore_listlen - 1 | endif + let curfile= w:netrw_explore_list[indx] + while indx < w:netrw_explore_listlen && curfile == w:netrw_explore_list[indx] + let indx= indx + 1 + endwhile + if indx >= w:netrw_explore_listlen | let indx= w:netrw_explore_listlen - 1 | endif + + elseif indx == -2 + " Pexplore + if !exists("w:netrw_explore_list") " sanity check + NetrwKeepj call netrw#ErrorMsg(s:WARNING,"using Pexplore or <s-up> improperly; see help for netrw-starstar",41) + if !has('nvim') && has("clipboard") && g:netrw_clipboard + if @* != keepregstar | sil! let @* = keepregstar | endif + if @+ != keepregplus | sil! let @+ = keepregplus | endif + endif + sil! let @/ = keepregslash + return + endif + let indx= w:netrw_explore_indx + if indx < 0 | let indx= 0 | endif + if indx >= w:netrw_explore_listlen | let indx= w:netrw_explore_listlen - 1 | endif + let curfile= w:netrw_explore_list[indx] + while indx >= 0 && curfile == w:netrw_explore_list[indx] + let indx= indx - 1 + endwhile + if indx < 0 | let indx= 0 | endif + + else + " Explore -- initialize + " build list of files to Explore with Nexplore/Pexplore + NetrwKeepj keepalt call s:NetrwClearExplore() + let w:netrw_explore_indx= 0 + if !exists("b:netrw_curdir") + let b:netrw_curdir= getcwd() + endif + + " switch on starpat to build the w:netrw_explore_list of files + if starpat == 1 + " starpat=1: Explore *//pattern (current directory only search for files containing pattern) + try + exe "NetrwKeepj noautocmd vimgrep /".pattern."/gj ".fnameescape(b:netrw_curdir)."/*" + catch /^Vim\%((\a\+)\)\=:E480/ + keepalt call netrw#ErrorMsg(s:WARNING,"no match with pattern<".pattern.">",76) + return + endtry + let w:netrw_explore_list = s:NetrwExploreListUniq(map(getqflist(),'bufname(v:val.bufnr)')) + if &hls | let keepregslash= s:ExplorePatHls(pattern) | endif + + elseif starpat == 2 + " starpat=2: Explore **//pattern (recursive descent search for files containing pattern) + try + exe "sil NetrwKeepj noautocmd keepalt vimgrep /".pattern."/gj "."**/*" + catch /^Vim\%((\a\+)\)\=:E480/ + keepalt call netrw#ErrorMsg(s:WARNING,'no files matched pattern<'.pattern.'>',45) + if &hls | let keepregslash= s:ExplorePatHls(pattern) | endif + if !has('nvim') && has("clipboard") && g:netrw_clipboard + if @* != keepregstar | sil! let @* = keepregstar | endif + if @+ != keepregplus | sil! let @+ = keepregplus | endif + endif + sil! let @/ = keepregslash + return + endtry + let s:netrw_curdir = b:netrw_curdir + let w:netrw_explore_list = getqflist() + let w:netrw_explore_list = s:NetrwExploreListUniq(map(w:netrw_explore_list,'s:netrw_curdir."/".bufname(v:val.bufnr)')) + if &hls | let keepregslash= s:ExplorePatHls(pattern) | endif + + elseif starpat == 3 + " starpat=3: Explore */filepat (search in current directory for filenames matching filepat) + let filepat= substitute(dirname,'^\*/','','') + let filepat= substitute(filepat,'^[%#<]','\\&','') + let w:netrw_explore_list= s:NetrwExploreListUniq(split(expand(b:netrw_curdir."/".filepat),'\n')) + if &hls | let keepregslash= s:ExplorePatHls(filepat) | endif + + elseif starpat == 4 + " starpat=4: Explore **/filepat (recursive descent search for filenames matching filepat) + let w:netrw_explore_list= s:NetrwExploreListUniq(split(expand(b:netrw_curdir."/".dirname),'\n')) + if &hls | let keepregslash= s:ExplorePatHls(dirname) | endif + endif " switch on starpat to build w:netrw_explore_list + + let w:netrw_explore_listlen = len(w:netrw_explore_list) + + if w:netrw_explore_listlen == 0 || (w:netrw_explore_listlen == 1 && w:netrw_explore_list[0] =~ '\*\*\/') + keepalt NetrwKeepj call netrw#ErrorMsg(s:WARNING,"no files matched",42) + if !has('nvim') && has("clipboard") && g:netrw_clipboard + if @* != keepregstar | sil! let @* = keepregstar | endif + if @+ != keepregplus | sil! let @+ = keepregplus | endif + endif + sil! let @/ = keepregslash + return + endif + endif " if indx ... endif + + " NetrwStatusLine support - for exploring support + let w:netrw_explore_indx= indx + + " wrap the indx around, but issue a note + if indx >= w:netrw_explore_listlen || indx < 0 + let indx = (indx < 0)? ( w:netrw_explore_listlen - 1 ) : 0 + let w:netrw_explore_indx= indx + keepalt NetrwKeepj call netrw#ErrorMsg(s:NOTE,"no more files match Explore pattern",43) + endif + + exe "let dirfile= w:netrw_explore_list[".indx."]" + let newdir= substitute(dirfile,'/[^/]*$','','e') + + call netrw#LocalBrowseCheck(newdir) + if !exists("w:netrw_liststyle") + let w:netrw_liststyle= g:netrw_liststyle + endif + if w:netrw_liststyle == s:THINLIST || w:netrw_liststyle == s:LONGLIST + keepalt NetrwKeepj call search('^'.substitute(dirfile,"^.*/","","").'\>',"W") + else + keepalt NetrwKeepj call search('\<'.substitute(dirfile,"^.*/","","").'\>',"w") + endif + let w:netrw_explore_mtchcnt = indx + 1 + let w:netrw_explore_bufnr = bufnr("%") + let w:netrw_explore_line = line(".") + keepalt NetrwKeepj call s:SetupNetrwStatusLine('%f %h%m%r%=%9*%{NetrwStatusLine()}') + + else + if !exists("g:netrw_quiet") + keepalt NetrwKeepj call netrw#ErrorMsg(s:WARNING,"your vim needs the +path_extra feature for Exploring with **!",44) + endif + if !has('nvim') && has("clipboard") && g:netrw_clipboard + if @* != keepregstar | sil! let @* = keepregstar | endif + if @+ != keepregplus | sil! let @+ = keepregplus | endif + endif + sil! let @/ = keepregslash + return + endif + + else + if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && dirname =~ '/' + sil! unlet w:netrw_treedict + sil! unlet w:netrw_treetop + endif + let newdir= dirname + if !exists("b:netrw_curdir") + NetrwKeepj call netrw#LocalBrowseCheck(getcwd()) + else + NetrwKeepj call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,newdir,0)) + endif + endif + + " visual display of **/ **// */ Exploration files + if exists("w:netrw_explore_indx") && exists("b:netrw_curdir") + if !exists("s:explore_prvdir") || s:explore_prvdir != b:netrw_curdir + " only update match list when current directory isn't the same as before + let s:explore_prvdir = b:netrw_curdir + let s:explore_match = "" + let dirlen = strlen(b:netrw_curdir) + if b:netrw_curdir !~ '/$' + let dirlen= dirlen + 1 + endif + let prvfname= "" + for fname in w:netrw_explore_list + if fname =~ '^'.b:netrw_curdir + if s:explore_match == "" + let s:explore_match= '\<'.escape(strpart(fname,dirlen),g:netrw_markfileesc).'\>' + else + let s:explore_match= s:explore_match.'\|\<'.escape(strpart(fname,dirlen),g:netrw_markfileesc).'\>' + endif + elseif fname !~ '^/' && fname != prvfname + if s:explore_match == "" + let s:explore_match= '\<'.escape(fname,g:netrw_markfileesc).'\>' + else + let s:explore_match= s:explore_match.'\|\<'.escape(fname,g:netrw_markfileesc).'\>' + endif + endif + let prvfname= fname + endfor + if has("syntax") && exists("g:syntax_on") && g:syntax_on + exe "2match netrwMarkFile /".s:explore_match."/" + endif + endif + echo "<s-up>==Pexplore <s-down>==Nexplore" + else + 2match none + if exists("s:explore_match") | unlet s:explore_match | endif + if exists("s:explore_prvdir") | unlet s:explore_prvdir | endif + endif + + " since Explore may be used to initialize netrw's browser, + " there's no danger of a late FocusGained event on initialization. + " Consequently, set s:netrw_events to 2. + let s:netrw_events= 2 + if !has('nvim') && has("clipboard") && g:netrw_clipboard + if @* != keepregstar | sil! let @* = keepregstar | endif + if @+ != keepregplus | sil! let @+ = keepregplus | endif + endif + sil! let @/ = keepregslash +endfun + +" --------------------------------------------------------------------- +" netrw#Lexplore: toggle Explorer window, keeping it on the left of the current tab {{{2 +" Uses g:netrw_chgwin : specifies the window where Lexplore files are to be opened +" t:netrw_lexposn : winsaveview() output (used on Lexplore window) +" t:netrw_lexbufnr: the buffer number of the Lexplore buffer (internal to this function) +" s:lexplore_win : window number of Lexplore window (serves to indicate which window is a Lexplore window) +" w:lexplore_buf : buffer number of Lexplore window (serves to indicate which window is a Lexplore window) +fun! netrw#Lexplore(count,rightside,...) + " call Dfunc("netrw#Lexplore(count=".a:count." rightside=".a:rightside.",...) a:0=".a:0." ft=".&ft) + let curwin= winnr() + + if a:0 > 0 && a:1 != "" + " if a netrw window is already on the left-side of the tab + " and a directory has been specified, explore with that + " directory. + let a1 = expand(a:1) + exe "1wincmd w" + if &ft == "netrw" + exe "Explore ".fnameescape(a1) + exe curwin."wincmd w" + let s:lexplore_win= curwin + let w:lexplore_buf= bufnr("%") + if exists("t:netrw_lexposn") + unlet t:netrw_lexposn + endif + return + endif + exe curwin."wincmd w" + else + let a1= "" + endif + + if exists("t:netrw_lexbufnr") + " check if t:netrw_lexbufnr refers to a netrw window + let lexwinnr = bufwinnr(t:netrw_lexbufnr) + else + let lexwinnr= 0 + endif + + if lexwinnr > 0 + " close down netrw explorer window + exe lexwinnr."wincmd w" + let g:netrw_winsize = -winwidth(0) + let t:netrw_lexposn = winsaveview() + close + if lexwinnr < curwin + let curwin= curwin - 1 + endif + if lexwinnr != curwin + exe curwin."wincmd w" + endif + unlet t:netrw_lexbufnr + + else + " open netrw explorer window + exe "1wincmd w" + let keep_altv = g:netrw_altv + let g:netrw_altv = 0 + if a:count != 0 + let netrw_winsize = g:netrw_winsize + let g:netrw_winsize = a:count + endif + let curfile= expand("%") + exe (a:rightside? "botright" : "topleft")." vertical ".((g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize) . " new" + if a:0 > 0 && a1 != "" + call netrw#Explore(0,0,0,a1) + exe "Explore ".fnameescape(a1) + elseif curfile =~ '^\a\{3,}://' + call netrw#Explore(0,0,0,substitute(curfile,'[^/\\]*$','','')) + else + call netrw#Explore(0,0,0,".") + endif + if a:count != 0 + let g:netrw_winsize = netrw_winsize + endif + setlocal winfixwidth + let g:netrw_altv = keep_altv + let t:netrw_lexbufnr = bufnr("%") + " done to prevent build-up of hidden buffers due to quitting and re-invocation of :Lexplore. + " Since the intended use of :Lexplore is to have an always-present explorer window, the extra + " effort to prevent mis-use of :Lex is warranted. + set bh=wipe + if exists("t:netrw_lexposn") + call winrestview(t:netrw_lexposn) + unlet t:netrw_lexposn + endif + endif + + " set up default window for editing via <cr> + if exists("g:netrw_chgwin") && g:netrw_chgwin == -1 + if a:rightside + let g:netrw_chgwin= 1 + else + let g:netrw_chgwin= 2 + endif + endif + +endfun + +" --------------------------------------------------------------------- +" netrw#Clean: remove netrw {{{2 +" supports :NetrwClean -- remove netrw from first directory on runtimepath +" :NetrwClean! -- remove netrw from all directories on runtimepath +fun! netrw#Clean(sys) + " call Dfunc("netrw#Clean(sys=".a:sys.")") + + if a:sys + let choice= confirm("Remove personal and system copies of netrw?","&Yes\n&No") + else + let choice= confirm("Remove personal copy of netrw?","&Yes\n&No") + endif + " call Decho("choice=".choice,'~'.expand("<slnum>")) + let diddel= 0 + let diddir= "" + + if choice == 1 + for dir in split(&rtp,',') + if filereadable(dir."/plugin/netrwPlugin.vim") + " call Decho("removing netrw-related files from ".dir,'~'.expand("<slnum>")) + if s:NetrwDelete(dir."/plugin/netrwPlugin.vim") |call netrw#ErrorMsg(1,"unable to remove ".dir."/plugin/netrwPlugin.vim",55) |endif + if s:NetrwDelete(dir."/autoload/netrwFileHandlers.vim")|call netrw#ErrorMsg(1,"unable to remove ".dir."/autoload/netrwFileHandlers.vim",55)|endif + if s:NetrwDelete(dir."/autoload/netrwSettings.vim") |call netrw#ErrorMsg(1,"unable to remove ".dir."/autoload/netrwSettings.vim",55) |endif + if s:NetrwDelete(dir."/autoload/netrw.vim") |call netrw#ErrorMsg(1,"unable to remove ".dir."/autoload/netrw.vim",55) |endif + if s:NetrwDelete(dir."/syntax/netrw.vim") |call netrw#ErrorMsg(1,"unable to remove ".dir."/syntax/netrw.vim",55) |endif + if s:NetrwDelete(dir."/syntax/netrwlist.vim") |call netrw#ErrorMsg(1,"unable to remove ".dir."/syntax/netrwlist.vim",55) |endif + let diddir= dir + let diddel= diddel + 1 + if !a:sys|break|endif + endif + endfor + endif + + echohl WarningMsg + if diddel == 0 + echomsg "netrw is either not installed or not removable" + elseif diddel == 1 + echomsg "removed one copy of netrw from <".diddir.">" + else + echomsg "removed ".diddel." copies of netrw" + endif + echohl None + + " call Dret("netrw#Clean") +endfun + +" --------------------------------------------------------------------- +" netrw#MakeTgt: make a target out of the directory name provided {{{2 +fun! netrw#MakeTgt(dname) + " call Dfunc("netrw#MakeTgt(dname<".a:dname.">)") + " simplify the target (eg. /abc/def/../ghi -> /abc/ghi) + let svpos = winsaveview() + " call Decho("saving posn to svpos<".string(svpos).">",'~'.expand("<slnum>")) + let s:netrwmftgt_islocal= (a:dname !~ '^\a\{3,}://') + " call Decho("s:netrwmftgt_islocal=".s:netrwmftgt_islocal,'~'.expand("<slnum>")) + if s:netrwmftgt_islocal + let netrwmftgt= simplify(a:dname) + else + let netrwmftgt= a:dname + endif + if exists("s:netrwmftgt") && netrwmftgt == s:netrwmftgt + " re-selected target, so just clear it + unlet s:netrwmftgt s:netrwmftgt_islocal + else + let s:netrwmftgt= netrwmftgt + endif + if g:netrw_fastbrowse <= 1 + call s:NetrwRefresh((b:netrw_curdir !~ '\a\{3,}://'),b:netrw_curdir) + endif + " call Decho("restoring posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))" + call winrestview(svpos) + " call Dret("netrw#MakeTgt") +endfun + +" --------------------------------------------------------------------- +" netrw#Obtain: {{{2 +" netrw#Obtain(islocal,fname[,tgtdirectory]) +" islocal=0 obtain from remote source +" =1 obtain from local source +" fname : a filename or a list of filenames +" tgtdir : optional place where files are to go (not present, uses getcwd()) +fun! netrw#Obtain(islocal,fname,...) + " call Dfunc("netrw#Obtain(islocal=".a:islocal." fname<".((type(a:fname) == 1)? a:fname : string(a:fname)).">) a:0=".a:0) + " NetrwStatusLine support - for obtaining support + + if type(a:fname) == 1 + let fnamelist= [ a:fname ] + elseif type(a:fname) == 3 + let fnamelist= a:fname + else + call netrw#ErrorMsg(s:ERROR,"attempting to use NetrwObtain on something not a filename or a list",62) + " call Dret("netrw#Obtain") + return + endif + " call Decho("fnamelist<".string(fnamelist).">",'~'.expand("<slnum>")) + if a:0 > 0 + let tgtdir= a:1 + else + let tgtdir= getcwd() + endif + " call Decho("tgtdir<".tgtdir.">",'~'.expand("<slnum>")) + + if exists("b:netrw_islocal") && b:netrw_islocal + " obtain a file from local b:netrw_curdir to (local) tgtdir + " call Decho("obtain a file from local ".b:netrw_curdir." to ".tgtdir,'~'.expand("<slnum>")) + if exists("b:netrw_curdir") && getcwd() != b:netrw_curdir + let topath= s:ComposePath(tgtdir,"") + if has("win32") + " transfer files one at time + " call Decho("transfer files one at a time",'~'.expand("<slnum>")) + for fname in fnamelist + " call Decho("system(".g:netrw_localcopycmd." ".s:ShellEscape(fname)." ".s:ShellEscape(topath).")",'~'.expand("<slnum>")) + call system(g:netrw_localcopycmd.g:netrw_localcopycmdopt." ".s:ShellEscape(fname)." ".s:ShellEscape(topath)) + if v:shell_error != 0 + call netrw#ErrorMsg(s:WARNING,"consider setting g:netrw_localcopycmd<".g:netrw_localcopycmd."> to something that works",80) + " call Dret("s:NetrwObtain 0 : failed: ".g:netrw_localcopycmd." ".s:ShellEscape(fname)." ".s:ShellEscape(topath)) + return + endif + endfor + else + " transfer files with one command + " call Decho("transfer files with one command",'~'.expand("<slnum>")) + let filelist= join(map(deepcopy(fnamelist),"s:ShellEscape(v:val)")) + " call Decho("system(".g:netrw_localcopycmd." ".filelist." ".s:ShellEscape(topath).")",'~'.expand("<slnum>")) + call system(g:netrw_localcopycmd.g:netrw_localcopycmdopt." ".filelist." ".s:ShellEscape(topath)) + if v:shell_error != 0 + call netrw#ErrorMsg(s:WARNING,"consider setting g:netrw_localcopycmd<".g:netrw_localcopycmd."> to something that works",80) + " call Dret("s:NetrwObtain 0 : failed: ".g:netrw_localcopycmd." ".filelist." ".s:ShellEscape(topath)) + return + endif + endif + elseif !exists("b:netrw_curdir") + call netrw#ErrorMsg(s:ERROR,"local browsing directory doesn't exist!",36) + else + call netrw#ErrorMsg(s:WARNING,"local browsing directory and current directory are identical",37) + endif + + else + " obtain files from remote b:netrw_curdir to local tgtdir + " call Decho("obtain a file from remote ".b:netrw_curdir." to ".tgtdir,'~'.expand("<slnum>")) + if type(a:fname) == 1 + call s:SetupNetrwStatusLine('%f %h%m%r%=%9*Obtaining '.a:fname) + endif + call s:NetrwMethod(b:netrw_curdir) + + if b:netrw_method == 4 + " obtain file using scp + " call Decho("obtain via scp (method#4)",'~'.expand("<slnum>")) + if exists("g:netrw_port") && g:netrw_port != "" + let useport= " ".g:netrw_scpport." ".g:netrw_port + else + let useport= "" + endif + if b:netrw_fname =~ '/' + let path= substitute(b:netrw_fname,'^\(.*/\).\{-}$','\1','') + else + let path= "" + endif + let filelist= join(map(deepcopy(fnamelist),'escape(s:ShellEscape(g:netrw_machine.":".path.v:val,1)," ")')) + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_scp_cmd.s:ShellEscape(useport,1)." ".filelist." ".s:ShellEscape(tgtdir,1)) + + elseif b:netrw_method == 2 + " obtain file using ftp + .netrc + " call Decho("obtain via ftp+.netrc (method #2)",'~'.expand("<slnum>")) + call s:SaveBufVars()|sil NetrwKeepj new|call s:RestoreBufVars() + let tmpbufnr= bufnr("%") + setl ff=unix + if exists("g:netrw_ftpmode") && g:netrw_ftpmode != "" + NetrwKeepj put =g:netrw_ftpmode + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + endif + + if exists("b:netrw_fname") && b:netrw_fname != "" + call setline(line("$")+1,'cd "'.b:netrw_fname.'"') + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + endif + + if exists("g:netrw_ftpextracmd") + NetrwKeepj put =g:netrw_ftpextracmd + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + endif + for fname in fnamelist + call setline(line("$")+1,'get "'.fname.'"') + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + endfor + if exists("g:netrw_port") && g:netrw_port != "" + call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1)) + else + call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)) + endif + " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) + if getline(1) !~ "^$" && !exists("g:netrw_quiet") && getline(1) !~ '^Trying ' + let debugkeep= &debug + setl debug=msg + call netrw#ErrorMsg(s:ERROR,getline(1),4) + let &debug= debugkeep + endif + + elseif b:netrw_method == 3 + " obtain with ftp + machine, id, passwd, and fname (ie. no .netrc) + " call Decho("obtain via ftp+mipf (method #3)",'~'.expand("<slnum>")) + call s:SaveBufVars()|sil NetrwKeepj new|call s:RestoreBufVars() + let tmpbufnr= bufnr("%") + setl ff=unix + + if exists("g:netrw_port") && g:netrw_port != "" + NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + else + NetrwKeepj put ='open '.g:netrw_machine + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + endif + + if exists("g:netrw_uid") && g:netrw_uid != "" + if exists("g:netrw_ftp") && g:netrw_ftp == 1 + NetrwKeepj put =g:netrw_uid + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + if exists("s:netrw_passwd") && s:netrw_passwd != "" + NetrwKeepj put ='\"'.s:netrw_passwd.'\"' + endif + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + elseif exists("s:netrw_passwd") + NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"' + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + endif + endif + + if exists("g:netrw_ftpmode") && g:netrw_ftpmode != "" + NetrwKeepj put =g:netrw_ftpmode + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + endif + + if exists("b:netrw_fname") && b:netrw_fname != "" + NetrwKeepj call setline(line("$")+1,'cd "'.b:netrw_fname.'"') + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + endif + + if exists("g:netrw_ftpextracmd") + NetrwKeepj put =g:netrw_ftpextracmd + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + endif + + if exists("g:netrw_ftpextracmd") + NetrwKeepj put =g:netrw_ftpextracmd + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + endif + for fname in fnamelist + NetrwKeepj call setline(line("$")+1,'get "'.fname.'"') + endfor + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + + " perform ftp: + " -i : turns off interactive prompting from ftp + " -n unix : DON'T use <.netrc>, even though it exists + " -n win32: quit being obnoxious about password + " Note: using "_dd to delete to the black hole register; avoids messing up @@ + NetrwKeepj norm! 1G"_dd + call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." ".g:netrw_ftp_options) + " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) + if getline(1) !~ "^$" + " call Decho("error<".getline(1).">",'~'.expand("<slnum>")) + if !exists("g:netrw_quiet") + NetrwKeepj call netrw#ErrorMsg(s:ERROR,getline(1),5) + endif + endif + + elseif b:netrw_method == 9 + " obtain file using sftp + " call Decho("obtain via sftp (method #9)",'~'.expand("<slnum>")) + if a:fname =~ '/' + let localfile= substitute(a:fname,'^.*/','','') + else + let localfile= a:fname + endif + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_sftp_cmd." ".s:ShellEscape(g:netrw_machine.":".b:netrw_fname,1).s:ShellEscape(localfile)." ".s:ShellEscape(tgtdir)) + + elseif !exists("b:netrw_method") || b:netrw_method < 0 + " probably a badly formed url; protocol not recognized + " call Dret("netrw#Obtain : unsupported method") + return + + else + " protocol recognized but not supported for Obtain (yet?) + if !exists("g:netrw_quiet") + NetrwKeepj call netrw#ErrorMsg(s:ERROR,"current protocol not supported for obtaining file",97) + endif + " call Dret("netrw#Obtain : current protocol not supported for obtaining file") + return + endif + + " restore status line + if type(a:fname) == 1 && exists("s:netrw_users_stl") + NetrwKeepj call s:SetupNetrwStatusLine(s:netrw_users_stl) + endif + + endif + + " cleanup + if exists("tmpbufnr") + if bufnr("%") != tmpbufnr + exe tmpbufnr."bw!" + else + q! + endif + endif + + " call Dret("netrw#Obtain") +endfun + +" --------------------------------------------------------------------- +" netrw#Nread: save position, call netrw#NetRead(), and restore position {{{2 +fun! netrw#Nread(mode,fname) + " call Dfunc("netrw#Nread(mode=".a:mode." fname<".a:fname.">)") + let svpos= winsaveview() + " call Decho("saving posn to svpos<".string(svpos).">",'~'.expand("<slnum>")) + call netrw#NetRead(a:mode,a:fname) + " call Decho("restoring posn to svpos<".string(svpos).">",'~'.expand("<slnum>")) + call winrestview(svpos) + + if exists("w:netrw_liststyle") && w:netrw_liststyle != s:TREELIST + if exists("w:netrw_bannercnt") + " start with cursor just after the banner + exe w:netrw_bannercnt + endif + endif + " call Dret("netrw#Nread") +endfun + +" ------------------------------------------------------------------------ +" s:NetrwOptionsSave: save options prior to setting to "netrw-buffer-standard" form {{{2 +" Options get restored by s:NetrwOptionsRestore() +" +" Option handling: +" * save user's options (s:NetrwOptionsSave) +" * set netrw-safe options (s:NetrwOptionsSafe) +" - change an option only when user option != safe option (s:netrwSetSafeSetting) +" * restore user's options (s:netrwOPtionsRestore) +" - restore a user option when != safe option (s:NetrwRestoreSetting) +" vt: (variable type) normally its either "w:" or "s:" +fun! s:NetrwOptionsSave(vt) + " call Dfunc("s:NetrwOptionsSave(vt<".a:vt.">) win#".winnr()." buf#".bufnr("%")."<".bufname(bufnr("%")).">"." winnr($)=".winnr("$")." mod=".&mod." ma=".&ma) + " call Decho(a:vt."netrw_optionsave".(exists("{a:vt}netrw_optionsave")? ("=".{a:vt}netrw_optionsave) : " doesn't exist"),'~'.expand("<slnum>")) + " call Decho("settings buf#".bufnr("%")."<".bufname("%").">: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo." a:vt=".a:vt." hid=".&hid,'~'.expand("<slnum>")) + " call Decho("(s:NetrwOptionsSave) lines=".&lines) + + if !exists("{a:vt}netrw_optionsave") + let {a:vt}netrw_optionsave= 1 + else + " call Dret("s:NetrwOptionsSave : options already saved") + return + endif + " call Decho("prior to save: fo=".&fo.(exists("+acd")? " acd=".&acd : " acd doesn't exist")." diff=".&l:diff,'~'.expand("<slnum>")) + + " Save current settings and current directory + " call Decho("saving current settings and current directory",'~'.expand("<slnum>")) + let s:yykeep = @@ + if exists("&l:acd")|let {a:vt}netrw_acdkeep = &l:acd|endif + let {a:vt}netrw_aikeep = &l:ai + let {a:vt}netrw_awkeep = &l:aw + let {a:vt}netrw_bhkeep = &l:bh + let {a:vt}netrw_blkeep = &l:bl + let {a:vt}netrw_btkeep = &l:bt + let {a:vt}netrw_bombkeep = &l:bomb + let {a:vt}netrw_cedit = &cedit + let {a:vt}netrw_cikeep = &l:ci + let {a:vt}netrw_cinkeep = &l:cin + let {a:vt}netrw_cinokeep = &l:cino + let {a:vt}netrw_comkeep = &l:com + let {a:vt}netrw_cpokeep = &l:cpo + let {a:vt}netrw_cuckeep = &l:cuc + let {a:vt}netrw_culkeep = &l:cul + " call Decho("(s:NetrwOptionsSave) COMBAK: cuc=".&l:cuc." cul=".&l:cul) + let {a:vt}netrw_diffkeep = &l:diff + let {a:vt}netrw_fenkeep = &l:fen + if !exists("g:netrw_ffkeep") || g:netrw_ffkeep + let {a:vt}netrw_ffkeep = &l:ff + endif + let {a:vt}netrw_fokeep = &l:fo " formatoptions + let {a:vt}netrw_gdkeep = &l:gd " gdefault + let {a:vt}netrw_gokeep = &go " guioptions + let {a:vt}netrw_hidkeep = &l:hidden + let {a:vt}netrw_imkeep = &l:im + let {a:vt}netrw_iskkeep = &l:isk + let {a:vt}netrw_lines = &lines + let {a:vt}netrw_lskeep = &l:ls + let {a:vt}netrw_makeep = &l:ma + let {a:vt}netrw_magickeep = &l:magic + let {a:vt}netrw_modkeep = &l:mod + let {a:vt}netrw_nukeep = &l:nu + let {a:vt}netrw_rnukeep = &l:rnu + let {a:vt}netrw_repkeep = &l:report + let {a:vt}netrw_rokeep = &l:ro + let {a:vt}netrw_selkeep = &l:sel + let {a:vt}netrw_spellkeep = &l:spell + if !g:netrw_use_noswf + let {a:vt}netrw_swfkeep = &l:swf + endif + let {a:vt}netrw_tskeep = &l:ts + let {a:vt}netrw_twkeep = &l:tw " textwidth + let {a:vt}netrw_wigkeep = &l:wig " wildignore + let {a:vt}netrw_wrapkeep = &l:wrap + let {a:vt}netrw_writekeep = &l:write + + " save a few selected netrw-related variables + " call Decho("saving a few selected netrw-related variables",'~'.expand("<slnum>")) + if g:netrw_keepdir + let {a:vt}netrw_dirkeep = getcwd() + " call Decho("saving to ".a:vt."netrw_dirkeep<".{a:vt}netrw_dirkeep.">",'~'.expand("<slnum>")) + endif + if !has('nvim') && has("clipboard") && g:netrw_clipboard + sil! let {a:vt}netrw_starkeep = @* + sil! let {a:vt}netrw_pluskeep = @+ + endif + sil! let {a:vt}netrw_slashkeep= @/ + + " call Decho("(s:NetrwOptionsSave) lines=".&lines) + " 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 Dret("s:NetrwOptionsSave : tab#".tabpagenr()." win#".winnr()) +endfun + +" --------------------------------------------------------------------- +" s:NetrwOptionsSafe: sets options to help netrw do its job {{{2 +" Use s:NetrwSaveOptions() to save user settings +" Use s:NetrwOptionsRestore() to restore user settings +fun! s:NetrwOptionsSafe(islocal) + " call Dfunc("s:NetrwOptionsSafe(islocal=".a:islocal.") win#".winnr()." buf#".bufnr("%")."<".bufname(bufnr("%"))."> winnr($)=".winnr("$")) + " call Decho("win#".winnr()."'s ft=".&ft,'~'.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>")) + if exists("+acd") | call s:NetrwSetSafeSetting("&l:acd",0)|endif + call s:NetrwSetSafeSetting("&l:ai",0) + call s:NetrwSetSafeSetting("&l:aw",0) + call s:NetrwSetSafeSetting("&l:bl",0) + call s:NetrwSetSafeSetting("&l:bomb",0) + if a:islocal + call s:NetrwSetSafeSetting("&l:bt","nofile") + else + call s:NetrwSetSafeSetting("&l:bt","acwrite") + endif + call s:NetrwSetSafeSetting("&l:ci",0) + call s:NetrwSetSafeSetting("&l:cin",0) + if g:netrw_fastbrowse > a:islocal + call s:NetrwSetSafeSetting("&l:bh","hide") + else + call s:NetrwSetSafeSetting("&l:bh","delete") + endif + call s:NetrwSetSafeSetting("&l:cino","") + call s:NetrwSetSafeSetting("&l:com","") + if &cpo =~ 'a' | call s:NetrwSetSafeSetting("&cpo",substitute(&cpo,'a','','g')) | endif + if &cpo =~ 'A' | call s:NetrwSetSafeSetting("&cpo",substitute(&cpo,'A','','g')) | endif + setl fo=nroql2 + if &go =~ 'a' | set go-=a | endif + if &go =~ 'A' | set go-=A | endif + if &go =~ 'P' | set go-=P | endif + call s:NetrwSetSafeSetting("&l:hid",0) + call s:NetrwSetSafeSetting("&l:im",0) + setl isk+=@ isk+=* isk+=/ + call s:NetrwSetSafeSetting("&l:magic",1) + if g:netrw_use_noswf + call s:NetrwSetSafeSetting("swf",0) + endif + call s:NetrwSetSafeSetting("&l:report",10000) + call s:NetrwSetSafeSetting("&l:sel","inclusive") + call s:NetrwSetSafeSetting("&l:spell",0) + call s:NetrwSetSafeSetting("&l:tw",0) + call s:NetrwSetSafeSetting("&l:wig","") + setl cedit& + + " set up cuc and cul based on g:netrw_cursor and listing style + " COMBAK -- cuc cul related + call s:NetrwCursor(0) + + " allow the user to override safe options + " call Decho("ft<".&ft."> ei=".&ei,'~'.expand("<slnum>")) + if &ft == "netrw" + " call Decho("do any netrw FileType autocmds (doau FileType netrw)",'~'.expand("<slnum>")) + keepalt NetrwKeepj doau FileType netrw + endif + + " call Decho("fo=".&fo.(exists("+acd")? " acd=".&acd : " acd doesn't exist")." bh=".&l:bh." bt<".&bt.">",'~'.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 Dret("s:NetrwOptionsSafe") +endfun + +" --------------------------------------------------------------------- +" s:NetrwOptionsRestore: restore options (based on prior s:NetrwOptionsSave) {{{2 +fun! s:NetrwOptionsRestore(vt) + if !exists("{a:vt}netrw_optionsave") + " filereadable() returns zero for remote files (e.g. scp://user@localhost//etc/fstab) + " Note: @ may not be in 'isfname', so '^\w\+://\f\+/' may not match + if filereadable(expand("%")) || expand("%") =~# '^\w\+://\f\+' + filetype detect + else + setl ft=netrw + endif + return + endif + unlet {a:vt}netrw_optionsave + + if exists("+acd") + if exists("{a:vt}netrw_acdkeep") + let curdir = getcwd() + let &l:acd = {a:vt}netrw_acdkeep + unlet {a:vt}netrw_acdkeep + if &l:acd + call s:NetrwLcd(curdir) + endif + endif + endif + call s:NetrwRestoreSetting(a:vt."netrw_aikeep","&l:ai") + call s:NetrwRestoreSetting(a:vt."netrw_awkeep","&l:aw") + call s:NetrwRestoreSetting(a:vt."netrw_blkeep","&l:bl") + call s:NetrwRestoreSetting(a:vt."netrw_btkeep","&l:bt") + call s:NetrwRestoreSetting(a:vt."netrw_bombkeep","&l:bomb") + call s:NetrwRestoreSetting(a:vt."netrw_cedit","&cedit") + call s:NetrwRestoreSetting(a:vt."netrw_cikeep","&l:ci") + call s:NetrwRestoreSetting(a:vt."netrw_cinkeep","&l:cin") + call s:NetrwRestoreSetting(a:vt."netrw_cinokeep","&l:cino") + call s:NetrwRestoreSetting(a:vt."netrw_comkeep","&l:com") + call s:NetrwRestoreSetting(a:vt."netrw_cpokeep","&l:cpo") + call s:NetrwRestoreSetting(a:vt."netrw_diffkeep","&l:diff") + call s:NetrwRestoreSetting(a:vt."netrw_fenkeep","&l:fen") + if exists("g:netrw_ffkeep") && g:netrw_ffkeep + call s:NetrwRestoreSetting(a:vt."netrw_ffkeep")","&l:ff") + endif + call s:NetrwRestoreSetting(a:vt."netrw_fokeep" ,"&l:fo") + call s:NetrwRestoreSetting(a:vt."netrw_gdkeep" ,"&l:gd") + call s:NetrwRestoreSetting(a:vt."netrw_gokeep" ,"&go") + call s:NetrwRestoreSetting(a:vt."netrw_hidkeep" ,"&l:hidden") + call s:NetrwRestoreSetting(a:vt."netrw_imkeep" ,"&l:im") + call s:NetrwRestoreSetting(a:vt."netrw_iskkeep" ,"&l:isk") + call s:NetrwRestoreSetting(a:vt."netrw_lines" ,"&lines") + call s:NetrwRestoreSetting(a:vt."netrw_lskeep" ,"&l:ls") + call s:NetrwRestoreSetting(a:vt."netrw_makeep" ,"&l:ma") + call s:NetrwRestoreSetting(a:vt."netrw_magickeep","&l:magic") + call s:NetrwRestoreSetting(a:vt."netrw_modkeep" ,"&l:mod") + call s:NetrwRestoreSetting(a:vt."netrw_nukeep" ,"&l:nu") + call s:NetrwRestoreSetting(a:vt."netrw_rnukeep" ,"&l:rnu") + call s:NetrwRestoreSetting(a:vt."netrw_repkeep" ,"&l:report") + call s:NetrwRestoreSetting(a:vt."netrw_rokeep" ,"&l:ro") + call s:NetrwRestoreSetting(a:vt."netrw_selkeep" ,"&l:sel") + call s:NetrwRestoreSetting(a:vt."netrw_spellkeep","&l:spell") + call s:NetrwRestoreSetting(a:vt."netrw_twkeep" ,"&l:tw") + call s:NetrwRestoreSetting(a:vt."netrw_wigkeep" ,"&l:wig") + call s:NetrwRestoreSetting(a:vt."netrw_wrapkeep" ,"&l:wrap") + call s:NetrwRestoreSetting(a:vt."netrw_writekeep","&l:write") + call s:NetrwRestoreSetting("s:yykeep","@@") + " former problem: start with liststyle=0; press <i> : result, following line resets l:ts. + " Fixed; in s:PerformListing, when w:netrw_liststyle is s:LONGLIST, will use a printf to pad filename with spaces + " rather than by appending a tab which previously was using "&ts" to set the desired spacing. (Sep 28, 2018) + call s:NetrwRestoreSetting(a:vt."netrw_tskeep","&l:ts") + + if exists("{a:vt}netrw_swfkeep") + if &directory == "" + " user hasn't specified a swapfile directory; + " netrw will temporarily set the swapfile directory + " to the current directory as returned by getcwd(). + let &l:directory= getcwd() + sil! let &l:swf = {a:vt}netrw_swfkeep + setl directory= + unlet {a:vt}netrw_swfkeep + elseif &l:swf != {a:vt}netrw_swfkeep + if !g:netrw_use_noswf + " following line causes a Press ENTER in windows -- can't seem to work around it!!! + sil! let &l:swf= {a:vt}netrw_swfkeep + endif + unlet {a:vt}netrw_swfkeep + endif + endif + if exists("{a:vt}netrw_dirkeep") && isdirectory(s:NetrwFile({a:vt}netrw_dirkeep)) && g:netrw_keepdir + let dirkeep = substitute({a:vt}netrw_dirkeep,'\\','/','g') + if exists("{a:vt}netrw_dirkeep") + call s:NetrwLcd(dirkeep) + unlet {a:vt}netrw_dirkeep + endif + endif + if !has('nvim') && has("clipboard") && g:netrw_clipboard + call s:NetrwRestoreSetting(a:vt."netrw_starkeep","@*") + call s:NetrwRestoreSetting(a:vt."netrw_pluskeep","@+") + endif + call s:NetrwRestoreSetting(a:vt."netrw_slashkeep","@/") + + " Moved the filetype detect here from NetrwGetFile() because remote files + " were having their filetype detect-generated settings overwritten by + " NetrwOptionRestore. + if &ft != "netrw" + filetype detect + endif +endfun + +" --------------------------------------------------------------------- +" s:NetrwSetSafeSetting: sets an option to a safe setting {{{2 +" but only when the options' value and the safe setting differ +" Doing this means that netrw will not come up as having changed a +" setting last when it really didn't actually change it. +" +" Called from s:NetrwOptionsSafe +" ex. call s:NetrwSetSafeSetting("&l:sel","inclusive") +fun! s:NetrwSetSafeSetting(setting,safesetting) + " call Dfunc("s:NetrwSetSafeSetting(setting<".a:setting."> safesetting<".a:safesetting.">)") + + if a:setting =~ '^&' + " call Decho("fyi: a:setting starts with &") + exe "let settingval= ".a:setting + " call Decho("fyi: settingval<".settingval.">") + + if settingval != a:safesetting + " call Decho("set setting<".a:setting."> to option value<".a:safesetting.">") + if type(a:safesetting) == 0 + exe "let ".a:setting."=".a:safesetting + elseif type(a:safesetting) == 1 + exe "let ".a:setting."= '".a:safesetting."'" + else + call netrw#ErrorMsg(s:ERROR,"(s:NetrwRestoreSetting) doesn't know how to restore ".a:setting." with a safesetting of type#".type(a:safesetting),105) + endif + endif + endif + + " call Dret("s:NetrwSetSafeSetting") +endfun + +" ------------------------------------------------------------------------ +" s:NetrwRestoreSetting: restores specified setting using associated keepvar, {{{2 +" but only if the setting value differs from the associated keepvar. +" Doing this means that netrw will not come up as having changed a +" setting last when it really didn't actually change it. +" +" Used by s:NetrwOptionsRestore() to restore each netrw-sensitive setting +" keepvars are set up by s:NetrwOptionsSave +fun! s:NetrwRestoreSetting(keepvar,setting) + """ call Dfunc("s:NetrwRestoreSetting(a:keepvar<".a:keepvar."> a:setting<".a:setting.">)") + + " typically called from s:NetrwOptionsRestore + " call s:NetrwRestoreSettings(keep-option-variable-name,'associated-option') + " ex. call s:NetrwRestoreSetting(a:vt."netrw_selkeep","&l:sel") + " Restores option (but only if different) from a:keepvar + if exists(a:keepvar) + exe "let keepvarval= ".a:keepvar + exe "let setting= ".a:setting + + "" call Decho("fyi: a:keepvar<".a:keepvar."> exists") + "" call Decho("fyi: keepvarval=".keepvarval) + "" call Decho("fyi: a:setting<".a:setting."> setting<".setting.">") + + if setting != keepvarval + "" call Decho("restore setting<".a:setting."> (currently=".setting.") to keepvarval<".keepvarval.">") + if type(a:setting) == 0 + exe "let ".a:setting."= ".keepvarval + elseif type(a:setting) == 1 + exe "let ".a:setting."= '".substitute(keepvarval,"'","''","g")."'" + else + call netrw#ErrorMsg(s:ERROR,"(s:NetrwRestoreSetting) doesn't know how to restore ".a:keepvar." with a setting of type#".type(a:setting),105) + endif + endif + + exe "unlet ".a:keepvar + endif + + "" call Dret("s:NetrwRestoreSetting") +endfun + +" --------------------------------------------------------------------- +" NetrwStatusLine: {{{2 +fun! NetrwStatusLine() + + " vvv NetrwStatusLine() debugging vvv + " let g:stlmsg="" + " if !exists("w:netrw_explore_bufnr") + " let g:stlmsg="!X<explore_bufnr>" + " elseif w:netrw_explore_bufnr != bufnr("%") + " let g:stlmsg="explore_bufnr!=".bufnr("%") + " endif + " if !exists("w:netrw_explore_line") + " let g:stlmsg=" !X<explore_line>" + " elseif w:netrw_explore_line != line(".") + " let g:stlmsg=" explore_line!={line(.)<".line(".").">" + " endif + " if !exists("w:netrw_explore_list") + " let g:stlmsg=" !X<explore_list>" + " endif + " ^^^ NetrwStatusLine() debugging ^^^ + + if !exists("w:netrw_explore_bufnr") || w:netrw_explore_bufnr != bufnr("%") || !exists("w:netrw_explore_line") || w:netrw_explore_line != line(".") || !exists("w:netrw_explore_list") + " restore user's status line + let &l:stl = s:netrw_users_stl + let &laststatus = s:netrw_users_ls + if exists("w:netrw_explore_bufnr")|unlet w:netrw_explore_bufnr|endif + if exists("w:netrw_explore_line") |unlet w:netrw_explore_line |endif + return "" + else + return "Match ".w:netrw_explore_mtchcnt." of ".w:netrw_explore_listlen + endif +endfun + +" =============================== +" Netrw Transfer Functions: {{{1 +" =============================== + +" ------------------------------------------------------------------------ +" netrw#NetRead: responsible for reading a file over the net {{{2 +" mode: =0 read remote file and insert before current line +" =1 read remote file and insert after current line +" =2 replace with remote file +" =3 obtain file, but leave in temporary format +fun! netrw#NetRead(mode,...) + " call Dfunc("netrw#NetRead(mode=".a:mode.",...) a:0=".a:0." ".g:loaded_netrw.((a:0 > 0)? " a:1<".a:1.">" : "")) + + " NetRead: save options {{{3 + call s:NetrwOptionsSave("w:") + call s:NetrwOptionsSafe(0) + call s:RestoreCursorline() + " NetrwSafeOptions sets a buffer up for a netrw listing, which includes buflisting off. + " However, this setting is not wanted for a remote editing session. The buffer should be "nofile", still. + setl bl + " call Decho("buf#".bufnr("%")."<".bufname("%")."> bl=".&bl." bt=".&bt." bh=".&bh,'~'.expand("<slnum>")) + + " NetRead: interpret mode into a readcmd {{{3 + if a:mode == 0 " read remote file before current line + let readcmd = "0r" + elseif a:mode == 1 " read file after current line + let readcmd = "r" + elseif a:mode == 2 " replace with remote file + let readcmd = "%r" + elseif a:mode == 3 " skip read of file (leave as temporary) + let readcmd = "t" + else + exe a:mode + let readcmd = "r" + endif + let ichoice = (a:0 == 0)? 0 : 1 + " call Decho("readcmd<".readcmd."> ichoice=".ichoice,'~'.expand("<slnum>")) + + " NetRead: get temporary filename {{{3 + let tmpfile= s:GetTempfile("") + if tmpfile == "" + " call Dret("netrw#NetRead : unable to get a tempfile!") + return + endif + + while ichoice <= a:0 + + " attempt to repeat with previous host-file-etc + if exists("b:netrw_lastfile") && a:0 == 0 + " call Decho("using b:netrw_lastfile<" . b:netrw_lastfile . ">",'~'.expand("<slnum>")) + let choice = b:netrw_lastfile + let ichoice= ichoice + 1 + + else + exe "let choice= a:" . ichoice + " call Decho("no lastfile: choice<" . choice . ">",'~'.expand("<slnum>")) + + if match(choice,"?") == 0 + " give help + echomsg 'NetRead Usage:' + echomsg ':Nread machine:path uses rcp' + echomsg ':Nread "machine path" uses ftp with <.netrc>' + echomsg ':Nread "machine id password path" uses ftp' + echomsg ':Nread dav://machine[:port]/path uses cadaver' + echomsg ':Nread fetch://machine/path uses fetch' + echomsg ':Nread ftp://[user@]machine[:port]/path uses ftp autodetects <.netrc>' + echomsg ':Nread http://[user@]machine/path uses http wget' + echomsg ':Nread file:///path uses elinks' + echomsg ':Nread https://[user@]machine/path uses http wget' + echomsg ':Nread rcp://[user@]machine/path uses rcp' + echomsg ':Nread rsync://machine[:port]/path uses rsync' + echomsg ':Nread scp://[user@]machine[[:#]port]/path uses scp' + echomsg ':Nread sftp://[user@]machine[[:#]port]/path uses sftp' + sleep 4 + break + + elseif match(choice,'^"') != -1 + " Reconstruct Choice if choice starts with '"' + " call Decho("reconstructing choice",'~'.expand("<slnum>")) + if match(choice,'"$') != -1 + " case "..." + let choice= strpart(choice,1,strlen(choice)-2) + else + " case "... ... ..." + let choice = strpart(choice,1,strlen(choice)-1) + let wholechoice = "" + + while match(choice,'"$') == -1 + let wholechoice = wholechoice . " " . choice + let ichoice = ichoice + 1 + if ichoice > a:0 + if !exists("g:netrw_quiet") + call netrw#ErrorMsg(s:ERROR,"Unbalanced string in filename '". wholechoice ."'",3) + endif + " call Dret("netrw#NetRead :2 getcwd<".getcwd().">") + return + endif + let choice= a:{ichoice} + endwhile + let choice= strpart(wholechoice,1,strlen(wholechoice)-1) . " " . strpart(choice,0,strlen(choice)-1) + endif + endif + endif + + " call Decho("choice<" . choice . ">",'~'.expand("<slnum>")) + let ichoice= ichoice + 1 + + " NetRead: Determine method of read (ftp, rcp, etc) {{{3 + call s:NetrwMethod(choice) + if !exists("b:netrw_method") || b:netrw_method < 0 + " call Dret("netrw#NetRead : unsupported method") + return + endif + let tmpfile= s:GetTempfile(b:netrw_fname) " apply correct suffix + + " Check whether or not NetrwBrowse() should be handling this request + " call Decho("checking if NetrwBrowse() should handle choice<".choice."> with netrw_list_cmd<".g:netrw_list_cmd.">",'~'.expand("<slnum>")) + if choice =~ "^.*[\/]$" && b:netrw_method != 5 && choice !~ '^https\=://' + " call Decho("yes, choice matches '^.*[\/]$'",'~'.expand("<slnum>")) + NetrwKeepj call s:NetrwBrowse(0,choice) + " call Dret("netrw#NetRead :3 getcwd<".getcwd().">") + return + endif + + " ============ + " NetRead: Perform Protocol-Based Read {{{3 + " =========================== + if exists("g:netrw_silent") && g:netrw_silent == 0 && &ch >= 1 + echo "(netrw) Processing your read request..." + endif + + "......................................... + " NetRead: (rcp) NetRead Method #1 {{{3 + if b:netrw_method == 1 " read with rcp + " call Decho("read via rcp (method #1)",'~'.expand("<slnum>")) + " ER: nothing done with g:netrw_uid yet? + " ER: on Win2K" rcp machine[.user]:file tmpfile + " ER: when machine contains '.' adding .user is required (use $USERNAME) + " ER: the tmpfile is full path: rcp sees C:\... as host C + if s:netrw_has_nt_rcp == 1 + if exists("g:netrw_uid") && ( g:netrw_uid != "" ) + let uid_machine = g:netrw_machine .'.'. g:netrw_uid + else + " Any way needed it machine contains a '.' + let uid_machine = g:netrw_machine .'.'. $USERNAME + endif + else + if exists("g:netrw_uid") && ( g:netrw_uid != "" ) + let uid_machine = g:netrw_uid .'@'. g:netrw_machine + else + let uid_machine = g:netrw_machine + endif + endif + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_rcp_cmd." ".s:netrw_rcpmode." ".s:ShellEscape(uid_machine.":".b:netrw_fname,1)." ".s:ShellEscape(tmpfile,1)) + let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) + let b:netrw_lastfile = choice + + "......................................... + " NetRead: (ftp + <.netrc>) NetRead Method #2 {{{3 + elseif b:netrw_method == 2 " read with ftp + <.netrc> + " call Decho("read via ftp+.netrc (method #2)",'~'.expand("<slnum>")) + let netrw_fname= b:netrw_fname + NetrwKeepj call s:SaveBufVars()|new|NetrwKeepj call s:RestoreBufVars() + let filtbuf= bufnr("%") + setl ff=unix + NetrwKeepj put =g:netrw_ftpmode + " call Decho("filter input: ".getline(line("$")),'~'.expand("<slnum>")) + if exists("g:netrw_ftpextracmd") + NetrwKeepj put =g:netrw_ftpextracmd + " call Decho("filter input: ".getline(line("$")),'~'.expand("<slnum>")) + endif + call setline(line("$")+1,'get "'.netrw_fname.'" '.tmpfile) + " call Decho("filter input: ".getline(line("$")),'~'.expand("<slnum>")) + if exists("g:netrw_port") && g:netrw_port != "" + call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1)) + else + call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)) + endif + " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) + if getline(1) !~ "^$" && !exists("g:netrw_quiet") && getline(1) !~ '^Trying ' + let debugkeep = &debug + setl debug=msg + NetrwKeepj call netrw#ErrorMsg(s:ERROR,getline(1),4) + let &debug = debugkeep + endif + call s:SaveBufVars() + keepj bd! + if bufname("%") == "" && getline("$") == "" && line('$') == 1 + " needed when one sources a file in a nolbl setting window via ftp + q! + endif + call s:RestoreBufVars() + let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) + let b:netrw_lastfile = choice + + "......................................... + " NetRead: (ftp + machine,id,passwd,filename) NetRead Method #3 {{{3 + elseif b:netrw_method == 3 " read with ftp + machine, id, passwd, and fname + " Construct execution string (four lines) which will be passed through filter + " call Decho("read via ftp+mipf (method #3)",'~'.expand("<slnum>")) + let netrw_fname= escape(b:netrw_fname,g:netrw_fname_escape) + NetrwKeepj call s:SaveBufVars()|new|NetrwKeepj call s:RestoreBufVars() + let filtbuf= bufnr("%") + setl ff=unix + if exists("g:netrw_port") && g:netrw_port != "" + NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port + " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) + else + NetrwKeepj put ='open '.g:netrw_machine + " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) + endif + + if exists("g:netrw_uid") && g:netrw_uid != "" + if exists("g:netrw_ftp") && g:netrw_ftp == 1 + NetrwKeepj put =g:netrw_uid + " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) + if exists("s:netrw_passwd") + NetrwKeepj put ='\"'.s:netrw_passwd.'\"' + endif + " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) + elseif exists("s:netrw_passwd") + NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"' + " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) + endif + endif + + if exists("g:netrw_ftpmode") && g:netrw_ftpmode != "" + NetrwKeepj put =g:netrw_ftpmode + " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) + endif + if exists("g:netrw_ftpextracmd") + NetrwKeepj put =g:netrw_ftpextracmd + " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) + endif + NetrwKeepj put ='get \"'.netrw_fname.'\" '.tmpfile + " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) + + " perform ftp: + " -i : turns off interactive prompting from ftp + " -n unix : DON'T use <.netrc>, even though it exists + " -n win32: quit being obnoxious about password + NetrwKeepj norm! 1G"_dd + call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." ".g:netrw_ftp_options) + " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) + if getline(1) !~ "^$" + " call Decho("error<".getline(1).">",'~'.expand("<slnum>")) + if !exists("g:netrw_quiet") + call netrw#ErrorMsg(s:ERROR,getline(1),5) + endif + endif + call s:SaveBufVars()|keepj bd!|call s:RestoreBufVars() + let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) + let b:netrw_lastfile = choice + + "......................................... + " NetRead: (scp) NetRead Method #4 {{{3 + elseif b:netrw_method == 4 " read with scp + " call Decho("read via scp (method #4)",'~'.expand("<slnum>")) + if exists("g:netrw_port") && g:netrw_port != "" + let useport= " ".g:netrw_scpport." ".g:netrw_port + else + let useport= "" + endif + " 'C' in 'C:\path\to\file' is handled as hostname on windows. + " This is workaround to avoid mis-handle windows local-path: + if g:netrw_scp_cmd =~ '^scp' && has("win32") + let tmpfile_get = substitute(tr(tmpfile, '\', '/'), '^\(\a\):[/\\]\(.*\)$', '/\1/\2', '') + else + let tmpfile_get = tmpfile + endif + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_scp_cmd.useport." ".escape(s:ShellEscape(g:netrw_machine.":".b:netrw_fname,1),' ')." ".s:ShellEscape(tmpfile_get,1)) + let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) + let b:netrw_lastfile = choice + + "......................................... + " NetRead: (http) NetRead Method #5 (wget) {{{3 + elseif b:netrw_method == 5 + " call Decho("read via http (method #5)",'~'.expand("<slnum>")) + if g:netrw_http_cmd == "" + if !exists("g:netrw_quiet") + call netrw#ErrorMsg(s:ERROR,"neither the wget nor the fetch command is available",6) + endif + " call Dret("netrw#NetRead :4 getcwd<".getcwd().">") + return + endif + + if match(b:netrw_fname,"#") == -1 || exists("g:netrw_http_xcmd") + " using g:netrw_http_cmd (usually elinks, links, curl, wget, or fetch) + " call Decho('using '.g:netrw_http_cmd.' (# not in b:netrw_fname<'.b:netrw_fname.">)",'~'.expand("<slnum>")) + if exists("g:netrw_http_xcmd") + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_cmd." ".s:ShellEscape(b:netrw_http."://".g:netrw_machine.b:netrw_fname,1)." ".g:netrw_http_xcmd." ".s:ShellEscape(tmpfile,1)) + else + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(b:netrw_http."://".g:netrw_machine.b:netrw_fname,1)) + endif + let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) + + else + " wget/curl/fetch plus a jump to an in-page marker (ie. http://abc/def.html#aMarker) + " call Decho("wget/curl plus jump (# in b:netrw_fname<".b:netrw_fname.">)",'~'.expand("<slnum>")) + let netrw_html= substitute(b:netrw_fname,"#.*$","","") + let netrw_tag = substitute(b:netrw_fname,"^.*#","","") + " call Decho("netrw_html<".netrw_html.">",'~'.expand("<slnum>")) + " call Decho("netrw_tag <".netrw_tag.">",'~'.expand("<slnum>")) + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(b:netrw_http."://".g:netrw_machine.netrw_html,1)) + let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) + " call Decho('<\s*a\s*name=\s*"'.netrw_tag.'"/','~'.expand("<slnum>")) + exe 'NetrwKeepj norm! 1G/<\s*a\s*name=\s*"'.netrw_tag.'"/'."\<CR>" + endif + let b:netrw_lastfile = choice + " call Decho("setl ro",'~'.expand("<slnum>")) + setl ro nomod + + "......................................... + " NetRead: (dav) NetRead Method #6 {{{3 + elseif b:netrw_method == 6 + " call Decho("read via cadaver (method #6)",'~'.expand("<slnum>")) + + if !executable(g:netrw_dav_cmd) + call netrw#ErrorMsg(s:ERROR,g:netrw_dav_cmd." is not executable",73) + " call Dret("netrw#NetRead : ".g:netrw_dav_cmd." not executable") + return + endif + if g:netrw_dav_cmd =~ "curl" + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_dav_cmd." ".s:ShellEscape("dav://".g:netrw_machine.b:netrw_fname,1)." ".s:ShellEscape(tmpfile,1)) + else + " Construct execution string (four lines) which will be passed through filter + let netrw_fname= escape(b:netrw_fname,g:netrw_fname_escape) + new + setl ff=unix + if exists("g:netrw_port") && g:netrw_port != "" + NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port + else + NetrwKeepj put ='open '.g:netrw_machine + endif + if exists("g:netrw_uid") && exists("s:netrw_passwd") && g:netrw_uid != "" + NetrwKeepj put ='user '.g:netrw_uid.' '.s:netrw_passwd + endif + NetrwKeepj put ='get '.netrw_fname.' '.tmpfile + NetrwKeepj put ='quit' + + " perform cadaver operation: + NetrwKeepj norm! 1G"_dd + call s:NetrwExe(s:netrw_silentxfer."%!".g:netrw_dav_cmd) + keepj bd! + endif + let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) + let b:netrw_lastfile = choice + + "......................................... + " NetRead: (rsync) NetRead Method #7 {{{3 + elseif b:netrw_method == 7 + " call Decho("read via rsync (method #7)",'~'.expand("<slnum>")) + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_rsync_cmd." ".s:ShellEscape(g:netrw_machine.g:netrw_rsync_sep.b:netrw_fname,1)." ".s:ShellEscape(tmpfile,1)) + let result = s:NetrwGetFile(readcmd,tmpfile, b:netrw_method) + let b:netrw_lastfile = choice + + "......................................... + " NetRead: (fetch) NetRead Method #8 {{{3 + " fetch://[user@]host[:http]/path + elseif b:netrw_method == 8 + " call Decho("read via fetch (method #8)",'~'.expand("<slnum>")) + if g:netrw_fetch_cmd == "" + if !exists("g:netrw_quiet") + NetrwKeepj call netrw#ErrorMsg(s:ERROR,"fetch command not available",7) + endif + " call Dret("NetRead") + return + endif + if exists("g:netrw_option") && g:netrw_option =~ ":https\=" + let netrw_option= "http" + else + let netrw_option= "ftp" + endif + " call Decho("read via fetch for ".netrw_option,'~'.expand("<slnum>")) + + if exists("g:netrw_uid") && g:netrw_uid != "" && exists("s:netrw_passwd") && s:netrw_passwd != "" + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_fetch_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(netrw_option."://".g:netrw_uid.':'.s:netrw_passwd.'@'.g:netrw_machine."/".b:netrw_fname,1)) + else + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_fetch_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(netrw_option."://".g:netrw_machine."/".b:netrw_fname,1)) + endif + + let result = s:NetrwGetFile(readcmd,tmpfile, b:netrw_method) + let b:netrw_lastfile = choice + " call Decho("setl ro",'~'.expand("<slnum>")) + setl ro nomod + + "......................................... + " NetRead: (sftp) NetRead Method #9 {{{3 + elseif b:netrw_method == 9 + " call Decho("read via sftp (method #9)",'~'.expand("<slnum>")) + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_sftp_cmd." ".s:ShellEscape(g:netrw_machine.":".b:netrw_fname,1)." ".tmpfile) + let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) + let b:netrw_lastfile = choice + + "......................................... + " NetRead: (file) NetRead Method #10 {{{3 + elseif b:netrw_method == 10 && exists("g:netrw_file_cmd") + " " call Decho("read via ".b:netrw_file_cmd." (method #10)",'~'.expand("<slnum>")) + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_file_cmd." ".s:ShellEscape(b:netrw_fname,1)." ".tmpfile) + let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) + let b:netrw_lastfile = choice + + "......................................... + " NetRead: Complain {{{3 + else + call netrw#ErrorMsg(s:WARNING,"unable to comply with your request<" . choice . ">",8) + endif + endwhile + + " NetRead: cleanup {{{3 + if exists("b:netrw_method") + " call Decho("cleanup b:netrw_method and b:netrw_fname",'~'.expand("<slnum>")) + unlet b:netrw_method + unlet b:netrw_fname + endif + if s:FileReadable(tmpfile) && tmpfile !~ '.tar.bz2$' && tmpfile !~ '.tar.gz$' && tmpfile !~ '.zip' && tmpfile !~ '.tar' && readcmd != 't' && tmpfile !~ '.tar.xz$' && tmpfile !~ '.txz' + " call Decho("cleanup by deleting tmpfile<".tmpfile.">",'~'.expand("<slnum>")) + NetrwKeepj call s:NetrwDelete(tmpfile) + endif + NetrwKeepj call s:NetrwOptionsRestore("w:") + + " call Dret("netrw#NetRead :5 getcwd<".getcwd().">") +endfun + +" ------------------------------------------------------------------------ +" netrw#NetWrite: responsible for writing a file over the net {{{2 +fun! netrw#NetWrite(...) range + " call Dfunc("netrw#NetWrite(a:0=".a:0.") ".g:loaded_netrw) + + " NetWrite: option handling {{{3 + let mod= 0 + call s:NetrwOptionsSave("w:") + call s:NetrwOptionsSafe(0) + + " NetWrite: Get Temporary Filename {{{3 + let tmpfile= s:GetTempfile("") + if tmpfile == "" + " call Dret("netrw#NetWrite : unable to get a tempfile!") + return + endif + + if a:0 == 0 + let ichoice = 0 + else + let ichoice = 1 + endif + + let curbufname= expand("%") + " call Decho("curbufname<".curbufname.">",'~'.expand("<slnum>")) + if &binary + " For binary writes, always write entire file. + " (line numbers don't really make sense for that). + " Also supports the writing of tar and zip files. + " call Decho("(write entire file) sil exe w! ".fnameescape(v:cmdarg)." ".fnameescape(tmpfile),'~'.expand("<slnum>")) + exe "sil NetrwKeepj w! ".fnameescape(v:cmdarg)." ".fnameescape(tmpfile) + elseif g:netrw_cygwin + " write (selected portion of) file to temporary + let cygtmpfile= substitute(tmpfile,g:netrw_cygdrive.'/\(.\)','\1:','') + " call Decho("(write selected portion) sil exe ".a:firstline."," . a:lastline . "w! ".fnameescape(v:cmdarg)." ".fnameescape(cygtmpfile),'~'.expand("<slnum>")) + exe "sil NetrwKeepj ".a:firstline."," . a:lastline . "w! ".fnameescape(v:cmdarg)." ".fnameescape(cygtmpfile) + else + " write (selected portion of) file to temporary + " call Decho("(write selected portion) sil exe ".a:firstline."," . a:lastline . "w! ".fnameescape(v:cmdarg)." ".fnameescape(tmpfile),'~'.expand("<slnum>")) + exe "sil NetrwKeepj ".a:firstline."," . a:lastline . "w! ".fnameescape(v:cmdarg)." ".fnameescape(tmpfile) + endif + + if curbufname == "" + " when the file is [No Name], and one attempts to Nwrite it, the buffer takes + " on the temporary file's name. Deletion of the temporary file during + " cleanup then causes an error message. + 0file! + endif + + " NetWrite: while choice loop: {{{3 + while ichoice <= a:0 + + " Process arguments: {{{4 + " attempt to repeat with previous host-file-etc + if exists("b:netrw_lastfile") && a:0 == 0 + " call Decho("using b:netrw_lastfile<" . b:netrw_lastfile . ">",'~'.expand("<slnum>")) + let choice = b:netrw_lastfile + let ichoice= ichoice + 1 + else + exe "let choice= a:" . ichoice + + " Reconstruct Choice when choice starts with '"' + if match(choice,"?") == 0 + echomsg 'NetWrite Usage:"' + echomsg ':Nwrite machine:path uses rcp' + echomsg ':Nwrite "machine path" uses ftp with <.netrc>' + echomsg ':Nwrite "machine id password path" uses ftp' + echomsg ':Nwrite dav://[user@]machine/path uses cadaver' + echomsg ':Nwrite fetch://[user@]machine/path uses fetch' + echomsg ':Nwrite ftp://machine[#port]/path uses ftp (autodetects <.netrc>)' + echomsg ':Nwrite rcp://machine/path uses rcp' + echomsg ':Nwrite rsync://[user@]machine/path uses rsync' + echomsg ':Nwrite scp://[user@]machine[[:#]port]/path uses scp' + echomsg ':Nwrite sftp://[user@]machine/path uses sftp' + sleep 4 + break + + elseif match(choice,"^\"") != -1 + if match(choice,"\"$") != -1 + " case "..." + let choice=strpart(choice,1,strlen(choice)-2) + else + " case "... ... ..." + let choice = strpart(choice,1,strlen(choice)-1) + let wholechoice = "" + + while match(choice,"\"$") == -1 + let wholechoice= wholechoice . " " . choice + let ichoice = ichoice + 1 + if choice > a:0 + if !exists("g:netrw_quiet") + call netrw#ErrorMsg(s:ERROR,"Unbalanced string in filename '". wholechoice ."'",13) + endif + " call Dret("netrw#NetWrite") + return + endif + let choice= a:{ichoice} + endwhile + let choice= strpart(wholechoice,1,strlen(wholechoice)-1) . " " . strpart(choice,0,strlen(choice)-1) + endif + endif + endif + let ichoice= ichoice + 1 + " call Decho("choice<" . choice . "> ichoice=".ichoice,'~'.expand("<slnum>")) + + " Determine method of write (ftp, rcp, etc) {{{4 + NetrwKeepj call s:NetrwMethod(choice) + if !exists("b:netrw_method") || b:netrw_method < 0 + " call Dfunc("netrw#NetWrite : unsupported method") + return + endif + + " ============= + " NetWrite: Perform Protocol-Based Write {{{3 + " ============================ + if exists("g:netrw_silent") && g:netrw_silent == 0 && &ch >= 1 + echo "(netrw) Processing your write request..." + " call Decho("Processing your write request...",'~'.expand("<slnum>")) + endif + + "......................................... + " NetWrite: (rcp) NetWrite Method #1 {{{3 + if b:netrw_method == 1 + " call Decho("write via rcp (method #1)",'~'.expand("<slnum>")) + if s:netrw_has_nt_rcp == 1 + if exists("g:netrw_uid") && ( g:netrw_uid != "" ) + let uid_machine = g:netrw_machine .'.'. g:netrw_uid + else + let uid_machine = g:netrw_machine .'.'. $USERNAME + endif + else + if exists("g:netrw_uid") && ( g:netrw_uid != "" ) + let uid_machine = g:netrw_uid .'@'. g:netrw_machine + else + let uid_machine = g:netrw_machine + endif + endif + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_rcp_cmd." ".s:netrw_rcpmode." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(uid_machine.":".b:netrw_fname,1)) + let b:netrw_lastfile = choice + + "......................................... + " NetWrite: (ftp + <.netrc>) NetWrite Method #2 {{{3 + elseif b:netrw_method == 2 + " call Decho("write via ftp+.netrc (method #2)",'~'.expand("<slnum>")) + let netrw_fname = b:netrw_fname + + " formerly just a "new...bd!", that changed the window sizes when equalalways. Using enew workaround instead + let bhkeep = &l:bh + let curbuf = bufnr("%") + setl bh=hide + keepj keepalt enew + + " call Decho("filter input window#".winnr(),'~'.expand("<slnum>")) + setl ff=unix + NetrwKeepj put =g:netrw_ftpmode + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + if exists("g:netrw_ftpextracmd") + NetrwKeepj put =g:netrw_ftpextracmd + " call Decho("filter input: ".getline("$"),'~'.expand("<slnum>")) + endif + NetrwKeepj call setline(line("$")+1,'put "'.tmpfile.'" "'.netrw_fname.'"') + " call Decho("filter input: ".getline("$"),'~'.expand("<slnum>")) + if exists("g:netrw_port") && g:netrw_port != "" + call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1)) + else + " call Decho("filter input window#".winnr(),'~'.expand("<slnum>")) + call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)) + endif + " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) + if getline(1) !~ "^$" + if !exists("g:netrw_quiet") + NetrwKeepj call netrw#ErrorMsg(s:ERROR,getline(1),14) + endif + let mod=1 + endif + + " remove enew buffer (quietly) + let filtbuf= bufnr("%") + exe curbuf."b!" + let &l:bh = bhkeep + exe filtbuf."bw!" + + let b:netrw_lastfile = choice + + "......................................... + " NetWrite: (ftp + machine, id, passwd, filename) NetWrite Method #3 {{{3 + elseif b:netrw_method == 3 + " Construct execution string (three or more lines) which will be passed through filter + " call Decho("read via ftp+mipf (method #3)",'~'.expand("<slnum>")) + let netrw_fname = b:netrw_fname + let bhkeep = &l:bh + + " formerly just a "new...bd!", that changed the window sizes when equalalways. Using enew workaround instead + let curbuf = bufnr("%") + setl bh=hide + keepj keepalt enew + setl ff=unix + + if exists("g:netrw_port") && g:netrw_port != "" + NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port + " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) + else + NetrwKeepj put ='open '.g:netrw_machine + " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) + endif + if exists("g:netrw_uid") && g:netrw_uid != "" + if exists("g:netrw_ftp") && g:netrw_ftp == 1 + NetrwKeepj put =g:netrw_uid + " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) + if exists("s:netrw_passwd") && s:netrw_passwd != "" + NetrwKeepj put ='\"'.s:netrw_passwd.'\"' + endif + " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) + elseif exists("s:netrw_passwd") && s:netrw_passwd != "" + NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"' + " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) + endif + endif + NetrwKeepj put =g:netrw_ftpmode + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + if exists("g:netrw_ftpextracmd") + NetrwKeepj put =g:netrw_ftpextracmd + " call Decho("filter input: ".getline("$"),'~'.expand("<slnum>")) + endif + NetrwKeepj put ='put \"'.tmpfile.'\" \"'.netrw_fname.'\"' + " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) + " save choice/id/password for future use + let b:netrw_lastfile = choice + + " perform ftp: + " -i : turns off interactive prompting from ftp + " -n unix : DON'T use <.netrc>, even though it exists + " -n win32: quit being obnoxious about password + NetrwKeepj norm! 1G"_dd + call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." ".g:netrw_ftp_options) + " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) + if getline(1) !~ "^$" + if !exists("g:netrw_quiet") + call netrw#ErrorMsg(s:ERROR,getline(1),15) + endif + let mod=1 + endif + + " remove enew buffer (quietly) + let filtbuf= bufnr("%") + exe curbuf."b!" + let &l:bh= bhkeep + exe filtbuf."bw!" + + "......................................... + " NetWrite: (scp) NetWrite Method #4 {{{3 + elseif b:netrw_method == 4 + " call Decho("write via scp (method #4)",'~'.expand("<slnum>")) + if exists("g:netrw_port") && g:netrw_port != "" + let useport= " ".g:netrw_scpport." ".fnameescape(g:netrw_port) + else + let useport= "" + endif + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_scp_cmd.useport." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(g:netrw_machine.":".b:netrw_fname,1)) + let b:netrw_lastfile = choice + + "......................................... + " NetWrite: (http) NetWrite Method #5 {{{3 + elseif b:netrw_method == 5 + " call Decho("write via http (method #5)",'~'.expand("<slnum>")) + let curl= substitute(g:netrw_http_put_cmd,'\s\+.*$',"","") + if executable(curl) + let url= g:netrw_choice + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_http_put_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(url,1) ) + elseif !exists("g:netrw_quiet") + call netrw#ErrorMsg(s:ERROR,"can't write to http using <".g:netrw_http_put_cmd.">",16) + endif + + "......................................... + " NetWrite: (dav) NetWrite Method #6 (cadaver) {{{3 + elseif b:netrw_method == 6 + " call Decho("write via cadaver (method #6)",'~'.expand("<slnum>")) + + " Construct execution string (four lines) which will be passed through filter + let netrw_fname = escape(b:netrw_fname,g:netrw_fname_escape) + let bhkeep = &l:bh + + " formerly just a "new...bd!", that changed the window sizes when equalalways. Using enew workaround instead + let curbuf = bufnr("%") + setl bh=hide + keepj keepalt enew + + setl ff=unix + if exists("g:netrw_port") && g:netrw_port != "" + NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port + else + NetrwKeepj put ='open '.g:netrw_machine + endif + if exists("g:netrw_uid") && exists("s:netrw_passwd") && g:netrw_uid != "" + NetrwKeepj put ='user '.g:netrw_uid.' '.s:netrw_passwd + endif + NetrwKeepj put ='put '.tmpfile.' '.netrw_fname + + " perform cadaver operation: + NetrwKeepj norm! 1G"_dd + call s:NetrwExe(s:netrw_silentxfer."%!".g:netrw_dav_cmd) + + " remove enew buffer (quietly) + let filtbuf= bufnr("%") + exe curbuf."b!" + let &l:bh = bhkeep + exe filtbuf."bw!" + + let b:netrw_lastfile = choice + + "......................................... + " NetWrite: (rsync) NetWrite Method #7 {{{3 + elseif b:netrw_method == 7 + " call Decho("write via rsync (method #7)",'~'.expand("<slnum>")) + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_rsync_cmd." ".s:ShellEscape(tmpfile,1)." ".s:ShellEscape(g:netrw_machine.g:netrw_rsync_sep.b:netrw_fname,1)) + let b:netrw_lastfile = choice + + "......................................... + " NetWrite: (sftp) NetWrite Method #9 {{{3 + elseif b:netrw_method == 9 + " call Decho("write via sftp (method #9)",'~'.expand("<slnum>")) + let netrw_fname= escape(b:netrw_fname,g:netrw_fname_escape) + if exists("g:netrw_uid") && ( g:netrw_uid != "" ) + let uid_machine = g:netrw_uid .'@'. g:netrw_machine + else + let uid_machine = g:netrw_machine + endif + + " formerly just a "new...bd!", that changed the window sizes when equalalways. Using enew workaround instead + let bhkeep = &l:bh + let curbuf = bufnr("%") + setl bh=hide + keepj keepalt enew + + setl ff=unix + call setline(1,'put "'.escape(tmpfile,'\').'" '.netrw_fname) + " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) + let sftpcmd= substitute(g:netrw_sftp_cmd,"%TEMPFILE%",escape(tmpfile,'\'),"g") + call s:NetrwExe(s:netrw_silentxfer."%!".sftpcmd.' '.s:ShellEscape(uid_machine,1)) + let filtbuf= bufnr("%") + exe curbuf."b!" + let &l:bh = bhkeep + exe filtbuf."bw!" + let b:netrw_lastfile = choice + + "......................................... + " NetWrite: Complain {{{3 + else + call netrw#ErrorMsg(s:WARNING,"unable to comply with your request<" . choice . ">",17) + let leavemod= 1 + endif + endwhile + + " NetWrite: Cleanup: {{{3 + " call Decho("cleanup",'~'.expand("<slnum>")) + if s:FileReadable(tmpfile) + " call Decho("tmpfile<".tmpfile."> readable, will now delete it",'~'.expand("<slnum>")) + call s:NetrwDelete(tmpfile) + endif + call s:NetrwOptionsRestore("w:") + + if a:firstline == 1 && a:lastline == line("$") + " restore modifiability; usually equivalent to set nomod + let &l:mod= mod + " call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) + elseif !exists("leavemod") + " indicate that the buffer has not been modified since last written + " call Decho("set nomod",'~'.expand("<slnum>")) + setl nomod + " call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) + endif + + " call Dret("netrw#NetWrite") +endfun + +" --------------------------------------------------------------------- +" netrw#NetSource: source a remotely hosted vim script {{{2 +" uses NetRead to get a copy of the file into a temporarily file, +" then sources that file, +" then removes that file. +fun! netrw#NetSource(...) + " call Dfunc("netrw#NetSource() a:0=".a:0) + if a:0 > 0 && a:1 == '?' + " give help + echomsg 'NetSource Usage:' + echomsg ':Nsource dav://machine[:port]/path uses cadaver' + echomsg ':Nsource fetch://machine/path uses fetch' + echomsg ':Nsource ftp://[user@]machine[:port]/path uses ftp autodetects <.netrc>' + echomsg ':Nsource http[s]://[user@]machine/path uses http wget' + echomsg ':Nsource rcp://[user@]machine/path uses rcp' + echomsg ':Nsource rsync://machine[:port]/path uses rsync' + echomsg ':Nsource scp://[user@]machine[[:#]port]/path uses scp' + echomsg ':Nsource sftp://[user@]machine[[:#]port]/path uses sftp' + sleep 4 + else + let i= 1 + while i <= a:0 + call netrw#NetRead(3,a:{i}) + " call Decho("s:netread_tmpfile<".s:netrw_tmpfile.">",'~'.expand("<slnum>")) + if s:FileReadable(s:netrw_tmpfile) + " call Decho("exe so ".fnameescape(s:netrw_tmpfile),'~'.expand("<slnum>")) + exe "so ".fnameescape(s:netrw_tmpfile) + " call Decho("delete(".s:netrw_tmpfile.")",'~'.expand("<slnum>")) + if delete(s:netrw_tmpfile) + call netrw#ErrorMsg(s:ERROR,"unable to delete directory <".s:netrw_tmpfile.">!",103) + endif + unlet s:netrw_tmpfile + else + call netrw#ErrorMsg(s:ERROR,"unable to source <".a:{i}.">!",48) + endif + let i= i + 1 + endwhile + endif + " call Dret("netrw#NetSource") +endfun + +" --------------------------------------------------------------------- +" netrw#SetTreetop: resets the tree top to the current directory/specified directory {{{2 +" (implements the :Ntree command) +fun! netrw#SetTreetop(iscmd,...) + + " iscmd==0: netrw#SetTreetop called using gn mapping + " iscmd==1: netrw#SetTreetop called using :Ntree from the command line + " clear out the current tree + if exists("w:netrw_treetop") + let inittreetop= w:netrw_treetop + unlet w:netrw_treetop + endif + if exists("w:netrw_treedict") + unlet w:netrw_treedict + endif + + if (a:iscmd == 0 || a:1 == "") && exists("inittreetop") + let treedir = s:NetrwTreePath(inittreetop) + else + if isdirectory(s:NetrwFile(a:1)) + let treedir = a:1 + let s:netrw_treetop = treedir + elseif exists("b:netrw_curdir") && (isdirectory(s:NetrwFile(b:netrw_curdir."/".a:1)) || a:1 =~ '^\a\{3,}://') + let treedir = b:netrw_curdir."/".a:1 + let s:netrw_treetop = treedir + else + " normally the cursor is left in the message window. + " However, here this results in the directory being listed in the message window, which is not wanted. + let netrwbuf= bufnr("%") + call netrw#ErrorMsg(s:ERROR,"sorry, ".a:1." doesn't seem to be a directory!",95) + exe bufwinnr(netrwbuf)."wincmd w" + let treedir = "." + let s:netrw_treetop = getcwd() + endif + endif + + " determine if treedir is remote or local + let islocal= expand("%") !~ '^\a\{3,}://' + + " browse the resulting directory + if islocal + call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(islocal,treedir,0)) + else + call s:NetrwBrowse(islocal,s:NetrwBrowseChgDir(islocal,treedir,0)) + endif + +endfun + +" =========================================== +" s:NetrwGetFile: Function to read temporary file "tfile" with command "readcmd". {{{2 +" readcmd == %r : replace buffer with newly read file +" == 0r : read file at top of buffer +" == r : read file after current line +" == t : leave file in temporary form (ie. don't read into buffer) +fun! s:NetrwGetFile(readcmd, tfile, method) + " call Dfunc("NetrwGetFile(readcmd<".a:readcmd.">,tfile<".a:tfile."> method<".a:method.">)") + + " readcmd=='t': simply do nothing + if a:readcmd == 't' + " call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) + " call Dret("NetrwGetFile : skip read of tfile<".a:tfile.">") + return + endif + + " get name of remote filename (ie. url and all) + let rfile= bufname("%") + " call Decho("rfile<".rfile.">",'~'.expand("<slnum>")) + + if exists("*NetReadFixup") + " for the use of NetReadFixup (not otherwise used internally) + let line2= line("$") + endif + + if a:readcmd[0] == '%' + " get file into buffer + " call Decho("get file into buffer",'~'.expand("<slnum>")) + + " rename the current buffer to the temp file (ie. tfile) + if g:netrw_cygwin + let tfile= substitute(a:tfile,g:netrw_cygdrive.'/\(.\)','\1:','') + else + let tfile= a:tfile + endif + call s:NetrwBufRename(tfile) + + " edit temporary file (ie. read the temporary file in) + if rfile =~ '\.zip$' + " call Decho("handling remote zip file with zip#Browse(tfile<".tfile.">)",'~'.expand("<slnum>")) + call zip#Browse(tfile) + elseif rfile =~ '\.tar$' + " call Decho("handling remote tar file with tar#Browse(tfile<".tfile.">)",'~'.expand("<slnum>")) + call tar#Browse(tfile) + elseif rfile =~ '\.tar\.gz$' + " call Decho("handling remote gzip-compressed tar file",'~'.expand("<slnum>")) + call tar#Browse(tfile) + elseif rfile =~ '\.tar\.bz2$' + " call Decho("handling remote bz2-compressed tar file",'~'.expand("<slnum>")) + call tar#Browse(tfile) + elseif rfile =~ '\.tar\.xz$' + " call Decho("handling remote xz-compressed tar file",'~'.expand("<slnum>")) + call tar#Browse(tfile) + elseif rfile =~ '\.txz$' + " call Decho("handling remote xz-compressed tar file (.txz)",'~'.expand("<slnum>")) + call tar#Browse(tfile) + else + " call Decho("edit temporary file",'~'.expand("<slnum>")) + NetrwKeepj e! + endif + + " rename buffer back to remote filename + call s:NetrwBufRename(rfile) + + " Jan 19, 2022: COMBAK -- bram problem with https://github.com/vim/vim/pull/9554.diff filetype + " Detect filetype of local version of remote file. + " Note that isk must not include a "/" for scripts.vim + " to process this detection correctly. + " call Decho("detect filetype of local version of remote file<".rfile.">",'~'.expand("<slnum>")) + " call Decho("..did_filetype()=".did_filetype()) + " setl ft= + " call Decho("..initial filetype<".&ft."> for buf#".bufnr()."<".bufname().">") + let iskkeep= &isk + setl isk-=/ + filetype detect + " call Decho("..local filetype<".&ft."> for buf#".bufnr()."<".bufname().">") + let &l:isk= iskkeep + " call Dredir("ls!","NetrwGetFile (renamed buffer back to remote filename<".rfile."> : expand(%)<".expand("%").">)") + let line1 = 1 + let line2 = line("$") + + elseif !&ma + " attempting to read a file after the current line in the file, but the buffer is not modifiable + NetrwKeepj call netrw#ErrorMsg(s:WARNING,"attempt to read<".a:tfile."> into a non-modifiable buffer!",94) + " call Dret("NetrwGetFile : attempt to read<".a:tfile."> into a non-modifiable buffer!") + return + + elseif s:FileReadable(a:tfile) + " read file after current line + " call Decho("read file<".a:tfile."> after current line",'~'.expand("<slnum>")) + let curline = line(".") + let lastline= line("$") + " call Decho("exe<".a:readcmd." ".fnameescape(v:cmdarg)." ".fnameescape(a:tfile)."> line#".curline,'~'.expand("<slnum>")) + exe "NetrwKeepj ".a:readcmd." ".fnameescape(v:cmdarg)." ".fnameescape(a:tfile) + let line1= curline + 1 + let line2= line("$") - lastline + 1 + + else + " not readable + " call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) + " call Decho("tfile<".a:tfile."> not readable",'~'.expand("<slnum>")) + NetrwKeepj call netrw#ErrorMsg(s:WARNING,"file <".a:tfile."> not readable",9) + " call Dret("NetrwGetFile : tfile<".a:tfile."> not readable") + return + endif + + " User-provided (ie. optional) fix-it-up command + if exists("*NetReadFixup") + " call Decho("calling NetReadFixup(method<".a:method."> line1=".line1." line2=".line2.")",'~'.expand("<slnum>")) + NetrwKeepj call NetReadFixup(a:method, line1, line2) + " else " Decho + " call Decho("NetReadFixup() not called, doesn't exist (line1=".line1." line2=".line2.")",'~'.expand("<slnum>")) + endif + + if has("gui") && has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu + " update the Buffers menu + NetrwKeepj call s:UpdateBuffersMenu() + endif + + " call Decho("readcmd<".a:readcmd."> cmdarg<".v:cmdarg."> tfile<".a:tfile."> readable=".s:FileReadable(a:tfile),'~'.expand("<slnum>")) + + " make sure file is being displayed + " redraw! + + " call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) + " call Dret("NetrwGetFile") +endfun + +" ------------------------------------------------------------------------ +" s:NetrwMethod: determine method of transfer {{{2 +" Input: +" choice = url [protocol:]//[userid@]hostname[:port]/[path-to-file] +" Output: +" b:netrw_method= 1: rcp +" 2: ftp + <.netrc> +" 3: ftp + machine, id, password, and [path]filename +" 4: scp +" 5: http[s] (wget) +" 6: dav +" 7: rsync +" 8: fetch +" 9: sftp +" 10: file +" g:netrw_machine= hostname +" b:netrw_fname = filename +" g:netrw_port = optional port number (for ftp) +" g:netrw_choice = copy of input url (choice) +fun! s:NetrwMethod(choice) + " call Dfunc("s:NetrwMethod(a:choice<".a:choice.">)") + + " sanity check: choice should have at least three slashes in it + if strlen(substitute(a:choice,'[^/]','','g')) < 3 + call netrw#ErrorMsg(s:ERROR,"not a netrw-style url; netrw uses protocol://[user@]hostname[:port]/[path])",78) + let b:netrw_method = -1 + " call Dret("s:NetrwMethod : incorrect url format<".a:choice.">") + return + endif + + " record current g:netrw_machine, if any + " curmachine used if protocol == ftp and no .netrc + if exists("g:netrw_machine") + let curmachine= g:netrw_machine + " call Decho("curmachine<".curmachine.">",'~'.expand("<slnum>")) + else + let curmachine= "N O T A HOST" + endif + if exists("g:netrw_port") + let netrw_port= g:netrw_port + endif + + " insure that netrw_ftp_cmd starts off every method determination + " with the current g:netrw_ftp_cmd + let s:netrw_ftp_cmd= g:netrw_ftp_cmd + + " initialization + let b:netrw_method = 0 + let g:netrw_machine = "" + let b:netrw_fname = "" + let g:netrw_port = "" + let g:netrw_choice = a:choice + + " Patterns: + " mipf : a:machine a:id password filename Use ftp + " mf : a:machine filename Use ftp + <.netrc> or g:netrw_uid s:netrw_passwd + " ftpurm : ftp://[user@]host[[#:]port]/filename Use ftp + <.netrc> or g:netrw_uid s:netrw_passwd + " rcpurm : rcp://[user@]host/filename Use rcp + " rcphf : [user@]host:filename Use rcp + " scpurm : scp://[user@]host[[#:]port]/filename Use scp + " httpurm : http[s]://[user@]host/filename Use wget + " davurm : dav[s]://host[:port]/path Use cadaver/curl + " rsyncurm : rsync://host[:port]/path Use rsync + " fetchurm : fetch://[user@]host[:http]/filename Use fetch (defaults to ftp, override for http) + " sftpurm : sftp://[user@]host/filename Use scp + " fileurm : file://[user@]host/filename Use elinks or links + let mipf = '^\(\S\+\)\s\+\(\S\+\)\s\+\(\S\+\)\s\+\(\S\+\)$' + let mf = '^\(\S\+\)\s\+\(\S\+\)$' + let ftpurm = '^ftp://\(\([^/]*\)@\)\=\([^/#:]\{-}\)\([#:]\d\+\)\=/\(.*\)$' + let rcpurm = '^rcp://\%(\([^/]*\)@\)\=\([^/]\{-}\)/\(.*\)$' + let rcphf = '^\(\(\h\w*\)@\)\=\(\h\w*\):\([^@]\+\)$' + let scpurm = '^scp://\([^/#:]\+\)\%([#:]\(\d\+\)\)\=/\(.*\)$' + let httpurm = '^https\=://\([^/]\{-}\)\(/.*\)\=$' + let davurm = '^davs\=://\([^/]\+\)/\(.*/\)\([-_.~[:alnum:]]\+\)$' + let rsyncurm = '^rsync://\([^/]\{-}\)/\(.*\)\=$' + let fetchurm = '^fetch://\(\([^/]*\)@\)\=\([^/#:]\{-}\)\(:http\)\=/\(.*\)$' + let sftpurm = '^sftp://\([^/]\{-}\)/\(.*\)\=$' + let fileurm = '^file\=://\(.*\)$' + + " call Decho("determine method:",'~'.expand("<slnum>")) + " Determine Method + " Method#1: rcp://user@hostname/...path-to-file {{{3 + if match(a:choice,rcpurm) == 0 + " call Decho("rcp://...",'~'.expand("<slnum>")) + let b:netrw_method = 1 + let userid = substitute(a:choice,rcpurm,'\1',"") + let g:netrw_machine = substitute(a:choice,rcpurm,'\2',"") + let b:netrw_fname = substitute(a:choice,rcpurm,'\3',"") + if userid != "" + let g:netrw_uid= userid + endif + + " Method#4: scp://user@hostname/...path-to-file {{{3 + elseif match(a:choice,scpurm) == 0 + " call Decho("scp://...",'~'.expand("<slnum>")) + let b:netrw_method = 4 + let g:netrw_machine = substitute(a:choice,scpurm,'\1',"") + let g:netrw_port = substitute(a:choice,scpurm,'\2',"") + let b:netrw_fname = substitute(a:choice,scpurm,'\3',"") + + " Method#5: http[s]://user@hostname/...path-to-file {{{3 + elseif match(a:choice,httpurm) == 0 + " call Decho("http[s]://...",'~'.expand("<slnum>")) + let b:netrw_method = 5 + let g:netrw_machine= substitute(a:choice,httpurm,'\1',"") + let b:netrw_fname = substitute(a:choice,httpurm,'\2',"") + let b:netrw_http = (a:choice =~ '^https:')? "https" : "http" + + " Method#6: dav://hostname[:port]/..path-to-file.. {{{3 + elseif match(a:choice,davurm) == 0 + " call Decho("dav://...",'~'.expand("<slnum>")) + let b:netrw_method= 6 + if a:choice =~ 'davs:' + let g:netrw_machine= 'https://'.substitute(a:choice,davurm,'\1/\2',"") + else + let g:netrw_machine= 'http://'.substitute(a:choice,davurm,'\1/\2',"") + endif + let b:netrw_fname = substitute(a:choice,davurm,'\3',"") + + " Method#7: rsync://user@hostname/...path-to-file {{{3 + elseif match(a:choice,rsyncurm) == 0 + " call Decho("rsync://...",'~'.expand("<slnum>")) + let b:netrw_method = 7 + let g:netrw_machine= substitute(a:choice,rsyncurm,'\1',"") + let b:netrw_fname = substitute(a:choice,rsyncurm,'\2',"") + + " Methods 2,3: ftp://[user@]hostname[[:#]port]/...path-to-file {{{3 + elseif match(a:choice,ftpurm) == 0 + " call Decho("ftp://...",'~'.expand("<slnum>")) + let userid = substitute(a:choice,ftpurm,'\2',"") + let g:netrw_machine= substitute(a:choice,ftpurm,'\3',"") + let g:netrw_port = substitute(a:choice,ftpurm,'\4',"") + let b:netrw_fname = substitute(a:choice,ftpurm,'\5',"") + " call Decho("g:netrw_machine<".g:netrw_machine.">",'~'.expand("<slnum>")) + if userid != "" + let g:netrw_uid= userid + endif + + if curmachine != g:netrw_machine + if exists("s:netrw_hup[".g:netrw_machine."]") + call NetUserPass("ftp:".g:netrw_machine) + elseif exists("s:netrw_passwd") + " if there's a change in hostname, require password re-entry + unlet s:netrw_passwd + endif + if exists("netrw_port") + unlet netrw_port + endif + endif + + if exists("g:netrw_uid") && exists("s:netrw_passwd") + let b:netrw_method = 3 + else + let host= substitute(g:netrw_machine,'\..*$','','') + if exists("s:netrw_hup[host]") + call NetUserPass("ftp:".host) + + elseif has("win32") && 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\>' + 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 + let b:netrw_method= 2 + elseif s:FileReadable(expand("$HOME/.netrc")) && !g:netrw_ignorenetrc + " call Decho("using <".expand("$HOME/.netrc")."> (readable)",'~'.expand("<slnum>")) + let b:netrw_method= 2 + else + if !exists("g:netrw_uid") || g:netrw_uid == "" + call NetUserPass() + elseif !exists("s:netrw_passwd") || s:netrw_passwd == "" + call NetUserPass(g:netrw_uid) + " else just use current g:netrw_uid and s:netrw_passwd + endif + let b:netrw_method= 3 + endif + endif + + " Method#8: fetch {{{3 + elseif match(a:choice,fetchurm) == 0 + " call Decho("fetch://...",'~'.expand("<slnum>")) + let b:netrw_method = 8 + let g:netrw_userid = substitute(a:choice,fetchurm,'\2',"") + let g:netrw_machine= substitute(a:choice,fetchurm,'\3',"") + let b:netrw_option = substitute(a:choice,fetchurm,'\4',"") + let b:netrw_fname = substitute(a:choice,fetchurm,'\5',"") + + " Method#3: Issue an ftp : "machine id password [path/]filename" {{{3 + elseif match(a:choice,mipf) == 0 + " call Decho("(ftp) host id pass file",'~'.expand("<slnum>")) + let b:netrw_method = 3 + let g:netrw_machine = substitute(a:choice,mipf,'\1',"") + let g:netrw_uid = substitute(a:choice,mipf,'\2',"") + let s:netrw_passwd = substitute(a:choice,mipf,'\3',"") + let b:netrw_fname = substitute(a:choice,mipf,'\4',"") + call NetUserPass(g:netrw_machine,g:netrw_uid,s:netrw_passwd) + + " Method#3: Issue an ftp: "hostname [path/]filename" {{{3 + elseif match(a:choice,mf) == 0 + " call Decho("(ftp) host file",'~'.expand("<slnum>")) + if exists("g:netrw_uid") && exists("s:netrw_passwd") + let b:netrw_method = 3 + let g:netrw_machine = substitute(a:choice,mf,'\1',"") + let b:netrw_fname = substitute(a:choice,mf,'\2',"") + + elseif s:FileReadable(expand("$HOME/.netrc")) + let b:netrw_method = 2 + let g:netrw_machine = substitute(a:choice,mf,'\1',"") + let b:netrw_fname = substitute(a:choice,mf,'\2',"") + endif + + " Method#9: sftp://user@hostname/...path-to-file {{{3 + elseif match(a:choice,sftpurm) == 0 + " call Decho("sftp://...",'~'.expand("<slnum>")) + let b:netrw_method = 9 + let g:netrw_machine= substitute(a:choice,sftpurm,'\1',"") + let b:netrw_fname = substitute(a:choice,sftpurm,'\2',"") + + " Method#1: Issue an rcp: hostname:filename" (this one should be last) {{{3 + elseif match(a:choice,rcphf) == 0 + " call Decho("(rcp) [user@]host:file) rcphf<".rcphf.">",'~'.expand("<slnum>")) + let b:netrw_method = 1 + let userid = substitute(a:choice,rcphf,'\2',"") + let g:netrw_machine = substitute(a:choice,rcphf,'\3',"") + let b:netrw_fname = substitute(a:choice,rcphf,'\4',"") + " call Decho('\1<'.substitute(a:choice,rcphf,'\1',"").">",'~'.expand("<slnum>")) + " call Decho('\2<'.substitute(a:choice,rcphf,'\2',"").">",'~'.expand("<slnum>")) + " call Decho('\3<'.substitute(a:choice,rcphf,'\3',"").">",'~'.expand("<slnum>")) + " call Decho('\4<'.substitute(a:choice,rcphf,'\4',"").">",'~'.expand("<slnum>")) + if userid != "" + let g:netrw_uid= userid + endif + + " Method#10: file://user@hostname/...path-to-file {{{3 + elseif match(a:choice,fileurm) == 0 && exists("g:netrw_file_cmd") + " call Decho("http[s]://...",'~'.expand("<slnum>")) + let b:netrw_method = 10 + let b:netrw_fname = substitute(a:choice,fileurm,'\1',"") + " call Decho('\1<'.substitute(a:choice,fileurm,'\1',"").">",'~'.expand("<slnum>")) + + " Cannot Determine Method {{{3 + else + if !exists("g:netrw_quiet") + call netrw#ErrorMsg(s:WARNING,"cannot determine method (format: protocol://[user@]hostname[:port]/[path])",45) + endif + let b:netrw_method = -1 + endif + "}}}3 + + if g:netrw_port != "" + " remove any leading [:#] from port number + let g:netrw_port = substitute(g:netrw_port,'[#:]\+','','') + elseif exists("netrw_port") + " retain port number as implicit for subsequent ftp operations + let g:netrw_port= netrw_port + endif + + " call Decho("a:choice <".a:choice.">",'~'.expand("<slnum>")) + " call Decho("b:netrw_method <".b:netrw_method.">",'~'.expand("<slnum>")) + " call Decho("g:netrw_machine<".g:netrw_machine.">",'~'.expand("<slnum>")) + " call Decho("g:netrw_port <".g:netrw_port.">",'~'.expand("<slnum>")) + " if exists("g:netrw_uid") "Decho + " call Decho("g:netrw_uid <".g:netrw_uid.">",'~'.expand("<slnum>")) + " endif "Decho + " if exists("s:netrw_passwd") "Decho + " call Decho("s:netrw_passwd <".s:netrw_passwd.">",'~'.expand("<slnum>")) + " endif "Decho + " call Decho("b:netrw_fname <".b:netrw_fname.">",'~'.expand("<slnum>")) + " call Dret("s:NetrwMethod : b:netrw_method=".b:netrw_method." g:netrw_port=".g:netrw_port) +endfun + +" --------------------------------------------------------------------- +" NetUserPass: set username and password for subsequent ftp transfer {{{2 +" Usage: :call NetUserPass() -- will prompt for userid and password +" :call NetUserPass("uid") -- will prompt for password +" :call NetUserPass("uid","password") -- sets global userid and password +" :call NetUserPass("ftp:host") -- looks up userid and password using hup dictionary +" :call NetUserPass("host","uid","password") -- sets hup dictionary with host, userid, password +fun! NetUserPass(...) + + " call Dfunc("NetUserPass() a:0=".a:0) + + if !exists('s:netrw_hup') + let s:netrw_hup= {} + endif + + if a:0 == 0 + " case: no input arguments + + " change host and username if not previously entered; get new password + if !exists("g:netrw_machine") + let g:netrw_machine= input('Enter hostname: ') + endif + if !exists("g:netrw_uid") || g:netrw_uid == "" + " get username (user-id) via prompt + let g:netrw_uid= input('Enter username: ') + endif + " get password via prompting + let s:netrw_passwd= inputsecret("Enter Password: ") + + " set up hup database + let host = substitute(g:netrw_machine,'\..*$','','') + if !exists('s:netrw_hup[host]') + let s:netrw_hup[host]= {} + endif + let s:netrw_hup[host].uid = g:netrw_uid + let s:netrw_hup[host].passwd = s:netrw_passwd + + elseif a:0 == 1 + " case: one input argument + + if a:1 =~ '^ftp:' + " get host from ftp:... url + " access userid and password from hup (host-user-passwd) dictionary + " call Decho("case a:0=1: a:1<".a:1."> (get host from ftp:... url)",'~'.expand("<slnum>")) + let host = substitute(a:1,'^ftp:','','') + let host = substitute(host,'\..*','','') + if exists("s:netrw_hup[host]") + let g:netrw_uid = s:netrw_hup[host].uid + let s:netrw_passwd = s:netrw_hup[host].passwd + " call Decho("get s:netrw_hup[".host."].uid <".s:netrw_hup[host].uid.">",'~'.expand("<slnum>")) + " call Decho("get s:netrw_hup[".host."].passwd<".s:netrw_hup[host].passwd.">",'~'.expand("<slnum>")) + else + let g:netrw_uid = input("Enter UserId: ") + let s:netrw_passwd = inputsecret("Enter Password: ") + endif + + else + " case: one input argument, not an url. Using it as a new user-id. + " call Decho("case a:0=1: a:1<".a:1."> (get host from input argument, not an url)",'~'.expand("<slnum>")) + if exists("g:netrw_machine") + if g:netrw_machine =~ '[0-9.]\+' + let host= g:netrw_machine + else + let host= substitute(g:netrw_machine,'\..*$','','') + endif + else + let g:netrw_machine= input('Enter hostname: ') + endif + let g:netrw_uid = a:1 + " call Decho("set g:netrw_uid= <".g:netrw_uid.">",'~'.expand("<slnum>")) + if exists("g:netrw_passwd") + " ask for password if one not previously entered + let s:netrw_passwd= g:netrw_passwd + else + let s:netrw_passwd = inputsecret("Enter Password: ") + endif + endif + + " call Decho("host<".host.">",'~'.expand("<slnum>")) + if exists("host") + if !exists('s:netrw_hup[host]') + let s:netrw_hup[host]= {} + endif + let s:netrw_hup[host].uid = g:netrw_uid + let s:netrw_hup[host].passwd = s:netrw_passwd + endif + + elseif a:0 == 2 + let g:netrw_uid = a:1 + let s:netrw_passwd = a:2 + + elseif a:0 == 3 + " enter hostname, user-id, and password into the hup dictionary + let host = substitute(a:1,'^\a\+:','','') + let host = substitute(host,'\..*$','','') + if !exists('s:netrw_hup[host]') + let s:netrw_hup[host]= {} + endif + let s:netrw_hup[host].uid = a:2 + let s:netrw_hup[host].passwd = a:3 + let g:netrw_uid = s:netrw_hup[host].uid + let s:netrw_passwd = s:netrw_hup[host].passwd + " call Decho("set s:netrw_hup[".host."].uid <".s:netrw_hup[host].uid.">",'~'.expand("<slnum>")) + " call Decho("set s:netrw_hup[".host."].passwd<".s:netrw_hup[host].passwd.">",'~'.expand("<slnum>")) + endif + + " call Dret("NetUserPass : uid<".g:netrw_uid."> passwd<".s:netrw_passwd.">") +endfun + +" ================================= +" Shared Browsing Support: {{{1 +" ================================= + +" --------------------------------------------------------------------- +" s:ExplorePatHls: converts an Explore pattern into a regular expression search pattern {{{2 +fun! s:ExplorePatHls(pattern) + " call Dfunc("s:ExplorePatHls(pattern<".a:pattern.">)") + let repat= substitute(a:pattern,'^**/\{1,2}','','') + " call Decho("repat<".repat.">",'~'.expand("<slnum>")) + let repat= escape(repat,'][.\') + " call Decho("repat<".repat.">",'~'.expand("<slnum>")) + let repat= '\<'.substitute(repat,'\*','\\(\\S\\+ \\)*\\S\\+','g').'\>' + " call Dret("s:ExplorePatHls repat<".repat.">") + return repat +endfun + +" --------------------------------------------------------------------- +" s:NetrwBookHistHandler: {{{2 +" 0: (user: <mb>) bookmark current directory +" 1: (user: <gb>) change to the bookmarked directory +" 2: (user: <qb>) list bookmarks +" 3: (browsing) records current directory history +" 4: (user: <u>) go up (previous) directory, using history +" 5: (user: <U>) go down (next) directory, using history +" 6: (user: <mB>) delete bookmark +fun! s:NetrwBookHistHandler(chg,curdir) + " call Dfunc("s:NetrwBookHistHandler(chg=".a:chg." curdir<".a:curdir.">) cnt=".v:count." histcnt=".g:netrw_dirhistcnt." histmax=".g:netrw_dirhistmax) + if !exists("g:netrw_dirhistmax") || g:netrw_dirhistmax <= 0 + " " call Dret("s:NetrwBookHistHandler - suppressed due to g:netrw_dirhistmax") + return + endif + + let ykeep = @@ + let curbufnr = bufnr("%") + + if a:chg == 0 + " bookmark the current directory + " call Decho("(user: <b>) bookmark the current directory",'~'.expand("<slnum>")) + if exists("s:netrwmarkfilelist_{curbufnr}") + call s:NetrwBookmark(0) + echo "bookmarked marked files" + else + call s:MakeBookmark(a:curdir) + echo "bookmarked the current directory" + endif + + try + call s:NetrwBookHistSave() + catch + endtry + + elseif a:chg == 1 + " change to the bookmarked directory + " call Decho("(user: <".v:count."gb>) change to the bookmarked directory",'~'.expand("<slnum>")) + if exists("g:netrw_bookmarklist[v:count-1]") + " call Decho("(user: <".v:count."gb>) bookmarklist=".string(g:netrw_bookmarklist),'~'.expand("<slnum>")) + exe "NetrwKeepj e ".fnameescape(g:netrw_bookmarklist[v:count-1]) + else + echomsg "Sorry, bookmark#".v:count." doesn't exist!" + endif + + elseif a:chg == 2 + " redraw! + let didwork= 0 + " list user's bookmarks + " call Decho("(user: <q>) list user's bookmarks",'~'.expand("<slnum>")) + if exists("g:netrw_bookmarklist") + " call Decho('list '.len(g:netrw_bookmarklist).' bookmarks','~'.expand("<slnum>")) + let cnt= 1 + for bmd in g:netrw_bookmarklist + " call Decho("Netrw Bookmark#".cnt.": ".g:netrw_bookmarklist[cnt-1],'~'.expand("<slnum>")) + echo printf("Netrw Bookmark#%-2d: %s",cnt,g:netrw_bookmarklist[cnt-1]) + let didwork = 1 + let cnt = cnt + 1 + endfor + endif + + " list directory history + " Note: history is saved only when PerformListing is done; + " ie. when netrw can re-use a netrw buffer, the current directory is not saved in the history. + let cnt = g:netrw_dirhistcnt + let first = 1 + let histcnt = 0 + if g:netrw_dirhistmax > 0 + while ( first || cnt != g:netrw_dirhistcnt ) + " call Decho("first=".first." cnt=".cnt." dirhistcnt=".g:netrw_dirhistcnt,'~'.expand("<slnum>")) + if exists("g:netrw_dirhist_{cnt}") + " call Decho("Netrw History#".histcnt.": ".g:netrw_dirhist_{cnt},'~'.expand("<slnum>")) + echo printf("Netrw History#%-2d: %s",histcnt,g:netrw_dirhist_{cnt}) + let didwork= 1 + endif + let histcnt = histcnt + 1 + let first = 0 + let cnt = ( cnt - 1 ) % g:netrw_dirhistmax + if cnt < 0 + let cnt= cnt + g:netrw_dirhistmax + endif + endwhile + else + let g:netrw_dirhistcnt= 0 + endif + if didwork + call inputsave()|call input("Press <cr> to continue")|call inputrestore() + endif + + elseif a:chg == 3 + " saves most recently visited directories (when they differ) + " call Decho("(browsing) record curdir history",'~'.expand("<slnum>")) + if !exists("g:netrw_dirhistcnt") || !exists("g:netrw_dirhist_{g:netrw_dirhistcnt}") || g:netrw_dirhist_{g:netrw_dirhistcnt} != a:curdir + if g:netrw_dirhistmax > 0 + let g:netrw_dirhistcnt = ( g:netrw_dirhistcnt + 1 ) % g:netrw_dirhistmax + let g:netrw_dirhist_{g:netrw_dirhistcnt} = a:curdir + endif + " call Decho("save dirhist#".g:netrw_dirhistcnt."<".g:netrw_dirhist_{g:netrw_dirhistcnt}.">",'~'.expand("<slnum>")) + endif + + elseif a:chg == 4 + " u: change to the previous directory stored on the history list + " call Decho("(user: <u>) chg to prev dir from history",'~'.expand("<slnum>")) + if g:netrw_dirhistmax > 0 + let g:netrw_dirhistcnt= ( g:netrw_dirhistcnt - v:count1 ) % g:netrw_dirhistmax + if g:netrw_dirhistcnt < 0 + let g:netrw_dirhistcnt= g:netrw_dirhistcnt + g:netrw_dirhistmax + endif + else + let g:netrw_dirhistcnt= 0 + endif + if exists("g:netrw_dirhist_{g:netrw_dirhistcnt}") + " call Decho("changedir u#".g:netrw_dirhistcnt."<".g:netrw_dirhist_{g:netrw_dirhistcnt}.">",'~'.expand("<slnum>")) + if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("b:netrw_curdir") + setl ma noro + " call Decho("setl ma noro",'~'.expand("<slnum>")) + sil! NetrwKeepj %d _ + setl nomod + " call Decho("setl nomod",'~'.expand("<slnum>")) + " call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) + endif + " call Decho("exe e! ".fnameescape(g:netrw_dirhist_{g:netrw_dirhistcnt}),'~'.expand("<slnum>")) + exe "NetrwKeepj e! ".fnameescape(g:netrw_dirhist_{g:netrw_dirhistcnt}) + else + if g:netrw_dirhistmax > 0 + let g:netrw_dirhistcnt= ( g:netrw_dirhistcnt + v:count1 ) % g:netrw_dirhistmax + else + let g:netrw_dirhistcnt= 0 + endif + echo "Sorry, no predecessor directory exists yet" + endif + + elseif a:chg == 5 + " U: change to the subsequent directory stored on the history list + " call Decho("(user: <U>) chg to next dir from history",'~'.expand("<slnum>")) + if g:netrw_dirhistmax > 0 + let g:netrw_dirhistcnt= ( g:netrw_dirhistcnt + 1 ) % g:netrw_dirhistmax + if exists("g:netrw_dirhist_{g:netrw_dirhistcnt}") + " call Decho("changedir U#".g:netrw_dirhistcnt."<".g:netrw_dirhist_{g:netrw_dirhistcnt}.">",'~'.expand("<slnum>")) + if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("b:netrw_curdir") + " call Decho("setl ma noro",'~'.expand("<slnum>")) + setl ma noro + sil! NetrwKeepj %d _ + " call Decho("removed all lines from buffer (%d)",'~'.expand("<slnum>")) + " call Decho("setl nomod",'~'.expand("<slnum>")) + setl nomod + " call Decho("(set nomod) ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) + endif + " call Decho("exe e! ".fnameescape(g:netrw_dirhist_{g:netrw_dirhistcnt}),'~'.expand("<slnum>")) + exe "NetrwKeepj e! ".fnameescape(g:netrw_dirhist_{g:netrw_dirhistcnt}) + else + let g:netrw_dirhistcnt= ( g:netrw_dirhistcnt - 1 ) % g:netrw_dirhistmax + if g:netrw_dirhistcnt < 0 + let g:netrw_dirhistcnt= g:netrw_dirhistcnt + g:netrw_dirhistmax + endif + echo "Sorry, no successor directory exists yet" + endif + else + let g:netrw_dirhistcnt= 0 + echo "Sorry, no successor directory exists yet (g:netrw_dirhistmax is ".g:netrw_dirhistmax.")" + endif + + elseif a:chg == 6 + " call Decho("(user: <mB>) delete bookmark'd directory",'~'.expand("<slnum>")) + if exists("s:netrwmarkfilelist_{curbufnr}") + call s:NetrwBookmark(1) + echo "removed marked files from bookmarks" + else + " delete the v:count'th bookmark + let iremove = v:count + let dremove = g:netrw_bookmarklist[iremove - 1] + " call Decho("delete bookmark#".iremove."<".g:netrw_bookmarklist[iremove - 1].">",'~'.expand("<slnum>")) + call s:MergeBookmarks() + " call Decho("remove g:netrw_bookmarklist[".(iremove-1)."]<".g:netrw_bookmarklist[(iremove-1)].">",'~'.expand("<slnum>")) + NetrwKeepj call remove(g:netrw_bookmarklist,iremove-1) + echo "removed ".dremove." from g:netrw_bookmarklist" + " call Decho("g:netrw_bookmarklist=".string(g:netrw_bookmarklist),'~'.expand("<slnum>")) + endif + " call Decho("resulting g:netrw_bookmarklist=".string(g:netrw_bookmarklist),'~'.expand("<slnum>")) + + try + call s:NetrwBookHistSave() + catch + endtry + endif + call s:NetrwBookmarkMenu() + call s:NetrwTgtMenu() + let @@= ykeep + " call Dret("s:NetrwBookHistHandler") +endfun + +" --------------------------------------------------------------------- +" s:NetrwBookHistRead: this function reads bookmarks and history {{{2 +" Will source the history file (.netrwhist) only if the g:netrw_disthistmax is > 0. +" Sister function: s:NetrwBookHistSave() +fun! s:NetrwBookHistRead() + " call Dfunc("s:NetrwBookHistRead()") + if !exists("g:netrw_dirhistmax") || g:netrw_dirhistmax <= 0 + " call Dret("s:NetrwBookHistRead - nothing read (suppressed due to dirhistmax=".(exists("g:netrw_dirhistmax")? g:netrw_dirhistmax : "n/a").")") + return + endif + let ykeep= @@ + + " read bookmarks + if !exists("s:netrw_initbookhist") + let home = s:NetrwHome() + let savefile= home."/.netrwbook" + if filereadable(s:NetrwFile(savefile)) + " call Decho("sourcing .netrwbook",'~'.expand("<slnum>")) + exe "keepalt NetrwKeepj so ".savefile + endif + + " read history + if g:netrw_dirhistmax > 0 + let savefile= home."/.netrwhist" + if filereadable(s:NetrwFile(savefile)) + " call Decho("sourcing .netrwhist",'~'.expand("<slnum>")) + exe "keepalt NetrwKeepj so ".savefile + endif + let s:netrw_initbookhist= 1 + au VimLeave * call s:NetrwBookHistSave() + endif + endif + + let @@= ykeep + " call Decho("dirhistmax=".(exists("g:netrw_dirhistmax")? g:netrw_dirhistmax : "n/a"),'~'.expand("<slnum>")) + " call Decho("dirhistcnt=".(exists("g:netrw_dirhistcnt")? g:netrw_dirhistcnt : "n/a"),'~'.expand("<slnum>")) + " call Dret("s:NetrwBookHistRead") +endfun + +" --------------------------------------------------------------------- +" s:NetrwBookHistSave: this function saves bookmarks and history to files {{{2 +" Sister function: s:NetrwBookHistRead() +" I used to do this via viminfo but that appears to +" be unreliable for long-term storage +" If g:netrw_dirhistmax is <= 0, no history or bookmarks +" will be saved. +" (s:NetrwBookHistHandler(3,...) used to record history) +fun! s:NetrwBookHistSave() + " call Dfunc("s:NetrwBookHistSave() dirhistmax=".g:netrw_dirhistmax." dirhistcnt=".g:netrw_dirhistcnt) + if !exists("g:netrw_dirhistmax") || g:netrw_dirhistmax <= 0 + " call Dret("s:NetrwBookHistSave : nothing saved (dirhistmax=".g:netrw_dirhistmax.")") + return + endif + + let savefile= s:NetrwHome()."/.netrwhist" + " call Decho("savefile<".savefile.">",'~'.expand("<slnum>")) + 1split + + " setting up a new buffer which will become .netrwhist + call s:NetrwEnew() + " call Decho("case g:netrw_use_noswf=".g:netrw_use_noswf.(exists("+acd")? " +acd" : " -acd"),'~'.expand("<slnum>")) + if g:netrw_use_noswf + setl cino= com= cpo-=a cpo-=A fo=nroql2 tw=0 report=10000 noswf + else + setl cino= com= cpo-=a cpo-=A fo=nroql2 tw=0 report=10000 + endif + setl nocin noai noci magic nospell nohid wig= noaw + setl ma noro write + if exists("+acd") | setl noacd | endif + sil! NetrwKeepj keepalt %d _ + + " rename enew'd file: .netrwhist -- no attempt to merge + " record dirhistmax and current dirhistcnt + " save history + " call Decho("saving history: dirhistmax=".g:netrw_dirhistmax." dirhistcnt=".g:netrw_dirhistcnt." lastline=".line("$"),'~'.expand("<slnum>")) + sil! keepalt file .netrwhist + call setline(1,"let g:netrw_dirhistmax =".g:netrw_dirhistmax) + call setline(2,"let g:netrw_dirhistcnt =".g:netrw_dirhistcnt) + if g:netrw_dirhistmax > 0 + let lastline = line("$") + let cnt = g:netrw_dirhistcnt + let first = 1 + while ( first || cnt != g:netrw_dirhistcnt ) + let lastline= lastline + 1 + if exists("g:netrw_dirhist_{cnt}") + call setline(lastline,'let g:netrw_dirhist_'.cnt."='".g:netrw_dirhist_{cnt}."'") + " call Decho("..".lastline.'let g:netrw_dirhist_'.cnt."='".g:netrw_dirhist_{cnt}."'",'~'.expand("<slnum>")) + endif + let first = 0 + let cnt = ( cnt - 1 ) % g:netrw_dirhistmax + if cnt < 0 + let cnt= cnt + g:netrw_dirhistmax + endif + endwhile + exe "sil! w! ".savefile + " call Decho("exe sil! w! ".savefile,'~'.expand("<slnum>")) + endif + + " save bookmarks + sil NetrwKeepj %d _ + if exists("g:netrw_bookmarklist") && g:netrw_bookmarklist != [] + " call Decho("saving bookmarks",'~'.expand("<slnum>")) + " merge and write .netrwbook + let savefile= s:NetrwHome()."/.netrwbook" + + if filereadable(s:NetrwFile(savefile)) + let booklist= deepcopy(g:netrw_bookmarklist) + exe "sil NetrwKeepj keepalt so ".savefile + for bdm in booklist + if index(g:netrw_bookmarklist,bdm) == -1 + call add(g:netrw_bookmarklist,bdm) + endif + endfor + call sort(g:netrw_bookmarklist) + endif + + " construct and save .netrwbook + call setline(1,"let g:netrw_bookmarklist= ".string(g:netrw_bookmarklist)) + exe "sil! w! ".savefile + " call Decho("exe sil! w! ".savefile,'~'.expand("<slnum>")) + endif + + " cleanup -- remove buffer used to construct history + let bgone= bufnr("%") + q! + exe "keepalt ".bgone."bwipe!" + + " call Dret("s:NetrwBookHistSave") +endfun + +" --------------------------------------------------------------------- +" s:NetrwBrowse: This function uses the command in g:netrw_list_cmd to provide a {{{2 +" list of the contents of a local or remote directory. It is assumed that the +" g:netrw_list_cmd has a string, USEPORT HOSTNAME, that needs to be substituted +" with the requested remote hostname first. +" Often called via: Explore/e dirname/etc -> netrw#LocalBrowseCheck() -> s:NetrwBrowse() +fun! s:NetrwBrowse(islocal,dirname) + if !exists("w:netrw_liststyle")|let w:netrw_liststyle= g:netrw_liststyle|endif + + " save alternate-file's filename if w:netrw_rexlocal doesn't exist + " 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("#") + endif + + " s:NetrwBrowse : initialize history {{{3 + if !exists("s:netrw_initbookhist") + NetrwKeepj call s:NetrwBookHistRead() + endif + + " s:NetrwBrowse : simplify the dirname (especially for ".."s in dirnames) {{{3 + if a:dirname !~ '^\a\{3,}://' + let dirname= simplify(a:dirname) + else + let dirname= a:dirname + endif + + " repoint t:netrw_lexbufnr if appropriate + if exists("t:netrw_lexbufnr") && bufnr("%") == t:netrw_lexbufnr + let repointlexbufnr= 1 + endif + + " s:NetrwBrowse : sanity checks: {{{3 + if exists("s:netrw_skipbrowse") + unlet s:netrw_skipbrowse + return + endif + if !exists("*shellescape") + NetrwKeepj call netrw#ErrorMsg(s:ERROR,"netrw can't run -- your vim is missing shellescape()",69) + return + endif + if !exists("*fnameescape") + NetrwKeepj call netrw#ErrorMsg(s:ERROR,"netrw can't run -- your vim is missing fnameescape()",70) + return + endif + + " s:NetrwBrowse : save options: {{{3 + call s:NetrwOptionsSave("w:") + + " s:NetrwBrowse : re-instate any marked files {{{3 + if has("syntax") && exists("g:syntax_on") && g:syntax_on + if exists("s:netrwmarkfilelist_{bufnr('%')}") + exe "2match netrwMarkFile /".s:netrwmarkfilemtch_{bufnr("%")}."/" + endif + endif + + if a:islocal && exists("w:netrw_acdkeep") && w:netrw_acdkeep + " s:NetrwBrowse : set up "safe" options for local directory/file {{{3 + if s:NetrwLcd(dirname) + return + endif + + elseif !a:islocal && dirname !~ '[\/]$' && dirname !~ '^"' + " s:NetrwBrowse : remote regular file handler {{{3 + if bufname(dirname) != "" + exe "NetrwKeepj b ".bufname(dirname) + else + " attempt transfer of remote regular file + + " remove any filetype indicator from end of dirname, except for the + " "this is a directory" indicator (/). + " There shouldn't be one of those here, anyway. + let path= substitute(dirname,'[*=@|]\r\=$','','e') + call s:RemotePathAnalysis(dirname) + + " s:NetrwBrowse : remote-read the requested file into current buffer {{{3 + call s:NetrwEnew(dirname) + call s:NetrwOptionsSafe(a:islocal) + setl ma noro + let b:netrw_curdir = dirname + let url = s:method."://".((s:user == "")? "" : s:user."@").s:machine.(s:port ? ":".s:port : "")."/".s:path + call s:NetrwBufRename(url) + exe "sil! NetrwKeepj keepalt doau BufReadPre ".fnameescape(s:fname) + sil call netrw#NetRead(2,url) + " netrw.vim and tar.vim have already handled decompression of the tarball; avoiding gzip.vim error + if s:path =~ '.bz2' + exe "sil NetrwKeepj keepalt doau BufReadPost ".fnameescape(substitute(s:fname,'\.bz2$','','')) + elseif s:path =~ '.gz' + exe "sil NetrwKeepj keepalt doau BufReadPost ".fnameescape(substitute(s:fname,'\.gz$','','')) + elseif s:path =~ '.gz' + exe "sil NetrwKeepj keepalt doau BufReadPost ".fnameescape(substitute(s:fname,'\.txz$','','')) + else + exe "sil NetrwKeepj keepalt doau BufReadPost ".fnameescape(s:fname) + endif + endif + + " s:NetrwBrowse : save certain window-oriented variables into buffer-oriented variables {{{3 + call s:SetBufWinVars() + call s:NetrwOptionsRestore("w:") + setl ma nomod noro + return + endif + + " use buffer-oriented WinVars if buffer variables exist but associated window variables don't {{{3 + call s:UseBufWinVars() + + " set up some variables {{{3 + let b:netrw_browser_active = 1 + let dirname = dirname + let s:last_sort_by = g:netrw_sort_by + + " set up menu {{{3 + NetrwKeepj call s:NetrwMenu(1) + + " get/set-up buffer {{{3 + let svpos = winsaveview() + + " NetrwGetBuffer might change buffers but s:rexposn_X was set for the + " previous buffer + let prevbufnr = bufnr('%') + let reusing= s:NetrwGetBuffer(a:islocal,dirname) + if exists("s:rexposn_".prevbufnr) + let s:rexposn_{bufnr('%')} = s:rexposn_{prevbufnr} + endif + + " maintain markfile highlighting + if has("syntax") && exists("g:syntax_on") && g:syntax_on + if exists("s:netrwmarkfilemtch_{bufnr('%')}") && s:netrwmarkfilemtch_{bufnr("%")} != "" + exe "2match netrwMarkFile /".s:netrwmarkfilemtch_{bufnr("%")}."/" + else + 2match none + endif + endif + if reusing && line("$") > 1 + call s:NetrwOptionsRestore("w:") + setl noma nomod nowrap + return + endif + + " set b:netrw_curdir to the new directory name {{{3 + let b:netrw_curdir= dirname + if b:netrw_curdir =~ '[/\\]$' + let b:netrw_curdir= substitute(b:netrw_curdir,'[/\\]$','','e') + endif + if b:netrw_curdir =~ '\a:$' && has("win32") + let b:netrw_curdir= b:netrw_curdir."/" + endif + if b:netrw_curdir == '' + if has("amiga") + " On the Amiga, the empty string connotes the current directory + let b:netrw_curdir= getcwd() + else + " under unix, when the root directory is encountered, the result + " from the preceding substitute is an empty string. + let b:netrw_curdir= '/' + endif + endif + if !a:islocal && b:netrw_curdir !~ '/$' + let b:netrw_curdir= b:netrw_curdir.'/' + endif + + " ------------ + " (local only) {{{3 + " ------------ + if a:islocal + " Set up ShellCmdPost handling. Append current buffer to browselist + call s:LocalFastBrowser() + + " handle g:netrw_keepdir: set vim's current directory to netrw's notion of the current directory {{{3 + if !g:netrw_keepdir + if !exists("&l:acd") || !&l:acd + if s:NetrwLcd(b:netrw_curdir) + return + endif + endif + endif + + " -------------------------------- + " remote handling: {{{3 + " -------------------------------- + else + + " analyze dirname and g:netrw_list_cmd {{{3 + if dirname =~# "^NetrwTreeListing\>" + let dirname= b:netrw_curdir + elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("b:netrw_curdir") + let dirname= substitute(b:netrw_curdir,'\\','/','g') + if dirname !~ '/$' + let dirname= dirname.'/' + endif + let b:netrw_curdir = dirname + else + let dirname = substitute(dirname,'\\','/','g') + endif + + let dirpat = '^\(\w\{-}\)://\(\w\+@\)\=\([^/]\+\)/\(.*\)$' + if dirname !~ dirpat + if !exists("g:netrw_quiet") + NetrwKeepj call netrw#ErrorMsg(s:ERROR,"netrw doesn't understand your dirname<".dirname.">",20) + endif + NetrwKeepj call s:NetrwOptionsRestore("w:") + setl noma nomod nowrap + return + endif + let b:netrw_curdir= dirname + endif " (additional remote handling) + + " ------------------------------- + " Perform Directory Listing: {{{3 + " ------------------------------- + NetrwKeepj call s:NetrwMaps(a:islocal) + NetrwKeepj call s:NetrwCommands(a:islocal) + NetrwKeepj call s:PerformListing(a:islocal) + + " restore option(s) + call s:NetrwOptionsRestore("w:") + + " If there is a rexposn: restore position with rexposn + " Otherwise : set rexposn + if exists("s:rexposn_".bufnr("%")) + NetrwKeepj call winrestview(s:rexposn_{bufnr('%')}) + if exists("w:netrw_bannercnt") && line(".") < w:netrw_bannercnt + NetrwKeepj exe w:netrw_bannercnt + endif + else + NetrwKeepj call s:SetRexDir(a:islocal,b:netrw_curdir) + endif + if v:version >= 700 && has("balloon_eval") && &beval == 0 && &l:bexpr == "" && !exists("g:netrw_nobeval") + let &l:bexpr= "netrw#BalloonHelp()" + setl beval + endif + + " repoint t:netrw_lexbufnr if appropriate + if exists("repointlexbufnr") + let t:netrw_lexbufnr= bufnr("%") + endif + + " restore position + if reusing + call winrestview(svpos) + endif + + " The s:LocalBrowseRefresh() function is called by an autocmd + " installed by s:LocalFastBrowser() when g:netrw_fastbrowse <= 1 (ie. slow or medium speed). + " However, s:NetrwBrowse() causes the FocusGained event to fire the first time. + return +endfun + +" --------------------------------------------------------------------- +" s:NetrwFile: because of g:netrw_keepdir, isdirectory(), type(), etc may or {{{2 +" may not apply correctly; ie. netrw's idea of the current directory may +" differ from vim's. This function insures that netrw's idea of the current +" directory is used. +" Returns a path to the file specified by a:fname +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'),'~'.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.">",'~'.expand("<slnum>")) + else + let fname= a:fname + endif + + if g:netrw_keepdir + " vim's idea of the current directory possibly may differ from netrw's + if !exists("b:netrw_curdir") + let b:netrw_curdir= getcwd() + endif + + if !exists("g:netrw_cygwin") && has("win32") + if fname =~ '^\' || fname =~ '^\a:\' + " windows, but full path given + let ret= fname + " "" call Decho("windows+full path: isdirectory(".fname.")",'~'.expand("<slnum>")) + else + " windows, relative path given + let ret= s:ComposePath(b:netrw_curdir,fname) + " "" call Decho("windows+rltv path: isdirectory(".fname.")",'~'.expand("<slnum>")) + endif + + elseif fname =~ '^/' + " not windows, full path given + let ret= fname + " "" call Decho("unix+full path: isdirectory(".fname.")",'~'.expand("<slnum>")) + else + " not windows, relative path given + let ret= s:ComposePath(b:netrw_curdir,fname) + " "" call Decho("unix+rltv path: isdirectory(".fname.")",'~'.expand("<slnum>")) + endif + else + " 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) + return ret +endfun + +" --------------------------------------------------------------------- +" s:NetrwFileInfo: supports qf (query for file information) {{{2 +fun! s:NetrwFileInfo(islocal,fname) + " call Dfunc("s:NetrwFileInfo(islocal=".a:islocal." fname<".a:fname.">) b:netrw_curdir<".b:netrw_curdir.">") + let ykeep= @@ + if a:islocal + let lsopt= "-lsad" + if g:netrw_sizestyle =~# 'H' + let lsopt= "-lsadh" + elseif g:netrw_sizestyle =~# 'h' + let lsopt= "-lsadh --si" + endif + " call Decho("(s:NetrwFileInfo) lsopt<".lsopt.">") + if (has("unix") || has("macunix")) && executable("/bin/ls") + + if getline(".") == "../" + echo system("/bin/ls ".lsopt." ".s:ShellEscape("..")) + " call Decho("#1: echo system(/bin/ls -lsad ".s:ShellEscape(..).")",'~'.expand("<slnum>")) + + elseif w:netrw_liststyle == s:TREELIST && getline(".") !~ '^'.s:treedepthstring + echo system("/bin/ls ".lsopt." ".s:ShellEscape(b:netrw_curdir)) + " call Decho("#2: echo system(/bin/ls -lsad ".s:ShellEscape(b:netrw_curdir).")",'~'.expand("<slnum>")) + + elseif exists("b:netrw_curdir") + echo system("/bin/ls ".lsopt." ".s:ShellEscape(s:ComposePath(b:netrw_curdir,a:fname))) + " call Decho("#3: echo system(/bin/ls -lsad ".s:ShellEscape(b:netrw_curdir.a:fname).")",'~'.expand("<slnum>")) + + else + " call Decho('using ls '.a:fname." using cwd<".getcwd().">",'~'.expand("<slnum>")) + echo system("/bin/ls ".lsopt." ".s:ShellEscape(s:NetrwFile(a:fname))) + " call Decho("#5: echo system(/bin/ls -lsad ".s:ShellEscape(a:fname).")",'~'.expand("<slnum>")) + endif + else + " use vim functions to return information about file below cursor + " call Decho("using vim functions to query for file info",'~'.expand("<slnum>")) + if !isdirectory(s:NetrwFile(a:fname)) && !filereadable(s:NetrwFile(a:fname)) && a:fname =~ '[*@/]' + let fname= substitute(a:fname,".$","","") + else + let fname= a:fname + endif + let t = getftime(s:NetrwFile(fname)) + let sz = getfsize(s:NetrwFile(fname)) + if g:netrw_sizestyle =~# "[hH]" + let sz= s:NetrwHumanReadable(sz) + endif + echo a:fname.": ".sz." ".strftime(g:netrw_timefmt,getftime(s:NetrwFile(fname))) + " call Decho("fname.": ".sz." ".strftime(g:netrw_timefmt,getftime(fname)),'~'.expand("<slnum>")) + endif + else + echo "sorry, \"qf\" not supported yet for remote files" + endif + let @@= ykeep + " call Dret("s:NetrwFileInfo") +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: [get a new|find an old netrw] buffer for a netrw listing {{{2 +" returns 0=cleared buffer +" 1=re-used buffer (buffer not cleared) +" Nov 09, 2020: tst952 shows that when user does :set hidden that NetrwGetBuffer will come up with a [No Name] buffer (hid fix) +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." hid=".&hid,'~'.expand("<slnum>")) + " call Decho("netrwbuf dictionary=".(exists("s:netrwbuf")? string(s:netrwbuf) : 'n/a'),'~'.expand("<slnum>")) + " call Dredir("ls!","s:NetrwGetBuffer") + let dirname= a:dirname + + " re-use buffer if possible {{{3 + " call Decho("--re-use a buffer if possible--",'~'.expand("<slnum>")) + if !exists("s:netrwbuf") + " call Decho(" s:netrwbuf initialized to {}",'~'.expand("<slnum>")) + let s:netrwbuf= {} + endif + " call Decho(" s:netrwbuf =".string(s:netrwbuf),'~'.expand("<slnum>")) + " call Decho(" w:netrw_liststyle =".(exists("w:netrw_liststyle")? w:netrw_liststyle : "n/a"),'~'.expand("<slnum>")) + + if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST + let bufnum = -1 + + if !empty(s:netrwbuf) && has_key(s:netrwbuf,s:NetrwFullPath(dirname)) + if has_key(s:netrwbuf,"NetrwTreeListing") + let bufnum= s:netrwbuf["NetrwTreeListing"] + else + let bufnum= s:netrwbuf[s:NetrwFullPath(dirname)] + endif + " call Decho(" NetrwTreeListing: bufnum#".bufnum,'~'.expand("<slnum>")) + if !bufexists(bufnum) + call remove(s:netrwbuf,"NetrwTreeListing") + let bufnum= -1 + endif + elseif bufnr("NetrwTreeListing") != -1 + let bufnum= bufnr("NetrwTreeListing") + " call Decho(" NetrwTreeListing".": bufnum#".bufnum,'~'.expand("<slnum>")) + else + " call Decho(" did not find a NetrwTreeListing buffer",'~'.expand("<slnum>")) + let bufnum= -1 + endif + + elseif 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,'~'.expand("<slnum>")) + if !bufexists(bufnum) + call remove(s:netrwbuf,s:NetrwFullPath(dirname)) + let bufnum= -1 + endif + + else + " call Decho(" lookup netrwbuf dictionary: s:netrwbuf[".s:NetrwFullPath(dirname)."] not a key",'~'.expand("<slnum>")) + let bufnum= -1 + endif + " call Decho(" bufnum#".bufnum,'~'.expand("<slnum>")) + + " highjack the current buffer + " IF the buffer already has the desired name + " AND it is empty + let curbuf = bufname("%") + if curbuf == '.' + let curbuf = getcwd() + endif + " call Dredir("ls!","NetrwGetFile (renamed buffer back to remote filename<".rfile."> : expand(%)<".expand("%").">)") + " call Decho("deciding if netrw may highjack the current buffer#".bufnr("%")."<".curbuf.">",'~'.expand("<slnum>")) + " call Decho("..dirname<".dirname."> IF dirname == bufname",'~'.expand("<slnum>")) + " call Decho("..curbuf<".curbuf.">",'~'.expand("<slnum>")) + " call Decho("..line($)=".line("$")." AND this is 1",'~'.expand("<slnum>")) + " call Decho("..getline(%)<".getline("%")."> AND this line is empty",'~'.expand("<slnum>")) + if dirname == curbuf && line("$") == 1 && getline("%") == "" + " call Dret("s:NetrwGetBuffer 0<cleared buffer> : highjacking buffer#".bufnr("%")) + return 0 + else " DEBUG + " call Decho("..did NOT highjack buffer",'~'.expand("<slnum>")) + endif + " Aug 14, 2021: was thinking about looking for a [No Name] buffer here and using it, but that might cause problems + + " get enew buffer and name it -or- re-use buffer {{{3 + 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>")) + " name the buffer + if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST + " Got enew buffer; transform into a NetrwTreeListing + " call Decho("--transform enew buffer#".bufnr("%")." into a NetrwTreeListing --",'~'.expand("<slnum>")) + let w:netrw_treebufnr = bufnr("%") + call s:NetrwBufRename("NetrwTreeListing") + if g:netrw_use_noswf + setl nobl bt=nofile noswf + else + setl nobl bt=nofile + endif + nnoremap <silent> <buffer> [[ :sil call <SID>TreeListMove('[[')<cr> + nnoremap <silent> <buffer> ]] :sil call <SID>TreeListMove(']]')<cr> + nnoremap <silent> <buffer> [] :sil call <SID>TreeListMove('[]')<cr> + nnoremap <silent> <buffer> ][ :sil call <SID>TreeListMove('][')<cr> + " call Decho(" tree listing bufnr=".w:netrw_treebufnr,'~'.expand("<slnum>")) + else + call s:NetrwBufRename(dirname) + " 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>")) + + else " Re-use the buffer + " call Decho("--re-use buffer#".bufnum." (bufnum#".bufnum.">=0 AND bufexists(".bufnum.")=".bufexists(bufnum)."!=0)",'~'.expand("<slnum>")) + " ignore all events + let eikeep= &ei + setl ei=all + + if &ft == "netrw" + " call Decho("buffer type is netrw; not using keepalt with b ".bufnum) + exe "sil! NetrwKeepj noswapfile b ".bufnum + " call Dredir("ls!","one") + else + " call Decho("buffer type is not netrw; using keepalt with b ".bufnum) + call s:NetrwEditBuf(bufnum) + " call Dredir("ls!","two") + endif + " call Decho(" line($)=".line("$"),'~'.expand("<slnum>")) + if bufname("%") == '.' + call s:NetrwBufRename(getcwd()) + endif + + " restore ei + let &ei= eikeep + + if line("$") <= 1 && getline(1) == "" + " empty buffer + NetrwKeepj call s:NetrwListSettings(a:islocal) + " 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 empty> : re-using buffer#".bufnr("%").", but its empty, so refresh it") + return 0 + + elseif g:netrw_fastbrowse == 0 || (a:islocal && g:netrw_fastbrowse == 1) + " call Decho("g:netrw_fastbrowse=".g:netrw_fastbrowse." a:islocal=".a:islocal.": clear buffer",'~'.expand("<slnum>")) + NetrwKeepj call s:NetrwListSettings(a:islocal) + 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<cleared buffer> : re-using buffer#".bufnr("%").", but refreshing due to g:netrw_fastbrowse=".g:netrw_fastbrowse) + return 0 + + elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST + " call Decho("--re-use tree listing--",'~'.expand("<slnum>")) + " call Decho(" clear buffer<".expand("%")."> with :%d",'~'.expand("<slnum>")) + setl ma + sil NetrwKeepj %d _ + NetrwKeepj call s:NetrwListSettings(a:islocal) + " 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<cleared buffer> : re-using buffer#".bufnr("%").", but treelist mode always needs a refresh") + return 0 + + else + " 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 1<buffer not cleared>") + return 1 + endif + endif + + " do netrw settings: make this buffer not-a-file, modifiable, not line-numbered, etc {{{3 + " fastbrowse Local Remote Hiding a buffer implies it may be re-used (fast) + " slow 0 D D Deleting a buffer implies it will not be re-used (slow) + " med 1 D H + " fast 2 H H + " call Decho("--do netrw settings: make this buffer#".bufnr("%")." not-a-file, modifiable, not line-numbered, etc--",'~'.expand("<slnum>")) + let fname= expand("%") + NetrwKeepj call s:NetrwListSettings(a:islocal) + call s:NetrwBufRename(fname) + + " delete all lines from buffer {{{3 + " call Decho("--delete all lines from buffer--",'~'.expand("<slnum>")) + " call Decho(" clear buffer<".expand("%")."> with :%d",'~'.expand("<slnum>")) + sil! keepalt 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<cleared buffer>") + return 0 +endfun + +" --------------------------------------------------------------------- +" s:NetrwGetcwd: get the current directory. {{{2 +" Change backslashes to forward slashes, if any. +" If doesc is true, escape certain troublesome characters +fun! s:NetrwGetcwd(doesc) + " call Dfunc("NetrwGetcwd(doesc=".a:doesc.")") + let curdir= substitute(getcwd(),'\\','/','ge') + if curdir !~ '[\/]$' + let curdir= curdir.'/' + endif + if a:doesc + let curdir= fnameescape(curdir) + endif + " call Dret("NetrwGetcwd <".curdir.">") + return curdir +endfun + +" --------------------------------------------------------------------- +" s:NetrwGetWord: it gets the directory/file named under the cursor {{{2 +fun! s:NetrwGetWord() + " call Dfunc("s:NetrwGetWord() liststyle=".s:ShowStyle()." virtcol=".virtcol(".")) + " call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) + let keepsol= &l:sol + setl nosol + + call s:UseBufWinVars() + + " insure that w:netrw_liststyle is set up + if !exists("w:netrw_liststyle") + if exists("g:netrw_liststyle") + let w:netrw_liststyle= g:netrw_liststyle + else + let w:netrw_liststyle= s:THINLIST + endif + " call Decho("w:netrw_liststyle=".w:netrw_liststyle,'~'.expand("<slnum>")) + endif + + if exists("w:netrw_bannercnt") && line(".") < w:netrw_bannercnt + " Active Banner support + " call Decho("active banner handling",'~'.expand("<slnum>")) + NetrwKeepj norm! 0 + let dirname= "./" + let curline= getline('.') + + if curline =~# '"\s*Sorted by\s' + NetrwKeepj norm! "_s + let s:netrw_skipbrowse= 1 + echo 'Pressing "s" also works' + + elseif curline =~# '"\s*Sort sequence:' + let s:netrw_skipbrowse= 1 + echo 'Press "S" to edit sorting sequence' + + elseif curline =~# '"\s*Quick Help:' + NetrwKeepj norm! ? + let s:netrw_skipbrowse= 1 + + elseif curline =~# '"\s*\%(Hiding\|Showing\):' + NetrwKeepj norm! a + let s:netrw_skipbrowse= 1 + echo 'Pressing "a" also works' + + elseif line("$") > w:netrw_bannercnt + exe 'sil NetrwKeepj '.w:netrw_bannercnt + endif + + elseif w:netrw_liststyle == s:THINLIST + " call Decho("thin column handling",'~'.expand("<slnum>")) + NetrwKeepj norm! 0 + let dirname= substitute(getline('.'),'\t -->.*$','','') + + elseif w:netrw_liststyle == s:LONGLIST + " call Decho("long column handling",'~'.expand("<slnum>")) + NetrwKeepj norm! 0 + let dirname= substitute(getline('.'),'^\(\%(\S\+ \)*\S\+\).\{-}$','\1','e') + + elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST + " call Decho("treelist handling",'~'.expand("<slnum>")) + let dirname= substitute(getline('.'),'^\('.s:treedepthstring.'\)*','','e') + let dirname= substitute(dirname,'\t -->.*$','','') + + else + " call Decho("obtain word from wide listing",'~'.expand("<slnum>")) + let dirname= getline('.') + + if !exists("b:netrw_cpf") + let b:netrw_cpf= 0 + exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g/^./if virtcol("$") > b:netrw_cpf|let b:netrw_cpf= virtcol("$")|endif' + call histdel("/",-1) + " "call Decho("computed cpf=".b:netrw_cpf,'~'.expand("<slnum>")) + endif + + " call Decho("buf#".bufnr("%")."<".bufname("%").">",'~'.expand("<slnum>")) + let filestart = (virtcol(".")/b:netrw_cpf)*b:netrw_cpf + " call Decho("filestart= ([virtcol=".virtcol(".")."]/[b:netrw_cpf=".b:netrw_cpf."])*b:netrw_cpf=".filestart." bannercnt=".w:netrw_bannercnt,'~'.expand("<slnum>")) + " call Decho("1: dirname<".dirname.">",'~'.expand("<slnum>")) + if filestart == 0 + NetrwKeepj norm! 0ma + else + call cursor(line("."),filestart+1) + NetrwKeepj norm! ma + endif + + let dict={} + " save the unnamed register and register 0-9 and a + let dict.a=[getreg('a'), getregtype('a')] + for i in range(0, 9) + let dict[i] = [getreg(i), getregtype(i)] + endfor + let dict.unnamed = [getreg(''), getregtype('')] + + let eofname= filestart + b:netrw_cpf + 1 + if eofname <= col("$") + call cursor(line("."),filestart+b:netrw_cpf+1) + NetrwKeepj norm! "ay`a + else + NetrwKeepj norm! "ay$ + endif + + let dirname = @a + call s:RestoreRegister(dict) + + " call Decho("2: dirname<".dirname.">",'~'.expand("<slnum>")) + let dirname= substitute(dirname,'\s\+$','','e') + " call Decho("3: dirname<".dirname.">",'~'.expand("<slnum>")) + endif + + " symlinks are indicated by a trailing "@". Remove it before further processing. + let dirname= substitute(dirname,"@$","","") + + " executables are indicated by a trailing "*". Remove it before further processing. + let dirname= substitute(dirname,"\*$","","") + + let &l:sol= keepsol + + " call Dret("s:NetrwGetWord <".dirname.">") + return dirname +endfun + +" --------------------------------------------------------------------- +" s:NetrwListSettings: make standard settings for making a netrw listing {{{2 +" g:netrw_bufsettings will be used after the listing is produced. +" Called by s:NetrwGetBuffer() +fun! s:NetrwListSettings(islocal) + " call Dfunc("s:NetrwListSettings(islocal=".a:islocal.")") + " 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>")) + let fname= bufname("%") + " " call Decho("setl bt=nofile nobl ma nonu nowrap noro nornu",'~'.expand("<slnum>")) + " nobl noma nomod nonu noma nowrap ro nornu (std g:netrw_bufsettings) + setl bt=nofile nobl ma nonu nowrap noro nornu + call s:NetrwBufRename(fname) + if g:netrw_use_noswf + setl noswf + endif + " call Dredir("ls!","s:NetrwListSettings") + " call Decho("exe setl ts=".(g:netrw_maxfilenamelen+1),'~'.expand("<slnum>")) + exe "setl ts=".(g:netrw_maxfilenamelen+1) + setl isk+=.,~,- + if g:netrw_fastbrowse > a:islocal + setl bh=hide + else + setl bh=delete + endif + " 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 Dret("s:NetrwListSettings") +endfun + +" --------------------------------------------------------------------- +" s:NetrwListStyle: change list style (thin - long - wide - tree) {{{2 +" islocal=0: remote browsing +" =1: local browsing +fun! s:NetrwListStyle(islocal) + let ykeep = @@ + let fname = s:NetrwGetWord() + if !exists("w:netrw_liststyle")|let w:netrw_liststyle= g:netrw_liststyle|endif + let svpos = winsaveview() + let w:netrw_liststyle = (w:netrw_liststyle + 1) % s:MAXLIST + + " repoint t:netrw_lexbufnr if appropriate + if exists("t:netrw_lexbufnr") && bufnr("%") == t:netrw_lexbufnr + let repointlexbufnr= 1 + endif + + if w:netrw_liststyle == s:THINLIST + " use one column listing + let g:netrw_list_cmd = substitute(g:netrw_list_cmd,' -l','','ge') + + elseif w:netrw_liststyle == s:LONGLIST + " use long list + let g:netrw_list_cmd = g:netrw_list_cmd." -l" + + elseif w:netrw_liststyle == s:WIDELIST + " give wide list + let g:netrw_list_cmd = substitute(g:netrw_list_cmd,' -l','','ge') + + elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST + let g:netrw_list_cmd = substitute(g:netrw_list_cmd,' -l','','ge') + + else + NetrwKeepj call netrw#ErrorMsg(s:WARNING,"bad value for g:netrw_liststyle (=".w:netrw_liststyle.")",46) + let g:netrw_liststyle = s:THINLIST + let w:netrw_liststyle = g:netrw_liststyle + let g:netrw_list_cmd = substitute(g:netrw_list_cmd,' -l','','ge') + endif + setl ma noro + + " clear buffer - this will cause NetrwBrowse/LocalBrowseCheck to do a refresh + sil! NetrwKeepj %d _ + " following prevents tree listing buffer from being marked "modified" + setl nomod + + " refresh the listing + NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) + NetrwKeepj call s:NetrwCursor(0) + + " repoint t:netrw_lexbufnr if appropriate + if exists("repointlexbufnr") + let t:netrw_lexbufnr= bufnr("%") + endif + + " restore position; keep cursor on the filename + " call Decho("restoring posn to svpos<".string(svpos).">",'~'.expand("<slnum>")) + NetrwKeepj call winrestview(svpos) + let @@= ykeep + +endfun + +" --------------------------------------------------------------------- +" s:NetrwBannerCtrl: toggles the display of the banner {{{2 +fun! s:NetrwBannerCtrl(islocal) + let ykeep= @@ + " toggle the banner (enable/suppress) + let g:netrw_banner= !g:netrw_banner + + " refresh the listing + let svpos= winsaveview() + call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) + + " keep cursor on the filename + if g:netrw_banner && exists("w:netrw_bannercnt") && line(".") >= w:netrw_bannercnt + let fname= s:NetrwGetWord() + sil NetrwKeepj $ + let result= search('\%(^\%(|\+\s\)\=\|\s\{2,}\)\zs'.escape(fname,'.\[]*$^').'\%(\s\{2,}\|$\)','bc') + " " call Decho("search result=".result." w:netrw_bannercnt=".(exists("w:netrw_bannercnt")? w:netrw_bannercnt : 'N/A'),'~'.expand("<slnum>")) + if result <= 0 && exists("w:netrw_bannercnt") + exe "NetrwKeepj ".w:netrw_bannercnt + endif + endif + let @@= ykeep + " call Dret("s:NetrwBannerCtrl : g:netrw_banner=".g:netrw_banner) +endfun + +" --------------------------------------------------------------------- +" s:NetrwBookmark: supports :NetrwMB[!] [file]s {{{2 +" +" No bang: enters files/directories into Netrw's bookmark system +" No argument and in netrw buffer: +" if there are marked files: bookmark marked files +" otherwise : bookmark file/directory under cursor +" No argument and not in netrw buffer: bookmarks current open file +" Has arguments: globs them individually and bookmarks them +" +" With bang: deletes files/directories from Netrw's bookmark system +fun! s:NetrwBookmark(del,...) + if a:0 == 0 + if &ft == "netrw" + let curbufnr = bufnr("%") + + if exists("s:netrwmarkfilelist_{curbufnr}") + " for every filename in the marked list + let svpos = winsaveview() + let islocal= expand("%") !~ '^\a\{3,}://' + for fname in s:netrwmarkfilelist_{curbufnr} + if a:del|call s:DeleteBookmark(fname)|else|call s:MakeBookmark(fname)|endif + endfor + let curdir = exists("b:netrw_curdir")? b:netrw_curdir : getcwd() + call s:NetrwUnmarkList(curbufnr,curdir) + NetrwKeepj call s:NetrwRefresh(islocal,s:NetrwBrowseChgDir(islocal,'./',0)) + NetrwKeepj call winrestview(svpos) + else + let fname= s:NetrwGetWord() + if a:del|call s:DeleteBookmark(fname)|else|call s:MakeBookmark(fname)|endif + endif + + else + " bookmark currently open file + let fname= expand("%") + if a:del|call s:DeleteBookmark(fname)|else|call s:MakeBookmark(fname)|endif + endif + + else + " bookmark specified files + " attempts to infer if working remote or local + " by deciding if the current file begins with an url + " Globbing cannot be done remotely. + let islocal= expand("%") !~ '^\a\{3,}://' + let i = 1 + while i <= a:0 + if islocal + if v:version > 704 || (v:version == 704 && has("patch656")) + let mbfiles= glob(fnameescape(a:{i}),0,1,1) + else + let mbfiles= glob(fnameescape(a:{i}),0,1) + endif + else + let mbfiles= [a:{i}] + endif + for mbfile in mbfiles + if a:del|call s:DeleteBookmark(mbfile)|else|call s:MakeBookmark(mbfile)|endif + endfor + let i= i + 1 + endwhile + endif + + " update the menu + call s:NetrwBookmarkMenu() +endfun + +" --------------------------------------------------------------------- +" s:NetrwBookmarkMenu: Uses menu priorities {{{2 +" .2.[cnt] for bookmarks, and +" .3.[cnt] for history +" (see s:NetrwMenu()) +fun! s:NetrwBookmarkMenu() + if !exists("s:netrw_menucnt") + return + endif + " call Dfunc("NetrwBookmarkMenu() histcnt=".g:netrw_dirhistcnt." menucnt=".s:netrw_menucnt) + + " the following test assures that gvim is running, has menus available, and has menus enabled. + if has("gui") && has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu + if exists("g:NetrwTopLvlMenu") + " call Decho("removing ".g:NetrwTopLvlMenu."Bookmarks menu item(s)",'~'.expand("<slnum>")) + exe 'sil! unmenu '.g:NetrwTopLvlMenu.'Bookmarks' + exe 'sil! unmenu '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History.Bookmark\ Delete' + endif + if !exists("s:netrw_initbookhist") + call s:NetrwBookHistRead() + endif + + " show bookmarked places + if exists("g:netrw_bookmarklist") && g:netrw_bookmarklist != [] && g:netrw_dirhistmax > 0 + let cnt= 1 + for bmd in g:netrw_bookmarklist + " call Decho('sil! menu '.g:NetrwMenuPriority.".2.".cnt." ".g:NetrwTopLvlMenu.'Bookmark.'.bmd.' :e '.bmd,'~'.expand("<slnum>")) + let bmd= escape(bmd,g:netrw_menu_escape) + + " show bookmarks for goto menu + exe 'sil! menu '.g:NetrwMenuPriority.".2.".cnt." ".g:NetrwTopLvlMenu.'Bookmarks.'.bmd.' :e '.bmd."\<cr>" + + " show bookmarks for deletion menu + exe 'sil! menu '.g:NetrwMenuPriority.".8.2.".cnt." ".g:NetrwTopLvlMenu.'Bookmarks\ and\ History.Bookmark\ Delete.'.bmd.' '.cnt."mB" + let cnt= cnt + 1 + endfor + + endif + + " show directory browsing history + if g:netrw_dirhistmax > 0 + let cnt = g:netrw_dirhistcnt + let first = 1 + let histcnt = 0 + while ( first || cnt != g:netrw_dirhistcnt ) + let histcnt = histcnt + 1 + let priority = g:netrw_dirhistcnt + histcnt + if exists("g:netrw_dirhist_{cnt}") + let histdir= escape(g:netrw_dirhist_{cnt},g:netrw_menu_escape) + " call Decho('sil! menu '.g:NetrwMenuPriority.".3.".priority." ".g:NetrwTopLvlMenu.'History.'.histdir.' :e '.histdir,'~'.expand("<slnum>")) + exe 'sil! menu '.g:NetrwMenuPriority.".3.".priority." ".g:NetrwTopLvlMenu.'History.'.histdir.' :e '.histdir."\<cr>" + endif + let first = 0 + let cnt = ( cnt - 1 ) % g:netrw_dirhistmax + if cnt < 0 + let cnt= cnt + g:netrw_dirhistmax + endif + endwhile + endif + + endif + " call Dret("NetrwBookmarkMenu") +endfun + +" --------------------------------------------------------------------- +" s:NetrwBrowseChgDir: constructs a new directory based on the current {{{2 +" directory and a new directory name. Also, if the +" "new directory name" is actually a file, +" NetrwBrowseChgDir() edits the file. +" cursor=0: newdir is relative to b:netrw_curdir +" =1: newdir is relative to the path to the word under the cursor in +" tree view +fun! s:NetrwBrowseChgDir(islocal,newdir,cursor,...) + let ykeep= @@ + if !exists("b:netrw_curdir") + " Don't try to change-directory: this can happen, for example, when netrw#ErrorMsg has been called + " and the current window is the NetrwMessage window. + let @@= ykeep + return + endif + + " NetrwBrowseChgDir; save options and initialize {{{3 + call s:SavePosn(s:netrw_posn) + NetrwKeepj call s:NetrwOptionsSave("s:") + NetrwKeepj call s:NetrwOptionsSafe(a:islocal) + + let newdir = a:newdir + if a:cursor && exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treetop") + " dirname is the path to the word under the cursor + let dirname = s:NetrwTreePath(w:netrw_treetop) + " newdir resolves to a directory and points to a directory in dirname + " /tmp/test/folder_symlink/ -> /tmp/test/original_folder/ + if a:islocal && fnamemodify(dirname, ':t') == newdir && isdirectory(resolve(dirname)) && resolve(dirname) == resolve(newdir) + let dirname = fnamemodify(resolve(dirname), ':p:h:h') + let newdir = fnamemodify(resolve(newdir), ':t') + endif + " Remove trailing "/" + let dirname = substitute(dirname, "/$", "", "") + + " If the word under the cursor is a directory (except for ../), NetrwTreePath + " returns the full path, including the word under the cursor, remove it + if newdir =~ "/$" && newdir != "../" + let dirname = fnamemodify(dirname, ":h") + endif + else + let dirname = b:netrw_curdir + endif + if has("win32") + let dirname = substitute(dirname,'\\','/','ge') + endif + let dolockout = 0 + let dorestore = 1 + + " ignore <cr>s when done in the banner + if g:netrw_banner + if exists("w:netrw_bannercnt") && line(".") < w:netrw_bannercnt && line("$") >= w:netrw_bannercnt + if getline(".") =~# 'Quick Help' + let g:netrw_quickhelp= (g:netrw_quickhelp + 1)%len(s:QuickHelp) + setl ma noro nowrap + NetrwKeepj call setline(line('.'),'" Quick Help: <F1>:help '.s:QuickHelp[g:netrw_quickhelp]) + setl noma nomod nowrap + NetrwKeepj call s:NetrwOptionsRestore("s:") + endif + endif + endif + + " set up o/s-dependent directory recognition pattern + if has("amiga") + let dirpat= '[\/:]$' + else + let dirpat= '[\/]$' + endif + + if dirname !~ dirpat + " apparently vim is "recognizing" that it is in a directory and + " is removing the trailing "/". Bad idea, so let's put it back. + let dirname= dirname.'/' + endif + + if newdir !~ dirpat && !(a:islocal && isdirectory(s:NetrwFile(s:ComposePath(dirname,newdir)))) + " ------------------------------ + " NetrwBrowseChgDir: edit a file {{{3 + " ------------------------------ + + " save position for benefit of Rexplore + let s:rexposn_{bufnr("%")}= winsaveview() + + if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict") && newdir !~ '^\(/\|\a:\)' + if dirname =~ '/$' + let dirname= dirname.newdir + else + let dirname= dirname."/".newdir + endif + elseif newdir =~ '^\(/\|\a:\)' + let dirname= newdir + else + let dirname= s:ComposePath(dirname,newdir) + endif + " this lets netrw#BrowseX avoid the edit + if a:0 < 1 + NetrwKeepj call s:NetrwOptionsRestore("s:") + let curdir= b:netrw_curdir + if !exists("s:didsplit") + if type(g:netrw_browse_split) == 3 + " open file in server + " Note that g:netrw_browse_split is a List: [servername,tabnr,winnr] + call s:NetrwServerEdit(a:islocal,dirname) + return + + elseif g:netrw_browse_split == 1 + " horizontally splitting the window first + let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winheight(0))/100 : -g:netrw_winsize + exe "keepalt ".(g:netrw_alto? "bel " : "abo ").winsz."wincmd s" + if !&ea + keepalt wincmd _ + endif + call s:SetRexDir(a:islocal,curdir) + + elseif g:netrw_browse_split == 2 + " vertically splitting the window first + let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize + exe "keepalt ".(g:netrw_alto? "top " : "bot ")."vert ".winsz."wincmd s" + if !&ea + keepalt wincmd | + endif + call s:SetRexDir(a:islocal,curdir) + + elseif g:netrw_browse_split == 3 + " open file in new tab + keepalt tabnew + 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) + if s:NetrwPrevWinOpen(2) == 3 + let @@= ykeep + return + endif + call s:SetRexDir(a:islocal,curdir) + + else + " handling a file, didn't split, so remove menu + call s:NetrwMenu(0) + " optional change to window + if g:netrw_chgwin >= 1 + if winnr("$")+1 == g:netrw_chgwin + " if g:netrw_chgwin is set to one more than the last window, then + " vertically split the last window to make that window available. + let curwin= winnr() + exe "NetrwKeepj keepalt ".winnr("$")."wincmd w" + vs + exe "NetrwKeepj keepalt ".g:netrw_chgwin."wincmd ".curwin + endif + exe "NetrwKeepj keepalt ".g:netrw_chgwin."wincmd w" + endif + call s:SetRexDir(a:islocal,curdir) + endif + + endif + + " the point where netrw actually edits the (local) file + " if its local only: LocalBrowseCheck() doesn't edit a file, but NetrwBrowse() will + " use keepalt to support :e # to return to a directory listing + if !&mod + " if e the new file would fail due to &mod, then don't change any of the flags + let dolockout= 1 + endif + if a:islocal + " some like c-^ to return to the last edited file + " others like c-^ to return to the netrw buffer + " Apr 30, 2020: used to have e! here. That can cause loss of a modified file, + " so emit error E37 instead. + call s:NetrwEditFile("e","",dirname) + call s:NetrwCursor(1) + if &hidden || &bufhidden == "hide" + " file came from vim's hidden storage. Don't "restore" options with it. + let dorestore= 0 + endif + else + endif + + " handle g:Netrw_funcref -- call external-to-netrw functions + " This code will handle g:Netrw_funcref as an individual function reference + " or as a list of function references. It will ignore anything that's not + " a function reference. See :help Funcref for information about function references. + if exists("g:Netrw_funcref") + if type(g:Netrw_funcref) == 2 + NetrwKeepj call g:Netrw_funcref() + elseif type(g:Netrw_funcref) == 3 + for Fncref in g:Netrw_funcref + if type(Fncref) == 2 + NetrwKeepj call Fncref() + endif + endfor + endif + endif + endif + + elseif newdir =~ '^/' + " ---------------------------------------------------- + " NetrwBrowseChgDir: just go to the new directory spec {{{3 + " ---------------------------------------------------- + let dirname = newdir + NetrwKeepj call s:SetRexDir(a:islocal,dirname) + NetrwKeepj call s:NetrwOptionsRestore("s:") + norm! m` + + elseif newdir == './' + " --------------------------------------------- + " NetrwBrowseChgDir: refresh the directory list {{{3 + " --------------------------------------------- + NetrwKeepj call s:SetRexDir(a:islocal,dirname) + norm! m` + + elseif newdir == '../' + " -------------------------------------- + " NetrwBrowseChgDir: go up one directory {{{3 + " -------------------------------------- + + if w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict") + " force a refresh + setl noro ma + NetrwKeepj %d _ + endif + + if has("amiga") + " amiga + if a:islocal + let dirname= substitute(dirname,'^\(.*[/:]\)\([^/]\+$\)','\1','') + let dirname= substitute(dirname,'/$','','') + else + let dirname= substitute(dirname,'^\(.*[/:]\)\([^/]\+/$\)','\1','') + endif + + elseif !g:netrw_cygwin && has("win32") + " windows + if a:islocal + let dirname= substitute(dirname,'^\(.*\)/\([^/]\+\)/$','\1','') + if dirname == "" + let dirname= '/' + endif + else + let dirname= substitute(dirname,'^\(\a\{3,}://.\{-}/\{1,2}\)\(.\{-}\)\([^/]\+\)/$','\1\2','') + endif + if dirname =~ '^\a:$' + let dirname= dirname.'/' + endif + + else + " unix or cygwin + if a:islocal + let dirname= substitute(dirname,'^\(.*\)/\([^/]\+\)/$','\1','') + if dirname == "" + let dirname= '/' + endif + else + let dirname= substitute(dirname,'^\(\a\{3,}://.\{-}/\{1,2}\)\(.\{-}\)\([^/]\+\)/$','\1\2','') + endif + endif + NetrwKeepj call s:SetRexDir(a:islocal,dirname) + norm! m` + + elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict") + " -------------------------------------- + " NetrwBrowseChgDir: Handle Tree Listing {{{3 + " -------------------------------------- + " force a refresh (for TREELIST, NetrwTreeDir() will force the refresh) + setl noro ma + if !(exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("b:netrw_curdir")) + NetrwKeepj %d _ + endif + let treedir = s:NetrwTreeDir(a:islocal) + let s:treecurpos = winsaveview() + let haskey = 0 + + " search treedict for tree dir as-is + if has_key(w:netrw_treedict,treedir) + let haskey= 1 + else + endif + + " search treedict for treedir with a [/@] appended + if !haskey && treedir !~ '[/@]$' + if has_key(w:netrw_treedict,treedir."/") + let treedir= treedir."/" + let haskey = 1 + else + endif + endif + + " search treedict for treedir with any trailing / elided + if !haskey && treedir =~ '/$' + let treedir= substitute(treedir,'/$','','') + if has_key(w:netrw_treedict,treedir) + let haskey = 1 + else + endif + endif + + if haskey + " close tree listing for selected subdirectory + call remove(w:netrw_treedict,treedir) + let dirname= w:netrw_treetop + else + " go down one directory + let dirname= substitute(treedir,'/*$','/','') + endif + NetrwKeepj call s:SetRexDir(a:islocal,dirname) + let s:treeforceredraw = 1 + + else + " ---------------------------------------- + " NetrwBrowseChgDir: Go down one directory {{{3 + " ---------------------------------------- + let dirname = s:ComposePath(dirname,newdir) + NetrwKeepj call s:SetRexDir(a:islocal,dirname) + norm! m` + endif + + " -------------------------------------- + " NetrwBrowseChgDir: Restore and Cleanup {{{3 + " -------------------------------------- + if dorestore + " dorestore is zero'd when a local file was hidden or bufhidden; + " in such a case, we want to keep whatever settings it may have. + NetrwKeepj call s:NetrwOptionsRestore("s:") + endif + if dolockout && dorestore + if filewritable(dirname) + setl ma noro nomod + else + setl ma ro nomod + endif + endif + call s:RestorePosn(s:netrw_posn) + let @@= ykeep + + return dirname +endfun + +" --------------------------------------------------------------------- +" s:NetrwBrowseUpDir: implements the "-" mappings {{{2 +" for thin, long, and wide: cursor placed just after banner +" for tree, keeps cursor on current filename +fun! s:NetrwBrowseUpDir(islocal) + if exists("w:netrw_bannercnt") && line(".") < w:netrw_bannercnt-1 + " this test needed because occasionally this function seems to be incorrectly called + " when multiple leftmouse clicks are taken when atop the one line help in the banner. + " I'm allowing the very bottom line to permit a "-" exit so that one may escape empty + " directories. + return + endif + + norm! 0 + if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict") + let curline= getline(".") + let swwline= winline() - 1 + if exists("w:netrw_treetop") + let b:netrw_curdir= w:netrw_treetop + elseif exists("b:netrw_curdir") + let w:netrw_treetop= b:netrw_curdir + else + let w:netrw_treetop= getcwd() + let b:netrw_curdir = w:netrw_treetop + endif + let curfile = getline(".") + let curpath = s:NetrwTreePath(w:netrw_treetop) + if a:islocal + call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,'../',0)) + else + call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,'../',0)) + endif + if w:netrw_treetop == '/' + keepj call search('^\M'.curfile,"w") + elseif curfile == '../' + keepj call search('^\M'.curfile,"wb") + else + while 1 + keepj call search('^\M'.s:treedepthstring.curfile,"wb") + let treepath= s:NetrwTreePath(w:netrw_treetop) + if treepath == curpath + break + endif + endwhile + endif + + else + call s:SavePosn(s:netrw_posn) + if exists("b:netrw_curdir") + let curdir= b:netrw_curdir + else + let curdir= expand(getcwd()) + endif + if a:islocal + call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,'../',0)) + else + call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,'../',0)) + endif + call s:RestorePosn(s:netrw_posn) + let curdir= substitute(curdir,'^.*[\/]','','') + let curdir= '\<'. escape(curdir, '~'). '/' + call search(curdir,'wc') + endif +endfun + +func s:redir() + " set up redirection (avoids browser messages) + " by default if not set, g:netrw_suppress_gx_mesg is true + if get(g:, 'netrw_suppress_gx_mesg', 1) + if &srr =~# "%s" + return printf(&srr, has("win32") ? "nul" : "/dev/null") + else + return &srr .. (has("win32") ? "nul" : "/dev/null") + endif + endif + return '' +endfunc + +if has('unix') + if has('win32unix') + " Cygwin provides cygstart + if executable('cygstart') + fun! netrw#Launch(args) + exe 'silent ! cygstart --hide' a:args s:redir() | redraw! + endfun + elseif !empty($MSYSTEM) && executable('start') + " MSYS2/Git Bash comes by default without cygstart; see + " https://www.msys2.org/wiki/How-does-MSYS2-differ-from-Cygwin + " Instead it provides /usr/bin/start script running `cmd.exe //c start` + " Adding "" //b` sets void title, hides cmd window and blocks path conversion + " of /b to \b\ " by MSYS2; see https://www.msys2.org/docs/filesystem-paths/ + fun! netrw#Launch(args) + exe 'silent !start "" //b' a:args s:redir() | redraw! + endfun + else + " imitate /usr/bin/start script for other environments and hope for the best + fun! netrw#Launch(args) + exe 'silent !cmd //c start "" //b' a:args s:redir() | redraw! + endfun + endif + elseif exists('$WSL_DISTRO_NAME') " use cmd.exe to start GUI apps in WSL + fun! netrw#Launch(args) + let args = a:args + exe 'silent !' .. + \ ((args =~? '\v<\f+\.(exe|com|bat|cmd)>') ? + \ 'cmd.exe /c start /b ' .. args : + \ 'nohup ' .. args .. ' ' .. s:redir() .. ' &') + \ | redraw! + endfun + else + fun! netrw#Launch(args) + exe ':silent ! nohup' a:args s:redir() (has('gui_running') ? '' : '&') | redraw! + endfun + endif +elseif has('win32') + fun! netrw#Launch(args) + exe 'silent !' .. (&shell =~? '\<cmd\.exe\>' ? '' : 'cmd.exe /c') + \ 'start "" /b' a:args s:redir() | redraw! + endfun +else + fun! netrw#Launch(dummy) + echom 'No common launcher found' + endfun +endif + +" Git Bash +if has('win32unix') + " (cyg)start suffices + let s:os_viewer = '' +" Windows / WSL +elseif executable('explorer.exe') + let s:os_viewer = 'explorer.exe' +" Linux / BSD +elseif executable('xdg-open') + let s:os_viewer = 'xdg-open' +" MacOS +elseif executable('open') + let s:os_viewer = 'open' +endif + +fun! s:viewer() + " g:netrw_browsex_viewer could be a string of program + its arguments, + " test if first argument is executable + if exists('g:netrw_browsex_viewer') && executable(split(g:netrw_browsex_viewer)[0]) + " extract any viewing options. Assumes that they're set apart by spaces. + " call Decho("extract any viewing options from g:netrw_browsex_viewer<".g:netrw_browsex_viewer.">",'~'.expand("<slnum>")) + if g:netrw_browsex_viewer =~ '\s' + let viewer = substitute(g:netrw_browsex_viewer,'\s.*$','','') + let viewopt = substitute(g:netrw_browsex_viewer,'^\S\+\s*','','')." " + let oviewer = '' + let cnt = 1 + while !executable(viewer) && viewer != oviewer + let viewer = substitute(g:netrw_browsex_viewer,'^\(\(^\S\+\s\+\)\{'.cnt.'}\S\+\)\(.*\)$','\1','') + let viewopt = substitute(g:netrw_browsex_viewer,'^\(\(^\S\+\s\+\)\{'.cnt.'}\S\+\)\(.*\)$','\3','')." " + let cnt = cnt + 1 + let oviewer = viewer + " call Decho("!exe: viewer<".viewer."> viewopt<".viewopt.">",'~'.expand("<slnum>")) + endwhile + else + let viewer = g:netrw_browsex_viewer + let viewopt = "" + endif + " call Decho("viewer<".viewer."> viewopt<".viewopt.">",'~'.expand("<slnum>")) + return viewer .. ' ' .. viewopt + else + if !exists('s:os_viewer') + call netrw#ErrorMsg(s:ERROR,"No program to open this path found. See :help Open for more information.",106) + else + return s:os_viewer + endif + endif +endfun + +fun! netrw#Open(file) abort + call netrw#Launch(s:viewer() .. ' ' .. shellescape(a:file, 1)) +endfun + +if !exists('g:netrw_regex_url') + let g:netrw_regex_url = '\%(\%(http\|ftp\|irc\)s\?\|file\)://\S\{-}' +endif + +" --------------------------------------------------------------------- +" netrw#BrowseX: (implements "x" and "gx") executes a special "viewer" script or program for the {{{2 +" given filename; typically this means given their extension. +" 0=local, 1=remote +fun! netrw#BrowseX(fname,remote) + if a:remote == 1 && a:fname !~ '^https\=:' && a:fname =~ '/$' + " remote directory, not a webpage access, looks like an attempt to do a directory listing + norm! gf + endif + + if exists("g:netrw_browsex_viewer") && exists("g:netrw_browsex_support_remote") && !g:netrw_browsex_support_remote + let remote = a:remote + else + let remote = 0 + endif + + let ykeep = @@ + let screenposn = winsaveview() + + " need to save and restore aw setting as gx can invoke this function from non-netrw buffers + let awkeep = &aw + set noaw + + " special core dump handler + if a:fname =~ '/core\(\.\d\+\)\=$' + if exists("g:Netrw_corehandler") + if type(g:Netrw_corehandler) == 2 + " g:Netrw_corehandler is a function reference (see :help Funcref) + call g:Netrw_corehandler(s:NetrwFile(a:fname)) + elseif type(g:Netrw_corehandler) == 3 + " g:Netrw_corehandler is a List of function references (see :help Funcref) + for Fncref in g:Netrw_corehandler + if type(Fncref) == 2 + call Fncref(a:fname) + endif + endfor + endif + call winrestview(screenposn) + let @@= ykeep + let &aw= awkeep + return + endif + endif + + " set up the filename + " (lower case the extension, make a local copy of a remote file) + let exten= substitute(a:fname,'.*\.\(.\{-}\)','\1','e') + if has("win32") + let exten= substitute(exten,'^.*$','\L&\E','') + endif + if exten =~ "[\\/]" + let exten= "" + endif + + if remote == 1 + " create a local copy + setl bh=delete + call netrw#NetRead(3,a:fname) + " attempt to rename tempfile + let basename= substitute(a:fname,'^\(.*\)/\(.*\)\.\([^.]*\)$','\2','') + let newname = substitute(s:netrw_tmpfile,'^\(.*\)/\(.*\)\.\([^.]*\)$','\1/'.basename.'.\3','') + if s:netrw_tmpfile != newname && newname != "" + if rename(s:netrw_tmpfile,newname) == 0 + " renaming succeeded + let fname= newname + else + " renaming failed + let fname= s:netrw_tmpfile + endif + else + let fname= s:netrw_tmpfile + endif + else + let fname= a:fname + " special ~ handler for local + if fname =~ '^\~' && expand("$HOME") != "" + let fname= s:NetrwFile(substitute(fname,'^\~',expand("$HOME"),'')) + endif + endif + + " although shellescape(..., 1) is used in netrw#Open(), it's insufficient + call netrw#Open(escape(fname, '#%')) + + " cleanup: remove temporary file, + " delete current buffer if success with handler, + " return to prior buffer (directory listing) + " Feb 12, 2008: had to de-activate removal of + " temporary file because it wasn't getting seen. + " if remote == 1 && fname != a:fname + " call s:NetrwDelete(fname) + " endif + + if remote == 1 + setl bh=delete bt=nofile + if g:netrw_use_noswf + setl noswf + endif + exe "sil! NetrwKeepj norm! \<c-o>" + endif + call winrestview(screenposn) + let @@ = ykeep + let &aw= awkeep +endfun + +" --------------------------------------------------------------------- +" netrw#GX: gets word under cursor for gx support {{{2 +" See also: netrw#BrowseXVis +" netrw#BrowseX +fun! netrw#GX() + " call Dfunc("netrw#GX()") + if &ft == "netrw" + let fname= s:NetrwGetWord() + else + let fname= exists("g:netrw_gx")? expand(g:netrw_gx) : s:GetURL() + endif + " call Dret("netrw#GX <".fname.">") + return fname +endfun + +fun! s:GetURL() abort + let URL = '' + if exists('*Netrw_get_URL_' .. &filetype) + let URL = call('Netrw_get_URL_' .. &filetype, []) + endif + if !empty(URL) | return URL | endif + " URLs end in letter, digit or forward slash + let URL = matchstr(expand("<cWORD>"), '\<' .. g:netrw_regex_url .. '\ze[^A-Za-z0-9/]*$') + if !empty(URL) | return URL | endif + + " Is it a file in the current work dir ... + let file = expand("<cfile>") + if filereadable(file) | return file | endif + " ... or in that of the current buffer? + let path = fnamemodify(expand('%'), ':p') + if isdirectory(path) + let dir = path + elseif filereadable(path) + let dir = fnamemodify(path, ':h') + endif + if exists('dir') && filereadable(dir..'/'..file) | return dir..'/'..file | endif + + return '' +endf + +" --------------------------------------------------------------------- +" netrw#BrowseXVis: used by gx in visual mode to select a file for browsing {{{2 +fun! netrw#BrowseXVis() + let dict={} + let dict.a=[getreg('a'), getregtype('a')] + norm! gv"ay + let gxfile= @a + call s:RestoreRegister(dict) + call netrw#BrowseX(gxfile,netrw#CheckIfRemote(gxfile)) +endfun + +" --------------------------------------------------------------------- +" s:NetrwBufRename: renames a buffer without the side effect of retaining an unlisted buffer having the old name {{{2 +" Using the file command on a "[No Name]" buffer does not seem to cause the old "[No Name]" buffer +" to become an unlisted buffer, so in that case don't bwipe it. +fun! s:NetrwBufRename(newname) + " call Dfunc("s:NetrwBufRename(newname<".a:newname.">) buf(%)#".bufnr("%")."<".bufname(bufnr("%")).">") + " call Dredir("ls!","s:NetrwBufRename (before rename)") + let oldbufname= bufname(bufnr("%")) + " call Decho("buf#".bufnr("%").": oldbufname<".oldbufname.">",'~'.expand("<slnum>")) + + if oldbufname != a:newname + " call Decho("do buffer rename: oldbufname<".oldbufname."> ≠ a:newname<".a:newname.">",'~'.expand("<slnum>")) + let b:junk= 1 + " call Decho("rename buffer: sil! keepj keepalt file ".fnameescape(a:newname),'~'.expand("<slnum>")) + exe 'sil! keepj keepalt file '.fnameescape(a:newname) + " call Dredir("ls!","s:NetrwBufRename (before bwipe)~".expand("<slnum>")) + let oldbufnr= bufnr(oldbufname) + " call Decho("oldbufname<".oldbufname."> oldbufnr#".oldbufnr,'~'.expand("<slnum>")) + " call Decho("bufnr(%)=".bufnr("%"),'~'.expand("<slnum>")) + if oldbufname != "" && oldbufnr != -1 && oldbufnr != bufnr("%") + " call Decho("bwipe ".oldbufnr,'~'.expand("<slnum>")) + exe "bwipe! ".oldbufnr + " else " Decho + " call Decho("did *not* bwipe buf#".oldbufnr,'~'.expand("<slnum>")) + " call Decho("..reason: if oldbufname<".oldbufname."> is empty",'~'.expand("<slnum>"))" + " call Decho("..reason: if oldbufnr#".oldbufnr." is -1",'~'.expand("<slnum>"))" + " call Decho("..reason: if oldbufnr#".oldbufnr." != bufnr(%)#".bufnr("%"),'~'.expand("<slnum>"))" + endif + " call Dredir("ls!","s:NetrwBufRename (after rename)") + " else " Decho + " call Decho("oldbufname<".oldbufname."> == a:newname: did *not* rename",'~'.expand("<slnum>")) + endif + + " call Dret("s:NetrwBufRename : buf#".bufnr("%").": oldname<".oldbufname."> newname<".a:newname."> expand(%)<".expand("%").">") +endfun + +" --------------------------------------------------------------------- +" netrw#CheckIfRemote: returns 1 if current file looks like an url, 0 else {{{2 +fun! netrw#CheckIfRemote(...) + " call Dfunc("netrw#CheckIfRemote() a:0=".a:0) + if a:0 > 0 + let curfile= a:1 + else + let curfile= expand("%") + endif + " Ignore terminal buffers + if &buftype ==# 'terminal' + return 0 + endif + " call Decho("curfile<".curfile.">") + if curfile =~ '^\a\{3,}://' + " call Dret("netrw#CheckIfRemote 1") + return 1 + else + " call Dret("netrw#CheckIfRemote 0") + return 0 + endif +endfun + +" --------------------------------------------------------------------- +" s:NetrwChgPerm: (implements "gp") change file permission {{{2 +fun! s:NetrwChgPerm(islocal,curdir) + let ykeep = @@ + call inputsave() + let newperm= input("Enter new permission: ") + call inputrestore() + let chgperm= substitute(g:netrw_chgperm,'\<FILENAME\>',s:ShellEscape(expand("<cfile>")),'') + let chgperm= substitute(chgperm,'\<PERM\>',s:ShellEscape(newperm),'') + call system(chgperm) + if v:shell_error != 0 + NetrwKeepj call netrw#ErrorMsg(1,"changing permission on file<".expand("<cfile>")."> seems to have failed",75) + endif + if a:islocal + NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) + endif + let @@= ykeep +endfun + +" --------------------------------------------------------------------- +" s:CheckIfKde: checks if kdeinit is running {{{2 +" Returns 0: kdeinit not running +" 1: kdeinit is running +fun! s:CheckIfKde() + " call Dfunc("s:CheckIfKde()") + " seems kde systems often have gnome-open due to dependencies, even though + " gnome-open's subsidiary display tools are largely absent. Kde systems + " usually have "kdeinit" running, though... (tnx Mikolaj Machowski) + if !exists("s:haskdeinit") + if has("unix") && executable("ps") && !has("win32unix") + let s:haskdeinit= system("ps -e") =~ '\<kdeinit' + if v:shell_error + let s:haskdeinit = 0 + endif + else + let s:haskdeinit= 0 + endif + " call Decho("setting s:haskdeinit=".s:haskdeinit,'~'.expand("<slnum>")) + endif + + " call Dret("s:CheckIfKde ".s:haskdeinit) + return s:haskdeinit +endfun + +" --------------------------------------------------------------------- +" s:NetrwClearExplore: clear explore variables (if any) {{{2 +fun! s:NetrwClearExplore() + " call Dfunc("s:NetrwClearExplore()") + 2match none + if exists("s:explore_match") |unlet s:explore_match |endif + if exists("s:explore_indx") |unlet s:explore_indx |endif + if exists("s:netrw_explore_prvdir") |unlet s:netrw_explore_prvdir |endif + if exists("s:dirstarstar") |unlet s:dirstarstar |endif + if exists("s:explore_prvdir") |unlet s:explore_prvdir |endif + if exists("w:netrw_explore_indx") |unlet w:netrw_explore_indx |endif + if exists("w:netrw_explore_listlen")|unlet w:netrw_explore_listlen|endif + if exists("w:netrw_explore_list") |unlet w:netrw_explore_list |endif + if exists("w:netrw_explore_bufnr") |unlet w:netrw_explore_bufnr |endif + " redraw! + " call Dret("s:NetrwClearExplore") +endfun + +" --------------------------------------------------------------------- +" s:NetrwEditBuf: decides whether or not to use keepalt to edit a buffer {{{2 +fun! s:NetrwEditBuf(bufnum) + " call Dfunc("s:NetrwEditBuf(fname<".a:bufnum.">)") + if exists("g:netrw_altfile") && g:netrw_altfile && &ft == "netrw" + " call Decho("exe sil! NetrwKeepj keepalt noswapfile b ".fnameescape(a:bufnum)) + exe "sil! NetrwKeepj keepalt noswapfile b ".fnameescape(a:bufnum) + else + " call Decho("exe sil! NetrwKeepj noswapfile b ".fnameescape(a:bufnum)) + exe "sil! NetrwKeepj noswapfile b ".fnameescape(a:bufnum) + endif + " call Dret("s:NetrwEditBuf") +endfun + +" --------------------------------------------------------------------- +" s:NetrwEditFile: decides whether or not to use keepalt to edit a file {{{2 +" NetrwKeepj [keepalt] <OPT> <CMD> <FILENAME> +fun! s:NetrwEditFile(cmd,opt,fname) + " call Dfunc("s:NetrwEditFile(cmd<".a:cmd.">,opt<".a:opt.">,fname<".a:fname.">) ft<".&ft.">") + if exists("g:netrw_altfile") && g:netrw_altfile && &ft == "netrw" + " call Decho("exe NetrwKeepj keepalt ".a:opt." ".a:cmd." ".fnameescape(a:fname)) + exe "NetrwKeepj keepalt ".a:opt." ".a:cmd." ".fnameescape(a:fname) + else + " call Decho("exe NetrwKeepj ".a:opt." ".a:cmd." ".fnameescape(a:fname)) + if a:cmd =~# 'e\%[new]!' && !&hidden && getbufvar(bufname('%'), '&modified', 0) + call setbufvar(bufname('%'), '&bufhidden', 'hide') + endif + exe "NetrwKeepj ".a:opt." ".a:cmd." ".fnameescape(a:fname) + endif + " call Dret("s:NetrwEditFile") +endfun + +" --------------------------------------------------------------------- +" s:NetrwExploreListUniq: {{{2 +fun! s:NetrwExploreListUniq(explist) + " this assumes that the list is already sorted + let newexplist= [] + for member in a:explist + if !exists("uniqmember") || member != uniqmember + let uniqmember = member + let newexplist = newexplist + [ member ] + endif + endfor + return newexplist +endfun + +" --------------------------------------------------------------------- +" s:NetrwForceChgDir: (gd support) Force treatment as a directory {{{2 +fun! s:NetrwForceChgDir(islocal,newdir) + let ykeep= @@ + if a:newdir !~ '/$' + " ok, looks like force is needed to get directory-style treatment + if a:newdir =~ '@$' + let newdir= substitute(a:newdir,'@$','/','') + elseif a:newdir =~ '[*=|\\]$' + let newdir= substitute(a:newdir,'.$','/','') + else + let newdir= a:newdir.'/' + endif + else + " should already be getting treatment as a directory + let newdir= a:newdir + endif + let newdir= s:NetrwBrowseChgDir(a:islocal,newdir,0) + call s:NetrwBrowse(a:islocal,newdir) + let @@= ykeep +endfun + +" --------------------------------------------------------------------- +" s:NetrwGlob: does glob() if local, remote listing otherwise {{{2 +" 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 + let keep_liststyle = w:netrw_liststyle + let w:netrw_liststyle = s:THINLIST + if s:NetrwRemoteListing() == 0 + keepj keepalt %s@/@@ + let filelist= getline(1,$) + q! + else + " remote listing error -- leave treedict unchanged + let filelist= w:netrw_treedict[a:direntry] + endif + let w:netrw_liststyle= keep_liststyle + else + let path= s:ComposePath(fnameescape(a:direntry), a:expr) + if has("win32") + " escape [ so it is not detected as wildcard character, see :h wildcard + let path= substitute(path, '[', '[[]', 'g') + endif + if v:version > 704 || (v:version == 704 && has("patch656")) + let filelist= glob(path,0,1,1) + else + let filelist= glob(path,0,1) + endif + if a:pare + let filelist= map(filelist,'substitute(v:val, "^.*/", "", "")') + endif + endif + return filelist +endfun + +" --------------------------------------------------------------------- +" s:NetrwForceFile: (gf support) Force treatment as a file {{{2 +fun! s:NetrwForceFile(islocal,newfile) + if a:newfile =~ '[/@*=|\\]$' + let newfile= substitute(a:newfile,'.$','','') + else + let newfile= a:newfile + endif + if a:islocal + call s:NetrwBrowseChgDir(a:islocal,newfile,0) + else + call s:NetrwBrowse(a:islocal,s:NetrwBrowseChgDir(a:islocal,newfile,0)) + endif +endfun + +" --------------------------------------------------------------------- +" s:NetrwHide: this function is invoked by the "a" map for browsing {{{2 +" and switches the hiding mode. The actual hiding is done by +" s:NetrwListHide(). +" g:netrw_hide= 0: show all +" 1: show not-hidden files +" 2: show hidden files only +fun! s:NetrwHide(islocal) + let ykeep= @@ + let svpos= winsaveview() + + if exists("s:netrwmarkfilelist_{bufnr('%')}") + + " hide the files in the markfile list + for fname in s:netrwmarkfilelist_{bufnr("%")} + if match(g:netrw_list_hide,'\<'.fname.'\>') != -1 + " remove fname from hiding list + let g:netrw_list_hide= substitute(g:netrw_list_hide,'..\<'.escape(fname,g:netrw_fname_escape).'\>..','','') + let g:netrw_list_hide= substitute(g:netrw_list_hide,',,',',','g') + let g:netrw_list_hide= substitute(g:netrw_list_hide,'^,\|,$','','') + else + " append fname to hiding list + if exists("g:netrw_list_hide") && g:netrw_list_hide != "" + let g:netrw_list_hide= g:netrw_list_hide.',\<'.escape(fname,g:netrw_fname_escape).'\>' + else + let g:netrw_list_hide= '\<'.escape(fname,g:netrw_fname_escape).'\>' + endif + endif + endfor + NetrwKeepj call s:NetrwUnmarkList(bufnr("%"),b:netrw_curdir) + let g:netrw_hide= 1 + + else + + " switch between show-all/show-not-hidden/show-hidden + let g:netrw_hide=(g:netrw_hide+1)%3 + exe "NetrwKeepj norm! 0" + if g:netrw_hide && g:netrw_list_hide == "" + NetrwKeepj call netrw#ErrorMsg(s:WARNING,"your hiding list is empty!",49) + let @@= ykeep + return + endif + endif + + NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) + NetrwKeepj call winrestview(svpos) + let @@= ykeep +endfun + +" --------------------------------------------------------------------- +" s:NetrwHideEdit: allows user to edit the file/directory hiding list {{{2 +fun! s:NetrwHideEdit(islocal) + let ykeep= @@ + " save current cursor position + let svpos= winsaveview() + + " get new hiding list from user + call inputsave() + let newhide= input("Edit Hiding List: ",g:netrw_list_hide) + call inputrestore() + let g:netrw_list_hide= newhide + + " refresh the listing + sil NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,"./",0)) + + " restore cursor position + call winrestview(svpos) + let @@= ykeep +endfun + +" --------------------------------------------------------------------- +" s:NetrwHidden: invoked by "gh" {{{2 +fun! s:NetrwHidden(islocal) + let ykeep= @@ + " save current position + let svpos = winsaveview() + + if g:netrw_list_hide =~ '\(^\|,\)\\(^\\|\\s\\s\\)\\zs\\.\\S\\+' + " remove .file pattern from hiding list + let g:netrw_list_hide= substitute(g:netrw_list_hide,'\(^\|,\)\\(^\\|\\s\\s\\)\\zs\\.\\S\\+','','') + elseif s:Strlen(g:netrw_list_hide) >= 1 + let g:netrw_list_hide= g:netrw_list_hide . ',\(^\|\s\s\)\zs\.\S\+' + else + let g:netrw_list_hide= '\(^\|\s\s\)\zs\.\S\+' + endif + if g:netrw_list_hide =~ '^,' + let g:netrw_list_hide= strpart(g:netrw_list_hide,1) + endif + + " refresh screen and return to saved position + NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) + NetrwKeepj call winrestview(svpos) + let @@= ykeep +endfun + +" --------------------------------------------------------------------- +" s:NetrwHome: this function determines a "home" for saving bookmarks and history {{{2 +fun! s:NetrwHome() + if exists("g:netrw_home") + let home= expand(g:netrw_home) + else + let home = stdpath('data') + endif + " insure that the home directory exists + if g:netrw_dirhistmax > 0 && !isdirectory(s:NetrwFile(home)) + " call Decho("insure that the home<".home."> directory exists") + if exists("g:netrw_mkdir") + " call Decho("call system(".g:netrw_mkdir." ".s:ShellEscape(s:NetrwFile(home)).")") + call system(g:netrw_mkdir." ".s:ShellEscape(s:NetrwFile(home))) + else + " call Decho("mkdir(".home.")") + call mkdir(home) + endif + endif + let g:netrw_home= home + return home +endfun + +" --------------------------------------------------------------------- +" s:NetrwLeftmouse: handles the <leftmouse> when in a netrw browsing window {{{2 +fun! s:NetrwLeftmouse(islocal) + if exists("s:netrwdrag") + return + endif + if &ft != "netrw" + return + endif + + let ykeep= @@ + " check if the status bar was clicked on instead of a file/directory name + while getchar(0) != 0 + "clear the input stream + endwhile + call feedkeys("\<LeftMouse>") + let c = getchar() + let mouse_lnum = v:mouse_lnum + let wlastline = line('w$') + let lastline = line('$') + if mouse_lnum >= wlastline + 1 || v:mouse_win != winnr() + " appears to be a status bar leftmouse click + let @@= ykeep + return + endif + " Dec 04, 2013: following test prevents leftmouse selection/deselection of directories and files in treelist mode + " Windows are separated by vertical separator bars - but the mouse seems to be doing what it should when dragging that bar + " without this test when its disabled. + " May 26, 2014: edit file, :Lex, resize window -- causes refresh. Reinstated a modified test. See if problems develop. + if v:mouse_col > virtcol('.') + let @@= ykeep + return + endif + + if a:islocal + if exists("b:netrw_curdir") + NetrwKeepj call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,s:NetrwGetWord(),1)) + endif + else + if exists("b:netrw_curdir") + NetrwKeepj call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,s:NetrwGetWord(),1)) + endif + endif + let @@= ykeep +endfun + +" --------------------------------------------------------------------- +" s:NetrwCLeftmouse: used to select a file/directory for a target {{{2 +fun! s:NetrwCLeftmouse(islocal) + if &ft != "netrw" + return + endif + call s:NetrwMarkFileTgt(a:islocal) +endfun + +" --------------------------------------------------------------------- +" s:NetrwServerEdit: edit file in a server gvim, usually NETRWSERVER (implements <c-r>){{{2 +" a:islocal=0 : <c-r> not used, remote +" a:islocal=1 : <c-r> not used, local +" a:islocal=2 : <c-r> used, remote +" a:islocal=3 : <c-r> used, local +fun! s:NetrwServerEdit(islocal,fname) + " call Dfunc("s:NetrwServerEdit(islocal=".a:islocal.",fname<".a:fname.">)") + let islocal = a:islocal%2 " =0: remote =1: local + let ctrlr = a:islocal >= 2 " =0: <c-r> not used =1: <c-r> used + + if (islocal && isdirectory(s:NetrwFile(a:fname))) || (!islocal && a:fname =~ '/$') + " handle directories in the local window -- not in the remote vim server + " user must have closed the NETRWSERVER window. Treat as normal editing from netrw. + let g:netrw_browse_split= 0 + if exists("s:netrw_browse_split") && exists("s:netrw_browse_split_".winnr()) + let g:netrw_browse_split= s:netrw_browse_split_{winnr()} + unlet s:netrw_browse_split_{winnr()} + endif + call s:NetrwBrowse(islocal,s:NetrwBrowseChgDir(islocal,a:fname,0)) + return + endif + + if has("clientserver") && executable("gvim") + + if exists("g:netrw_browse_split") && type(g:netrw_browse_split) == 3 + let srvrname = g:netrw_browse_split[0] + let tabnum = g:netrw_browse_split[1] + let winnum = g:netrw_browse_split[2] + + if serverlist() !~ '\<'.srvrname.'\>' + if !ctrlr + " user must have closed the server window and the user did not use <c-r>, but + " used something like <cr>. + if exists("g:netrw_browse_split") + unlet g:netrw_browse_split + endif + let g:netrw_browse_split= 0 + if exists("s:netrw_browse_split_".winnr()) + let g:netrw_browse_split= s:netrw_browse_split_{winnr()} + endif + call s:NetrwBrowseChgDir(islocal,a:fname,0) + return + + elseif has("win32") && executable("start") + " start up remote netrw server under windows + call system("start gvim --servername ".srvrname) + + else + " start up remote netrw server under linux + call system("gvim --servername ".srvrname) + endif + endif + + call remote_send(srvrname,":tabn ".tabnum."\<cr>") + call remote_send(srvrname,":".winnum."wincmd w\<cr>") + call remote_send(srvrname,":e ".fnameescape(s:NetrwFile(a:fname))."\<cr>") + else + + if serverlist() !~ '\<'.g:netrw_servername.'\>' + + if !ctrlr + if exists("g:netrw_browse_split") + unlet g:netrw_browse_split + endif + let g:netrw_browse_split= 0 + call s:NetrwBrowse(islocal,s:NetrwBrowseChgDir(islocal,a:fname,0)) + return + + else + if has("win32") && executable("start") + " start up remote netrw server under windows + call system("start gvim --servername ".g:netrw_servername) + else + " start up remote netrw server under linux + call system("gvim --servername ".g:netrw_servername) + endif + endif + endif + + while 1 + try + call remote_send(g:netrw_servername,":e ".fnameescape(s:NetrwFile(a:fname))."\<cr>") + break + catch /^Vim\%((\a\+)\)\=:E241/ + sleep 200m + endtry + endwhile + + if exists("g:netrw_browse_split") + if type(g:netrw_browse_split) != 3 + let s:netrw_browse_split_{winnr()}= g:netrw_browse_split + endif + unlet g:netrw_browse_split + endif + let g:netrw_browse_split= [g:netrw_servername,1,1] + endif + + else + call netrw#ErrorMsg(s:ERROR,"you need a gui-capable vim and client-server to use <ctrl-r>",98) + endif + +endfun + +" --------------------------------------------------------------------- +" s:NetrwSLeftmouse: marks the file under the cursor. May be dragged to select additional files {{{2 +fun! s:NetrwSLeftmouse(islocal) + if &ft != "netrw" + return + endif + " call Dfunc("s:NetrwSLeftmouse(islocal=".a:islocal.")") + + let s:ngw= s:NetrwGetWord() + call s:NetrwMarkFile(a:islocal,s:ngw) + + " call Dret("s:NetrwSLeftmouse") +endfun + +" --------------------------------------------------------------------- +" s:NetrwSLeftdrag: invoked via a shift-leftmouse and dragging {{{2 +" Used to mark multiple files. +fun! s:NetrwSLeftdrag(islocal) + " call Dfunc("s:NetrwSLeftdrag(islocal=".a:islocal.")") + if !exists("s:netrwdrag") + let s:netrwdrag = winnr() + if a:islocal + nno <silent> <s-leftrelease> <leftmouse>:<c-u>call <SID>NetrwSLeftrelease(1)<cr> + else + nno <silent> <s-leftrelease> <leftmouse>:<c-u>call <SID>NetrwSLeftrelease(0)<cr> + endif + endif + let ngw = s:NetrwGetWord() + if !exists("s:ngw") || s:ngw != ngw + call s:NetrwMarkFile(a:islocal,ngw) + endif + let s:ngw= ngw + " call Dret("s:NetrwSLeftdrag : s:netrwdrag=".s:netrwdrag." buf#".bufnr("%")) +endfun + +" --------------------------------------------------------------------- +" s:NetrwSLeftrelease: terminates shift-leftmouse dragging {{{2 +fun! s:NetrwSLeftrelease(islocal) + " call Dfunc("s:NetrwSLeftrelease(islocal=".a:islocal.") s:netrwdrag=".s:netrwdrag." buf#".bufnr("%")) + if exists("s:netrwdrag") + nunmap <s-leftrelease> + let ngw = s:NetrwGetWord() + if !exists("s:ngw") || s:ngw != ngw + call s:NetrwMarkFile(a:islocal,ngw) + endif + if exists("s:ngw") + unlet s:ngw + endif + unlet s:netrwdrag + endif + " call Dret("s:NetrwSLeftrelease") +endfun + +" --------------------------------------------------------------------- +" s:NetrwListHide: uses [range]g~...~d to delete files that match {{{2 +" comma-separated patterns given in g:netrw_list_hide +fun! s:NetrwListHide() + " call Dfunc("s:NetrwListHide() g:netrw_hide=".g:netrw_hide." g:netrw_list_hide<".g:netrw_list_hide.">") + " call Decho("initial: ".string(getline(w:netrw_bannercnt,'$'))) + let ykeep= @@ + + " find a character not in the "hide" string to use as a separator for :g and :v commands + " How-it-works: take the hiding command, convert it into a range. + " Duplicate characters don't matter. + " Remove all such characters from the '/~@#...890' string. + " Use the first character left as a separator character. + " call Decho("find a character not in the hide string to use as a separator",'~'.expand("<slnum>")) + let listhide= g:netrw_list_hide + let sep = strpart(substitute('~@#$%^&*{};:,<.>?|1234567890','['.escape(listhide,'-]^\').']','','ge'),1,1) + " call Decho("sep<".sep."> (sep not in hide string)",'~'.expand("<slnum>")) + + while listhide != "" + if listhide =~ ',' + let hide = substitute(listhide,',.*$','','e') + let listhide = substitute(listhide,'^.\{-},\(.*\)$','\1','e') + else + let hide = listhide + let listhide = "" + endif + " call Decho("..extracted pattern from listhide: hide<".hide."> g:netrw_sort_by<".g:netrw_sort_by.'>','~'.expand("<slnum>")) + if g:netrw_sort_by =~ '^[ts]' + if hide =~ '^\^' + " call Decho("..modify hide to handle a \"^...\" pattern",'~'.expand("<slnum>")) + let hide= substitute(hide,'^\^','^\(\\d\\+/\)','') + elseif hide =~ '^\\(\^' + let hide= substitute(hide,'^\\(\^','\\(^\\(\\d\\+/\\)','') + endif + " call Decho("..hide<".hide."> listhide<".listhide.'>','~'.expand("<slnum>")) + endif + + " Prune the list by hiding any files which match + " call Decho("..prune the list by hiding any files which ".((g:netrw_hide == 1)? "" : "don't")."match hide<".hide.">") + if g:netrw_hide == 1 + " call Decho("..hiding<".hide.">",'~'.expand("<slnum>")) + exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$g'.sep.hide.sep.'d' + elseif g:netrw_hide == 2 + " call Decho("..showing<".hide.">",'~'.expand("<slnum>")) + exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$g'.sep.hide.sep.'s@^@ /-KEEP-/ @' + endif + " call Decho("..result: ".string(getline(w:netrw_bannercnt,'$')),'~'.expand("<slnum>")) + endwhile + + if g:netrw_hide == 2 + exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$v@^ /-KEEP-/ @d' + " call Decho("..v KEEP: ".string(getline(w:netrw_bannercnt,'$')),'~'.expand("<slnum>")) + exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s@^\%( /-KEEP-/ \)\+@@e' + " call Decho("..g KEEP: ".string(getline(w:netrw_bannercnt,'$')),'~'.expand("<slnum>")) + endif + + " remove any blank lines that have somehow remained. + " This seems to happen under Windows. + exe 'sil! NetrwKeepj 1,$g@^\s*$@d' + + let @@= ykeep + " call Dret("s:NetrwListHide") +endfun + +" --------------------------------------------------------------------- +" s:NetrwMakeDir: this function makes a directory (both local and remote) {{{2 +" implements the "d" mapping. +fun! s:NetrwMakeDir(usrhost) + + let ykeep= @@ + " get name of new directory from user. A bare <CR> will skip. + " if its currently a directory, also request will be skipped, but with + " a message. + call inputsave() + let newdirname= input("Please give directory name: ") + call inputrestore() + + if newdirname == "" + let @@= ykeep + return + endif + + if a:usrhost == "" + + " Local mkdir: + " sanity checks + let fullnewdir= b:netrw_curdir.'/'.newdirname + if isdirectory(s:NetrwFile(fullnewdir)) + if !exists("g:netrw_quiet") + NetrwKeepj call netrw#ErrorMsg(s:WARNING,"<".newdirname."> is already a directory!",24) + endif + let @@= ykeep + return + endif + if s:FileReadable(fullnewdir) + if !exists("g:netrw_quiet") + NetrwKeepj call netrw#ErrorMsg(s:WARNING,"<".newdirname."> is already a file!",25) + endif + let @@= ykeep + return + endif + + " requested new local directory is neither a pre-existing file or + " directory, so make it! + if exists("*mkdir") + if has("unix") + call mkdir(fullnewdir,"p",xor(0777, system("umask"))) + else + call mkdir(fullnewdir,"p") + endif + else + let netrw_origdir= s:NetrwGetcwd(1) + if s:NetrwLcd(b:netrw_curdir) + return + endif + call s:NetrwExe("sil! !".g:netrw_localmkdir.g:netrw_localmkdiropt.' '.s:ShellEscape(newdirname,1)) + if v:shell_error != 0 + let @@= ykeep + call netrw#ErrorMsg(s:ERROR,"consider setting g:netrw_localmkdir<".g:netrw_localmkdir."> to something that works",80) + return + endif + if !g:netrw_keepdir + if s:NetrwLcd(netrw_origdir) + return + endif + endif + endif + + if v:shell_error == 0 + " refresh listing + let svpos= winsaveview() + call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0)) + call winrestview(svpos) + elseif !exists("g:netrw_quiet") + call netrw#ErrorMsg(s:ERROR,"unable to make directory<".newdirname.">",26) + endif + + elseif !exists("b:netrw_method") || b:netrw_method == 4 + " Remote mkdir: using ssh + let mkdircmd = s:MakeSshCmd(g:netrw_mkdir_cmd) + let newdirname= substitute(b:netrw_curdir,'^\%(.\{-}/\)\{3}\(.*\)$','\1','').newdirname + call s:NetrwExe("sil! !".mkdircmd." ".s:ShellEscape(newdirname,1)) + if v:shell_error == 0 + " refresh listing + let svpos= winsaveview() + NetrwKeepj call s:NetrwRefresh(0,s:NetrwBrowseChgDir(0,'./',0)) + NetrwKeepj call winrestview(svpos) + elseif !exists("g:netrw_quiet") + NetrwKeepj call netrw#ErrorMsg(s:ERROR,"unable to make directory<".newdirname.">",27) + endif + + elseif b:netrw_method == 2 + " Remote mkdir: using ftp+.netrc + let svpos= winsaveview() + if exists("b:netrw_fname") + let remotepath= b:netrw_fname + else + let remotepath= "" + endif + call s:NetrwRemoteFtpCmd(remotepath,g:netrw_remote_mkdir.' "'.newdirname.'"') + NetrwKeepj call s:NetrwRefresh(0,s:NetrwBrowseChgDir(0,'./',0)) + NetrwKeepj call winrestview(svpos) + + elseif b:netrw_method == 3 + " Remote mkdir: using ftp + machine, id, passwd, and fname (ie. no .netrc) + let svpos= winsaveview() + if exists("b:netrw_fname") + let remotepath= b:netrw_fname + else + let remotepath= "" + endif + call s:NetrwRemoteFtpCmd(remotepath,g:netrw_remote_mkdir.' "'.newdirname.'"') + NetrwKeepj call s:NetrwRefresh(0,s:NetrwBrowseChgDir(0,'./',0)) + NetrwKeepj call winrestview(svpos) + endif + + let @@= ykeep +endfun + +" --------------------------------------------------------------------- +" s:TreeSqueezeDir: allows a shift-cr (gvim only) to squeeze the current tree-listing directory {{{2 +fun! s:TreeSqueezeDir(islocal) + if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict") + " its a tree-listing style + let curdepth = substitute(getline('.'),'^\(\%('.s:treedepthstring.'\)*\)[^'.s:treedepthstring.'].\{-}$','\1','e') + let stopline = (exists("w:netrw_bannercnt")? (w:netrw_bannercnt + 1) : 1) + let depth = strchars(substitute(curdepth,' ','','g')) + let srch = -1 + if depth >= 2 + NetrwKeepj norm! 0 + let curdepthm1= substitute(curdepth,'^'.s:treedepthstring,'','') + let srch = search('^'.curdepthm1.'\%('.s:treedepthstring.'\)\@!','bW',stopline) + elseif depth == 1 + NetrwKeepj norm! 0 + let treedepthchr= substitute(s:treedepthstring,' ','','') + let srch = search('^[^'.treedepthchr.']','bW',stopline) + endif + if srch > 0 + call s:NetrwBrowse(a:islocal,s:NetrwBrowseChgDir(a:islocal,s:NetrwGetWord(),1)) + exe srch + endif + endif +endfun + +" --------------------------------------------------------------------- +" s:NetrwMaps: {{{2 +fun! s:NetrwMaps(islocal) + + " mouse <Plug> maps: {{{3 + if g:netrw_mousemaps && g:netrw_retmap + " call Decho("set up Rexplore 2-leftmouse",'~'.expand("<slnum>")) + if !hasmapto("<Plug>NetrwReturn") + if maparg("<2-leftmouse>","n") == "" || maparg("<2-leftmouse>","n") =~ '^-$' + nmap <unique> <silent> <2-leftmouse> <Plug>NetrwReturn + elseif maparg("<c-leftmouse>","n") == "" + nmap <unique> <silent> <c-leftmouse> <Plug>NetrwReturn + endif + endif + nno <silent> <Plug>NetrwReturn :Rexplore<cr> + endif + + " generate default <Plug> maps {{{3 + if !hasmapto('<Plug>NetrwHide') |nmap <buffer> <silent> <nowait> a <Plug>NetrwHide_a|endif + if !hasmapto('<Plug>NetrwBrowseUpDir') |nmap <buffer> <silent> <nowait> - <Plug>NetrwBrowseUpDir|endif + if !hasmapto('<Plug>NetrwOpenFile') |nmap <buffer> <silent> <nowait> % <Plug>NetrwOpenFile|endif + if !hasmapto('<Plug>NetrwBadd_cb') |nmap <buffer> <silent> <nowait> cb <Plug>NetrwBadd_cb|endif + if !hasmapto('<Plug>NetrwBadd_cB') |nmap <buffer> <silent> <nowait> cB <Plug>NetrwBadd_cB|endif + if !hasmapto('<Plug>NetrwLcd') |nmap <buffer> <silent> <nowait> cd <Plug>NetrwLcd|endif + if !hasmapto('<Plug>NetrwSetChgwin') |nmap <buffer> <silent> <nowait> C <Plug>NetrwSetChgwin|endif + if !hasmapto('<Plug>NetrwRefresh') |nmap <buffer> <silent> <nowait> <c-l> <Plug>NetrwRefresh|endif + if !hasmapto('<Plug>NetrwLocalBrowseCheck') |nmap <buffer> <silent> <nowait> <cr> <Plug>NetrwLocalBrowseCheck|endif + if !hasmapto('<Plug>NetrwServerEdit') |nmap <buffer> <silent> <nowait> <c-r> <Plug>NetrwServerEdit|endif + if !hasmapto('<Plug>NetrwMakeDir') |nmap <buffer> <silent> <nowait> d <Plug>NetrwMakeDir|endif + if !hasmapto('<Plug>NetrwBookHistHandler_gb')|nmap <buffer> <silent> <nowait> gb <Plug>NetrwBookHistHandler_gb|endif + + if a:islocal + " local normal-mode maps {{{3 + nnoremap <buffer> <silent> <Plug>NetrwHide_a :<c-u>call <SID>NetrwHide(1)<cr> + nnoremap <buffer> <silent> <Plug>NetrwBrowseUpDir :<c-u>call <SID>NetrwBrowseUpDir(1)<cr> + nnoremap <buffer> <silent> <Plug>NetrwOpenFile :<c-u>call <SID>NetrwOpenFile(1)<cr> + nnoremap <buffer> <silent> <Plug>NetrwBadd_cb :<c-u>call <SID>NetrwBadd(1,0)<cr> + nnoremap <buffer> <silent> <Plug>NetrwBadd_cB :<c-u>call <SID>NetrwBadd(1,1)<cr> + nnoremap <buffer> <silent> <Plug>NetrwLcd :<c-u>call <SID>NetrwLcd(b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <Plug>NetrwSetChgwin :<c-u>call <SID>NetrwSetChgwin()<cr> + nnoremap <buffer> <silent> <Plug>NetrwLocalBrowseCheck :<c-u>call netrw#LocalBrowseCheck(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),1))<cr> + nnoremap <buffer> <silent> <Plug>NetrwServerEdit :<c-u>call <SID>NetrwServerEdit(3,<SID>NetrwGetWord())<cr> + nnoremap <buffer> <silent> <Plug>NetrwMakeDir :<c-u>call <SID>NetrwMakeDir("")<cr> + nnoremap <buffer> <silent> <Plug>NetrwBookHistHandler_gb :<c-u>call <SID>NetrwBookHistHandler(1,b:netrw_curdir)<cr> + " --------------------------------------------------------------------- + nnoremap <buffer> <silent> <nowait> gd :<c-u>call <SID>NetrwForceChgDir(1,<SID>NetrwGetWord())<cr> + nnoremap <buffer> <silent> <nowait> gf :<c-u>call <SID>NetrwForceFile(1,<SID>NetrwGetWord())<cr> + nnoremap <buffer> <silent> <nowait> gh :<c-u>call <SID>NetrwHidden(1)<cr> + nnoremap <buffer> <silent> <nowait> gn :<c-u>call netrw#SetTreetop(0,<SID>NetrwGetWord())<cr> + nnoremap <buffer> <silent> <nowait> gp :<c-u>call <SID>NetrwChgPerm(1,b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> I :<c-u>call <SID>NetrwBannerCtrl(1)<cr> + nnoremap <buffer> <silent> <nowait> i :<c-u>call <SID>NetrwListStyle(1)<cr> + nnoremap <buffer> <silent> <nowait> ma :<c-u>call <SID>NetrwMarkFileArgList(1,0)<cr> + nnoremap <buffer> <silent> <nowait> mA :<c-u>call <SID>NetrwMarkFileArgList(1,1)<cr> + nnoremap <buffer> <silent> <nowait> mb :<c-u>call <SID>NetrwBookHistHandler(0,b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> mB :<c-u>call <SID>NetrwBookHistHandler(6,b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> mc :<c-u>call <SID>NetrwMarkFileCopy(1)<cr> + nnoremap <buffer> <silent> <nowait> md :<c-u>call <SID>NetrwMarkFileDiff(1)<cr> + nnoremap <buffer> <silent> <nowait> me :<c-u>call <SID>NetrwMarkFileEdit(1)<cr> + nnoremap <buffer> <silent> <nowait> mf :<c-u>call <SID>NetrwMarkFile(1,<SID>NetrwGetWord())<cr> + nnoremap <buffer> <silent> <nowait> mF :<c-u>call <SID>NetrwUnmarkList(bufnr("%"),b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> mg :<c-u>call <SID>NetrwMarkFileGrep(1)<cr> + nnoremap <buffer> <silent> <nowait> mh :<c-u>call <SID>NetrwMarkHideSfx(1)<cr> + nnoremap <buffer> <silent> <nowait> mm :<c-u>call <SID>NetrwMarkFileMove(1)<cr> + " nnoremap <buffer> <silent> <nowait> mp :<c-u>call <SID>NetrwMarkFilePrint(1)<cr> + nnoremap <buffer> <silent> <nowait> mr :<c-u>call <SID>NetrwMarkFileRegexp(1)<cr> + nnoremap <buffer> <silent> <nowait> ms :<c-u>call <SID>NetrwMarkFileSource(1)<cr> + nnoremap <buffer> <silent> <nowait> mT :<c-u>call <SID>NetrwMarkFileTag(1)<cr> + nnoremap <buffer> <silent> <nowait> mt :<c-u>call <SID>NetrwMarkFileTgt(1)<cr> + nnoremap <buffer> <silent> <nowait> mu :<c-u>call <SID>NetrwUnMarkFile(1)<cr> + nnoremap <buffer> <silent> <nowait> mv :<c-u>call <SID>NetrwMarkFileVimCmd(1)<cr> + nnoremap <buffer> <silent> <nowait> mx :<c-u>call <SID>NetrwMarkFileExe(1,0)<cr> + nnoremap <buffer> <silent> <nowait> mX :<c-u>call <SID>NetrwMarkFileExe(1,1)<cr> + nnoremap <buffer> <silent> <nowait> mz :<c-u>call <SID>NetrwMarkFileCompress(1)<cr> + nnoremap <buffer> <silent> <nowait> O :<c-u>call <SID>NetrwObtain(1)<cr> + nnoremap <buffer> <silent> <nowait> o :call <SID>NetrwSplit(3)<cr> + nnoremap <buffer> <silent> <nowait> p :<c-u>call <SID>NetrwPreview(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),1,1))<cr> + nnoremap <buffer> <silent> <nowait> P :<c-u>call <SID>NetrwPrevWinOpen(1)<cr> + nnoremap <buffer> <silent> <nowait> qb :<c-u>call <SID>NetrwBookHistHandler(2,b:netrw_curdir)<cr> + 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> 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> + nnoremap <buffer> <silent> <nowait> t :call <SID>NetrwSplit(4)<cr> + nnoremap <buffer> <silent> <nowait> Th :<c-u>call <SID>NetrwSetTgt(1,'h',v:count)<cr> + nnoremap <buffer> <silent> <nowait> u :<c-u>call <SID>NetrwBookHistHandler(4,expand("%"))<cr> + nnoremap <buffer> <silent> <nowait> U :<c-u>call <SID>NetrwBookHistHandler(5,expand("%"))<cr> + nnoremap <buffer> <silent> <nowait> v :call <SID>NetrwSplit(5)<cr> + nnoremap <buffer> <silent> <nowait> x :<c-u>call netrw#BrowseX(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),1,0),0)"<cr> + nnoremap <buffer> <silent> <nowait> X :<c-u>call <SID>NetrwLocalExecute(expand("<cword>"))"<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,'./',0))<cr> + if !hasmapto('<Plug>NetrwHideEdit') + nmap <buffer> <unique> <c-h> <Plug>NetrwHideEdit + endif + nnoremap <buffer> <silent> <Plug>NetrwHideEdit :call <SID>NetrwHideEdit(1)<cr> + if !hasmapto('<Plug>NetrwRefresh') + nmap <buffer> <unique> <c-l> <Plug>NetrwRefresh + endif + nnoremap <buffer> <silent> <Plug>NetrwRefresh <c-l>:call <SID>NetrwRefresh(1,<SID>NetrwBrowseChgDir(1,(exists("w:netrw_liststyle") && exists("w:netrw_treetop") && w:netrw_liststyle == 3)? w:netrw_treetop : './',0))<cr> + if s:didstarstar || !mapcheck("<s-down>","n") + nnoremap <buffer> <silent> <s-down> :Nexplore<cr> + endif + if s:didstarstar || !mapcheck("<s-up>","n") + nnoremap <buffer> <silent> <s-up> :Pexplore<cr> + endif + if !hasmapto('<Plug>NetrwTreeSqueeze') + nmap <buffer> <silent> <nowait> <s-cr> <Plug>NetrwTreeSqueeze + endif + nnoremap <buffer> <silent> <Plug>NetrwTreeSqueeze :call <SID>TreeSqueezeDir(1)<cr> + let mapsafecurdir = escape(b:netrw_curdir, s:netrw_map_escape) + if g:netrw_mousemaps == 1 + nmap <buffer> <leftmouse> <Plug>NetrwLeftmouse + nmap <buffer> <c-leftmouse> <Plug>NetrwCLeftmouse + nmap <buffer> <middlemouse> <Plug>NetrwMiddlemouse + nmap <buffer> <s-leftmouse> <Plug>NetrwSLeftmouse + nmap <buffer> <s-leftdrag> <Plug>NetrwSLeftdrag + nmap <buffer> <2-leftmouse> <Plug>Netrw2Leftmouse + imap <buffer> <leftmouse> <Plug>ILeftmouse + imap <buffer> <middlemouse> <Plug>IMiddlemouse + nno <buffer> <silent> <Plug>NetrwLeftmouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwLeftmouse(1)<cr> + nno <buffer> <silent> <Plug>NetrwCLeftmouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwCLeftmouse(1)<cr> + nno <buffer> <silent> <Plug>NetrwMiddlemouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwPrevWinOpen(1)<cr> + nno <buffer> <silent> <Plug>NetrwSLeftmouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwSLeftmouse(1)<cr> + nno <buffer> <silent> <Plug>NetrwSLeftdrag :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwSLeftdrag(1)<cr> + nmap <buffer> <silent> <Plug>Netrw2Leftmouse - + exe 'nnoremap <buffer> <silent> <rightmouse> :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' + exe 'vnoremap <buffer> <silent> <rightmouse> :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' + endif + exe 'nnoremap <buffer> <silent> <nowait> <del> :call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' + exe 'nnoremap <buffer> <silent> <nowait> D :call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' + exe 'nnoremap <buffer> <silent> <nowait> R :call <SID>NetrwLocalRename("'.mapsafecurdir.'")<cr>' + exe 'nnoremap <buffer> <silent> <nowait> d :call <SID>NetrwMakeDir("")<cr>' + exe 'vnoremap <buffer> <silent> <nowait> <del> :call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' + exe 'vnoremap <buffer> <silent> <nowait> D :call <SID>NetrwLocalRm("'.mapsafecurdir.'")<cr>' + exe 'vnoremap <buffer> <silent> <nowait> R :call <SID>NetrwLocalRename("'.mapsafecurdir.'")<cr>' + nnoremap <buffer> <F1> :he netrw-quickhelp<cr> + + " support user-specified maps + call netrw#UserMaps(1) + + else + " remote normal-mode maps {{{3 + call s:RemotePathAnalysis(b:netrw_curdir) + nnoremap <buffer> <silent> <Plug>NetrwHide_a :<c-u>call <SID>NetrwHide(0)<cr> + nnoremap <buffer> <silent> <Plug>NetrwBrowseUpDir :<c-u>call <SID>NetrwBrowseUpDir(0)<cr> + nnoremap <buffer> <silent> <Plug>NetrwOpenFile :<c-u>call <SID>NetrwOpenFile(0)<cr> + nnoremap <buffer> <silent> <Plug>NetrwBadd_cb :<c-u>call <SID>NetrwBadd(0,0)<cr> + nnoremap <buffer> <silent> <Plug>NetrwBadd_cB :<c-u>call <SID>NetrwBadd(0,1)<cr> + nnoremap <buffer> <silent> <Plug>NetrwLcd :<c-u>call <SID>NetrwLcd(b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <Plug>NetrwSetChgwin :<c-u>call <SID>NetrwSetChgwin()<cr> + nnoremap <buffer> <silent> <Plug>NetrwRefresh :<c-u>call <SID>NetrwRefresh(0,<SID>NetrwBrowseChgDir(0,'./',0))<cr> + nnoremap <buffer> <silent> <Plug>NetrwLocalBrowseCheck :<c-u>call <SID>NetrwBrowse(0,<SID>NetrwBrowseChgDir(0,<SID>NetrwGetWord(),1))<cr> + nnoremap <buffer> <silent> <Plug>NetrwServerEdit :<c-u>call <SID>NetrwServerEdit(2,<SID>NetrwGetWord())<cr> + nnoremap <buffer> <silent> <Plug>NetrwBookHistHandler_gb :<c-u>call <SID>NetrwBookHistHandler(1,b:netrw_curdir)<cr> + " --------------------------------------------------------------------- + nnoremap <buffer> <silent> <nowait> gd :<c-u>call <SID>NetrwForceChgDir(0,<SID>NetrwGetWord())<cr> + nnoremap <buffer> <silent> <nowait> gf :<c-u>call <SID>NetrwForceFile(0,<SID>NetrwGetWord())<cr> + nnoremap <buffer> <silent> <nowait> gh :<c-u>call <SID>NetrwHidden(0)<cr> + nnoremap <buffer> <silent> <nowait> gp :<c-u>call <SID>NetrwChgPerm(0,b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> I :<c-u>call <SID>NetrwBannerCtrl(1)<cr> + nnoremap <buffer> <silent> <nowait> i :<c-u>call <SID>NetrwListStyle(0)<cr> + nnoremap <buffer> <silent> <nowait> ma :<c-u>call <SID>NetrwMarkFileArgList(0,0)<cr> + nnoremap <buffer> <silent> <nowait> mA :<c-u>call <SID>NetrwMarkFileArgList(0,1)<cr> + nnoremap <buffer> <silent> <nowait> mb :<c-u>call <SID>NetrwBookHistHandler(0,b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> mB :<c-u>call <SID>NetrwBookHistHandler(6,b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> mc :<c-u>call <SID>NetrwMarkFileCopy(0)<cr> + nnoremap <buffer> <silent> <nowait> md :<c-u>call <SID>NetrwMarkFileDiff(0)<cr> + nnoremap <buffer> <silent> <nowait> me :<c-u>call <SID>NetrwMarkFileEdit(0)<cr> + nnoremap <buffer> <silent> <nowait> mf :<c-u>call <SID>NetrwMarkFile(0,<SID>NetrwGetWord())<cr> + nnoremap <buffer> <silent> <nowait> mF :<c-u>call <SID>NetrwUnmarkList(bufnr("%"),b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> mg :<c-u>call <SID>NetrwMarkFileGrep(0)<cr> + nnoremap <buffer> <silent> <nowait> mh :<c-u>call <SID>NetrwMarkHideSfx(0)<cr> + nnoremap <buffer> <silent> <nowait> mm :<c-u>call <SID>NetrwMarkFileMove(0)<cr> + " nnoremap <buffer> <silent> <nowait> mp :<c-u>call <SID>NetrwMarkFilePrint(0)<cr> + nnoremap <buffer> <silent> <nowait> mr :<c-u>call <SID>NetrwMarkFileRegexp(0)<cr> + nnoremap <buffer> <silent> <nowait> ms :<c-u>call <SID>NetrwMarkFileSource(0)<cr> + nnoremap <buffer> <silent> <nowait> mT :<c-u>call <SID>NetrwMarkFileTag(0)<cr> + nnoremap <buffer> <silent> <nowait> mt :<c-u>call <SID>NetrwMarkFileTgt(0)<cr> + nnoremap <buffer> <silent> <nowait> mu :<c-u>call <SID>NetrwUnMarkFile(0)<cr> + nnoremap <buffer> <silent> <nowait> mv :<c-u>call <SID>NetrwMarkFileVimCmd(0)<cr> + nnoremap <buffer> <silent> <nowait> mx :<c-u>call <SID>NetrwMarkFileExe(0,0)<cr> + nnoremap <buffer> <silent> <nowait> mX :<c-u>call <SID>NetrwMarkFileExe(0,1)<cr> + nnoremap <buffer> <silent> <nowait> mz :<c-u>call <SID>NetrwMarkFileCompress(0)<cr> + nnoremap <buffer> <silent> <nowait> O :<c-u>call <SID>NetrwObtain(0)<cr> + nnoremap <buffer> <silent> <nowait> o :call <SID>NetrwSplit(0)<cr> + nnoremap <buffer> <silent> <nowait> p :<c-u>call <SID>NetrwPreview(<SID>NetrwBrowseChgDir(1,<SID>NetrwGetWord(),1,1))<cr> + nnoremap <buffer> <silent> <nowait> P :<c-u>call <SID>NetrwPrevWinOpen(0)<cr> + nnoremap <buffer> <silent> <nowait> qb :<c-u>call <SID>NetrwBookHistHandler(2,b:netrw_curdir)<cr> + 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,'./',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> + nnoremap <buffer> <silent> <nowait> t :call <SID>NetrwSplit(1)<cr> + nnoremap <buffer> <silent> <nowait> Th :<c-u>call <SID>NetrwSetTgt(0,'h',v:count)<cr> + nnoremap <buffer> <silent> <nowait> u :<c-u>call <SID>NetrwBookHistHandler(4,b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> U :<c-u>call <SID>NetrwBookHistHandler(5,b:netrw_curdir)<cr> + nnoremap <buffer> <silent> <nowait> v :call <SID>NetrwSplit(2)<cr> + nnoremap <buffer> <silent> <nowait> x :<c-u>call netrw#BrowseX(<SID>NetrwBrowseChgDir(0,<SID>NetrwGetWord(),1),1)<cr> + nmap <buffer> <nowait> gx x + if !hasmapto('<Plug>NetrwHideEdit') + nmap <buffer> <c-h> <Plug>NetrwHideEdit + endif + nnoremap <buffer> <silent> <Plug>NetrwHideEdit :call <SID>NetrwHideEdit(0)<cr> + if !hasmapto('<Plug>NetrwRefresh') + nmap <buffer> <c-l> <Plug>NetrwRefresh + endif + if !hasmapto('<Plug>NetrwTreeSqueeze') + nmap <buffer> <silent> <nowait> <s-cr> <Plug>NetrwTreeSqueeze + endif + nnoremap <buffer> <silent> <Plug>NetrwTreeSqueeze :call <SID>TreeSqueezeDir(0)<cr> + + let mapsafepath = escape(s:path, s:netrw_map_escape) + let mapsafeusermach = escape(((s:user == "")? "" : s:user."@").s:machine, s:netrw_map_escape) + + nnoremap <buffer> <silent> <Plug>NetrwRefresh :call <SID>NetrwRefresh(0,<SID>NetrwBrowseChgDir(0,'./',0))<cr> + if g:netrw_mousemaps == 1 + nmap <buffer> <leftmouse> <Plug>NetrwLeftmouse + nno <buffer> <silent> <Plug>NetrwLeftmouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwLeftmouse(0)<cr> + nmap <buffer> <c-leftmouse> <Plug>NetrwCLeftmouse + nno <buffer> <silent> <Plug>NetrwCLeftmouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwCLeftmouse(0)<cr> + nmap <buffer> <s-leftmouse> <Plug>NetrwSLeftmouse + nno <buffer> <silent> <Plug>NetrwSLeftmouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwSLeftmouse(0)<cr> + nmap <buffer> <s-leftdrag> <Plug>NetrwSLeftdrag + nno <buffer> <silent> <Plug>NetrwSLeftdrag :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwSLeftdrag(0)<cr> + nmap <middlemouse> <Plug>NetrwMiddlemouse + nno <buffer> <silent> <middlemouse> <Plug>NetrwMiddlemouse :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwPrevWinOpen(0)<cr> + nmap <buffer> <2-leftmouse> <Plug>Netrw2Leftmouse + nmap <buffer> <silent> <Plug>Netrw2Leftmouse - + imap <buffer> <leftmouse> <Plug>ILeftmouse + imap <buffer> <middlemouse> <Plug>IMiddlemouse + imap <buffer> <s-leftmouse> <Plug>ISLeftmouse + exe 'nnoremap <buffer> <silent> <rightmouse> :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' + exe 'vnoremap <buffer> <silent> <rightmouse> :exec "norm! \<lt>leftmouse>"<bar>call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' + endif + exe 'nnoremap <buffer> <silent> <nowait> <del> :call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' + exe 'nnoremap <buffer> <silent> <nowait> d :call <SID>NetrwMakeDir("'.mapsafeusermach.'")<cr>' + exe 'nnoremap <buffer> <silent> <nowait> D :call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' + exe 'nnoremap <buffer> <silent> <nowait> R :call <SID>NetrwRemoteRename("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' + exe 'vnoremap <buffer> <silent> <nowait> <del> :call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' + exe 'vnoremap <buffer> <silent> <nowait> D :call <SID>NetrwRemoteRm("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' + exe 'vnoremap <buffer> <silent> <nowait> R :call <SID>NetrwRemoteRename("'.mapsafeusermach.'","'.mapsafepath.'")<cr>' + nnoremap <buffer> <F1> :he netrw-quickhelp<cr> + + " support user-specified maps + call netrw#UserMaps(0) + endif " }}}3 +endfun + +" --------------------------------------------------------------------- +" s:NetrwCommands: set up commands {{{2 +" If -buffer, the command is only available from within netrw buffers +" Otherwise, the command is available from any window, so long as netrw +" has been used at least once in the session. +fun! s:NetrwCommands(islocal) + " call Dfunc("s:NetrwCommands(islocal=".a:islocal.")") + + com! -nargs=* -complete=file -bang NetrwMB call s:NetrwBookmark(<bang>0,<f-args>) + com! -nargs=* NetrwC call s:NetrwSetChgwin(<q-args>) + com! Rexplore if exists("w:netrw_rexlocal")|call s:NetrwRexplore(w:netrw_rexlocal,exists("w:netrw_rexdir")? w:netrw_rexdir : ".")|else|call netrw#ErrorMsg(s:WARNING,"win#".winnr()." not a former netrw window",79)|endif + if a:islocal + com! -buffer -nargs=+ -complete=file MF call s:NetrwMarkFiles(1,<f-args>) + else + com! -buffer -nargs=+ -complete=file MF call s:NetrwMarkFiles(0,<f-args>) + endif + com! -buffer -nargs=? -complete=file MT call s:NetrwMarkTarget(<q-args>) + + " call Dret("s:NetrwCommands") +endfun + +" --------------------------------------------------------------------- +" s:NetrwMarkFiles: apply s:NetrwMarkFile() to named file(s) {{{2 +" glob()ing only works with local files +fun! s:NetrwMarkFiles(islocal,...) + " call Dfunc("s:NetrwMarkFiles(islocal=".a:islocal."...) a:0=".a:0) + let curdir = s:NetrwGetCurdir(a:islocal) + let i = 1 + while i <= a:0 + if a:islocal + if v:version > 704 || (v:version == 704 && has("patch656")) + let mffiles= glob(a:{i},0,1,1) + else + let mffiles= glob(a:{i},0,1) + endif + else + let mffiles= [a:{i}] + endif + " call Decho("mffiles".string(mffiles),'~'.expand("<slnum>")) + for mffile in mffiles + " call Decho("mffile<".mffile.">",'~'.expand("<slnum>")) + call s:NetrwMarkFile(a:islocal,mffile) + endfor + let i= i + 1 + endwhile + " call Dret("s:NetrwMarkFiles") +endfun + +" --------------------------------------------------------------------- +" s:NetrwMarkTarget: implements :MT (mark target) {{{2 +fun! s:NetrwMarkTarget(...) + if a:0 == 0 || (a:0 == 1 && a:1 == "") + let curdir = s:NetrwGetCurdir(1) + let tgt = b:netrw_curdir + else + let curdir = s:NetrwGetCurdir((a:1 =~ '^\a\{3,}://')? 0 : 1) + let tgt = a:1 + endif + let s:netrwmftgt = tgt + let s:netrwmftgt_islocal = tgt !~ '^\a\{3,}://' + let curislocal = b:netrw_curdir !~ '^\a\{3,}://' + let svpos = winsaveview() + call s:NetrwRefresh(curislocal,s:NetrwBrowseChgDir(curislocal,'./',0)) + call winrestview(svpos) +endfun + +" --------------------------------------------------------------------- +" s:NetrwMarkFile: (invoked by mf) This function is used to both {{{2 +" mark and unmark files. If a markfile list exists, +" then the rename and delete functions will use it instead +" of whatever may happen to be under the cursor at that +" moment. When the mouse and gui are available, +" shift-leftmouse may also be used to mark files. +" +" Creates two lists +" s:netrwmarkfilelist -- holds complete paths to all marked files +" s:netrwmarkfilelist_# -- holds list of marked files in current-buffer's directory (#==bufnr()) +" +" Creates a marked file match string +" s:netrwmarfilemtch_# -- used with 2match to display marked files +" +" Creates a buffer version of islocal +" b:netrw_islocal +fun! s:NetrwMarkFile(islocal,fname) + " call Dfunc("s:NetrwMarkFile(islocal=".a:islocal." fname<".a:fname.">)") + " call Decho("bufnr(%)=".bufnr("%").": ".bufname("%"),'~'.expand("<slnum>")) + + " sanity check + if empty(a:fname) + " call Dret("s:NetrwMarkFile : empty fname") + return + endif + let curdir = s:NetrwGetCurdir(a:islocal) + + let ykeep = @@ + let curbufnr= bufnr("%") + let leader= '\%(^\|\s\)\zs' + if a:fname =~ '\a$' + let trailer = '\>[@=|\/\*]\=\ze\%( \|\t\|$\)' + else + let trailer = '[@=|\/\*]\=\ze\%( \|\t\|$\)' + endif + + if exists("s:netrwmarkfilelist_".curbufnr) + " markfile list pre-exists + " call Decho("case s:netrwmarkfilelist_".curbufnr." already exists",'~'.expand("<slnum>")) + " call Decho("starting s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>")) + " call Decho("starting s:netrwmarkfilemtch_".curbufnr."<".s:netrwmarkfilemtch_{curbufnr}.">",'~'.expand("<slnum>")) + let b:netrw_islocal= a:islocal + + if index(s:netrwmarkfilelist_{curbufnr},a:fname) == -1 + " append filename to buffer's markfilelist + " call Decho("append filename<".a:fname."> to local markfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>")) + call add(s:netrwmarkfilelist_{curbufnr},a:fname) + let s:netrwmarkfilemtch_{curbufnr}= s:netrwmarkfilemtch_{curbufnr}.'\|'.leader.escape(a:fname,g:netrw_markfileesc).trailer + + else + " remove filename from buffer's markfilelist + " call Decho("remove filename<".a:fname."> from local markfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>")) + call filter(s:netrwmarkfilelist_{curbufnr},'v:val != a:fname') + if s:netrwmarkfilelist_{curbufnr} == [] + " local markfilelist is empty; remove it entirely + " call Decho("markfile list now empty",'~'.expand("<slnum>")) + call s:NetrwUnmarkList(curbufnr,curdir) + else + " rebuild match list to display markings correctly + " call Decho("rebuild s:netrwmarkfilemtch_".curbufnr,'~'.expand("<slnum>")) + let s:netrwmarkfilemtch_{curbufnr}= "" + let first = 1 + for fname in s:netrwmarkfilelist_{curbufnr} + if first + let s:netrwmarkfilemtch_{curbufnr}= s:netrwmarkfilemtch_{curbufnr}.leader.escape(fname,g:netrw_markfileesc).trailer + else + let s:netrwmarkfilemtch_{curbufnr}= s:netrwmarkfilemtch_{curbufnr}.'\|'.leader.escape(fname,g:netrw_markfileesc).trailer + endif + let first= 0 + endfor + " call Decho("ending s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>")) + endif + endif + + else + " initialize new markfilelist + " call Decho("case: initialize new markfilelist",'~'.expand("<slnum>")) + + " call Decho("add fname<".a:fname."> to new markfilelist_".curbufnr,'~'.expand("<slnum>")) + let s:netrwmarkfilelist_{curbufnr}= [] + call add(s:netrwmarkfilelist_{curbufnr},substitute(a:fname,'[|@]$','','')) + " call Decho("ending s:netrwmarkfilelist_{curbufnr}<".string(s:netrwmarkfilelist_{curbufnr}).">",'~'.expand("<slnum>")) + + " build initial markfile matching pattern + if a:fname =~ '/$' + let s:netrwmarkfilemtch_{curbufnr}= leader.escape(a:fname,g:netrw_markfileesc) + else + let s:netrwmarkfilemtch_{curbufnr}= leader.escape(a:fname,g:netrw_markfileesc).trailer + endif + " call Decho("ending s:netrwmarkfilemtch_".curbufnr."<".s:netrwmarkfilemtch_{curbufnr}.">",'~'.expand("<slnum>")) + endif + + " handle global markfilelist + if exists("s:netrwmarkfilelist") + let dname= s:ComposePath(b:netrw_curdir,a:fname) + if index(s:netrwmarkfilelist,dname) == -1 + " append new filename to global markfilelist + call add(s:netrwmarkfilelist,s:ComposePath(b:netrw_curdir,a:fname)) + " call Decho("append filename<".a:fname."> to global s:markfilelist<".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>")) + else + " remove new filename from global markfilelist + " call Decho("remove new filename from global s:markfilelist",'~'.expand("<slnum>")) + " call Decho("..filter(".string(s:netrwmarkfilelist).",'v:val != '.".dname.")",'~'.expand("<slnum>")) + call filter(s:netrwmarkfilelist,'v:val != "'.dname.'"') + " call Decho("..ending s:netrwmarkfilelist <".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>")) + if s:netrwmarkfilelist == [] + " call Decho("s:netrwmarkfilelist is empty; unlet it",'~'.expand("<slnum>")) + unlet s:netrwmarkfilelist + endif + endif + else + " initialize new global-directory markfilelist + let s:netrwmarkfilelist= [] + call add(s:netrwmarkfilelist,s:ComposePath(b:netrw_curdir,a:fname)) + " call Decho("init s:netrwmarkfilelist<".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>")) + endif + + " set up 2match'ing to netrwmarkfilemtch_# list + if has("syntax") && exists("g:syntax_on") && g:syntax_on + if exists("s:netrwmarkfilemtch_{curbufnr}") && s:netrwmarkfilemtch_{curbufnr} != "" + " " call Decho("exe 2match netrwMarkFile /".s:netrwmarkfilemtch_{curbufnr}."/",'~'.expand("<slnum>")) + if exists("g:did_drchip_netrwlist_syntax") + exe "2match netrwMarkFile /".s:netrwmarkfilemtch_{curbufnr}."/" + endif + else + " " call Decho("2match none",'~'.expand("<slnum>")) + 2match none + endif + endif + let @@= ykeep + " call Decho("s:netrwmarkfilelist[".(exists("s:netrwmarkfilelist")? string(s:netrwmarkfilelist) : "")."] (avail in all buffers)",'~'.expand("<slnum>")) + " call Dret("s:NetrwMarkFile : s:netrwmarkfilelist_".curbufnr."<".(exists("s:netrwmarkfilelist_{curbufnr}")? string(s:netrwmarkfilelist_{curbufnr}) : " doesn't exist")."> (buf#".curbufnr."list)") +endfun + +" --------------------------------------------------------------------- +" s:NetrwMarkFileArgList: ma: move the marked file list to the argument list (tomflist=0) {{{2 +" mA: move the argument list to marked file list (tomflist=1) +" Uses the global marked file list +fun! s:NetrwMarkFileArgList(islocal,tomflist) + let svpos = winsaveview() + let curdir = s:NetrwGetCurdir(a:islocal) + let curbufnr = bufnr("%") + + if a:tomflist + " mA: move argument list to marked file list + while argc() + let fname= argv(0) + exe "argdel ".fnameescape(fname) + call s:NetrwMarkFile(a:islocal,fname) + endwhile + + else + " ma: move marked file list to argument list + if exists("s:netrwmarkfilelist") + + " for every filename in the marked list + for fname in s:netrwmarkfilelist + exe "argadd ".fnameescape(fname) + endfor " for every file in the marked list + + " unmark list and refresh + call s:NetrwUnmarkList(curbufnr,curdir) + NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) + NetrwKeepj call winrestview(svpos) + endif + endif +endfun + +" --------------------------------------------------------------------- +" s:NetrwMarkFileCompress: (invoked by mz) This function is used to {{{2 +" compress/decompress files using the programs +" in g:netrw_compress and g:netrw_uncompress, +" using g:netrw_compress_suffix to know which to +" do. By default: +" g:netrw_compress = "gzip" +" g:netrw_decompress = { ".gz" : "gunzip" , ".bz2" : "bunzip2" , ".zip" : "unzip" , ".tar" : "tar -xf", ".xz" : "unxz"} +fun! s:NetrwMarkFileCompress(islocal) + let svpos = winsaveview() + let curdir = s:NetrwGetCurdir(a:islocal) + let curbufnr = bufnr("%") + + " sanity check + if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) + NetrwKeepj call netrw#ErrorMsg(2,"there are no marked files in this window (:help netrw-mf)",66) + return + endif + + if exists("s:netrwmarkfilelist_{curbufnr}") && exists("g:netrw_compress") && exists("g:netrw_decompress") + + " for every filename in the marked list + for fname in s:netrwmarkfilelist_{curbufnr} + let sfx= substitute(fname,'^.\{-}\(\.[[:alnum:]]\+\)$','\1','') + if exists("g:netrw_decompress['".sfx."']") + " fname has a suffix indicating that its compressed; apply associated decompression routine + let exe= g:netrw_decompress[sfx] + let exe= netrw#WinPath(exe) + if a:islocal + if g:netrw_keepdir + let fname= s:ShellEscape(s:ComposePath(curdir,fname)) + endif + call system(exe." ".fname) + if v:shell_error + NetrwKeepj call netrw#ErrorMsg(s:WARNING,"unable to apply<".exe."> to file<".fname.">",50) + endif + else + let fname= s:ShellEscape(b:netrw_curdir.fname,1) + NetrwKeepj call s:RemoteSystem(exe." ".fname) + endif + + endif + unlet sfx + + if exists("exe") + unlet exe + elseif a:islocal + " fname not a compressed file, so compress it + call system(netrw#WinPath(g:netrw_compress)." ".s:ShellEscape(s:ComposePath(b:netrw_curdir,fname))) + if v:shell_error + call netrw#ErrorMsg(s:WARNING,"consider setting g:netrw_compress<".g:netrw_compress."> to something that works",104) + endif + else + " fname not a compressed file, so compress it + NetrwKeepj call s:RemoteSystem(netrw#WinPath(g:netrw_compress)." ".s:ShellEscape(fname)) + endif + endfor " for every file in the marked list + + call s:NetrwUnmarkList(curbufnr,curdir) + NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) + NetrwKeepj call winrestview(svpos) + endif +endfun + +" --------------------------------------------------------------------- +" s:NetrwMarkFileCopy: (invoked by mc) copy marked files to target {{{2 +" If no marked files, then set up directory as the +" target. Currently does not support copying entire +" directories. Uses the local-buffer marked file list. +" Returns 1=success (used by NetrwMarkFileMove()) +" 0=failure +fun! s:NetrwMarkFileCopy(islocal,...) + " call Dfunc("s:NetrwMarkFileCopy(islocal=".a:islocal.") target<".(exists("s:netrwmftgt")? s:netrwmftgt : '---')."> a:0=".a:0) + + let curdir = s:NetrwGetCurdir(a:islocal) + let curbufnr = bufnr("%") + if b:netrw_curdir !~ '/$' + if !exists("b:netrw_curdir") + let b:netrw_curdir= curdir + endif + let b:netrw_curdir= b:netrw_curdir."/" + endif + + " sanity check + if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) + NetrwKeepj call netrw#ErrorMsg(2,"there are no marked files in this window (:help netrw-mf)",66) + " call Dret("s:NetrwMarkFileCopy") + return + endif + " call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>")) + + if !exists("s:netrwmftgt") + NetrwKeepj call netrw#ErrorMsg(s:ERROR,"your marked file target is empty! (:help netrw-mt)",67) + " call Dret("s:NetrwMarkFileCopy 0") + return 0 + endif + " call Decho("sanity chk passed: s:netrwmftgt<".s:netrwmftgt.">",'~'.expand("<slnum>")) + + if a:islocal && s:netrwmftgt_islocal + " Copy marked files, local directory to local directory + " call Decho("copy from local to local",'~'.expand("<slnum>")) + if !executable(g:netrw_localcopycmd) + call netrw#ErrorMsg(s:ERROR,"g:netrw_localcopycmd<".g:netrw_localcopycmd."> not executable on your system, aborting",91) + " call Dfunc("s:NetrwMarkFileMove : g:netrw_localcopycmd<".g:netrw_localcopycmd."> n/a!") + return + endif + + " copy marked files while within the same directory (ie. allow renaming) + if s:StripTrailingSlash(simplify(s:netrwmftgt)) == s:StripTrailingSlash(simplify(b:netrw_curdir)) + if len(s:netrwmarkfilelist_{bufnr('%')}) == 1 + " only one marked file + " call Decho("case: only one marked file",'~'.expand("<slnum>")) + let args = s:ShellEscape(b:netrw_curdir.s:netrwmarkfilelist_{bufnr('%')}[0]) + let oldname = s:netrwmarkfilelist_{bufnr('%')}[0] + elseif a:0 == 1 + " call Decho("case: handling one input argument",'~'.expand("<slnum>")) + " this happens when the next case was used to recursively call s:NetrwMarkFileCopy() + let args = s:ShellEscape(b:netrw_curdir.a:1) + let oldname = a:1 + else + " copy multiple marked files inside the same directory + " call Decho("case: handling a multiple marked files",'~'.expand("<slnum>")) + let s:recursive= 1 + for oldname in s:netrwmarkfilelist_{bufnr("%")} + let ret= s:NetrwMarkFileCopy(a:islocal,oldname) + if ret == 0 + break + endif + endfor + unlet s:recursive + call s:NetrwUnmarkList(curbufnr,curdir) + " call Dret("s:NetrwMarkFileCopy ".ret) + return ret + endif + + call inputsave() + let newname= input("Copy ".oldname." to : ",oldname,"file") + call inputrestore() + if newname == "" + " call Dret("s:NetrwMarkFileCopy 0") + return 0 + endif + let args= s:ShellEscape(oldname) + let tgt = s:ShellEscape(s:netrwmftgt.'/'.newname) + else + let args= join(map(deepcopy(s:netrwmarkfilelist_{bufnr('%')}),"s:ShellEscape(b:netrw_curdir.\"/\".v:val)")) + let tgt = s:ShellEscape(s:netrwmftgt) + endif + if !g:netrw_cygwin && has("win32") + let args= substitute(args,'/','\\','g') + let tgt = substitute(tgt, '/','\\','g') + endif + if args =~ "'" |let args= substitute(args,"'\\(.*\\)'",'\1','')|endif + if tgt =~ "'" |let tgt = substitute(tgt ,"'\\(.*\\)'",'\1','')|endif + if args =~ '//'|let args= substitute(args,'//','/','g')|endif + if tgt =~ '//'|let tgt = substitute(tgt ,'//','/','g')|endif + " call Decho("args <".args.">",'~'.expand("<slnum>")) + " call Decho("tgt <".tgt.">",'~'.expand("<slnum>")) + if isdirectory(s:NetrwFile(args)) + " call Decho("args<".args."> is a directory",'~'.expand("<slnum>")) + let copycmd= g:netrw_localcopydircmd + " call Decho("using copydircmd<".copycmd.">",'~'.expand("<slnum>")) + if !g:netrw_cygwin && has("win32") + " window's xcopy doesn't copy a directory to a target properly. Instead, it copies a directory's + " contents to a target. One must append the source directory name to the target to get xcopy to + " do the right thing. + let tgt= tgt.'\'.substitute(a:1,'^.*[\\/]','','') + " call Decho("modified tgt for xcopy",'~'.expand("<slnum>")) + endif + else + let copycmd= g:netrw_localcopycmd + endif + if g:netrw_localcopycmd =~ '\s' + let copycmd = substitute(copycmd,'\s.*$','','') + let copycmdargs = substitute(copycmd,'^.\{-}\(\s.*\)$','\1','') + let copycmd = netrw#WinPath(copycmd).copycmdargs + else + let copycmd = netrw#WinPath(copycmd) + endif + " call Decho("args <".args.">",'~'.expand("<slnum>")) + " call Decho("tgt <".tgt.">",'~'.expand("<slnum>")) + " call Decho("copycmd<".copycmd.">",'~'.expand("<slnum>")) + " call Decho("system(".copycmd." '".args."' '".tgt."')",'~'.expand("<slnum>")) + call system(copycmd.g:netrw_localcopycmdopt." '".args."' '".tgt."'") + if v:shell_error != 0 + if exists("b:netrw_curdir") && b:netrw_curdir != getcwd() && g:netrw_keepdir + call netrw#ErrorMsg(s:ERROR,"copy failed; perhaps due to vim's current directory<".getcwd()."> not matching netrw's (".b:netrw_curdir.") (see :help netrw-cd)",101) + else + call netrw#ErrorMsg(s:ERROR,"tried using g:netrw_localcopycmd<".g:netrw_localcopycmd.">; it doesn't work!",80) + endif + " call Dret("s:NetrwMarkFileCopy 0 : failed: system(".g:netrw_localcopycmd." ".args." ".s:ShellEscape(s:netrwmftgt)) + return 0 + endif + + elseif a:islocal && !s:netrwmftgt_islocal + " Copy marked files, local directory to remote directory + " call Decho("copy from local to remote",'~'.expand("<slnum>")) + NetrwKeepj call s:NetrwUpload(s:netrwmarkfilelist_{bufnr('%')},s:netrwmftgt) + + elseif !a:islocal && s:netrwmftgt_islocal + " Copy marked files, remote directory to local directory + " call Decho("copy from remote to local",'~'.expand("<slnum>")) + NetrwKeepj call netrw#Obtain(a:islocal,s:netrwmarkfilelist_{bufnr('%')},s:netrwmftgt) + + elseif !a:islocal && !s:netrwmftgt_islocal + " Copy marked files, remote directory to remote directory + " call Decho("copy from remote to remote",'~'.expand("<slnum>")) + let curdir = getcwd() + let tmpdir = s:GetTempfile("") + if tmpdir !~ '/' + let tmpdir= curdir."/".tmpdir + endif + if exists("*mkdir") + call mkdir(tmpdir) + else + call s:NetrwExe("sil! !".g:netrw_localmkdir.g:netrw_localmkdiropt.' '.s:ShellEscape(tmpdir,1)) + if v:shell_error != 0 + call netrw#ErrorMsg(s:WARNING,"consider setting g:netrw_localmkdir<".g:netrw_localmkdir."> to something that works",80) + " call Dret("s:NetrwMarkFileCopy : failed: sil! !".g:netrw_localmkdir.' '.s:ShellEscape(tmpdir,1) ) + return + endif + endif + if isdirectory(s:NetrwFile(tmpdir)) + if s:NetrwLcd(tmpdir) + " call Dret("s:NetrwMarkFileCopy : lcd failure") + return + endif + NetrwKeepj call netrw#Obtain(a:islocal,s:netrwmarkfilelist_{bufnr('%')},tmpdir) + let localfiles= map(deepcopy(s:netrwmarkfilelist_{bufnr('%')}),'substitute(v:val,"^.*/","","")') + NetrwKeepj call s:NetrwUpload(localfiles,s:netrwmftgt) + if getcwd() == tmpdir + for fname in s:netrwmarkfilelist_{bufnr('%')} + NetrwKeepj call s:NetrwDelete(fname) + endfor + if s:NetrwLcd(curdir) + " call Dret("s:NetrwMarkFileCopy : lcd failure") + return + endif + if delete(tmpdir,"d") + call netrw#ErrorMsg(s:ERROR,"unable to delete directory <".tmpdir.">!",103) + endif + else + if s:NetrwLcd(curdir) + " call Dret("s:NetrwMarkFileCopy : lcd failure") + return + endif + endif + endif + endif + + " ------- + " 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>")) + " call Decho(" curdir =".curdir,'~'.expand("<slnum>")) + " call Decho(" a:islocal =".a:islocal,'~'.expand("<slnum>")) + " call Decho(" curbufnr =".curbufnr,'~'.expand("<slnum>")) + if exists("s:recursive") + " call Decho(" s:recursive =".s:recursive,'~'.expand("<slnum>")) + else + " call Decho(" s:recursive =n/a",'~'.expand("<slnum>")) + endif + " see s:LocalFastBrowser() for g:netrw_fastbrowse interpretation (refreshing done for both slow and medium) + if g:netrw_fastbrowse <= 1 + NetrwKeepj call s:LocalBrowseRefresh() + else + " refresh local and targets for fast browsing + if !exists("s:recursive") + " remove markings from local buffer + " call Decho(" remove markings from local buffer",'~'.expand("<slnum>")) + NetrwKeepj call s:NetrwUnmarkList(curbufnr,curdir) + endif + + " refresh buffers + if s:netrwmftgt_islocal + " call Decho(" refresh s:netrwmftgt=".s:netrwmftgt,'~'.expand("<slnum>")) + NetrwKeepj call s:NetrwRefreshDir(s:netrwmftgt_islocal,s:netrwmftgt) + endif + if a:islocal && s:netrwmftgt != curdir + " call Decho(" refresh curdir=".curdir,'~'.expand("<slnum>")) + NetrwKeepj call s:NetrwRefreshDir(a:islocal,curdir) + endif + endif + + " call Dret("s:NetrwMarkFileCopy 1") + return 1 +endfun + +" --------------------------------------------------------------------- +" s:NetrwMarkFileDiff: (invoked by md) This function is used to {{{2 +" invoke vim's diff mode on the marked files. +" Either two or three files can be so handled. +" Uses the global marked file list. +fun! s:NetrwMarkFileDiff(islocal) + " call Dfunc("s:NetrwMarkFileDiff(islocal=".a:islocal.") b:netrw_curdir<".b:netrw_curdir.">") + let curbufnr= bufnr("%") + + " sanity check + if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) + NetrwKeepj call netrw#ErrorMsg(2,"there are no marked files in this window (:help netrw-mf)",66) + " call Dret("s:NetrwMarkFileDiff") + return + endif + let curdir= s:NetrwGetCurdir(a:islocal) + " call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>")) + + if exists("s:netrwmarkfilelist_{".curbufnr."}") + let cnt = 0 + for fname in s:netrwmarkfilelist + let cnt= cnt + 1 + if cnt == 1 + " call Decho("diffthis: fname<".fname.">",'~'.expand("<slnum>")) + exe "NetrwKeepj e ".fnameescape(fname) + diffthis + elseif cnt == 2 || cnt == 3 + below vsplit + " call Decho("diffthis: ".fname,'~'.expand("<slnum>")) + exe "NetrwKeepj e ".fnameescape(fname) + diffthis + else + break + endif + endfor + call s:NetrwUnmarkList(curbufnr,curdir) + endif + + " call Dret("s:NetrwMarkFileDiff") +endfun + +" --------------------------------------------------------------------- +" s:NetrwMarkFileEdit: (invoked by me) put marked files on arg list and start editing them {{{2 +" Uses global markfilelist +fun! s:NetrwMarkFileEdit(islocal) + " call Dfunc("s:NetrwMarkFileEdit(islocal=".a:islocal.")") + + let curdir = s:NetrwGetCurdir(a:islocal) + let curbufnr = bufnr("%") + + " sanity check + if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) + NetrwKeepj call netrw#ErrorMsg(2,"there are no marked files in this window (:help netrw-mf)",66) + " call Dret("s:NetrwMarkFileEdit") + return + endif + " call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>")) + + if exists("s:netrwmarkfilelist_{curbufnr}") + call s:SetRexDir(a:islocal,curdir) + let flist= join(map(deepcopy(s:netrwmarkfilelist), "fnameescape(v:val)")) + " unmark markedfile list + " call s:NetrwUnmarkList(curbufnr,curdir) + call s:NetrwUnmarkAll() + " call Decho("exe sil args ".flist,'~'.expand("<slnum>")) + exe "sil args ".flist + endif + echo "(use :bn, :bp to navigate files; :Rex to return)" + + " call Dret("s:NetrwMarkFileEdit") +endfun + +" --------------------------------------------------------------------- +" s:NetrwMarkFileQFEL: convert a quickfix-error or location list into a marked file list {{{2 +fun! s:NetrwMarkFileQFEL(islocal,qfel) + " call Dfunc("s:NetrwMarkFileQFEL(islocal=".a:islocal.",qfel)") + call s:NetrwUnmarkAll() + let curbufnr= bufnr("%") + + if !empty(a:qfel) + for entry in a:qfel + let bufnmbr= entry["bufnr"] + " call Decho("bufname(".bufnmbr.")<".bufname(bufnmbr)."> line#".entry["lnum"]." text=".entry["text"],'~'.expand("<slnum>")) + if !exists("s:netrwmarkfilelist_{curbufnr}") + " call Decho("case: no marked file list",'~'.expand("<slnum>")) + call s:NetrwMarkFile(a:islocal,bufname(bufnmbr)) + elseif index(s:netrwmarkfilelist_{curbufnr},bufname(bufnmbr)) == -1 + " s:NetrwMarkFile will remove duplicate entries from the marked file list. + " So, this test lets two or more hits on the same pattern to be ignored. + " call Decho("case: ".bufname(bufnmbr)." not currently in marked file list",'~'.expand("<slnum>")) + call s:NetrwMarkFile(a:islocal,bufname(bufnmbr)) + else + " call Decho("case: ".bufname(bufnmbr)." already in marked file list",'~'.expand("<slnum>")) + endif + endfor + echo "(use me to edit marked files)" + else + call netrw#ErrorMsg(s:WARNING,"can't convert quickfix error list; its empty!",92) + endif + + " call Dret("s:NetrwMarkFileQFEL") +endfun + +" --------------------------------------------------------------------- +" s:NetrwMarkFileExe: (invoked by mx and mX) execute arbitrary system command on marked files {{{2 +" mx enbloc=0: Uses the local marked-file list, applies command to each file individually +" mX enbloc=1: Uses the global marked-file list, applies command to entire list +fun! s:NetrwMarkFileExe(islocal,enbloc) + let svpos = winsaveview() + let curdir = s:NetrwGetCurdir(a:islocal) + let curbufnr = bufnr("%") + + if a:enbloc == 0 + " individually apply command to files, one at a time + " sanity check + if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) + NetrwKeepj call netrw#ErrorMsg(2,"there are no marked files in this window (:help netrw-mf)",66) + return + endif + + if exists("s:netrwmarkfilelist_{curbufnr}") + " get the command + call inputsave() + let cmd= input("Enter command: ","","file") + call inputrestore() + if cmd == "" + return + endif + + " apply command to marked files, individually. Substitute: filename -> % + " If no %, then append a space and the filename to the command + for fname in s:netrwmarkfilelist_{curbufnr} + if a:islocal + if g:netrw_keepdir + let fname= s:ShellEscape(netrw#WinPath(s:ComposePath(curdir,fname))) + endif + else + let fname= s:ShellEscape(netrw#WinPath(b:netrw_curdir.fname)) + endif + if cmd =~ '%' + let xcmd= substitute(cmd,'%',fname,'g') + else + let xcmd= cmd.' '.fname + endif + if a:islocal + let ret= system(xcmd) + else + let ret= s:RemoteSystem(xcmd) + endif + if v:shell_error < 0 + NetrwKeepj call netrw#ErrorMsg(s:ERROR,"command<".xcmd."> failed, aborting",54) + break + else + if ret !=# '' + echo "\n" + " skip trailing new line + echo ret[0:-2] + else + echo ret + endif + endif + endfor + + " unmark marked file list + call s:NetrwUnmarkList(curbufnr,curdir) + + " refresh the listing + NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) + NetrwKeepj call winrestview(svpos) + else + NetrwKeepj call netrw#ErrorMsg(s:ERROR,"no files marked!",59) + endif + + else " apply command to global list of files, en bloc + + call inputsave() + let cmd= input("Enter command: ","","file") + call inputrestore() + if cmd == "" + return + endif + if cmd =~ '%' + let cmd= substitute(cmd,'%',join(map(s:netrwmarkfilelist,'s:ShellEscape(v:val)'),' '),'g') + else + let cmd= cmd.' '.join(map(s:netrwmarkfilelist,'s:ShellEscape(v:val)'),' ') + endif + if a:islocal + call system(cmd) + if v:shell_error < 0 + NetrwKeepj call netrw#ErrorMsg(s:ERROR,"command<".xcmd."> failed, aborting",54) + endif + else + let ret= s:RemoteSystem(cmd) + endif + call s:NetrwUnmarkAll() + + " refresh the listing + NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) + NetrwKeepj call winrestview(svpos) + + endif +endfun + +" --------------------------------------------------------------------- +" s:NetrwMarkHideSfx: (invoked by mh) (un)hide files having same suffix +" as the marked file(s) (toggles suffix presence) +" Uses the local marked file list. +fun! s:NetrwMarkHideSfx(islocal) + let svpos = winsaveview() + let curbufnr = bufnr("%") + + " s:netrwmarkfilelist_{curbufnr}: the List of marked files + if exists("s:netrwmarkfilelist_{curbufnr}") + + for fname in s:netrwmarkfilelist_{curbufnr} + " construct suffix pattern + if fname =~ '\.' + let sfxpat= "^.*".substitute(fname,'^.*\(\.[^. ]\+\)$','\1','') + else + let sfxpat= '^\%(\%(\.\)\@!.\)*$' + endif + " determine if its in the hiding list or not + let inhidelist= 0 + if g:netrw_list_hide != "" + let itemnum = 0 + let hidelist= split(g:netrw_list_hide,',') + for hidepat in hidelist + if sfxpat == hidepat + let inhidelist= 1 + break + endif + let itemnum= itemnum + 1 + endfor + endif + if inhidelist + " remove sfxpat from list + call remove(hidelist,itemnum) + let g:netrw_list_hide= join(hidelist,",") + elseif g:netrw_list_hide != "" + " append sfxpat to non-empty list + let g:netrw_list_hide= g:netrw_list_hide.",".sfxpat + else + " set hiding list to sfxpat + let g:netrw_list_hide= sfxpat + endif + endfor + + " refresh the listing + NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) + NetrwKeepj call winrestview(svpos) + else + NetrwKeepj call netrw#ErrorMsg(s:ERROR,"no files marked!",59) + endif +endfun + +" --------------------------------------------------------------------- +" s:NetrwMarkFileVimCmd: (invoked by mv) execute arbitrary vim command on marked files, one at a time {{{2 +" Uses the local marked-file list. +fun! s:NetrwMarkFileVimCmd(islocal) + let svpos = winsaveview() + let curdir = s:NetrwGetCurdir(a:islocal) + let curbufnr = bufnr("%") + + " sanity check + if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) + NetrwKeepj call netrw#ErrorMsg(2,"there are no marked files in this window (:help netrw-mf)",66) + return + endif + + if exists("s:netrwmarkfilelist_{curbufnr}") + " get the command + call inputsave() + let cmd= input("Enter vim command: ","","file") + call inputrestore() + if cmd == "" + return + endif + + " apply command to marked files. Substitute: filename -> % + " If no %, then append a space and the filename to the command + for fname in s:netrwmarkfilelist_{curbufnr} + if a:islocal + 1split + exe "sil! NetrwKeepj keepalt e ".fnameescape(fname) + exe cmd + exe "sil! keepalt wq!" + else + echo "sorry, \"mv\" not supported yet for remote files" + endif + endfor + + " unmark marked file list + call s:NetrwUnmarkList(curbufnr,curdir) + + " refresh the listing + NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) + NetrwKeepj call winrestview(svpos) + else + NetrwKeepj call netrw#ErrorMsg(s:ERROR,"no files marked!",59) + endif +endfun + +" --------------------------------------------------------------------- +" s:NetrwMarkHideSfx: (invoked by mh) (un)hide files having same suffix +" as the marked file(s) (toggles suffix presence) +" Uses the local marked file list. +fun! s:NetrwMarkHideSfx(islocal) + let svpos = winsaveview() + let curbufnr = bufnr("%") + + " s:netrwmarkfilelist_{curbufnr}: the List of marked files + if exists("s:netrwmarkfilelist_{curbufnr}") + + for fname in s:netrwmarkfilelist_{curbufnr} + " construct suffix pattern + if fname =~ '\.' + let sfxpat= "^.*".substitute(fname,'^.*\(\.[^. ]\+\)$','\1','') + else + let sfxpat= '^\%(\%(\.\)\@!.\)*$' + endif + " determine if its in the hiding list or not + let inhidelist= 0 + if g:netrw_list_hide != "" + let itemnum = 0 + let hidelist= split(g:netrw_list_hide,',') + for hidepat in hidelist + if sfxpat == hidepat + let inhidelist= 1 + break + endif + let itemnum= itemnum + 1 + endfor + endif + if inhidelist + " remove sfxpat from list + call remove(hidelist,itemnum) + let g:netrw_list_hide= join(hidelist,",") + elseif g:netrw_list_hide != "" + " append sfxpat to non-empty list + let g:netrw_list_hide= g:netrw_list_hide.",".sfxpat + else + " set hiding list to sfxpat + let g:netrw_list_hide= sfxpat + endif + endfor + + " refresh the listing + NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) + NetrwKeepj call winrestview(svpos) + else + NetrwKeepj call netrw#ErrorMsg(s:ERROR,"no files marked!",59) + endif +endfun + +" --------------------------------------------------------------------- +" s:NetrwMarkFileGrep: (invoked by mg) This function applies vimgrep to marked files {{{2 +" Uses the global markfilelist +fun! s:NetrwMarkFileGrep(islocal) + " call Dfunc("s:NetrwMarkFileGrep(islocal=".a:islocal.")") + let svpos = winsaveview() + " call Decho("saving posn to svpos<".string(svpos).">",'~'.expand("<slnum>")) + let curbufnr = bufnr("%") + let curdir = s:NetrwGetCurdir(a:islocal) + + if exists("s:netrwmarkfilelist") + " call Decho("using s:netrwmarkfilelist".string(s:netrwmarkfilelist).">",'~'.expand("<slnum>")) + let netrwmarkfilelist= join(map(deepcopy(s:netrwmarkfilelist), "fnameescape(v:val)")) + " call Decho("keeping copy of s:netrwmarkfilelist in function-local variable,'~'.expand("<slnum>"))" + call s:NetrwUnmarkAll() + else + " call Decho('no marked files, using "*"','~'.expand("<slnum>")) + let netrwmarkfilelist= "*" + endif + + " ask user for pattern + " call Decho("ask user for search pattern",'~'.expand("<slnum>")) + call inputsave() + let pat= input("Enter pattern: ","") + call inputrestore() + let patbang = "" + if pat =~ '^!' + let patbang = "!" + let pat = strpart(pat,2) + endif + if pat =~ '^\i' + let pat = escape(pat,'/') + let pat = '/'.pat.'/' + else + let nonisi = pat[0] + endif + + " use vimgrep for both local and remote + " call Decho("exe vimgrep".patbang." ".pat." ".netrwmarkfilelist,'~'.expand("<slnum>")) + try + exe "NetrwKeepj noautocmd vimgrep".patbang." ".pat." ".netrwmarkfilelist + catch /^Vim\%((\a\+)\)\=:E480/ + NetrwKeepj call netrw#ErrorMsg(s:WARNING,"no match with pattern<".pat.">",76) + " call Dret("s:NetrwMarkFileGrep : unable to find pattern<".pat.">") + return + endtry + echo "(use :cn, :cp to navigate, :Rex to return)" + + 2match none + " call Decho("restoring posn to svpos<".string(svpos).">",'~'.expand("<slnum>")) + NetrwKeepj call winrestview(svpos) + + 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$' + call s:NetrwMarkFileQFEL(a:islocal,getqflist()) + endif + endif + + " call Dret("s:NetrwMarkFileGrep") +endfun + +" --------------------------------------------------------------------- +" s:NetrwMarkFileMove: (invoked by mm) execute arbitrary command on marked files, one at a time {{{2 +" uses the global marked file list +" s:netrwmfloc= 0: target directory is remote +" = 1: target directory is local +fun! s:NetrwMarkFileMove(islocal) + " call Dfunc("s:NetrwMarkFileMove(islocal=".a:islocal.")") + let curdir = s:NetrwGetCurdir(a:islocal) + let curbufnr = bufnr("%") + + " sanity check + if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) + NetrwKeepj call netrw#ErrorMsg(2,"there are no marked files in this window (:help netrw-mf)",66) + " call Dret("s:NetrwMarkFileMove") + return + endif + " call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>")) + + if !exists("s:netrwmftgt") + NetrwKeepj call netrw#ErrorMsg(2,"your marked file target is empty! (:help netrw-mt)",67) + " call Dret("s:NetrwMarkFileCopy 0") + return 0 + endif + " call Decho("sanity chk passed: s:netrwmftgt<".s:netrwmftgt.">",'~'.expand("<slnum>")) + + if a:islocal && s:netrwmftgt_islocal + " move: local -> local + " call Decho("move from local to local",'~'.expand("<slnum>")) + " call Decho("local to local move",'~'.expand("<slnum>")) + if !executable(g:netrw_localmovecmd) + call netrw#ErrorMsg(s:ERROR,"g:netrw_localmovecmd<".g:netrw_localmovecmd."> not executable on your system, aborting",90) + " call Dfunc("s:NetrwMarkFileMove : g:netrw_localmovecmd<".g:netrw_localmovecmd."> n/a!") + return + endif + let tgt = s:ShellEscape(s:netrwmftgt) + " call Decho("tgt<".tgt.">",'~'.expand("<slnum>")) + if !g:netrw_cygwin && has("win32") + let tgt= substitute(tgt, '/','\\','g') + " call Decho("windows exception: tgt<".tgt.">",'~'.expand("<slnum>")) + if g:netrw_localmovecmd =~ '\s' + let movecmd = substitute(g:netrw_localmovecmd,'\s.*$','','') + let movecmdargs = substitute(g:netrw_localmovecmd,'^.\{-}\(\s.*\)$','\1','') + let movecmd = netrw#WinPath(movecmd).movecmdargs + " call Decho("windows exception: movecmd<".movecmd."> (#1: had a space)",'~'.expand("<slnum>")) + else + let movecmd = netrw#WinPath(g:netrw_localmovecmd) + " call Decho("windows exception: movecmd<".movecmd."> (#2: no space)",'~'.expand("<slnum>")) + endif + else + let movecmd = netrw#WinPath(g:netrw_localmovecmd) + " call Decho("movecmd<".movecmd."> (#3 linux or cygwin)",'~'.expand("<slnum>")) + endif + for fname in s:netrwmarkfilelist_{bufnr("%")} + if g:netrw_keepdir + " Jul 19, 2022: fixing file move when g:netrw_keepdir is 1 + let fname= b:netrw_curdir."/".fname + endif + if !g:netrw_cygwin && has("win32") + let fname= substitute(fname,'/','\\','g') + endif + " call Decho("system(".movecmd." ".s:ShellEscape(fname)." ".tgt.")",'~'.expand("<slnum>")) + let ret= system(movecmd.g:netrw_localmovecmdopt." ".s:ShellEscape(fname)." ".tgt) + if v:shell_error != 0 + if exists("b:netrw_curdir") && b:netrw_curdir != getcwd() && !g:netrw_keepdir + call netrw#ErrorMsg(s:ERROR,"move failed; perhaps due to vim's current directory<".getcwd()."> not matching netrw's (".b:netrw_curdir.") (see :help netrw-cd)",100) + else + call netrw#ErrorMsg(s:ERROR,"tried using g:netrw_localmovecmd<".g:netrw_localmovecmd.">; it doesn't work!",54) + endif + break + endif + endfor + + elseif a:islocal && !s:netrwmftgt_islocal + " move: local -> remote + " call Decho("move from local to remote",'~'.expand("<slnum>")) + " call Decho("copy",'~'.expand("<slnum>")) + let mflist= s:netrwmarkfilelist_{bufnr("%")} + NetrwKeepj call s:NetrwMarkFileCopy(a:islocal) + " call Decho("remove",'~'.expand("<slnum>")) + for fname in mflist + let barefname = substitute(fname,'^\(.*/\)\(.\{-}\)$','\2','') + let ok = s:NetrwLocalRmFile(b:netrw_curdir,barefname,1) + endfor + unlet mflist + + elseif !a:islocal && s:netrwmftgt_islocal + " move: remote -> local + " call Decho("move from remote to local",'~'.expand("<slnum>")) + " call Decho("copy",'~'.expand("<slnum>")) + let mflist= s:netrwmarkfilelist_{bufnr("%")} + NetrwKeepj call s:NetrwMarkFileCopy(a:islocal) + " call Decho("remove",'~'.expand("<slnum>")) + for fname in mflist + let barefname = substitute(fname,'^\(.*/\)\(.\{-}\)$','\2','') + let ok = s:NetrwRemoteRmFile(b:netrw_curdir,barefname,1) + endfor + unlet mflist + + elseif !a:islocal && !s:netrwmftgt_islocal + " move: remote -> remote + " call Decho("move from remote to remote",'~'.expand("<slnum>")) + " call Decho("copy",'~'.expand("<slnum>")) + let mflist= s:netrwmarkfilelist_{bufnr("%")} + NetrwKeepj call s:NetrwMarkFileCopy(a:islocal) + " call Decho("remove",'~'.expand("<slnum>")) + for fname in mflist + let barefname = substitute(fname,'^\(.*/\)\(.\{-}\)$','\2','') + let ok = s:NetrwRemoteRmFile(b:netrw_curdir,barefname,1) + endfor + unlet mflist + endif + + " ------- + " cleanup + " ------- + " call Decho("cleanup",'~'.expand("<slnum>")) + + " remove markings from local buffer + call s:NetrwUnmarkList(curbufnr,curdir) " remove markings from local buffer + + " refresh buffers + if !s:netrwmftgt_islocal + " call Decho("refresh netrwmftgt<".s:netrwmftgt.">",'~'.expand("<slnum>")) + NetrwKeepj call s:NetrwRefreshDir(s:netrwmftgt_islocal,s:netrwmftgt) + endif + if a:islocal + " call Decho("refresh b:netrw_curdir<".b:netrw_curdir.">",'~'.expand("<slnum>")) + NetrwKeepj call s:NetrwRefreshDir(a:islocal,b:netrw_curdir) + endif + if g:netrw_fastbrowse <= 1 + " call Decho("since g:netrw_fastbrowse=".g:netrw_fastbrowse.", perform shell cmd refresh",'~'.expand("<slnum>")) + NetrwKeepj call s:LocalBrowseRefresh() + endif + + " call Dret("s:NetrwMarkFileMove") +endfun + +" --------------------------------------------------------------------- +" s:NetrwMarkFilePrint: (invoked by mp) This function prints marked files {{{2 +" using the hardcopy command. Local marked-file list only. +fun! s:NetrwMarkFilePrint(islocal) + " call Dfunc("s:NetrwMarkFilePrint(islocal=".a:islocal.")") + let curbufnr= bufnr("%") + + " sanity check + if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) + NetrwKeepj call netrw#ErrorMsg(2,"there are no marked files in this window (:help netrw-mf)",66) + " call Dret("s:NetrwMarkFilePrint") + return + endif + " call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>")) + let curdir= s:NetrwGetCurdir(a:islocal) + + if exists("s:netrwmarkfilelist_{curbufnr}") + let netrwmarkfilelist = s:netrwmarkfilelist_{curbufnr} + call s:NetrwUnmarkList(curbufnr,curdir) + for fname in netrwmarkfilelist + if a:islocal + if g:netrw_keepdir + let fname= s:ComposePath(curdir,fname) + endif + else + let fname= curdir.fname + endif + 1split + " the autocmds will handle both local and remote files + " call Decho("exe sil e ".escape(fname,' '),'~'.expand("<slnum>")) + exe "sil NetrwKeepj e ".fnameescape(fname) + " call Decho("hardcopy",'~'.expand("<slnum>")) + hardcopy + q + endfor + 2match none + endif + " call Dret("s:NetrwMarkFilePrint") +endfun + +" --------------------------------------------------------------------- +" s:NetrwMarkFileRegexp: (invoked by mr) This function is used to mark {{{2 +" files when given a regexp (for which a prompt is +" issued) (matches to name of files). +fun! s:NetrwMarkFileRegexp(islocal) + " call Dfunc("s:NetrwMarkFileRegexp(islocal=".a:islocal.")") + + " get the regular expression + call inputsave() + let regexp= input("Enter regexp: ","","file") + call inputrestore() + + if a:islocal + let curdir= s:NetrwGetCurdir(a:islocal) + " call Decho("curdir<".fnameescape(curdir).">") + " 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 || (v:version == 704 && has("patch656")) + let filelist= glob(s:ComposePath(dirname,regexp),0,1,1) + else + let files = glob(s:ComposePath(dirname,regexp),0,0) + let filelist= split(files,"\n") + endif + " call Decho("files<".string(filelist).">",'~'.expand("<slnum>")) + + " mark the list of files + for fname in filelist + if fname =~ '^'.fnameescape(curdir) + " call Decho("fname<".substitute(fname,'^'.fnameescape(curdir).'/','','').">",'~'.expand("<slnum>")) + NetrwKeepj call s:NetrwMarkFile(a:islocal,substitute(fname,'^'.fnameescape(curdir).'/','','')) + else + " call Decho("fname<".fname.">",'~'.expand("<slnum>")) + NetrwKeepj call s:NetrwMarkFile(a:islocal,substitute(fname,'^.*/','','')) + endif + endfor + + else + " call Decho("handle remote regexp",'~'.expand("<slnum>")) + + " convert displayed listing into a filelist + let eikeep = &ei + let areg = @a + sil NetrwKeepj %y a + setl ei=all ma + " call Decho("setl ei=all ma",'~'.expand("<slnum>")) + 1split + NetrwKeepj call s:NetrwEnew() + NetrwKeepj call s:NetrwOptionsSafe(a:islocal) + sil NetrwKeepj norm! "ap + NetrwKeepj 2 + let bannercnt= search('^" =====','W') + exe "sil NetrwKeepj 1,".bannercnt."d" + setl bt=nofile + if g:netrw_liststyle == s:LONGLIST + sil NetrwKeepj %s/\s\{2,}\S.*$//e + call histdel("/",-1) + elseif g:netrw_liststyle == s:WIDELIST + sil NetrwKeepj %s/\s\{2,}/\r/ge + call histdel("/",-1) + elseif g:netrw_liststyle == s:TREELIST + exe 'sil NetrwKeepj %s/^'.s:treedepthstring.' //e' + sil! NetrwKeepj g/^ .*$/d + call histdel("/",-1) + call histdel("/",-1) + endif + " convert regexp into the more usual glob-style format + let regexp= substitute(regexp,'\*','.*','g') + " call Decho("regexp<".regexp.">",'~'.expand("<slnum>")) + exe "sil! NetrwKeepj v/".escape(regexp,'/')."/d" + call histdel("/",-1) + let filelist= getline(1,line("$")) + q! + for filename in filelist + NetrwKeepj call s:NetrwMarkFile(a:islocal,substitute(filename,'^.*/','','')) + endfor + unlet filelist + let @a = areg + let &ei = eikeep + endif + echo " (use me to edit marked files)" + + " call Dret("s:NetrwMarkFileRegexp") +endfun + +" --------------------------------------------------------------------- +" s:NetrwMarkFileSource: (invoked by ms) This function sources marked files {{{2 +" Uses the local marked file list. +fun! s:NetrwMarkFileSource(islocal) + " call Dfunc("s:NetrwMarkFileSource(islocal=".a:islocal.")") + let curbufnr= bufnr("%") + + " sanity check + if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) + NetrwKeepj call netrw#ErrorMsg(2,"there are no marked files in this window (:help netrw-mf)",66) + " call Dret("s:NetrwMarkFileSource") + return + endif + " call Decho("sanity chk passed: s:netrwmarkfilelist_".curbufnr."<".string(s:netrwmarkfilelist_{curbufnr}),'~'.expand("<slnum>")) + let curdir= s:NetrwGetCurdir(a:islocal) + + if exists("s:netrwmarkfilelist_{curbufnr}") + let netrwmarkfilelist = s:netrwmarkfilelist_{bufnr("%")} + call s:NetrwUnmarkList(curbufnr,curdir) + for fname in netrwmarkfilelist + if a:islocal + if g:netrw_keepdir + let fname= s:ComposePath(curdir,fname) + endif + else + let fname= curdir.fname + endif + " the autocmds will handle sourcing both local and remote files + " call Decho("exe so ".fnameescape(fname),'~'.expand("<slnum>")) + exe "so ".fnameescape(fname) + endfor + 2match none + endif + " call Dret("s:NetrwMarkFileSource") +endfun + +" --------------------------------------------------------------------- +" s:NetrwMarkFileTag: (invoked by mT) This function applies g:netrw_ctags to marked files {{{2 +" Uses the global markfilelist +fun! s:NetrwMarkFileTag(islocal) + let svpos = winsaveview() + let curdir = s:NetrwGetCurdir(a:islocal) + let curbufnr = bufnr("%") + + " sanity check + if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) + NetrwKeepj call netrw#ErrorMsg(2,"there are no marked files in this window (:help netrw-mf)",66) + return + endif + + if exists("s:netrwmarkfilelist") + let netrwmarkfilelist= join(map(deepcopy(s:netrwmarkfilelist), "s:ShellEscape(v:val,".!a:islocal.")")) + call s:NetrwUnmarkAll() + + if a:islocal + + call system(g:netrw_ctags." ".netrwmarkfilelist) + if v:shell_error + call netrw#ErrorMsg(s:ERROR,"g:netrw_ctags<".g:netrw_ctags."> is not executable!",51) + endif + + else + let cmd = s:RemoteSystem(g:netrw_ctags." ".netrwmarkfilelist) + call netrw#Obtain(a:islocal,"tags") + let curdir= b:netrw_curdir + 1split + NetrwKeepj e tags + let path= substitute(curdir,'^\(.*\)/[^/]*$','\1/','') + exe 'NetrwKeepj %s/\t\(\S\+\)\t/\t'.escape(path,"/\n\r\\").'\1\t/e' + call histdel("/",-1) + wq! + endif + 2match none + call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) + call winrestview(svpos) + endif +endfun + +" --------------------------------------------------------------------- +" s:NetrwMarkFileTgt: (invoked by mt) This function sets up a marked file target {{{2 +" Sets up two variables, +" s:netrwmftgt : holds the target directory +" s:netrwmftgt_islocal : 0=target directory is remote +" 1=target directory is local +fun! s:NetrwMarkFileTgt(islocal) + let svpos = winsaveview() + let curdir = s:NetrwGetCurdir(a:islocal) + let hadtgt = exists("s:netrwmftgt") + if !exists("w:netrw_bannercnt") + let w:netrw_bannercnt= b:netrw_bannercnt + endif + + " set up target + if line(".") < w:netrw_bannercnt + " if cursor in banner region, use b:netrw_curdir for the target unless its already the target + if exists("s:netrwmftgt") && exists("s:netrwmftgt_islocal") && s:netrwmftgt == b:netrw_curdir + unlet s:netrwmftgt s:netrwmftgt_islocal + if g:netrw_fastbrowse <= 1 + call s:LocalBrowseRefresh() + endif + call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) + call winrestview(svpos) + return + else + let s:netrwmftgt= b:netrw_curdir + endif + + else + " get word under cursor. + " * If directory, use it for the target. + " * If file, use b:netrw_curdir for the target + let curword= s:NetrwGetWord() + let tgtdir = s:ComposePath(curdir,curword) + if a:islocal && isdirectory(s:NetrwFile(tgtdir)) + let s:netrwmftgt = tgtdir + elseif !a:islocal && tgtdir =~ '/$' + let s:netrwmftgt = tgtdir + else + let s:netrwmftgt = curdir + endif + endif + if a:islocal + " simplify the target (eg. /abc/def/../ghi -> /abc/ghi) + let s:netrwmftgt= simplify(s:netrwmftgt) + endif + if g:netrw_cygwin + let s:netrwmftgt= substitute(system("cygpath ".s:ShellEscape(s:netrwmftgt)),'\n$','','') + let s:netrwmftgt= substitute(s:netrwmftgt,'\n$','','') + endif + let s:netrwmftgt_islocal= a: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 s:LocalBrowseRefresh() + endif + " call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) + if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST + call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,w:netrw_treetop,0)) + else + call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) + endif + call winrestview(svpos) + if !hadtgt + sil! NetrwKeepj norm! j + endif +endfun + +" --------------------------------------------------------------------- +" s:NetrwGetCurdir: gets current directory and sets up b:netrw_curdir if necessary {{{2 +fun! s:NetrwGetCurdir(islocal) + " call Dfunc("s:NetrwGetCurdir(islocal=".a:islocal.")") + + if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST + let b:netrw_curdir = s:NetrwTreePath(w:netrw_treetop) + " call Decho("set b:netrw_curdir<".b:netrw_curdir."> (used s:NetrwTreeDir)",'~'.expand("<slnum>")) + elseif !exists("b:netrw_curdir") + let b:netrw_curdir= getcwd() + " 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",'~'.expand("<slnum>")) + if b:netrw_curdir !~ '\<\a\{3,}://' + let curdir= b:netrw_curdir + " call Decho("g:netrw_keepdir=".g:netrw_keepdir,'~'.expand("<slnum>")) + if g:netrw_keepdir == 0 + call s:NetrwLcd(curdir) + endif + endif + + " call Dret("s:NetrwGetCurdir <".curdir.">") + return b:netrw_curdir +endfun + +" --------------------------------------------------------------------- +" s:NetrwOpenFile: query user for a filename and open it {{{2 +fun! s:NetrwOpenFile(islocal) + " call Dfunc("s:NetrwOpenFile(islocal=".a:islocal.")") + let ykeep= @@ + call inputsave() + let fname= input("Enter filename: ") + call inputrestore() + " call Decho("(s:NetrwOpenFile) fname<".fname.">",'~'.expand("<slnum>")) + + " determine if Lexplore is in use + if exists("t:netrw_lexbufnr") + " check if t:netrw_lexbufnr refers to a netrw window + " call Decho("(s:netrwOpenFile) ..t:netrw_lexbufnr=".t:netrw_lexbufnr,'~'.expand("<slnum>")) + let lexwinnr = bufwinnr(t:netrw_lexbufnr) + if lexwinnr != -1 && exists("g:netrw_chgwin") && g:netrw_chgwin != -1 + " call Decho("(s:netrwOpenFile) ..Lexplore in use",'~'.expand("<slnum>")) + exe "NetrwKeepj keepalt ".g:netrw_chgwin."wincmd w" + exe "NetrwKeepj e ".fnameescape(fname) + let @@= ykeep + " call Dret("s:NetrwOpenFile : creating a file with Lexplore mode") + endif + endif + + " Does the filename contain a path? + if fname !~ '[/\\]' + if exists("b:netrw_curdir") + if exists("g:netrw_quiet") + let netrw_quiet_keep = g:netrw_quiet + endif + let g:netrw_quiet = 1 + " save position for benefit of Rexplore + let s:rexposn_{bufnr("%")}= winsaveview() + " call Decho("saving posn to s:rexposn_".bufnr("%")."<".string(s:rexposn_{bufnr("%")}).">",'~'.expand("<slnum>")) + if b:netrw_curdir =~ '/$' + exe "NetrwKeepj e ".fnameescape(b:netrw_curdir.fname) + else + exe "e ".fnameescape(b:netrw_curdir."/".fname) + endif + if exists("netrw_quiet_keep") + let g:netrw_quiet= netrw_quiet_keep + else + unlet g:netrw_quiet + endif + endif + else + exe "NetrwKeepj e ".fnameescape(fname) + endif + let @@= ykeep + " call Dret("s:NetrwOpenFile") +endfun + +" --------------------------------------------------------------------- +" netrw#Shrink: shrinks/expands a netrw or Lexplorer window {{{2 +" For the mapping to this function be made via +" netrwPlugin, you'll need to have had +" g:netrw_usetab set to non-zero. +fun! netrw#Shrink() + " call Dfunc("netrw#Shrink() ft<".&ft."> winwidth=".winwidth(0)." lexbuf#".((exists("t:netrw_lexbufnr"))? t:netrw_lexbufnr : 'n/a')) + let curwin = winnr() + let wiwkeep = &wiw + set wiw=1 + + if &ft == "netrw" + if winwidth(0) > g:netrw_wiw + let t:netrw_winwidth= winwidth(0) + exe "vert resize ".g:netrw_wiw + wincmd l + if winnr() == curwin + wincmd h + endif + " call Decho("vert resize 0",'~'.expand("<slnum>")) + else + exe "vert resize ".t:netrw_winwidth + " call Decho("vert resize ".t:netrw_winwidth,'~'.expand("<slnum>")) + endif + + elseif exists("t:netrw_lexbufnr") + exe bufwinnr(t:netrw_lexbufnr)."wincmd w" + if winwidth(bufwinnr(t:netrw_lexbufnr)) > g:netrw_wiw + let t:netrw_winwidth= winwidth(0) + exe "vert resize ".g:netrw_wiw + wincmd l + if winnr() == curwin + wincmd h + endif + " call Decho("vert resize 0",'~'.expand("<slnum>")) + elseif winwidth(bufwinnr(t:netrw_lexbufnr)) >= 0 + exe "vert resize ".t:netrw_winwidth + " call Decho("vert resize ".t:netrw_winwidth,'~'.expand("<slnum>")) + else + call netrw#Lexplore(0,0) + endif + + else + call netrw#Lexplore(0,0) + endif + let wiw= wiwkeep + + " call Dret("netrw#Shrink") +endfun + +" --------------------------------------------------------------------- +" s:NetSortSequence: allows user to edit the sorting sequence {{{2 +fun! s:NetSortSequence(islocal) + let ykeep= @@ + let svpos= winsaveview() + call inputsave() + let newsortseq= input("Edit Sorting Sequence: ",g:netrw_sort_sequence) + call inputrestore() + + " refresh the listing + let g:netrw_sort_sequence= newsortseq + NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) + NetrwKeepj call winrestview(svpos) + let @@= ykeep +endfun + +" --------------------------------------------------------------------- +" s:NetrwUnmarkList: delete local marked file list and remove their contents from the global marked-file list {{{2 +" User access provided by the <mF> mapping. (see :help netrw-mF) +" Used by many MarkFile functions. +fun! s:NetrwUnmarkList(curbufnr,curdir) + " call Dfunc("s:NetrwUnmarkList(curbufnr=".a:curbufnr." curdir<".a:curdir.">)") + + " remove all files in local marked-file list from global list + if exists("s:netrwmarkfilelist") + for mfile in s:netrwmarkfilelist_{a:curbufnr} + let dfile = s:ComposePath(a:curdir,mfile) " prepend directory to mfile + let idx = index(s:netrwmarkfilelist,dfile) " get index in list of dfile + call remove(s:netrwmarkfilelist,idx) " remove from global list + endfor + if s:netrwmarkfilelist == [] + unlet s:netrwmarkfilelist + endif + + " getting rid of the local marked-file lists is easy + unlet s:netrwmarkfilelist_{a:curbufnr} + endif + if exists("s:netrwmarkfilemtch_{a:curbufnr}") + unlet s:netrwmarkfilemtch_{a:curbufnr} + endif + 2match none + " call Dret("s:NetrwUnmarkList") +endfun + +" --------------------------------------------------------------------- +" s:NetrwUnmarkAll: remove the global marked file list and all local ones {{{2 +fun! s:NetrwUnmarkAll() + " call Dfunc("s:NetrwUnmarkAll()") + if exists("s:netrwmarkfilelist") + unlet s:netrwmarkfilelist + endif + sil call s:NetrwUnmarkAll2() + 2match none + " call Dret("s:NetrwUnmarkAll") +endfun + +" --------------------------------------------------------------------- +" s:NetrwUnmarkAll2: unmark all files from all buffers {{{2 +fun! s:NetrwUnmarkAll2() + " call Dfunc("s:NetrwUnmarkAll2()") + redir => netrwmarkfilelist_let + let + redir END + let netrwmarkfilelist_list= split(netrwmarkfilelist_let,'\n') " convert let string into a let list + call filter(netrwmarkfilelist_list,"v:val =~ '^s:netrwmarkfilelist_'") " retain only those vars that start as s:netrwmarkfilelist_ + call map(netrwmarkfilelist_list,"substitute(v:val,'\\s.*$','','')") " remove what the entries are equal to + for flist in netrwmarkfilelist_list + let curbufnr= substitute(flist,'s:netrwmarkfilelist_','','') + unlet s:netrwmarkfilelist_{curbufnr} + unlet s:netrwmarkfilemtch_{curbufnr} + endfor + " call Dret("s:NetrwUnmarkAll2") +endfun + +" --------------------------------------------------------------------- +" s:NetrwUnMarkFile: called via mu map; unmarks *all* marked files, both global and buffer-local {{{2 +" +" Marked files are in two types of lists: +" s:netrwmarkfilelist -- holds complete paths to all marked files +" s:netrwmarkfilelist_# -- holds list of marked files in current-buffer's directory (#==bufnr()) +" +" Marked files suitable for use with 2match are in: +" s:netrwmarkfilemtch_# -- used with 2match to display marked files +fun! s:NetrwUnMarkFile(islocal) + let svpos = winsaveview() + let curbufnr = bufnr("%") + + " unmark marked file list + " (although I expect s:NetrwUpload() to do it, I'm just making sure) + if exists("s:netrwmarkfilelist") + " " call Decho("unlet'ing: s:netrwmarkfilelist",'~'.expand("<slnum>")) + unlet s:netrwmarkfilelist + endif + + let ibuf= 1 + while ibuf < bufnr("$") + if exists("s:netrwmarkfilelist_".ibuf) + unlet s:netrwmarkfilelist_{ibuf} + unlet s:netrwmarkfilemtch_{ibuf} + endif + let ibuf = ibuf + 1 + endwhile + 2match none + + " call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) + call winrestview(svpos) +endfun + +" --------------------------------------------------------------------- +" s:NetrwMenu: generates the menu for gvim and netrw {{{2 +fun! s:NetrwMenu(domenu) + + if !exists("g:NetrwMenuPriority") + let g:NetrwMenuPriority= 80 + endif + + if has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu + " call Dfunc("NetrwMenu(domenu=".a:domenu.")") + + if !exists("s:netrw_menu_enabled") && a:domenu + " call Decho("initialize menu",'~'.expand("<slnum>")) + let s:netrw_menu_enabled= 1 + exe 'sil! menu '.g:NetrwMenuPriority.'.1 '.g:NetrwTopLvlMenu.'Help<tab><F1> <F1>' + exe 'sil! menu '.g:NetrwMenuPriority.'.5 '.g:NetrwTopLvlMenu.'-Sep1- :' + exe 'sil! menu '.g:NetrwMenuPriority.'.6 '.g:NetrwTopLvlMenu.'Go\ Up\ Directory<tab>- -' + exe 'sil! menu '.g:NetrwMenuPriority.'.7 '.g:NetrwTopLvlMenu.'Apply\ Special\ Viewer<tab>x x' + if g:netrw_dirhistmax > 0 + exe 'sil! menu '.g:NetrwMenuPriority.'.8.1 '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History.Bookmark\ Current\ Directory<tab>mb mb' + exe 'sil! menu '.g:NetrwMenuPriority.'.8.4 '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History.Goto\ Prev\ Dir\ (History)<tab>u u' + exe 'sil! menu '.g:NetrwMenuPriority.'.8.5 '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History.Goto\ Next\ Dir\ (History)<tab>U U' + exe 'sil! menu '.g:NetrwMenuPriority.'.8.6 '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History.List<tab>qb qb' + else + exe 'sil! menu '.g:NetrwMenuPriority.'.8 '.g:NetrwTopLvlMenu.'Bookmarks\ and\ History :echo "(disabled)"'."\<cr>" + endif + exe 'sil! menu '.g:NetrwMenuPriority.'.9.1 '.g:NetrwTopLvlMenu.'Browsing\ Control.Horizontal\ Split<tab>o o' + exe 'sil! menu '.g:NetrwMenuPriority.'.9.2 '.g:NetrwTopLvlMenu.'Browsing\ Control.Vertical\ Split<tab>v v' + exe 'sil! menu '.g:NetrwMenuPriority.'.9.3 '.g:NetrwTopLvlMenu.'Browsing\ Control.New\ Tab<tab>t t' + exe 'sil! menu '.g:NetrwMenuPriority.'.9.4 '.g:NetrwTopLvlMenu.'Browsing\ Control.Preview<tab>p p' + exe 'sil! menu '.g:NetrwMenuPriority.'.9.5 '.g:NetrwTopLvlMenu.'Browsing\ Control.Edit\ File\ Hiding\ List<tab><ctrl-h>'." \<c-h>'" + exe 'sil! menu '.g:NetrwMenuPriority.'.9.6 '.g:NetrwTopLvlMenu.'Browsing\ Control.Edit\ Sorting\ Sequence<tab>S S' + exe 'sil! menu '.g:NetrwMenuPriority.'.9.7 '.g:NetrwTopLvlMenu.'Browsing\ Control.Quick\ Hide/Unhide\ Dot\ Files<tab>'."gh gh" + exe 'sil! menu '.g:NetrwMenuPriority.'.9.8 '.g:NetrwTopLvlMenu.'Browsing\ Control.Refresh\ Listing<tab>'."<ctrl-l> \<c-l>" + exe 'sil! menu '.g:NetrwMenuPriority.'.9.9 '.g:NetrwTopLvlMenu.'Browsing\ Control.Settings/Options<tab>:NetrwSettings '.":NetrwSettings\<cr>" + exe 'sil! menu '.g:NetrwMenuPriority.'.10 '.g:NetrwTopLvlMenu.'Delete\ File/Directory<tab>D D' + exe 'sil! menu '.g:NetrwMenuPriority.'.11.1 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.Create\ New\ File<tab>% %' + exe 'sil! menu '.g:NetrwMenuPriority.'.11.1 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.In\ Current\ Window<tab><cr> '."\<cr>" + exe 'sil! menu '.g:NetrwMenuPriority.'.11.2 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.Preview\ File/Directory<tab>p p' + exe 'sil! menu '.g:NetrwMenuPriority.'.11.3 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.In\ Previous\ Window<tab>P P' + exe 'sil! menu '.g:NetrwMenuPriority.'.11.4 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.In\ New\ Window<tab>o o' + exe 'sil! menu '.g:NetrwMenuPriority.'.11.5 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.In\ New\ Tab<tab>t t' + exe 'sil! menu '.g:NetrwMenuPriority.'.11.5 '.g:NetrwTopLvlMenu.'Edit\ File/Dir.In\ New\ Vertical\ Window<tab>v v' + exe 'sil! menu '.g:NetrwMenuPriority.'.12.1 '.g:NetrwTopLvlMenu.'Explore.Directory\ Name :Explore ' + exe 'sil! menu '.g:NetrwMenuPriority.'.12.2 '.g:NetrwTopLvlMenu.'Explore.Filenames\ Matching\ Pattern\ (curdir\ only)<tab>:Explore\ */ :Explore */' + exe 'sil! menu '.g:NetrwMenuPriority.'.12.2 '.g:NetrwTopLvlMenu.'Explore.Filenames\ Matching\ Pattern\ (+subdirs)<tab>:Explore\ **/ :Explore **/' + exe 'sil! menu '.g:NetrwMenuPriority.'.12.3 '.g:NetrwTopLvlMenu.'Explore.Files\ Containing\ String\ Pattern\ (curdir\ only)<tab>:Explore\ *// :Explore *//' + exe 'sil! menu '.g:NetrwMenuPriority.'.12.4 '.g:NetrwTopLvlMenu.'Explore.Files\ Containing\ String\ Pattern\ (+subdirs)<tab>:Explore\ **// :Explore **//' + exe 'sil! menu '.g:NetrwMenuPriority.'.12.4 '.g:NetrwTopLvlMenu.'Explore.Next\ Match<tab>:Nexplore :Nexplore<cr>' + exe 'sil! menu '.g:NetrwMenuPriority.'.12.4 '.g:NetrwTopLvlMenu.'Explore.Prev\ Match<tab>:Pexplore :Pexplore<cr>' + exe 'sil! menu '.g:NetrwMenuPriority.'.13 '.g:NetrwTopLvlMenu.'Make\ Subdirectory<tab>d d' + exe 'sil! menu '.g:NetrwMenuPriority.'.14.1 '.g:NetrwTopLvlMenu.'Marked\ Files.Mark\ File<tab>mf mf' + exe 'sil! menu '.g:NetrwMenuPriority.'.14.2 '.g:NetrwTopLvlMenu.'Marked\ Files.Mark\ Files\ by\ Regexp<tab>mr mr' + exe 'sil! menu '.g:NetrwMenuPriority.'.14.3 '.g:NetrwTopLvlMenu.'Marked\ Files.Hide-Show-List\ Control<tab>a a' + exe 'sil! menu '.g:NetrwMenuPriority.'.14.4 '.g:NetrwTopLvlMenu.'Marked\ Files.Copy\ To\ Target<tab>mc mc' + exe 'sil! menu '.g:NetrwMenuPriority.'.14.5 '.g:NetrwTopLvlMenu.'Marked\ Files.Delete<tab>D D' + exe 'sil! menu '.g:NetrwMenuPriority.'.14.6 '.g:NetrwTopLvlMenu.'Marked\ Files.Diff<tab>md md' + exe 'sil! menu '.g:NetrwMenuPriority.'.14.7 '.g:NetrwTopLvlMenu.'Marked\ Files.Edit<tab>me me' + exe 'sil! menu '.g:NetrwMenuPriority.'.14.8 '.g:NetrwTopLvlMenu.'Marked\ Files.Exe\ Cmd<tab>mx mx' + exe 'sil! menu '.g:NetrwMenuPriority.'.14.9 '.g:NetrwTopLvlMenu.'Marked\ Files.Move\ To\ Target<tab>mm mm' + exe 'sil! menu '.g:NetrwMenuPriority.'.14.10 '.g:NetrwTopLvlMenu.'Marked\ Files.Obtain<tab>O O' + exe 'sil! menu '.g:NetrwMenuPriority.'.14.11 '.g:NetrwTopLvlMenu.'Marked\ Files.Print<tab>mp mp' + exe 'sil! menu '.g:NetrwMenuPriority.'.14.12 '.g:NetrwTopLvlMenu.'Marked\ Files.Replace<tab>R R' + exe 'sil! menu '.g:NetrwMenuPriority.'.14.13 '.g:NetrwTopLvlMenu.'Marked\ Files.Set\ Target<tab>mt mt' + exe 'sil! menu '.g:NetrwMenuPriority.'.14.14 '.g:NetrwTopLvlMenu.'Marked\ Files.Tag<tab>mT mT' + exe 'sil! menu '.g:NetrwMenuPriority.'.14.15 '.g:NetrwTopLvlMenu.'Marked\ Files.Zip/Unzip/Compress/Uncompress<tab>mz mz' + exe 'sil! menu '.g:NetrwMenuPriority.'.15 '.g:NetrwTopLvlMenu.'Obtain\ File<tab>O O' + exe 'sil! menu '.g:NetrwMenuPriority.'.16.1.1 '.g:NetrwTopLvlMenu.'Style.Listing.thin<tab>i :let w:netrw_liststyle=0<cr><c-L>' + exe 'sil! menu '.g:NetrwMenuPriority.'.16.1.1 '.g:NetrwTopLvlMenu.'Style.Listing.long<tab>i :let w:netrw_liststyle=1<cr><c-L>' + exe 'sil! menu '.g:NetrwMenuPriority.'.16.1.1 '.g:NetrwTopLvlMenu.'Style.Listing.wide<tab>i :let w:netrw_liststyle=2<cr><c-L>' + exe 'sil! menu '.g:NetrwMenuPriority.'.16.1.1 '.g:NetrwTopLvlMenu.'Style.Listing.tree<tab>i :let w:netrw_liststyle=3<cr><c-L>' + exe 'sil! menu '.g:NetrwMenuPriority.'.16.2.1 '.g:NetrwTopLvlMenu.'Style.Normal-Hide-Show.Show\ All<tab>a :let g:netrw_hide=0<cr><c-L>' + exe 'sil! menu '.g:NetrwMenuPriority.'.16.2.3 '.g:NetrwTopLvlMenu.'Style.Normal-Hide-Show.Normal<tab>a :let g:netrw_hide=1<cr><c-L>' + exe 'sil! menu '.g:NetrwMenuPriority.'.16.2.2 '.g:NetrwTopLvlMenu.'Style.Normal-Hide-Show.Hidden\ Only<tab>a :let g:netrw_hide=2<cr><c-L>' + exe 'sil! menu '.g:NetrwMenuPriority.'.16.3 '.g:NetrwTopLvlMenu.'Style.Reverse\ Sorting\ Order<tab>'."r r" + exe 'sil! menu '.g:NetrwMenuPriority.'.16.4.1 '.g:NetrwTopLvlMenu.'Style.Sorting\ Method.Name<tab>s :let g:netrw_sort_by="name"<cr><c-L>' + exe 'sil! menu '.g:NetrwMenuPriority.'.16.4.2 '.g:NetrwTopLvlMenu.'Style.Sorting\ Method.Time<tab>s :let g:netrw_sort_by="time"<cr><c-L>' + exe 'sil! menu '.g:NetrwMenuPriority.'.16.4.3 '.g:NetrwTopLvlMenu.'Style.Sorting\ Method.Size<tab>s :let g:netrw_sort_by="size"<cr><c-L>' + exe 'sil! menu '.g:NetrwMenuPriority.'.16.4.3 '.g:NetrwTopLvlMenu.'Style.Sorting\ Method.Exten<tab>s :let g:netrw_sort_by="exten"<cr><c-L>' + exe 'sil! menu '.g:NetrwMenuPriority.'.17 '.g:NetrwTopLvlMenu.'Rename\ File/Directory<tab>R R' + exe 'sil! menu '.g:NetrwMenuPriority.'.18 '.g:NetrwTopLvlMenu.'Set\ Current\ Directory<tab>c c' + let s:netrw_menucnt= 28 + call s:NetrwBookmarkMenu() " provide some history! uses priorities 2,3, reserves 4, 8.2.x + call s:NetrwTgtMenu() " let bookmarks and history be easy targets + + elseif !a:domenu + let s:netrwcnt = 0 + let curwin = winnr() + windo if getline(2) =~# "Netrw" | let s:netrwcnt= s:netrwcnt + 1 | endif + exe curwin."wincmd w" + + if s:netrwcnt <= 1 + " call Decho("clear menus",'~'.expand("<slnum>")) + exe 'sil! unmenu '.g:NetrwTopLvlMenu + " call Decho('exe sil! unmenu '.g:NetrwTopLvlMenu.'*','~'.expand("<slnum>")) + sil! unlet s:netrw_menu_enabled + endif + endif + " call Dret("NetrwMenu") + return + endif + +endfun + +" --------------------------------------------------------------------- +" s:NetrwObtain: obtain file under cursor or from markfile list {{{2 +" Used by the O maps (as <SID>NetrwObtain()) +fun! s:NetrwObtain(islocal) + " call Dfunc("NetrwObtain(islocal=".a:islocal.")") + + let ykeep= @@ + if exists("s:netrwmarkfilelist_{bufnr('%')}") + let islocal= s:netrwmarkfilelist_{bufnr('%')}[1] !~ '^\a\{3,}://' + call netrw#Obtain(islocal,s:netrwmarkfilelist_{bufnr('%')}) + call s:NetrwUnmarkList(bufnr('%'),b:netrw_curdir) + else + call netrw#Obtain(a:islocal,s:NetrwGetWord()) + endif + let @@= ykeep + + " call Dret("NetrwObtain") +endfun + +" --------------------------------------------------------------------- +" s:NetrwPrevWinOpen: open file/directory in previous window. {{{2 +" If there's only one window, then the window will first be split. +" Returns: +" choice = 0 : didn't have to choose +" choice = 1 : saved modified file in window first +" choice = 2 : didn't save modified file, opened window +" choice = 3 : cancel open +fun! s:NetrwPrevWinOpen(islocal) + let ykeep= @@ + " grab a copy of the b:netrw_curdir to pass it along to newly split windows + let curdir = b:netrw_curdir + + " get last window number and the word currently under the cursor + let origwin = winnr() + let lastwinnr = winnr("$") + let curword = s:NetrwGetWord() + let choice = 0 + let s:prevwinopen= 1 " lets s:NetrwTreeDir() know that NetrwPrevWinOpen called it (s:NetrwTreeDir() will unlet s:prevwinopen) + let s:treedir = s:NetrwTreeDir(a:islocal) + let curdir = s:treedir + + let didsplit = 0 + if lastwinnr == 1 + " if only one window, open a new one first + " g:netrw_preview=0: preview window shown in a horizontally split window + " g:netrw_preview=1: preview window shown in a vertically split window + if g:netrw_preview + " vertically split preview window + let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize + exe (g:netrw_alto? "top " : "bot ")."vert ".winsz."wincmd s" + else + " horizontally split preview window + let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winheight(0))/100 : -g:netrw_winsize + exe (g:netrw_alto? "bel " : "abo ").winsz."wincmd s" + endif + let didsplit = 1 + + else + NetrwKeepj call s:SaveBufVars() + let eikeep= &ei + setl ei=all + wincmd p + + if exists("s:lexplore_win") && s:lexplore_win == winnr() + " whoops -- user trying to open file in the Lexplore window. + " Use Lexplore's opening-file window instead. + " exe g:netrw_chgwin."wincmd w" + wincmd p + call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,s:NetrwGetWord(),1)) + endif + + " prevwinnr: the window number of the "prev" window + " prevbufnr: the buffer number of the buffer in the "prev" window + " bnrcnt : the qty of windows open on the "prev" buffer + let prevwinnr = winnr() + let prevbufnr = bufnr("%") + let prevbufname = bufname("%") + let prevmod = &mod + let bnrcnt = 0 + NetrwKeepj call s:RestoreBufVars() + + " if the previous window's buffer has been changed (ie. its modified flag is set), + " and it doesn't appear in any other extant window, then ask the + " user if s/he wants to abandon modifications therein. + if prevmod + windo if winbufnr(0) == prevbufnr | let bnrcnt=bnrcnt+1 | endif + exe prevwinnr."wincmd w" + + if bnrcnt == 1 && &hidden == 0 + " only one copy of the modified buffer in a window, and + " hidden not set, so overwriting will lose the modified file. Ask first... + let choice = confirm("Save modified buffer<".prevbufname."> first?","&Yes\n&No\n&Cancel") + let &ei= eikeep + + if choice == 1 + " Yes -- write file & then browse + let v:errmsg= "" + sil w + if v:errmsg != "" + call netrw#ErrorMsg(s:ERROR,"unable to write <".(exists("prevbufname")? prevbufname : 'n/a').">!",30) + exe origwin."wincmd w" + let &ei = eikeep + let @@ = ykeep + return choice + endif + + elseif choice == 2 + " No -- don't worry about changed file, just browse anyway + echomsg "**note** changes to ".prevbufname." abandoned" + + else + " Cancel -- don't do this + exe origwin."wincmd w" + let &ei= eikeep + let @@ = ykeep + return choice + endif + endif + endif + let &ei= eikeep + endif + + " restore b:netrw_curdir (window split/enew may have lost it) + let b:netrw_curdir= curdir + if a:islocal < 2 + if a:islocal + call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(a:islocal,curword,0)) + else + call s:NetrwBrowse(a:islocal,s:NetrwBrowseChgDir(a:islocal,curword,0)) + endif + endif + let @@= ykeep + return choice +endfun + +" --------------------------------------------------------------------- +" s:NetrwUpload: load fname to tgt (used by NetrwMarkFileCopy()) {{{2 +" Always assumed to be local -> remote +" call s:NetrwUpload(filename, target) +" call s:NetrwUpload(filename, target, fromdirectory) +fun! s:NetrwUpload(fname,tgt,...) + " call Dfunc("s:NetrwUpload(fname<".((type(a:fname) == 1)? a:fname : string(a:fname))."> tgt<".a:tgt.">) a:0=".a:0) + + if a:tgt =~ '^\a\{3,}://' + let tgtdir= substitute(a:tgt,'^\a\{3,}://[^/]\+/\(.\{-}\)$','\1','') + else + let tgtdir= substitute(a:tgt,'^\(.*\)/[^/]*$','\1','') + endif + " call Decho("tgtdir<".tgtdir.">",'~'.expand("<slnum>")) + + if a:0 > 0 + let fromdir= a:1 + else + let fromdir= getcwd() + endif + " call Decho("fromdir<".fromdir.">",'~'.expand("<slnum>")) + + if type(a:fname) == 1 + " handle uploading a single file using NetWrite + " call Decho("handle uploading a single file via NetWrite",'~'.expand("<slnum>")) + 1split + " call Decho("exe e ".fnameescape(s:NetrwFile(a:fname)),'~'.expand("<slnum>")) + exe "NetrwKeepj e ".fnameescape(s:NetrwFile(a:fname)) + " call Decho("now locally editing<".expand("%").">, has ".line("$")." lines",'~'.expand("<slnum>")) + if a:tgt =~ '/$' + let wfname= substitute(a:fname,'^.*/','','') + " call Decho("exe w! ".fnameescape(wfname),'~'.expand("<slnum>")) + exe "w! ".fnameescape(a:tgt.wfname) + else + " call Decho("writing local->remote: exe w ".fnameescape(a:tgt),'~'.expand("<slnum>")) + exe "w ".fnameescape(a:tgt) + " call Decho("done writing local->remote",'~'.expand("<slnum>")) + endif + q! + + elseif type(a:fname) == 3 + " handle uploading a list of files via scp + " call Decho("handle uploading a list of files via scp",'~'.expand("<slnum>")) + let curdir= getcwd() + if a:tgt =~ '^scp:' + if s:NetrwLcd(fromdir) + " call Dret("s:NetrwUpload : lcd failure") + return + endif + let filelist= deepcopy(s:netrwmarkfilelist_{bufnr('%')}) + let args = join(map(filelist,"s:ShellEscape(v:val, 1)")) + if exists("g:netrw_port") && g:netrw_port != "" + let useport= " ".g:netrw_scpport." ".g:netrw_port + else + let useport= "" + endif + let machine = substitute(a:tgt,'^scp://\([^/:]\+\).*$','\1','') + let tgt = substitute(a:tgt,'^scp://[^/]\+/\(.*\)$','\1','') + call s:NetrwExe(s:netrw_silentxfer."!".g:netrw_scp_cmd.s:ShellEscape(useport,1)." ".args." ".s:ShellEscape(machine.":".tgt,1)) + if s:NetrwLcd(curdir) + " call Dret("s:NetrwUpload : lcd failure") + return + endif + + elseif a:tgt =~ '^ftp:' + call s:NetrwMethod(a:tgt) + + if b:netrw_method == 2 + " handle uploading a list of files via ftp+.netrc + let netrw_fname = b:netrw_fname + sil NetrwKeepj new + " call Decho("filter input window#".winnr(),'~'.expand("<slnum>")) + + NetrwKeepj put =g:netrw_ftpmode + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + + if exists("g:netrw_ftpextracmd") + NetrwKeepj put =g:netrw_ftpextracmd + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + endif + + NetrwKeepj call setline(line("$")+1,'lcd "'.fromdir.'"') + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + + if tgtdir == "" + let tgtdir= '/' + endif + NetrwKeepj call setline(line("$")+1,'cd "'.tgtdir.'"') + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + + for fname in a:fname + NetrwKeepj call setline(line("$")+1,'put "'.s:NetrwFile(fname).'"') + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + endfor + + if exists("g:netrw_port") && g:netrw_port != "" + call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1)) + else + " call Decho("filter input window#".winnr(),'~'.expand("<slnum>")) + call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)) + endif + " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) + sil NetrwKeepj g/Local directory now/d + call histdel("/",-1) + if getline(1) !~ "^$" && !exists("g:netrw_quiet") && getline(1) !~ '^Trying ' + call netrw#ErrorMsg(s:ERROR,getline(1),14) + else + bw!|q + endif + + elseif b:netrw_method == 3 + " upload with ftp + machine, id, passwd, and fname (ie. no .netrc) + let netrw_fname= b:netrw_fname + NetrwKeepj call s:SaveBufVars()|sil NetrwKeepj new|NetrwKeepj call s:RestoreBufVars() + let tmpbufnr= bufnr("%") + setl ff=unix + + if exists("g:netrw_port") && g:netrw_port != "" + NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + else + NetrwKeepj put ='open '.g:netrw_machine + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + endif + + if exists("g:netrw_uid") && g:netrw_uid != "" + if exists("g:netrw_ftp") && g:netrw_ftp == 1 + NetrwKeepj put =g:netrw_uid + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + if exists("s:netrw_passwd") + NetrwKeepj call setline(line("$")+1,'"'.s:netrw_passwd.'"') + endif + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + elseif exists("s:netrw_passwd") + NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"' + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + endif + endif + + NetrwKeepj call setline(line("$")+1,'lcd "'.fromdir.'"') + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + + if exists("b:netrw_fname") && b:netrw_fname != "" + NetrwKeepj call setline(line("$")+1,'cd "'.b:netrw_fname.'"') + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + endif + + if exists("g:netrw_ftpextracmd") + NetrwKeepj put =g:netrw_ftpextracmd + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + endif + + for fname in a:fname + NetrwKeepj call setline(line("$")+1,'put "'.fname.'"') + " call Decho("filter input: ".getline('$'),'~'.expand("<slnum>")) + endfor + + " perform ftp: + " -i : turns off interactive prompting from ftp + " -n unix : DON'T use <.netrc>, even though it exists + " -n win32: quit being obnoxious about password + NetrwKeepj norm! 1G"_dd + call s:NetrwExe(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." ".g:netrw_ftp_options) + " If the result of the ftp operation isn't blank, show an error message (tnx to Doug Claar) + sil NetrwKeepj g/Local directory now/d + call histdel("/",-1) + if getline(1) !~ "^$" && !exists("g:netrw_quiet") && getline(1) !~ '^Trying ' + let debugkeep= &debug + setl debug=msg + call netrw#ErrorMsg(s:ERROR,getline(1),15) + let &debug = debugkeep + let mod = 1 + else + bw!|q + endif + elseif !exists("b:netrw_method") || b:netrw_method < 0 + " call Dret("s:#NetrwUpload : unsupported method") + return + endif + else + call netrw#ErrorMsg(s:ERROR,"can't obtain files with protocol from<".a:tgt.">",63) + endif + endif + + " call Dret("s:NetrwUpload") +endfun + +" --------------------------------------------------------------------- +" s:NetrwPreview: supports netrw's "p" map {{{2 +fun! s:NetrwPreview(path) range + " call Dfunc("NetrwPreview(path<".a:path.">)") + " call Decho("g:netrw_alto =".(exists("g:netrw_alto")? g:netrw_alto : 'n/a'),'~'.expand("<slnum>")) + " call Decho("g:netrw_preview=".(exists("g:netrw_preview")? g:netrw_preview : 'n/a'),'~'.expand("<slnum>")) + let ykeep= @@ + NetrwKeepj call s:NetrwOptionsSave("s:") + if a:path !~ '^\*\{1,2}/' && a:path !~ '^\a\{3,}://' + NetrwKeepj call s:NetrwOptionsSafe(1) + else + NetrwKeepj call s:NetrwOptionsSafe(0) + endif + if has("quickfix") + " call Decho("has quickfix",'~'.expand("<slnum>")) + if !isdirectory(s:NetrwFile(a:path)) + " call Decho("good; not previewing a directory",'~'.expand("<slnum>")) + if g:netrw_preview + " vertical split + let pvhkeep = &pvh + let winsz = (g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize + let &pvh = winwidth(0) - winsz + " call Decho("g:netrw_preview: winsz=".winsz." &pvh=".&pvh." (temporarily) g:netrw_winsize=".g:netrw_winsize,'~'.expand("<slnum>")) + else + " horizontal split + let pvhkeep = &pvh + let winsz = (g:netrw_winsize > 0)? (g:netrw_winsize*winheight(0))/100 : -g:netrw_winsize + let &pvh = winheight(0) - winsz + " call Decho("!g:netrw_preview: winsz=".winsz." &pvh=".&pvh." (temporarily) g:netrw_winsize=".g:netrw_winsize,'~'.expand("<slnum>")) + endif + " g:netrw_preview g:netrw_alto + " 1 : vert 1: top -- preview window is vertically split off and on the left + " 1 : vert 0: bot -- preview window is vertically split off and on the right + " 0 : 1: top -- preview window is horizontally split off and on the top + " 0 : 0: bot -- preview window is horizontally split off and on the bottom + " + " Note that the file being previewed is already known to not be a directory, hence we can avoid doing a LocalBrowseCheck() check via + " the BufEnter event set up in netrwPlugin.vim + " call Decho("exe ".(g:netrw_alto? "top " : "bot ").(g:netrw_preview? "vert " : "")."pedit ".fnameescape(a:path),'~'.expand("<slnum>")) + let eikeep = &ei + set ei=BufEnter + exe (g:netrw_alto? "top " : "bot ").(g:netrw_preview? "vert " : "")."pedit ".fnameescape(a:path) + let &ei= eikeep + " call Decho("winnr($)=".winnr("$"),'~'.expand("<slnum>")) + if exists("pvhkeep") + let &pvh= pvhkeep + endif + elseif !exists("g:netrw_quiet") + NetrwKeepj call netrw#ErrorMsg(s:WARNING,"sorry, cannot preview a directory such as <".a:path.">",38) + endif + elseif !exists("g:netrw_quiet") + NetrwKeepj call netrw#ErrorMsg(s:WARNING,"sorry, to preview your vim needs the quickfix feature compiled in",39) + endif + NetrwKeepj call s:NetrwOptionsRestore("s:") + let @@= ykeep + " call Dret("NetrwPreview") +endfun + +" --------------------------------------------------------------------- +" s:NetrwRefresh: {{{2 +fun! s:NetrwRefresh(islocal,dirname) + " call Dfunc("s:NetrwRefresh(islocal<".a:islocal.">,dirname=".a:dirname.") g:netrw_hide=".g:netrw_hide." g:netrw_sort_direction=".g:netrw_sort_direction) + " at the current time (Mar 19, 2007) all calls to NetrwRefresh() call NetrwBrowseChgDir() first. + setl ma noro + " call Decho("setl ma noro",'~'.expand("<slnum>")) + " call Decho("clear buffer<".expand("%")."> with :%d",'~'.expand("<slnum>")) + let ykeep = @@ + if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST + if !exists("w:netrw_treetop") + if exists("b:netrw_curdir") + let w:netrw_treetop= b:netrw_curdir + else + let w:netrw_treetop= getcwd() + endif + endif + NetrwKeepj call s:NetrwRefreshTreeDict(w:netrw_treetop) + endif + + " save the cursor position before refresh. + let screenposn = winsaveview() + " call Decho("saving posn to screenposn<".string(screenposn).">",'~'.expand("<slnum>")) + + " call Decho("win#".winnr().": ".winheight(0)."x".winwidth(0)." curfile<".expand("%").">",'~'.expand("<slnum>")) + " call Decho("clearing buffer prior to refresh",'~'.expand("<slnum>")) + sil! NetrwKeepj %d _ + if a:islocal + NetrwKeepj call netrw#LocalBrowseCheck(a:dirname) + else + NetrwKeepj call s:NetrwBrowse(a:islocal,a:dirname) + endif + + " restore position + " call Decho("restoring posn to screenposn<".string(screenposn).">",'~'.expand("<slnum>")) + NetrwKeepj call winrestview(screenposn) + + " restore file marks + if has("syntax") && exists("g:syntax_on") && g:syntax_on + if exists("s:netrwmarkfilemtch_{bufnr('%')}") && s:netrwmarkfilemtch_{bufnr("%")} != "" + " " call Decho("exe 2match netrwMarkFile /".s:netrwmarkfilemtch_{bufnr("%")}."/",'~'.expand("<slnum>")) + exe "2match netrwMarkFile /".s:netrwmarkfilemtch_{bufnr("%")}."/" + else + " " call Decho("2match none (bufnr(%)=".bufnr("%")."<".bufname("%").">)",'~'.expand("<slnum>")) + 2match none + endif + endif + + " restore + let @@= ykeep + " call Dret("s:NetrwRefresh") +endfun + +" --------------------------------------------------------------------- +" s:NetrwRefreshDir: refreshes a directory by name {{{2 +" Called by NetrwMarkFileCopy() +" Interfaces to s:NetrwRefresh() and s:LocalBrowseRefresh() +fun! s:NetrwRefreshDir(islocal,dirname) + if g:netrw_fastbrowse == 0 + " slowest mode (keep buffers refreshed, local or remote) + let tgtwin= bufwinnr(a:dirname) + + if tgtwin > 0 + " tgtwin is being displayed, so refresh it + let curwin= winnr() + exe tgtwin."wincmd w" + NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) + exe curwin."wincmd w" + + elseif bufnr(a:dirname) > 0 + let bn= bufnr(a:dirname) + exe "sil keepj bd ".bn + endif + + elseif g:netrw_fastbrowse <= 1 + NetrwKeepj call s:LocalBrowseRefresh() + endif +endfun + +" --------------------------------------------------------------------- +" s:NetrwSetChgwin: set g:netrw_chgwin; a <cr> will use the specified +" window number to do its editing in. +" Supports [count]C where the count, if present, is used to specify +" a window to use for editing via the <cr> mapping. +fun! s:NetrwSetChgwin(...) + " call Dfunc("s:NetrwSetChgwin() v:count=".v:count) + if a:0 > 0 + " call Decho("a:1<".a:1.">",'~'.expand("<slnum>")) + if a:1 == "" " :NetrwC win# + let g:netrw_chgwin= winnr() + else " :NetrwC + let g:netrw_chgwin= a:1 + endif + elseif v:count > 0 " [count]C + let g:netrw_chgwin= v:count + else " C + let g:netrw_chgwin= winnr() + endif + echo "editing window now set to window#".g:netrw_chgwin + " call Dret("s:NetrwSetChgwin : g:netrw_chgwin=".g:netrw_chgwin) +endfun + +" --------------------------------------------------------------------- +" s:NetrwSetSort: sets up the sort based on the g:netrw_sort_sequence {{{2 +" What this function does is to compute a priority for the patterns +" in the g:netrw_sort_sequence. It applies a substitute to any +" "files" that satisfy each pattern, putting the priority / in +" front. An "*" pattern handles the default priority. +fun! s:NetrwSetSort() + " call Dfunc("SetSort() bannercnt=".w:netrw_bannercnt) + let ykeep= @@ + if w:netrw_liststyle == s:LONGLIST + let seqlist = substitute(g:netrw_sort_sequence,'\$','\\%(\t\\|\$\\)','ge') + else + let seqlist = g:netrw_sort_sequence + endif + " sanity check -- insure that * appears somewhere + if seqlist == "" + let seqlist= '*' + elseif seqlist !~ '\*' + let seqlist= seqlist.',*' + endif + let priority = 1 + while seqlist != "" + if seqlist =~ ',' + let seq = substitute(seqlist,',.*$','','e') + let seqlist = substitute(seqlist,'^.\{-},\(.*\)$','\1','e') + else + let seq = seqlist + let seqlist = "" + endif + if priority < 10 + let spriority= "00".priority.g:netrw_sepchr + elseif priority < 100 + let spriority= "0".priority.g:netrw_sepchr + else + let spriority= priority.g:netrw_sepchr + endif + " call Decho("priority=".priority." spriority<".spriority."> seq<".seq."> seqlist<".seqlist.">",'~'.expand("<slnum>")) + + " sanity check + if w:netrw_bannercnt > line("$") + " apparently no files were left after a Hiding pattern was used + " call Dret("SetSort : no files left after hiding") + return + endif + if seq == '*' + let starpriority= spriority + else + exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g/'.seq.'/s/^/'.spriority.'/' + call histdel("/",-1) + " sometimes multiple sorting patterns will match the same file or directory. + " The following substitute is intended to remove the excess matches. + exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g/^\d\{3}'.g:netrw_sepchr.'\d\{3}\//s/^\d\{3}'.g:netrw_sepchr.'\(\d\{3}\/\).\@=/\1/e' + NetrwKeepj call histdel("/",-1) + endif + let priority = priority + 1 + endwhile + if exists("starpriority") + exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$v/^\d\{3}'.g:netrw_sepchr.'/s/^/'.starpriority.'/e' + NetrwKeepj call histdel("/",-1) + endif + + " Following line associated with priority -- items that satisfy a priority + " pattern get prefixed by ###/ which permits easy sorting by priority. + " Sometimes files can satisfy multiple priority patterns -- only the latest + " priority pattern needs to be retained. So, at this point, these excess + " priority prefixes need to be removed, but not directories that happen to + " be just digits themselves. + exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$s/^\(\d\{3}'.g:netrw_sepchr.'\)\%(\d\{3}'.g:netrw_sepchr.'\)\+\ze./\1/e' + NetrwKeepj call histdel("/",-1) + let @@= ykeep + + " call Dret("SetSort") +endfun + +" --------------------------------------------------------------------- +" s:NetrwSetTgt: sets the target to the specified choice index {{{2 +" Implements [count]Tb (bookhist<b>) +" [count]Th (bookhist<h>) +" See :help netrw-qb for how to make the choice. +fun! s:NetrwSetTgt(islocal,bookhist,choice) + " call Dfunc("s:NetrwSetTgt(islocal=".a:islocal." bookhist<".a:bookhist."> choice#".a:choice.")") + + if a:bookhist == 'b' + " supports choosing a bookmark as a target using a qb-generated list + let choice= a:choice - 1 + if exists("g:netrw_bookmarklist[".choice."]") + call netrw#MakeTgt(g:netrw_bookmarklist[choice]) + else + echomsg "Sorry, bookmark#".a:choice." doesn't exist!" + endif + + elseif a:bookhist == 'h' + " supports choosing a history stack entry as a target using a qb-generated list + let choice= (a:choice % g:netrw_dirhistmax) + 1 + if exists("g:netrw_dirhist_".choice) + let histentry = g:netrw_dirhist_{choice} + call netrw#MakeTgt(histentry) + else + echomsg "Sorry, history#".a:choice." not available!" + endif + endif + + " refresh the display + if !exists("b:netrw_curdir") + let b:netrw_curdir= getcwd() + endif + call s:NetrwRefresh(a:islocal,b:netrw_curdir) + + " call Dret("s:NetrwSetTgt") +endfun + +" ===================================================================== +" s:NetrwSortStyle: change sorting style (name - time - size - exten) and refresh display {{{2 +fun! s:NetrwSortStyle(islocal) + NetrwKeepj call s:NetrwSaveWordPosn() + let svpos= winsaveview() + + 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,'./',0)) + NetrwKeepj call winrestview(svpos) +endfun + +" --------------------------------------------------------------------- +" s:NetrwSplit: mode {{{2 +" =0 : net and o +" =1 : net and t +" =2 : net and v +" =3 : local and o +" =4 : local and t +" =5 : local and v +fun! s:NetrwSplit(mode) + + let ykeep= @@ + call s:SaveWinVars() + + if a:mode == 0 + " remote and o + let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winheight(0))/100 : -g:netrw_winsize + if winsz == 0|let winsz= ""|endif + exe (g:netrw_alto? "bel " : "abo ").winsz."wincmd s" + let s:didsplit= 1 + NetrwKeepj call s:RestoreWinVars() + NetrwKeepj call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,s:NetrwGetWord(),1)) + unlet s:didsplit + + elseif a:mode == 1 + " remote and t + let newdir = s:NetrwBrowseChgDir(0,s:NetrwGetWord(),1) + tabnew + let s:didsplit= 1 + NetrwKeepj call s:RestoreWinVars() + NetrwKeepj call s:NetrwBrowse(0,newdir) + unlet s:didsplit + + elseif a:mode == 2 + " remote and v + let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize + if winsz == 0|let winsz= ""|endif + exe (g:netrw_altv? "rightb " : "lefta ").winsz."wincmd v" + let s:didsplit= 1 + NetrwKeepj call s:RestoreWinVars() + NetrwKeepj call s:NetrwBrowse(0,s:NetrwBrowseChgDir(0,s:NetrwGetWord(),1)) + unlet s:didsplit + + elseif a:mode == 3 + " local and o + let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winheight(0))/100 : -g:netrw_winsize + if winsz == 0|let winsz= ""|endif + exe (g:netrw_alto? "bel " : "abo ").winsz."wincmd s" + let s:didsplit= 1 + NetrwKeepj call s:RestoreWinVars() + NetrwKeepj call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,s:NetrwGetWord(),1)) + unlet s:didsplit + + elseif a:mode == 4 + " local and t + let cursorword = s:NetrwGetWord() + let eikeep = &ei + let netrw_winnr = winnr() + let netrw_line = line(".") + let netrw_col = virtcol(".") + NetrwKeepj norm! H0 + let netrw_hline = line(".") + setl ei=all + exe "NetrwKeepj norm! ".netrw_hline."G0z\<CR>" + exe "NetrwKeepj norm! ".netrw_line."G0".netrw_col."\<bar>" + let &ei = eikeep + let netrw_curdir = s:NetrwTreeDir(0) + tabnew + let b:netrw_curdir = netrw_curdir + let s:didsplit = 1 + NetrwKeepj call s:RestoreWinVars() + NetrwKeepj call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,cursorword,0)) + if &ft == "netrw" + setl ei=all + exe "NetrwKeepj norm! ".netrw_hline."G0z\<CR>" + exe "NetrwKeepj norm! ".netrw_line."G0".netrw_col."\<bar>" + let &ei= eikeep + endif + unlet s:didsplit + + elseif a:mode == 5 + " local and v + let winsz= (g:netrw_winsize > 0)? (g:netrw_winsize*winwidth(0))/100 : -g:netrw_winsize + if winsz == 0|let winsz= ""|endif + exe (g:netrw_altv? "rightb " : "lefta ").winsz."wincmd v" + let s:didsplit= 1 + NetrwKeepj call s:RestoreWinVars() + NetrwKeepj call netrw#LocalBrowseCheck(s:NetrwBrowseChgDir(1,s:NetrwGetWord(),1)) + unlet s:didsplit + + else + NetrwKeepj call netrw#ErrorMsg(s:ERROR,"(NetrwSplit) unsupported mode=".a:mode,45) + endif + + let @@= ykeep +endfun + +" --------------------------------------------------------------------- +" s:NetrwTgtMenu: {{{2 +fun! s:NetrwTgtMenu() + if !exists("s:netrw_menucnt") + return + endif + " call Dfunc("s:NetrwTgtMenu()") + + " the following test assures that gvim is running, has menus available, and has menus enabled. + if has("gui") && has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu + if exists("g:NetrwTopLvlMenu") + " call Decho("removing ".g:NetrwTopLvlMenu."Bookmarks menu item(s)",'~'.expand("<slnum>")) + exe 'sil! unmenu '.g:NetrwTopLvlMenu.'Targets' + endif + if !exists("s:netrw_initbookhist") + call s:NetrwBookHistRead() + endif + + " try to cull duplicate entries + let tgtdict={} + + " target bookmarked places + if exists("g:netrw_bookmarklist") && g:netrw_bookmarklist != [] && g:netrw_dirhistmax > 0 + " call Decho("installing bookmarks as easy targets",'~'.expand("<slnum>")) + let cnt= 1 + for bmd in g:netrw_bookmarklist + if has_key(tgtdict,bmd) + let cnt= cnt + 1 + continue + endif + let tgtdict[bmd]= cnt + let ebmd= escape(bmd,g:netrw_menu_escape) + " show bookmarks for goto menu + " call Decho("menu: Targets: ".bmd,'~'.expand("<slnum>")) + exe 'sil! menu <silent> '.g:NetrwMenuPriority.".19.1.".cnt." ".g:NetrwTopLvlMenu.'Targets.'.ebmd." :call netrw#MakeTgt('".bmd."')\<cr>" + let cnt= cnt + 1 + endfor + endif + + " target directory browsing history + if exists("g:netrw_dirhistmax") && g:netrw_dirhistmax > 0 + " call Decho("installing history as easy targets (histmax=".g:netrw_dirhistmax.")",'~'.expand("<slnum>")) + let histcnt = 1 + while histcnt <= g:netrw_dirhistmax + let priority = g:netrw_dirhistcnt + histcnt + if exists("g:netrw_dirhist_{histcnt}") + let histentry = g:netrw_dirhist_{histcnt} + if has_key(tgtdict,histentry) + let histcnt = histcnt + 1 + continue + endif + let tgtdict[histentry] = histcnt + let ehistentry = escape(histentry,g:netrw_menu_escape) + " call Decho("menu: Targets: ".histentry,'~'.expand("<slnum>")) + exe 'sil! menu <silent> '.g:NetrwMenuPriority.".19.2.".priority." ".g:NetrwTopLvlMenu.'Targets.'.ehistentry." :call netrw#MakeTgt('".histentry."')\<cr>" + endif + let histcnt = histcnt + 1 + endwhile + endif + endif + " call Dret("s:NetrwTgtMenu") +endfun + +" --------------------------------------------------------------------- +" s:NetrwTreeDir: determine tree directory given current cursor position {{{2 +" (full path directory with trailing slash returned) +fun! s:NetrwTreeDir(islocal) + + if exists("s:treedir") && exists("s:prevwinopen") + " s:NetrwPrevWinOpen opens a "previous" window -- and thus needs to and does call s:NetrwTreeDir early + let treedir= s:treedir + unlet s:treedir + unlet s:prevwinopen + return treedir + endif + if exists("s:prevwinopen") + unlet s:prevwinopen + endif + + if !exists("b:netrw_curdir") || b:netrw_curdir == "" + let b:netrw_curdir= getcwd() + endif + let treedir = b:netrw_curdir + let s:treecurpos= winsaveview() + + if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST + + " extract tree directory if on a line specifying a subdirectory (ie. ends with "/") + let curline= substitute(getline('.'),"\t -->.*$",'','') + if curline =~ '/$' + let treedir= substitute(getline('.'),'^\%('.s:treedepthstring.'\)*\([^'.s:treedepthstring.'].\{-}\)$','\1','e') + elseif curline =~ '@$' + let potentialdir= resolve(s:NetrwTreePath(w:netrw_treetop)) + else + let treedir= "" + endif + + " detect user attempting to close treeroot + if curline !~ '^'.s:treedepthstring && getline('.') != '..' + " now force a refresh + sil! NetrwKeepj %d _ + return b:netrw_curdir + endif + + " COMBAK: a symbolic link may point anywhere -- so it will be used to start a new treetop + " if a:islocal && curline =~ '@$' && isdirectory(s:NetrwFile(potentialdir)) + " let newdir = w:netrw_treetop.'/'.potentialdir + if a:islocal && curline =~ '@$' + if isdirectory(s:NetrwFile(potentialdir)) + let treedir = potentialdir + let w:netrw_treetop = treedir + endif + else + let potentialdir= s:NetrwFile(substitute(curline,'^'.s:treedepthstring.'\+ \(.*\)@$','\1','')) + let treedir = s:NetrwTreePath(w:netrw_treetop) + endif + endif + + " sanity maintenance: keep those //s away... + let treedir= substitute(treedir,'//$','/','') + return treedir +endfun + +" --------------------------------------------------------------------- +" s:NetrwTreeDisplay: recursive tree display {{{2 +fun! s:NetrwTreeDisplay(dir,depth) + " ensure that there are no folds + setl nofen + + " install ../ and shortdir + if a:depth == "" + call setline(line("$")+1,'../') + endif + if a:dir =~ '^\a\{3,}://' + if a:dir == w:netrw_treetop + let shortdir= a:dir + else + let shortdir= substitute(a:dir,'^.*/\([^/]\+\)/$','\1/','e') + endif + call setline(line("$")+1,a:depth.shortdir) + else + let shortdir= substitute(a:dir,'^.*/','','e') + call setline(line("$")+1,a:depth.shortdir.'/') + endif + " append a / to dir if its missing one + let dir= a:dir + + " display subtrees (if any) + let depth= s:treedepthstring.a:depth + + " implement g:netrw_hide for tree listings (uses g:netrw_list_hide) + if g:netrw_hide == 1 + " hide given patterns + let listhide= split(g:netrw_list_hide,',') + for pat in listhide + call filter(w:netrw_treedict[dir],'v:val !~ "'.escape(pat,'\\').'"') + endfor + + elseif g:netrw_hide == 2 + " show given patterns (only) + let listhide= split(g:netrw_list_hide,',') + let entries=[] + for entry in w:netrw_treedict[dir] + for pat in listhide + if entry =~ pat + call add(entries,entry) + break + endif + endfor + endfor + let w:netrw_treedict[dir]= entries + endif + if depth != "" + " always remove "." and ".." entries when there's depth + call filter(w:netrw_treedict[dir],'v:val !~ "\\.\\.$"') + call filter(w:netrw_treedict[dir],'v:val !~ "\\.\\./$"') + call filter(w:netrw_treedict[dir],'v:val !~ "\\.$"') + call filter(w:netrw_treedict[dir],'v:val !~ "\\./$"') + endif + + for entry in w:netrw_treedict[dir] + if dir =~ '/$' + let direntry= substitute(dir.entry,'[@/]$','','e') + else + let direntry= substitute(dir.'/'.entry,'[@/]$','','e') + endif + if entry =~ '/$' && has_key(w:netrw_treedict,direntry) + NetrwKeepj call s:NetrwTreeDisplay(direntry,depth) + elseif entry =~ '/$' && has_key(w:netrw_treedict,direntry.'/') + NetrwKeepj call s:NetrwTreeDisplay(direntry.'/',depth) + elseif entry =~ '@$' && has_key(w:netrw_treedict,direntry.'@') + NetrwKeepj call s:NetrwTreeDisplay(direntry.'@',depth) + else + sil! NetrwKeepj call setline(line("$")+1,depth.entry) + endif + endfor +endfun + +" --------------------------------------------------------------------- +" s:NetrwRefreshTreeDict: updates the contents information for a tree (w:netrw_treedict) {{{2 +fun! s:NetrwRefreshTreeDict(dir) + if !exists("w:netrw_treedict") + return + endif + + for entry in w:netrw_treedict[a:dir] + let direntry= substitute(a:dir.'/'.entry,'[@/]$','','e') + + if entry =~ '/$' && has_key(w:netrw_treedict,direntry) + NetrwKeepj call s:NetrwRefreshTreeDict(direntry) + let filelist = s:NetrwLocalListingList(direntry,0) + let w:netrw_treedict[direntry] = sort(filelist) + + elseif entry =~ '/$' && has_key(w:netrw_treedict,direntry.'/') + NetrwKeepj call s:NetrwRefreshTreeDict(direntry.'/') + let filelist = s:NetrwLocalListingList(direntry.'/',0) + let w:netrw_treedict[direntry] = sort(filelist) + + elseif entry =~ '@$' && has_key(w:netrw_treedict,direntry.'@') + NetrwKeepj call s:NetrwRefreshTreeDict(direntry.'/') + let liststar = s:NetrwGlob(direntry.'/','*',1) + let listdotstar= s:NetrwGlob(direntry.'/','.*',1) + + else + endif + endfor +endfun + +" --------------------------------------------------------------------- +" s:NetrwTreeListing: displays tree listing from treetop on down, using NetrwTreeDisplay() {{{2 +" Called by s:PerformListing() +fun! s:NetrwTreeListing(dirname) + if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST + + " update the treetop + if !exists("w:netrw_treetop") + let w:netrw_treetop= a:dirname + let s:netrw_treetop= w:netrw_treetop + " use \V in case the directory contains specials chars like '$' or '~' + elseif (w:netrw_treetop =~ ('^'.'\V'.a:dirname) && s:Strlen(a:dirname) < s:Strlen(w:netrw_treetop)) + \ || a:dirname !~ ('^'.'\V'.w:netrw_treetop) + let w:netrw_treetop= a:dirname + let s:netrw_treetop= w:netrw_treetop + endif + if exists("w:netrw_treetop") + let s:netrw_treetop= w:netrw_treetop + else + let w:netrw_treetop= getcwd() + let s:netrw_treetop= w:netrw_treetop + endif + + if !exists("w:netrw_treedict") + " insure that we have a treedict, albeit empty + let w:netrw_treedict= {} + endif + + " update the dictionary for the current directory + exe "sil! NetrwKeepj keepp ".w:netrw_bannercnt.',$g@^\.\.\=/$@d _' + let w:netrw_treedict[a:dirname]= getline(w:netrw_bannercnt,line("$")) + exe "sil! NetrwKeepj ".w:netrw_bannercnt.",$d _" + + " if past banner, record word + if exists("w:netrw_bannercnt") && line(".") > w:netrw_bannercnt + let fname= expand("<cword>") + else + let fname= "" + endif + + " display from treetop on down + NetrwKeepj call s:NetrwTreeDisplay(w:netrw_treetop,"") + + " remove any blank line remaining as line#1 (happens in treelisting mode with banner suppressed) + while getline(1) =~ '^\s*$' && byte2line(1) > 0 + 1d + endwhile + + exe "setl ".g:netrw_bufsettings + + return + endif +endfun + +" --------------------------------------------------------------------- +" s:NetrwTreePath: returns path to current file/directory in tree listing {{{2 +" Normally, treetop is w:netrw_treetop, but a +" user of the function ( netrw#SetTreetop() ) +" wipes that out prior to calling this function +fun! s:NetrwTreePath(treetop) + " call Dfunc("s:NetrwTreePath(treetop<".a:treetop.">) line#".line(".")."<".getline(".").">") + if line(".") < w:netrw_bannercnt + 2 + let treedir= a:treetop + if treedir !~ '/$' + let treedir= treedir.'/' + endif + " call Dret("s:NetrwTreePath ".treedir." : line#".line(".")." ≤ ".(w:netrw_bannercnt+2)) + return treedir + endif + + let svpos = winsaveview() + " call Decho("saving posn to svpos<".string(svpos).">",'~'.expand("<slnum>")) + let depth = substitute(getline('.'),'^\(\%('.s:treedepthstring.'\)*\)[^'.s:treedepthstring.'].\{-}$','\1','e') + " call Decho("depth<".depth."> 1st subst",'~'.expand("<slnum>")) + let depth = substitute(depth,'^'.s:treedepthstring,'','') + " call Decho("depth<".depth."> 2nd subst (first depth removed)",'~'.expand("<slnum>")) + let curline= getline('.') + " call Decho("curline<".curline.'>','~'.expand("<slnum>")) + if curline =~ '/$' + " call Decho("extract tree directory from current line",'~'.expand("<slnum>")) + let treedir= substitute(curline,'^\%('.s:treedepthstring.'\)*\([^'.s:treedepthstring.'].\{-}\)$','\1','e') + " call Decho("treedir<".treedir.">",'~'.expand("<slnum>")) + elseif curline =~ '@\s\+-->' + " call Decho("extract tree directory using symbolic link",'~'.expand("<slnum>")) + let treedir= substitute(curline,'^\%('.s:treedepthstring.'\)*\([^'.s:treedepthstring.'].\{-}\)$','\1','e') + let treedir= substitute(treedir,'@\s\+-->.*$','','e') + " call Decho("treedir<".treedir.">",'~'.expand("<slnum>")) + else + " call Decho("do not extract tree directory from current line and set treedir to empty",'~'.expand("<slnum>")) + let treedir= "" + endif + " construct treedir by searching backwards at correct depth + " call Decho("construct treedir by searching backwards for correct depth",'~'.expand("<slnum>")) + " call Decho("initial treedir<".treedir."> depth<".depth.">",'~'.expand("<slnum>")) + while depth != "" && search('^'.depth.'[^'.s:treedepthstring.'].\{-}/$','bW') + let dirname= substitute(getline('.'),'^\('.s:treedepthstring.'\)*','','e') + let treedir= dirname.treedir + let depth = substitute(depth,'^'.s:treedepthstring,'','') + " call Decho("constructing treedir<".treedir.">: dirname<".dirname."> while depth<".depth.">",'~'.expand("<slnum>")) + endwhile + " call Decho("treedir#1<".treedir.">",'~'.expand("<slnum>")) + if a:treetop =~ '/$' + let treedir= a:treetop.treedir + else + let treedir= a:treetop.'/'.treedir + endif + " call Decho("treedir#2<".treedir.">",'~'.expand("<slnum>")) + let treedir= substitute(treedir,'//$','/','') + " call Decho("treedir#3<".treedir.">",'~'.expand("<slnum>")) + " call Decho("restoring posn to svpos<".string(svpos).">",'~'.expand("<slnum>"))" + call winrestview(svpos) + " call Dret("s:NetrwTreePath <".treedir.">") + return treedir +endfun + +" --------------------------------------------------------------------- +" s:NetrwWideListing: {{{2 +fun! s:NetrwWideListing() + + if w:netrw_liststyle == s:WIDELIST + " call Dfunc("NetrwWideListing() w:netrw_liststyle=".w:netrw_liststyle.' fo='.&fo.' l:fo='.&l:fo) + " look for longest filename (cpf=characters per filename) + " cpf: characters per filename + " fpl: filenames per line + " fpc: filenames per column + setl ma noro + let dict={} + " save the unnamed register and register 0-9 and a + let dict.a=[getreg('a'), getregtype('a')] + for i in range(0, 9) + let dict[i] = [getreg(i), getregtype(i)] + endfor + let dict.unnamed = [getreg(''), getregtype('')] + " call Decho("setl ma noro",'~'.expand("<slnum>")) + let b:netrw_cpf= 0 + if line("$") >= w:netrw_bannercnt + " determine the maximum filename size; use that to set cpf + exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g/^./if virtcol("$") > b:netrw_cpf|let b:netrw_cpf= virtcol("$")|endif' + NetrwKeepj call histdel("/",-1) + else + " restore stored registers + call s:RestoreRegister(dict) + " call Dret("NetrwWideListing") + return + endif + " allow for two spaces to separate columns + let b:netrw_cpf= b:netrw_cpf + 2 + " call Decho("b:netrw_cpf=max_filename_length+2=".b:netrw_cpf,'~'.expand("<slnum>")) + + " determine qty files per line (fpl) + let w:netrw_fpl= winwidth(0)/b:netrw_cpf + if w:netrw_fpl <= 0 + let w:netrw_fpl= 1 + endif + " call Decho("fpl= [winwidth=".winwidth(0)."]/[b:netrw_cpf=".b:netrw_cpf.']='.w:netrw_fpl,'~'.expand("<slnum>")) + + " make wide display + " fpc: files per column of wide listing + exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$s/^.*$/\=escape(printf("%-'.b:netrw_cpf.'S",submatch(0)),"\\")/' + NetrwKeepj call histdel("/",-1) + let fpc = (line("$") - w:netrw_bannercnt + w:netrw_fpl)/w:netrw_fpl + let newcolstart = w:netrw_bannercnt + fpc + let newcolend = newcolstart + fpc - 1 + " call Decho("bannercnt=".w:netrw_bannercnt." fpl=".w:netrw_fpl." fpc=".fpc." newcol[".newcolstart.",".newcolend."]",'~'.expand("<slnum>")) + if !has('nvim') && has("clipboard") && g:netrw_clipboard + " call Decho("(s:NetrwWideListing) save @* and @+",'~'.expand("<slnum>")) + sil! let keepregstar = @* + sil! let keepregplus = @+ + endif + while line("$") >= newcolstart + if newcolend > line("$") | let newcolend= line("$") | endif + let newcolqty= newcolend - newcolstart + exe newcolstart + " COMBAK: both of the visual-mode using lines below are problematic vis-a-vis @* + if newcolqty == 0 + exe "sil! NetrwKeepj norm! 0\<c-v>$h\"ax".w:netrw_bannercnt."G$\"ap" + else + exe "sil! NetrwKeepj norm! 0\<c-v>".newcolqty.'j$h"ax'.w:netrw_bannercnt.'G$"ap' + endif + exe "sil! NetrwKeepj ".newcolstart.','.newcolend.'d _' + exe 'sil! NetrwKeepj '.w:netrw_bannercnt + endwhile + if !has('nvim') && has("clipboard") + " call Decho("(s:NetrwWideListing) restore @* and @+",'~'.expand("<slnum>")) + if @* != keepregstar | sil! let @* = keepregstar | endif + if @+ != keepregplus | sil! let @+ = keepregplus | endif + endif + exe "sil! NetrwKeepj ".w:netrw_bannercnt.',$s/\s\+$//e' + NetrwKeepj call histdel("/",-1) + exe 'nno <buffer> <silent> w :call search(''^.\\|\s\s\zs\S'',''W'')'."\<cr>" + exe 'nno <buffer> <silent> b :call search(''^.\\|\s\s\zs\S'',''bW'')'."\<cr>" + " call Decho("NetrwWideListing) setl noma nomod ro",'~'.expand("<slnum>")) + exe "setl ".g:netrw_bufsettings + call s:RestoreRegister(dict) + " call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) + " call Dret("NetrwWideListing") + return + else + if hasmapto("w","n") + sil! nunmap <buffer> w + endif + if hasmapto("b","n") + sil! nunmap <buffer> b + endif + endif +endfun + +" --------------------------------------------------------------------- +" s:PerformListing: {{{2 +fun! s:PerformListing(islocal) + " call Dfunc("s:PerformListing(islocal=".a:islocal.")") + " call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol()." line($)=".line("$"),'~'.expand("<slnum>")) + " call Decho("settings: ".((&l:ma == 0)? "no" : "")."ma ".((&l:mod == 0)? "no" : "")."mod ".((&l:bl == 0)? "no" : "")."bl ".((&l:ro == 0)? "no" : "")."ro fo=".&l:fo. " (enter)"." ei<".&ei.">",'~'.expand("<slnum>")) + sil! NetrwKeepj %d _ + " call DechoBuf(bufnr("%")) + + " set up syntax highlighting {{{3 + " call Decho("--set up syntax highlighting (ie. setl ft=netrw)",'~'.expand("<slnum>")) + sil! setl ft=netrw + + NetrwKeepj call s:NetrwOptionsSafe(a:islocal) + setl noro ma + " call Decho("setl noro ma bh=".&bh,'~'.expand("<slnum>")) + + " if exists("g:netrw_silent") && g:netrw_silent == 0 && &ch >= 1 " Decho + " call Decho("Processing your browsing request...",'~'.expand("<slnum>")) + " endif " Decho + + " call Decho('w:netrw_liststyle='.(exists("w:netrw_liststyle")? w:netrw_liststyle : 'n/a'),'~'.expand("<slnum>")) + if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict") + " force a refresh for tree listings + " call Decho("force refresh for treelisting: clear buffer<".expand("%")."> with :%d",'~'.expand("<slnum>")) + sil! NetrwKeepj %d _ + endif + + " save current directory on directory history list + NetrwKeepj call s:NetrwBookHistHandler(3,b:netrw_curdir) + + " Set up the banner {{{3 + if g:netrw_banner + " call Decho("--set up banner",'~'.expand("<slnum>")) + NetrwKeepj call setline(1,'" ============================================================================') + if exists("g:netrw_pchk") + " this undocumented option allows pchk to run with different versions of netrw without causing spurious + " failure detections. + NetrwKeepj call setline(2,'" Netrw Directory Listing') + else + NetrwKeepj call setline(2,'" Netrw Directory Listing (netrw '.g:loaded_netrw.')') + endif + if exists("g:netrw_pchk") + let curdir= substitute(b:netrw_curdir,expand("$HOME"),'~','') + else + let curdir= b:netrw_curdir + endif + if exists("g:netrw_bannerbackslash") && g:netrw_bannerbackslash + NetrwKeepj call setline(3,'" '.substitute(curdir,'/','\\','g')) + else + NetrwKeepj call setline(3,'" '.curdir) + endif + let w:netrw_bannercnt= 3 + NetrwKeepj exe "sil! NetrwKeepj ".w:netrw_bannercnt + else + " call Decho("--no banner",'~'.expand("<slnum>")) + NetrwKeepj 1 + let w:netrw_bannercnt= 1 + endif + " call Decho("w:netrw_bannercnt=".w:netrw_bannercnt." win#".winnr(),'~'.expand("<slnum>")) + " call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol()." line($)=".line("$"),'~'.expand("<slnum>")) + + " construct sortby string: [name|time|size|exten] [reversed] + let sortby= g:netrw_sort_by + 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" + " call Decho("directories will be sorted by name",'~'.expand("<slnum>")) + " sorted by name (also includes the sorting sequence in the banner) + NetrwKeepj put ='\" Sorted by '.sortby + NetrwKeepj put ='\" Sort sequence: '.g:netrw_sort_sequence + let w:netrw_bannercnt= w:netrw_bannercnt + 2 + else + " call Decho("directories will be sorted by size or time",'~'.expand("<slnum>")) + " sorted by time, size, exten + NetrwKeepj put ='\" Sorted by '.sortby + let w:netrw_bannercnt= w:netrw_bannercnt + 1 + endif + exe "sil! NetrwKeepj ".w:netrw_bannercnt + " else " Decho + " call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>")) + endif + + " show copy/move target, if any {{{3 + if g:netrw_banner + if exists("s:netrwmftgt") && exists("s:netrwmftgt_islocal") + " call Decho("--show copy/move target<".s:netrwmftgt.">",'~'.expand("<slnum>")) + NetrwKeepj put ='' + if s:netrwmftgt_islocal + sil! NetrwKeepj call setline(line("."),'" Copy/Move Tgt: '.s:netrwmftgt.' (local)') + else + sil! NetrwKeepj call setline(line("."),'" Copy/Move Tgt: '.s:netrwmftgt.' (remote)') + endif + let w:netrw_bannercnt= w:netrw_bannercnt + 1 + else + " call Decho("s:netrwmftgt does not exist, don't make Copy/Move Tgt",'~'.expand("<slnum>")) + endif + exe "sil! NetrwKeepj ".w:netrw_bannercnt + endif + + " Hiding... -or- Showing... {{{3 + if g:netrw_banner + " call Decho("--handle hiding/showing in banner (g:netrw_hide=".g:netrw_hide." g:netrw_list_hide<".g:netrw_list_hide.">)",'~'.expand("<slnum>")) + if g:netrw_list_hide != "" && g:netrw_hide + if g:netrw_hide == 1 + NetrwKeepj put ='\" Hiding: '.g:netrw_list_hide + else + NetrwKeepj put ='\" Showing: '.g:netrw_list_hide + endif + let w:netrw_bannercnt= w:netrw_bannercnt + 1 + endif + exe "NetrwKeepj ".w:netrw_bannercnt + + " call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) + let quickhelp = g:netrw_quickhelp%len(s:QuickHelp) + " call Decho("quickhelp =".quickhelp,'~'.expand("<slnum>")) + NetrwKeepj put ='\" Quick Help: <F1>:help '.s:QuickHelp[quickhelp] + " call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) + NetrwKeepj put ='\" ==============================================================================' + let w:netrw_bannercnt= w:netrw_bannercnt + 2 + " else " Decho + " call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>")) + endif + + " bannercnt should index the line just after the banner + if g:netrw_banner + let w:netrw_bannercnt= w:netrw_bannercnt + 1 + exe "sil! NetrwKeepj ".w:netrw_bannercnt + " call Decho("--w:netrw_bannercnt=".w:netrw_bannercnt." (should index line just after banner) line($)=".line("$"),'~'.expand("<slnum>")) + " else " Decho + " call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>")) + endif + + " get list of files + " call Decho("--Get list of files - islocal=".a:islocal,'~'.expand("<slnum>")) + if a:islocal + NetrwKeepj call s:LocalListing() + else " remote + NetrwKeepj let badresult= s:NetrwRemoteListing() + if badresult + " call Decho("w:netrw_bannercnt=".(exists("w:netrw_bannercnt")? w:netrw_bannercnt : 'n/a')." win#".winnr()." buf#".bufnr("%")."<".bufname("%").">",'~'.expand("<slnum>")) + " call Dret("s:PerformListing : error detected by NetrwRemoteListing") + return + endif + endif + + " manipulate the directory listing (hide, sort) {{{3 + if !exists("w:netrw_bannercnt") + let w:netrw_bannercnt= 0 + endif + " call Decho("--manipulate directory listing (hide, sort)",'~'.expand("<slnum>")) + " call Decho("g:netrw_banner=".g:netrw_banner." w:netrw_bannercnt=".w:netrw_bannercnt." (banner complete)",'~'.expand("<slnum>")) + " call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>")) + + if !g:netrw_banner || line("$") >= w:netrw_bannercnt + " call Decho("manipulate directory listing (support hide)",'~'.expand("<slnum>")) + " call Decho("g:netrw_hide=".g:netrw_hide." g:netrw_list_hide<".g:netrw_list_hide.">",'~'.expand("<slnum>")) + if g:netrw_hide && g:netrw_list_hide != "" + NetrwKeepj call s:NetrwListHide() + endif + 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" + " sort by name + " call Decho("sort by name",'~'.expand("<slnum>")) + 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' + " name: sort by name of file + exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$sort'.' '.g:netrw_sort_options + else + " reverse direction sorting + exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$sort!'.' '.g:netrw_sort_options + endif + endif + + " remove priority pattern prefix + " call Decho("remove priority pattern prefix",'~'.expand("<slnum>")) + exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\d\{3}'.g:netrw_sepchr.'//e' + NetrwKeepj call histdel("/",-1) + + elseif g:netrw_sort_by =~# "^ext" + " exten: sort by extension + " The histdel(...,-1) calls remove the last search from the search history + " call Decho("sort by extension",'~'.expand("<slnum>")) + exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$g+/+s/^/001'.g:netrw_sepchr.'/' + NetrwKeepj call histdel("/",-1) + exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$v+[./]+s/^/002'.g:netrw_sepchr.'/' + NetrwKeepj call histdel("/",-1) + exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$v+['.g:netrw_sepchr.'/]+s/^\(.*\.\)\(.\{-\}\)$/\2'.g:netrw_sepchr.'&/e' + 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' + " normal direction sorting + exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$sort'.' '.g:netrw_sort_options + else + " reverse direction sorting + exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$sort!'.' '.g:netrw_sort_options + endif + endif + exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^.\{-}'.g:netrw_sepchr.'//e' + NetrwKeepj call histdel("/",-1) + + 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' + " call Decho('exe sil NetrwKeepj '.w:netrw_bannercnt.',$sort','~'.expand("<slnum>")) + exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$sort'.' '.g:netrw_sort_options + else + " call Decho('exe sil NetrwKeepj '.w:netrw_bannercnt.',$sort!','~'.expand("<slnum>")) + exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$sort!'.' '.g:netrw_sort_options + endif + " call Decho("remove leading digits/ (sorting) information from listing",'~'.expand("<slnum>")) + exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\d\{-}\///e' + NetrwKeepj call histdel("/",-1) + endif + endif + + 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 + call histdel("/",-1) + endif + endif + endif + " call Decho("g:netrw_banner=".g:netrw_banner.": banner ".(g:netrw_banner? "enabled" : "suppressed").": (line($)=".line("$")." byte2line(1)=".byte2line(1)." bannercnt=".w:netrw_bannercnt.")",'~'.expand("<slnum>")) + + " convert to wide/tree listing {{{3 + " call Decho("--modify display if wide/tree listing style",'~'.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. " (internal#1)",'~'.expand("<slnum>")) + NetrwKeepj call s:NetrwWideListing() + " 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. " (internal#2)",'~'.expand("<slnum>")) + NetrwKeepj call s:NetrwTreeListing(b:netrw_curdir) + " 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. " (internal#3)",'~'.expand("<slnum>")) + + " resolve symbolic links if local and (thin or tree) + if a:islocal && (w:netrw_liststyle == s:THINLIST || (exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST)) + " call Decho("--resolve symbolic links if local and thin|tree",'~'.expand("<slnum>")) + sil! keepp g/@$/call s:ShowLink() + endif + + if exists("w:netrw_bannercnt") && (line("$") >= w:netrw_bannercnt || !g:netrw_banner) + " place cursor on the top-left corner of the file listing + " call Decho("--place cursor on top-left corner of file listing",'~'.expand("<slnum>")) + exe 'sil! '.w:netrw_bannercnt + sil! NetrwKeepj norm! 0 + " call Decho(" tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol()." line($)=".line("$"),'~'.expand("<slnum>")) + else + " call Decho("--did NOT place cursor on top-left corner",'~'.expand("<slnum>")) + " call Decho(" w:netrw_bannercnt=".(exists("w:netrw_bannercnt")? w:netrw_bannercnt : 'n/a'),'~'.expand("<slnum>")) + " call Decho(" line($)=".line("$"),'~'.expand("<slnum>")) + " call Decho(" g:netrw_banner=".(exists("g:netrw_banner")? g:netrw_banner : 'n/a'),'~'.expand("<slnum>")) + endif + + " record previous current directory + let w:netrw_prvdir= b:netrw_curdir + " call Decho("--record netrw_prvdir<".w:netrw_prvdir.">",'~'.expand("<slnum>")) + + " save certain window-oriented variables into buffer-oriented variables {{{3 + " call Decho("--save some window-oriented variables into buffer oriented variables",'~'.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. " (internal#4)",'~'.expand("<slnum>")) + NetrwKeepj call s:SetBufWinVars() + " 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. " (internal#5)",'~'.expand("<slnum>")) + NetrwKeepj call s:NetrwOptionsRestore("w:") + " 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. " (internal#6)",'~'.expand("<slnum>")) + + " set display to netrw display settings + " call Decho("--set display to netrw display settings (".g:netrw_bufsettings.")",'~'.expand("<slnum>")) + exe "setl ".g:netrw_bufsettings + " 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. " (internal#7)",'~'.expand("<slnum>")) + if g:netrw_liststyle == s:LONGLIST + " call Decho("exe setl ts=".(g:netrw_maxfilenamelen+1),'~'.expand("<slnum>")) + exe "setl ts=".(g:netrw_maxfilenamelen+1) + endif + " call Decho("PerformListing buffer:",'~'.expand("<slnum>")) + " call DechoBuf(bufnr("%")) + + if exists("s:treecurpos") + " call Decho("s:treecurpos exists; restore posn",'~'.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. " (internal#8)",'~'.expand("<slnum>")) + " call Decho("restoring posn to s:treecurpos<".string(s:treecurpos).">",'~'.expand("<slnum>")) + NetrwKeepj call winrestview(s:treecurpos) + unlet s:treecurpos + endif + + " 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. " (return)",'~'.expand("<slnum>")) + " call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol()." line($)=".line("$"),'~'.expand("<slnum>")) + " call Dret("s:PerformListing : curpos<".string(getpos(".")).">") +endfun + +" --------------------------------------------------------------------- +" s:SetupNetrwStatusLine: {{{2 +fun! s:SetupNetrwStatusLine(statline) + " call Dfunc("SetupNetrwStatusLine(statline<".a:statline.">)") + + if !exists("s:netrw_setup_statline") + let s:netrw_setup_statline= 1 + " call Decho("do first-time status line setup",'~'.expand("<slnum>")) + + if !exists("s:netrw_users_stl") + let s:netrw_users_stl= &stl + endif + if !exists("s:netrw_users_ls") + let s:netrw_users_ls= &laststatus + endif + + " set up User9 highlighting as needed + let dict={} + let dict.a=[getreg('a'), getregtype('a')] + redir @a + try + hi User9 + catch /^Vim\%((\a\{3,})\)\=:E411/ + if &bg == "dark" + hi User9 ctermfg=yellow ctermbg=blue guifg=yellow guibg=blue + else + hi User9 ctermbg=yellow ctermfg=blue guibg=yellow guifg=blue + endif + endtry + redir END + call s:RestoreRegister(dict) + endif + + " set up status line (may use User9 highlighting) + " insure that windows have a statusline + " make sure statusline is displayed + let &l:stl=a:statline + setl laststatus=2 + " call Decho("stl=".&stl,'~'.expand("<slnum>")) + redraw + + " call Dret("SetupNetrwStatusLine : stl=".&stl) +endfun + +" ========================================= +" Remote Directory Browsing Support: {{{1 +" ========================================= + +" --------------------------------------------------------------------- +" s:NetrwRemoteFtpCmd: unfortunately, not all ftp servers honor options for ls {{{2 +" This function assumes that a long listing will be received. Size, time, +" and reverse sorts will be requested of the server but not otherwise +" enforced here. +fun! s:NetrwRemoteFtpCmd(path,listcmd) + " call Dfunc("NetrwRemoteFtpCmd(path<".a:path."> listcmd<".a:listcmd.">) w:netrw_method=".(exists("w:netrw_method")? w:netrw_method : (exists("b:netrw_method")? b:netrw_method : "???"))) + " call Decho("line($)=".line("$")." win#".winnr()." w:netrw_bannercnt=".w:netrw_bannercnt,'~'.expand("<slnum>")) + " sanity check: {{{3 + if !exists("w:netrw_method") + if exists("b:netrw_method") + let w:netrw_method= b:netrw_method + else + call netrw#ErrorMsg(2,"(s:NetrwRemoteFtpCmd) internal netrw error",93) + " call Dret("NetrwRemoteFtpCmd") + return + endif + endif + + " WinXX ftp uses unix style input, so set ff to unix " {{{3 + let ffkeep= &ff + setl ma ff=unix noro + " call Decho("setl ma ff=unix noro",'~'.expand("<slnum>")) + + " clear off any older non-banner lines " {{{3 + " note that w:netrw_bannercnt indexes the line after the banner + " call Decho('exe sil! NetrwKeepj '.w:netrw_bannercnt.",$d _ (clear off old non-banner lines)",'~'.expand("<slnum>")) + exe "sil! NetrwKeepj ".w:netrw_bannercnt.",$d _" + + "......................................... + if w:netrw_method == 2 || w:netrw_method == 5 " {{{3 + " ftp + <.netrc>: Method #2 + if a:path != "" + NetrwKeepj put ='cd \"'.a:path.'\"' + endif + if exists("g:netrw_ftpextracmd") + NetrwKeepj put =g:netrw_ftpextracmd + " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) + endif + NetrwKeepj call setline(line("$")+1,a:listcmd) + " exe "NetrwKeepj ".w:netrw_bannercnt.',$g/^./call Decho("ftp#".line(".").": ".getline("."),''~''.expand("<slnum>"))' + if exists("g:netrw_port") && g:netrw_port != "" + " call Decho("exe ".s:netrw_silentxfer.w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1),'~'.expand("<slnum>")) + exe s:netrw_silentxfer." NetrwKeepj ".w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1)." ".s:ShellEscape(g:netrw_port,1) + else + " call Decho("exe ".s:netrw_silentxfer.w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1),'~'.expand("<slnum>")) + exe s:netrw_silentxfer." NetrwKeepj ".w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".s:ShellEscape(g:netrw_machine,1) + endif + + "......................................... + elseif w:netrw_method == 3 " {{{3 + " ftp + machine,id,passwd,filename: Method #3 + setl ff=unix + if exists("g:netrw_port") && g:netrw_port != "" + NetrwKeepj put ='open '.g:netrw_machine.' '.g:netrw_port + else + NetrwKeepj put ='open '.g:netrw_machine + endif + + " handle userid and password + let host= substitute(g:netrw_machine,'\..*$','','') + " call Decho("host<".host.">",'~'.expand("<slnum>")) + if exists("s:netrw_hup") && exists("s:netrw_hup[host]") + call NetUserPass("ftp:".host) + endif + if exists("g:netrw_uid") && g:netrw_uid != "" + if exists("g:netrw_ftp") && g:netrw_ftp == 1 + NetrwKeepj put =g:netrw_uid + if exists("s:netrw_passwd") && s:netrw_passwd != "" + NetrwKeepj put ='\"'.s:netrw_passwd.'\"' + endif + elseif exists("s:netrw_passwd") + NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"' + endif + endif + + if a:path != "" + NetrwKeepj put ='cd \"'.a:path.'\"' + endif + if exists("g:netrw_ftpextracmd") + NetrwKeepj put =g:netrw_ftpextracmd + " call Decho("filter input: ".getline('.'),'~'.expand("<slnum>")) + endif + NetrwKeepj call setline(line("$")+1,a:listcmd) + + " perform ftp: + " -i : turns off interactive prompting from ftp + " -n unix : DON'T use <.netrc>, even though it exists + " -n win32: quit being obnoxious about password + if exists("w:netrw_bannercnt") + " exe w:netrw_bannercnt.',$g/^./call Decho("ftp#".line(".").": ".getline("."),''~''.expand("<slnum>"))' + call s:NetrwExe(s:netrw_silentxfer.w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." ".g:netrw_ftp_options) + " else " Decho + " call Decho("WARNING: w:netrw_bannercnt doesn't exist!",'~'.expand("<slnum>")) + " g/^./call Decho("SKIPPING ftp#".line(".").": ".getline("."),'~'.expand("<slnum>")) + endif + + "......................................... + elseif w:netrw_method == 9 " {{{3 + " sftp username@machine: Method #9 + " s:netrw_sftp_cmd + setl ff=unix + + " restore settings + let &l:ff= ffkeep + " call Dret("NetrwRemoteFtpCmd") + return + + "......................................... + else " {{{3 + NetrwKeepj call netrw#ErrorMsg(s:WARNING,"unable to comply with your request<" . bufname("%") . ">",23) + endif + + " cleanup for Windows " {{{3 + if has("win32") + sil! NetrwKeepj %s/\r$//e + NetrwKeepj call histdel("/",-1) + endif + if a:listcmd == "dir" + " infer directory/link based on the file permission string + sil! NetrwKeepj g/d\%([-r][-w][-x]\)\{3}/NetrwKeepj s@$@/@e + sil! NetrwKeepj g/l\%([-r][-w][-x]\)\{3}/NetrwKeepj s/$/@/e + NetrwKeepj call histdel("/",-1) + NetrwKeepj call histdel("/",-1) + if w:netrw_liststyle == s:THINLIST || w:netrw_liststyle == s:WIDELIST || (exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST) + exe "sil! NetrwKeepj ".w:netrw_bannercnt.',$s/^\%(\S\+\s\+\)\{8}//e' + NetrwKeepj call histdel("/",-1) + endif + endif + + " ftp's listing doesn't seem to include ./ or ../ " {{{3 + if !search('^\.\/$\|\s\.\/$','wn') + exe 'NetrwKeepj '.w:netrw_bannercnt + NetrwKeepj put ='./' + endif + if !search('^\.\.\/$\|\s\.\.\/$','wn') + exe 'NetrwKeepj '.w:netrw_bannercnt + NetrwKeepj put ='../' + endif + + " restore settings " {{{3 + let &l:ff= ffkeep + " call Dret("NetrwRemoteFtpCmd") +endfun + +" --------------------------------------------------------------------- +" s:NetrwRemoteListing: {{{2 +fun! s:NetrwRemoteListing() + " call Dfunc("s:NetrwRemoteListing() b:netrw_curdir<".b:netrw_curdir.">) win#".winnr()) + + if !exists("w:netrw_bannercnt") && exists("s:bannercnt") + let w:netrw_bannercnt= s:bannercnt + endif + if !exists("w:netrw_bannercnt") && exists("b:bannercnt") + let w:netrw_bannercnt= b:bannercnt + endif + + call s:RemotePathAnalysis(b:netrw_curdir) + + " sanity check: + if exists("b:netrw_method") && b:netrw_method =~ '[235]' + " call Decho("b:netrw_method=".b:netrw_method,'~'.expand("<slnum>")) + if !executable("ftp") + " call Decho("ftp is not executable",'~'.expand("<slnum>")) + if !exists("g:netrw_quiet") + call netrw#ErrorMsg(s:ERROR,"this system doesn't support remote directory listing via ftp",18) + endif + call s:NetrwOptionsRestore("w:") + " call Dret("s:NetrwRemoteListing -1") + return -1 + endif + + elseif !exists("g:netrw_list_cmd") || g:netrw_list_cmd == '' + " call Decho("g:netrw_list_cmd<",(exists("g:netrw_list_cmd")? 'n/a' : "-empty-").">",'~'.expand("<slnum>")) + if !exists("g:netrw_quiet") + if g:netrw_list_cmd == "" + NetrwKeepj call netrw#ErrorMsg(s:ERROR,"your g:netrw_list_cmd is empty; perhaps ".g:netrw_ssh_cmd." is not executable on your system",47) + else + NetrwKeepj call netrw#ErrorMsg(s:ERROR,"this system doesn't support remote directory listing via ".g:netrw_list_cmd,19) + endif + endif + + NetrwKeepj call s:NetrwOptionsRestore("w:") + " call Dret("s:NetrwRemoteListing -1") + return -1 + endif " (remote handling sanity check) + " call Decho("passed remote listing sanity checks",'~'.expand("<slnum>")) + + if exists("b:netrw_method") + " call Decho("setting w:netrw_method to b:netrw_method<".b:netrw_method.">",'~'.expand("<slnum>")) + let w:netrw_method= b:netrw_method + endif + + if s:method == "ftp" + " use ftp to get remote file listing {{{3 + " 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' + let listcmd= g:netrw_ftp_timelist_cmd + 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>")) + call s:NetrwRemoteFtpCmd(s:path,listcmd) + " exe "sil! keepalt NetrwKeepj ".w:netrw_bannercnt.',$g/^./call Decho("raw listing: ".getline("."),''~''.expand("<slnum>"))' + + " report on missing file or directory messages + if search('[Nn]o such file or directory\|Failed to change directory') + let mesg= getline(".") + if exists("w:netrw_bannercnt") + setl ma + exe w:netrw_bannercnt.",$d _" + setl noma + endif + NetrwKeepj call s:NetrwOptionsRestore("w:") + call netrw#ErrorMsg(s:WARNING,mesg,96) + " call Dret("s:NetrwRemoteListing : -1") + return -1 + endif + + if w:netrw_liststyle == s:THINLIST || w:netrw_liststyle == s:WIDELIST || (exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST) + " shorten the listing + " call Decho("generate short listing",'~'.expand("<slnum>")) + exe "sil! keepalt NetrwKeepj ".w:netrw_bannercnt + + " cleanup + if g:netrw_ftp_browse_reject != "" + exe "sil! keepalt NetrwKeepj g/".g:netrw_ftp_browse_reject."/NetrwKeepj d" + NetrwKeepj call histdel("/",-1) + endif + sil! NetrwKeepj %s/\r$//e + NetrwKeepj call histdel("/",-1) + + " if there's no ../ listed, then put ../ in + let line1= line(".") + exe "sil! NetrwKeepj ".w:netrw_bannercnt + let line2= search('\.\.\/\%(\s\|$\)','cnW') + " call Decho("search(".'\.\.\/\%(\s\|$\)'."','cnW')=".line2." w:netrw_bannercnt=".w:netrw_bannercnt,'~'.expand("<slnum>")) + if line2 == 0 + " call Decho("netrw is putting ../ into listing",'~'.expand("<slnum>")) + sil! NetrwKeepj put='../' + endif + exe "sil! NetrwKeepj ".line1 + sil! NetrwKeepj norm! 0 + + " call Decho("line1=".line1." line2=".line2." line(.)=".line("."),'~'.expand("<slnum>")) + if search('^\d\{2}-\d\{2}-\d\{2}\s','n') " M$ ftp site cleanup + " call Decho("M$ ftp cleanup",'~'.expand("<slnum>")) + exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\d\{2}-\d\{2}-\d\{2}\s\+\d\+:\d\+[AaPp][Mm]\s\+\%(<DIR>\|\d\+\)\s\+//' + NetrwKeepj call histdel("/",-1) + else " normal ftp cleanup + " call Decho("normal ftp cleanup",'~'.expand("<slnum>")) + exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\(\%(\S\+\s\+\)\{7}\S\+\)\s\+\(\S.*\)$/\2/e' + exe "sil! NetrwKeepj ".w:netrw_bannercnt.',$g/ -> /s# -> .*/$#/#e' + exe "sil! NetrwKeepj ".w:netrw_bannercnt.',$g/ -> /s# -> .*$#/#e' + NetrwKeepj call histdel("/",-1) + NetrwKeepj call histdel("/",-1) + NetrwKeepj call histdel("/",-1) + endif + endif + + else + " use ssh to get remote file listing {{{3 + " call Decho("use ssh to get remote file listing: s:path<".s:path.">",'~'.expand("<slnum>")) + let listcmd= s:MakeSshCmd(g:netrw_list_cmd) + " call Decho("listcmd<".listcmd."> (using g:netrw_list_cmd)",'~'.expand("<slnum>")) + if g:netrw_scp_cmd =~ '^pscp' + " call Decho("1: exe r! ".s:ShellEscape(listcmd.s:path, 1),'~'.expand("<slnum>")) + exe "NetrwKeepj r! ".listcmd.s:ShellEscape(s:path, 1) + " remove rubbish and adjust listing format of 'pscp' to 'ssh ls -FLa' like + sil! NetrwKeepj g/^Listing directory/NetrwKeepj d + sil! NetrwKeepj g/^d[-rwx][-rwx][-rwx]/NetrwKeepj s+$+/+e + sil! NetrwKeepj g/^l[-rwx][-rwx][-rwx]/NetrwKeepj s+$+@+e + NetrwKeepj call histdel("/",-1) + NetrwKeepj call histdel("/",-1) + NetrwKeepj call histdel("/",-1) + if g:netrw_liststyle != s:LONGLIST + sil! NetrwKeepj g/^[dlsp-][-rwx][-rwx][-rwx]/NetrwKeepj s/^.*\s\(\S\+\)$/\1/e + NetrwKeepj call histdel("/",-1) + endif + else + if s:path == "" + " call Decho("2: exe r! ".listcmd,'~'.expand("<slnum>")) + exe "NetrwKeepj keepalt r! ".listcmd + else + " call Decho("3: exe r! ".listcmd.' '.s:ShellEscape(fnameescape(s:path),1),'~'.expand("<slnum>")) + exe "NetrwKeepj keepalt r! ".listcmd.' '.s:ShellEscape(fnameescape(s:path),1) + " call Decho("listcmd<".listcmd."> path<".s:path.">",'~'.expand("<slnum>")) + endif + endif + + " cleanup + if g:netrw_ssh_browse_reject != "" + " call Decho("cleanup: exe sil! g/".g:netrw_ssh_browse_reject."/NetrwKeepj d",'~'.expand("<slnum>")) + exe "sil! g/".g:netrw_ssh_browse_reject."/NetrwKeepj d" + NetrwKeepj call histdel("/",-1) + endif + endif + + if w:netrw_liststyle == s:LONGLIST + " do a long listing; these substitutions need to be done prior to sorting {{{3 + " call Decho("fix long listing:",'~'.expand("<slnum>")) + + if s:method == "ftp" + " cleanup + exe "sil! NetrwKeepj ".w:netrw_bannercnt + while getline('.') =~# g:netrw_ftp_browse_reject + sil! NetrwKeepj d + endwhile + " if there's no ../ listed, then put ../ in + let line1= line(".") + sil! NetrwKeepj 1 + sil! NetrwKeepj call search('^\.\.\/\%(\s\|$\)','W') + let line2= line(".") + if line2 == 0 + if b:netrw_curdir != '/' + exe 'sil! NetrwKeepj '.w:netrw_bannercnt."put='../'" + endif + endif + exe "sil! NetrwKeepj ".line1 + sil! NetrwKeepj norm! 0 + endif + + if search('^\d\{2}-\d\{2}-\d\{2}\s','n') " M$ ftp site cleanup + " call Decho("M$ ftp site listing cleanup",'~'.expand("<slnum>")) + exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\(\d\{2}-\d\{2}-\d\{2}\s\+\d\+:\d\+[AaPp][Mm]\s\+\%(<DIR>\|\d\+\)\s\+\)\(\w.*\)$/\2\t\1/' + elseif exists("w:netrw_bannercnt") && w:netrw_bannercnt <= line("$") + " call Decho("normal ftp site listing cleanup: bannercnt=".w:netrw_bannercnt." line($)=".line("$"),'~'.expand("<slnum>")) + exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$s/ -> .*$//e' + exe 'sil NetrwKeepj '.w:netrw_bannercnt.',$s/^\(\%(\S\+\s\+\)\{7}\S\+\)\s\+\(\S.*\)$/\2 \t\1/e' + exe 'sil NetrwKeepj '.w:netrw_bannercnt + NetrwKeepj call histdel("/",-1) + NetrwKeepj call histdel("/",-1) + NetrwKeepj call histdel("/",-1) + endif + endif + + " if exists("w:netrw_bannercnt") && w:netrw_bannercnt <= line("$") " Decho + " exe "NetrwKeepj ".w:netrw_bannercnt.',$g/^./call Decho("listing: ".getline("."),''~''.expand("<slnum>"))' + " endif " Decho + + " call Dret("s:NetrwRemoteListing 0") + return 0 +endfun + +" --------------------------------------------------------------------- +" s:NetrwRemoteRm: remove/delete a remote file or directory {{{2 +fun! s:NetrwRemoteRm(usrhost,path) range + let svpos= winsaveview() + + let all= 0 + if exists("s:netrwmarkfilelist_{bufnr('%')}") + " remove all marked files + for fname in s:netrwmarkfilelist_{bufnr("%")} + let ok= s:NetrwRemoteRmFile(a:path,fname,all) + if ok =~# 'q\%[uit]' + break + elseif ok =~# 'a\%[ll]' + let all= 1 + endif + endfor + call s:NetrwUnmarkList(bufnr("%"),b:netrw_curdir) + + else + " remove files specified by range + + " preparation for removing multiple files/directories + let keepsol = &l:sol + setl nosol + let ctr = a:firstline + + " remove multiple files and directories + while ctr <= a:lastline + exe "NetrwKeepj ".ctr + let ok= s:NetrwRemoteRmFile(a:path,s:NetrwGetWord(),all) + if ok =~# 'q\%[uit]' + break + elseif ok =~# 'a\%[ll]' + let all= 1 + endif + let ctr= ctr + 1 + endwhile + let &l:sol = keepsol + endif + + " refresh the (remote) directory listing + NetrwKeepj call s:NetrwRefresh(0,s:NetrwBrowseChgDir(0,'./',0)) + NetrwKeepj call winrestview(svpos) +endfun + +" --------------------------------------------------------------------- +" s:NetrwRemoteRmFile: {{{2 +fun! s:NetrwRemoteRmFile(path,rmfile,all) + " call Dfunc("s:NetrwRemoteRmFile(path<".a:path."> rmfile<".a:rmfile.">) all=".a:all) + + let all= a:all + let ok = "" + + if a:rmfile !~ '^"' && (a:rmfile =~ '@$' || a:rmfile !~ '[\/]$') + " attempt to remove file + " call Decho("attempt to remove file (all=".all.")",'~'.expand("<slnum>")) + if !all + echohl Statement + " call Decho("case all=0:",'~'.expand("<slnum>")) + call inputsave() + let ok= input("Confirm deletion of file<".a:rmfile."> ","[{y(es)},n(o),a(ll),q(uit)] ") + call inputrestore() + echohl NONE + if ok == "" + let ok="no" + endif + let ok= substitute(ok,'\[{y(es)},n(o),a(ll),q(uit)]\s*','','e') + if ok =~# 'a\%[ll]' + let all= 1 + endif + endif + + 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>")) + let path= a:path + if path =~ '^\a\{3,}://' + let path= substitute(path,'^\a\{3,}://[^/]\+/','','') + endif + sil! NetrwKeepj .,$d _ + call s:NetrwRemoteFtpCmd(path,"delete ".'"'.a:rmfile.'"') + else + " call Decho("case ssh: g:netrw_rm_cmd<".g:netrw_rm_cmd.">",'~'.expand("<slnum>")) + let netrw_rm_cmd= s:MakeSshCmd(g:netrw_rm_cmd) + " call Decho("netrw_rm_cmd<".netrw_rm_cmd.">",'~'.expand("<slnum>")) + if !exists("b:netrw_curdir") + NetrwKeepj call netrw#ErrorMsg(s:ERROR,"for some reason b:netrw_curdir doesn't exist!",53) + let ok="q" + else + let remotedir= substitute(b:netrw_curdir,'^.\{-}//[^/]\+/\(.*\)$','\1','') + " call Decho("netrw_rm_cmd<".netrw_rm_cmd.">",'~'.expand("<slnum>")) + " call Decho("remotedir<".remotedir.">",'~'.expand("<slnum>")) + " call Decho("rmfile<".a:rmfile.">",'~'.expand("<slnum>")) + if remotedir != "" + let netrw_rm_cmd= netrw_rm_cmd." ".s:ShellEscape(fnameescape(remotedir.a:rmfile)) + else + let netrw_rm_cmd= netrw_rm_cmd." ".s:ShellEscape(fnameescape(a:rmfile)) + endif + " call Decho("call system(".netrw_rm_cmd.")",'~'.expand("<slnum>")) + let ret= system(netrw_rm_cmd) + if v:shell_error != 0 + if exists("b:netrw_curdir") && b:netrw_curdir != getcwd() && !g:netrw_keepdir + call netrw#ErrorMsg(s:ERROR,"remove failed; perhaps due to vim's current directory<".getcwd()."> not matching netrw's (".b:netrw_curdir.") (see :help netrw-cd)",102) + else + call netrw#ErrorMsg(s:WARNING,"cmd<".netrw_rm_cmd."> failed",60) + endif + 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]' + " call Decho("ok==".ok,'~'.expand("<slnum>")) + endif + + else + " attempt to remove directory + " call Decho("attempt to remove directory",'~'.expand("<slnum>")) + if !all + call inputsave() + let ok= input("Confirm deletion of directory<".a:rmfile."> ","[{y(es)},n(o),a(ll),q(uit)] ") + call inputrestore() + if ok == "" + let ok="no" + endif + let ok= substitute(ok,'\[{y(es)},n(o),a(ll),q(uit)]\s*','','e') + if ok =~# 'a\%[ll]' + let all= 1 + endif + endif + + 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 + let rmfile = substitute(a:path.a:rmfile,'/$','','') + let netrw_rmdir_cmd = s:MakeSshCmd(netrw#WinPath(g:netrw_rmdir_cmd)).' '.s:ShellEscape(netrw#WinPath(rmfile)) + " call Decho("attempt to remove dir: system(".netrw_rmdir_cmd.")",'~'.expand("<slnum>")) + let ret= system(netrw_rmdir_cmd) + " call Decho("returned=".ret." errcode=".v:shell_error,'~'.expand("<slnum>")) + + if v:shell_error != 0 + " call Decho("v:shell_error not 0",'~'.expand("<slnum>")) + let netrw_rmf_cmd= s:MakeSshCmd(netrw#WinPath(g:netrw_rmf_cmd)).' '.s:ShellEscape(netrw#WinPath(substitute(rmfile,'[\/]$','','e'))) + " call Decho("2nd attempt to remove dir: system(".netrw_rmf_cmd.")",'~'.expand("<slnum>")) + let ret= system(netrw_rmf_cmd) + " call Decho("returned=".ret." errcode=".v:shell_error,'~'.expand("<slnum>")) + + if v:shell_error != 0 && !exists("g:netrw_quiet") + NetrwKeepj call netrw#ErrorMsg(s:ERROR,"unable to remove directory<".rmfile."> -- is it empty?",22) + endif + endif + endif + + elseif ok =~# 'q\%[uit]' + " call Decho("ok==".ok,'~'.expand("<slnum>")) + endif + endif + + " call Dret("s:NetrwRemoteRmFile ".ok) + return ok +endfun + +" --------------------------------------------------------------------- +" s:NetrwRemoteRename: rename a remote file or directory {{{2 +fun! s:NetrwRemoteRename(usrhost,path) range + + " preparation for removing multiple files/directories + let svpos = winsaveview() + " call Decho("saving posn to svpos<".string(svpos).">",'~'.expand("<slnum>")) + let ctr = a:firstline + let rename_cmd = s:MakeSshCmd(g:netrw_rename_cmd) + + " rename files given by the markfilelist + if exists("s:netrwmarkfilelist_{bufnr('%')}") + for oldname in s:netrwmarkfilelist_{bufnr("%")} + if exists("subfrom") + let newname= substitute(oldname,subfrom,subto,'') + else + call inputsave() + let newname= input("Moving ".oldname." to : ",oldname) + call inputrestore() + if newname =~ '^s/' + let subfrom = substitute(newname,'^s/\([^/]*\)/.*/$','\1','') + let subto = substitute(newname,'^s/[^/]*/\(.*\)/$','\1','') + let newname = substitute(oldname,subfrom,subto,'') + endif + endif + + if exists("w:netrw_method") && (w:netrw_method == 2 || w:netrw_method == 3) + NetrwKeepj call s:NetrwRemoteFtpCmd(a:path,"rename ".oldname." ".newname) + else + let oldname= s:ShellEscape(a:path.oldname) + let newname= s:ShellEscape(a:path.newname) + let ret = system(netrw#WinPath(rename_cmd).' '.oldname.' '.newname) + endif + + endfor + call s:NetrwUnMarkFile(1) + + else + + " attempt to rename files/directories + let keepsol= &l:sol + setl nosol + while ctr <= a:lastline + exe "NetrwKeepj ".ctr + + let oldname= s:NetrwGetWord() + + call inputsave() + let newname= input("Moving ".oldname." to : ",oldname) + call inputrestore() + + if exists("w:netrw_method") && (w:netrw_method == 2 || w:netrw_method == 3) + call s:NetrwRemoteFtpCmd(a:path,"rename ".oldname." ".newname) + else + let oldname= s:ShellEscape(a:path.oldname) + let newname= s:ShellEscape(a:path.newname) + let ret = system(netrw#WinPath(rename_cmd).' '.oldname.' '.newname) + endif + + let ctr= ctr + 1 + endwhile + let &l:sol= keepsol + endif + + " refresh the directory + NetrwKeepj call s:NetrwRefresh(0,s:NetrwBrowseChgDir(0,'./',0)) + NetrwKeepj call winrestview(svpos) +endfun + +" ========================================== +" Local Directory Browsing Support: {{{1 +" ========================================== + +" --------------------------------------------------------------------- +" netrw#FileUrlEdit: handles editing file://* files {{{2 +" Should accept: file://localhost/etc/fstab +" file:///etc/fstab +" file:///c:/WINDOWS/clock.avi +" file:///c|/WINDOWS/clock.avi +" file://localhost/c:/WINDOWS/clock.avi +" file://localhost/c|/WINDOWS/clock.avi +" file://c:/foo.txt +" file:///c:/foo.txt +" and %XX (where X is [0-9a-fA-F] is converted into a character with the given hexadecimal value +fun! netrw#FileUrlEdit(fname) + " call Dfunc("netrw#FileUrlEdit(fname<".a:fname.">)") + let fname = a:fname + if fname =~ '^file://localhost/' + " call Decho('converting file://localhost/ -to- file:///','~'.expand("<slnum>")) + let fname= substitute(fname,'^file://localhost/','file:///','') + " call Decho("fname<".fname.">",'~'.expand("<slnum>")) + endif + if has("win32") + if fname =~ '^file:///\=\a[|:]/' + " call Decho('converting file:///\a|/ -to- file://\a:/','~'.expand("<slnum>")) + let fname = substitute(fname,'^file:///\=\(\a\)[|:]/','file://\1:/','') + " call Decho("fname<".fname.">",'~'.expand("<slnum>")) + endif + endif + let fname2396 = netrw#RFC2396(fname) + let fname2396e= fnameescape(fname2396) + let plainfname= substitute(fname2396,'file://\(.*\)','\1',"") + if has("win32") + " call Decho("windows exception for plainfname",'~'.expand("<slnum>")) + if plainfname =~ '^/\+\a:' + " call Decho('removing leading "/"s','~'.expand("<slnum>")) + let plainfname= substitute(plainfname,'^/\+\(\a:\)','\1','') + endif + endif + + " call Decho("fname2396<".fname2396.">",'~'.expand("<slnum>")) + " call Decho("plainfname<".plainfname.">",'~'.expand("<slnum>")) + exe "sil doau BufReadPre ".fname2396e + exe 'NetrwKeepj keepalt edit '.plainfname + exe 'sil! NetrwKeepj keepalt bdelete '.fnameescape(a:fname) + + " call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) + " call Dret("netrw#FileUrlEdit") + exe "sil doau BufReadPost ".fname2396e +endfun + +" --------------------------------------------------------------------- +" netrw#LocalBrowseCheck: {{{2 +fun! netrw#LocalBrowseCheck(dirname) + " This function is called by netrwPlugin.vim's s:LocalBrowseCheck(), s:NetrwRexplore(), + " and by <cr> when atop a listed file/directory (via a buffer-local map) + " + " unfortunate interaction -- split window debugging can't be used here, must use + " D-echoRemOn or D-echoTabOn as the BufEnter event triggers + " another call to LocalBrowseCheck() when attempts to write + " to the DBG buffer are made. + " + " The &ft == "netrw" test was installed because the BufEnter event + " 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("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>")) + " getting E930: Cannot use :redir inside execute + "" call Dredir("ls!","netrw#LocalBrowseCheck") + " call Decho("tab#".tabpagenr()." win#".winnr()." buf#".bufnr("%")."<".bufname("%")."> line#".line(".")." col#".col(".")." winline#".winline()." wincol#".wincol(),'~'.expand("<slnum>")) + " call Decho("current buffer#".bufnr("%")."<".bufname("%")."> ft=".&ft,'~'.expand("<slnum>")) + + let ykeep= @@ + if isdirectory(s:NetrwFile(a:dirname)) + " call Decho("is-directory ft<".&ft."> b:netrw_curdir<".(exists("b:netrw_curdir")? b:netrw_curdir : " doesn't exist")."> dirname<".a:dirname.">"." line($)=".line("$")." ft<".&ft."> g:netrw_fastbrowse=".g:netrw_fastbrowse,'~'.expand("<slnum>")) + + if &ft != "netrw" || (exists("b:netrw_curdir") && b:netrw_curdir != a:dirname) || g:netrw_fastbrowse <= 1 + " call Decho("case 1 : ft=".&ft,'~'.expand("<slnum>")) + " call Decho("s:rexposn_".bufnr("%")."<".bufname("%")."> ".(exists("s:rexposn_".bufnr("%"))? "exists" : "does not exist"),'~'.expand("<slnum>")) + sil! NetrwKeepj keepalt call s:NetrwBrowse(1,a:dirname) + + elseif &ft == "netrw" && line("$") == 1 + " call Decho("case 2 (ft≡netrw && line($)≡1)",'~'.expand("<slnum>")) + sil! NetrwKeepj keepalt call s:NetrwBrowse(1,a:dirname) + + elseif exists("s:treeforceredraw") + " call Decho("case 3 (treeforceredraw)",'~'.expand("<slnum>")) + 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 + endif + + " The following code wipes out currently unused netrw buffers + " IF g:netrw_fastbrowse is zero (ie. slow browsing selected) + " AND IF the listing style is not a tree listing + if exists("g:netrw_fastbrowse") && g:netrw_fastbrowse == 0 && g:netrw_liststyle != s:TREELIST + " call Decho("wiping out currently unused netrw buffers",'~'.expand("<slnum>")) + let ibuf = 1 + let buflast = bufnr("$") + while ibuf <= buflast + if bufwinnr(ibuf) == -1 && isdirectory(s:NetrwFile(bufname(ibuf))) + exe "sil! keepj keepalt ".ibuf."bw!" + endif + let ibuf= ibuf + 1 + endwhile + endif + let @@= ykeep + " 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>")) + " not a directory, ignore it + " call Dret("netrw#LocalBrowseCheck : not a directory, ignoring it; dirname<".a:dirname.">") +endfun + +" --------------------------------------------------------------------- +" s:LocalBrowseRefresh: this function is called after a user has {{{2 +" performed any shell command. The idea is to cause all local-browsing +" buffers to be refreshed after a user has executed some shell command, +" on the chance that s/he removed/created a file/directory with it. +fun! s:LocalBrowseRefresh() + " determine which buffers currently reside in a tab + if !exists("s:netrw_browselist") + return + endif + if !exists("w:netrw_bannercnt") + return + endif + if !empty(getcmdwintype()) + " cannot move away from cmdline window, see :h E11 + return + endif + if exists("s:netrw_events") && s:netrw_events == 1 + " s:LocalFastBrowser gets called (indirectly) from a + let s:netrw_events= 2 + return + endif + let itab = 1 + let buftablist = [] + let ykeep = @@ + while itab <= tabpagenr("$") + let buftablist = buftablist + tabpagebuflist() + let itab = itab + 1 + sil! tabn + endwhile + " GO through all buffers on netrw_browselist (ie. just local-netrw buffers): + " | refresh any netrw window + " | wipe out any non-displaying netrw buffer + let curwinid = win_getid(winnr()) + let ibl = 0 + for ibuf in s:netrw_browselist + if bufwinnr(ibuf) == -1 && index(buftablist,ibuf) == -1 + " wipe out any non-displaying netrw buffer + " (ibuf not shown in a current window AND + " ibuf not in any tab) + exe "sil! keepj bd ".fnameescape(ibuf) + call remove(s:netrw_browselist,ibl) + continue + elseif index(tabpagebuflist(),ibuf) != -1 + " refresh any netrw buffer + exe bufwinnr(ibuf)."wincmd w" + 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 + endif + if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST + NetrwKeepj call s:NetrwRefreshTreeDict(w:netrw_treetop) + endif + NetrwKeepj call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0)) + endif + let ibl= ibl + 1 + endfor + call win_gotoid(curwinid) + let @@= ykeep +endfun + +" --------------------------------------------------------------------- +" s:LocalFastBrowser: handles setting up/taking down fast browsing for the local browser {{{2 +" +" g:netrw_ Directory Is +" fastbrowse Local Remote +" slow 0 D D D=Deleting a buffer implies it will not be re-used (slow) +" med 1 D H H=Hiding a buffer implies it may be re-used (fast) +" fast 2 H H +" +" Deleting a buffer means that it will be re-loaded when examined, hence "slow". +" Hiding a buffer means that it will be re-used when examined, hence "fast". +" (re-using a buffer may not be as accurate) +" +" s:netrw_events : doesn't exist, s:LocalFastBrowser() will install autocmds with medium-speed or fast browsing +" =1: autocmds installed, but ignore next FocusGained event to avoid initial double-refresh of listing. +" BufEnter may be first event, then a FocusGained event. Ignore the first FocusGained event. +" If :Explore used: it sets s:netrw_events to 2, so no FocusGained events are ignored. +" =2: autocmds installed (doesn't ignore any FocusGained events) +fun! s:LocalFastBrowser() + + " initialize browselist, a list of buffer numbers that the local browser has used + if !exists("s:netrw_browselist") + let s:netrw_browselist= [] + endif + + " append current buffer to fastbrowse list + if empty(s:netrw_browselist) || bufnr("%") > s:netrw_browselist[-1] + call add(s:netrw_browselist,bufnr("%")) + endif + + " enable autocmd events to handle refreshing/removing local browser buffers + " If local browse buffer is currently showing: refresh it + " If local browse buffer is currently hidden : wipe it + " g:netrw_fastbrowse=0 : slow speed, never re-use directory listing + " =1 : medium speed, re-use directory listing for remote only + " =2 : fast speed, always re-use directory listing when possible + if g:netrw_fastbrowse <= 1 && !exists("#ShellCmdPost") && !exists("s:netrw_events") + let s:netrw_events= 1 + augroup AuNetrwEvent + au! + if has("win32") + au ShellCmdPost * call s:LocalBrowseRefresh() + else + au ShellCmdPost,FocusGained * call s:LocalBrowseRefresh() + endif + augroup END + + " user must have changed fastbrowse to its fast setting, so remove + " the associated autocmd events + elseif g:netrw_fastbrowse > 1 && exists("#ShellCmdPost") && exists("s:netrw_events") + unlet s:netrw_events + augroup AuNetrwEvent + au! + augroup END + augroup! AuNetrwEvent + endif +endfun + +fun! s:NetrwLocalListingList(dirname,setmaxfilenamelen) + " get the list of files contained in the current directory + let dirname = a:dirname + let dirnamelen = strlen(dirname) + let filelist = s:NetrwGlob(dirname,"*",0) + let filelist = filelist + s:NetrwGlob(dirname,".*",0) + + if g:netrw_cygwin == 0 && has("win32") + elseif index(filelist,'..') == -1 && dirname !~ '/' + " include ../ in the glob() entry if its missing + let filelist= filelist+[s:ComposePath(dirname,"../")] + endif + + if a:setmaxfilenamelen && get(g:, 'netrw_dynamic_maxfilenamelen', 0) + let filelistcopy = map(deepcopy(filelist),'fnamemodify(v:val, ":t")') + let g:netrw_maxfilenamelen = max(map(filelistcopy,'len(v:val)')) + 1 + endif + + let resultfilelist = [] + for filename in filelist + + if getftype(filename) == "link" + " indicate a symbolic link + let pfile= filename."@" + + elseif getftype(filename) == "socket" + " indicate a socket + let pfile= filename."=" + + elseif getftype(filename) == "fifo" + " indicate a fifo + let pfile= filename."|" + + elseif isdirectory(s:NetrwFile(filename)) + " indicate a directory + let pfile= filename."/" + + elseif exists("b:netrw_curdir") && b:netrw_curdir !~ '^.*://' && !isdirectory(s:NetrwFile(filename)) + if has("win32") + if filename =~ '\.[eE][xX][eE]$' || filename =~ '\.[cC][oO][mM]$' || filename =~ '\.[bB][aA][tT]$' + " indicate an executable + let pfile= filename."*" + else + " normal file + let pfile= filename + endif + elseif executable(filename) + " indicate an executable + let pfile= filename."*" + else + " normal file + let pfile= filename + endif + + else + " normal file + let pfile= filename + endif + + if pfile =~ '//$' + let pfile= substitute(pfile,'//$','/','e') + endif + let pfile= strpart(pfile,dirnamelen) + let pfile= substitute(pfile,'^[/\\]','','e') + + if w:netrw_liststyle == s:LONGLIST + let longfile = printf("%-".g:netrw_maxfilenamelen."S",pfile) + let sz = getfsize(filename) + let szlen = 15 - (strdisplaywidth(longfile) - g:netrw_maxfilenamelen) + let szlen = (szlen > 0) ? szlen : 0 + + if g:netrw_sizestyle =~# "[hH]" + let sz= s:NetrwHumanReadable(sz) + endif + let fsz = printf("%".szlen."S",sz) + let pfile= longfile." ".fsz." ".strftime(g:netrw_timefmt,getftime(filename)) + endif + + if g:netrw_sort_by =~# "^t" + " sort by time (handles time up to 1 quintillion seconds, US) + " Decorate listing by prepending a timestamp/ . Sorting will then be done based on time. + let t = getftime(filename) + let ft = printf("%018d",t) + let ftpfile= ft.'/'.pfile + let resultfilelist += [ftpfile] + + elseif g:netrw_sort_by =~ "^s" + " sort by size (handles file sizes up to 1 quintillion bytes, US) + let sz = getfsize(filename) + let fsz = printf("%018d",sz) + let fszpfile= fsz.'/'.pfile + let resultfilelist += [fszpfile] + + else + " sort by name + let resultfilelist += [pfile] + endif + endfor + + return resultfilelist +endfun + +" --------------------------------------------------------------------- +" s:LocalListing: does the job of "ls" for local directories {{{2 +fun! s:LocalListing() + + let filelist = s:NetrwLocalListingList(b:netrw_curdir, 1) + for filename in filelist + sil! NetrwKeepj put =filename + endfor + + " cleanup any windows mess at end-of-line + sil! NetrwKeepj g/^$/d + sil! NetrwKeepj %s/\r$//e + call histdel("/",-1) + exe "setl ts=".(g:netrw_maxfilenamelen+1) +endfun + +" --------------------------------------------------------------------- +" s:NetrwLocalExecute: uses system() to execute command under cursor ("X" command support) {{{2 +fun! s:NetrwLocalExecute(cmd) + " call Dfunc("s:NetrwLocalExecute(cmd<".a:cmd.">)") + let ykeep= @@ + " sanity check + if !executable(a:cmd) + call netrw#ErrorMsg(s:ERROR,"the file<".a:cmd."> is not executable!",89) + let @@= ykeep + " call Dret("s:NetrwLocalExecute") + return + endif + + let optargs= input(":!".a:cmd,"","file") + " call Decho("optargs<".optargs.">",'~'.expand("<slnum>")) + let result= system(a:cmd.optargs) + " call Decho("result,'~'.expand("<slnum>")) + + " strip any ansi escape sequences off + let result = substitute(result,"\e\\[[0-9;]*m","","g") + + " show user the result(s) + echomsg result + let @@= ykeep + + " call Dret("s:NetrwLocalExecute") +endfun + +" --------------------------------------------------------------------- +" s:NetrwLocalRename: rename a local file or directory {{{2 +fun! s:NetrwLocalRename(path) range + + if !exists("w:netrw_bannercnt") + let w:netrw_bannercnt= b:netrw_bannercnt + endif + + " preparation for removing multiple files/directories + let ykeep = @@ + let ctr = a:firstline + let svpos = winsaveview() + let all = 0 + + " rename files given by the markfilelist + if exists("s:netrwmarkfilelist_{bufnr('%')}") + for oldname in s:netrwmarkfilelist_{bufnr("%")} + if exists("subfrom") + let newname= substitute(oldname,subfrom,subto,'') + else + call inputsave() + let newname= input("Moving ".oldname." to : ",oldname,"file") + call inputrestore() + if newname =~ '' + " two ctrl-x's : ignore all of string preceding the ctrl-x's + let newname = substitute(newname,'^.*','','') + elseif newname =~ '' + " one ctrl-x : ignore portion of string preceding ctrl-x but after last / + let newname = substitute(newname,'[^/]*','','') + endif + if newname =~ '^s/' + let subfrom = substitute(newname,'^s/\([^/]*\)/.*/$','\1','') + let subto = substitute(newname,'^s/[^/]*/\(.*\)/$','\1','') + let newname = substitute(oldname,subfrom,subto,'') + endif + endif + if !all && filereadable(newname) + call inputsave() + let response= input("File<".newname."> already exists; do you want to overwrite it? (y/all/n) ") + call inputrestore() + if response == "all" + let all= 1 + elseif response != "y" && response != "yes" + " refresh the directory + NetrwKeepj call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0)) + NetrwKeepj call winrestview(svpos) + let @@= ykeep + return + endif + endif + call rename(oldname,newname) + endfor + call s:NetrwUnmarkList(bufnr("%"),b:netrw_curdir) + + else + + " attempt to rename files/directories + while ctr <= a:lastline + exe "NetrwKeepj ".ctr + + " sanity checks + if line(".") < w:netrw_bannercnt + let ctr= ctr + 1 + continue + endif + let curword= s:NetrwGetWord() + if curword == "./" || curword == "../" + let ctr= ctr + 1 + continue + endif + + NetrwKeepj norm! 0 + let oldname= s:ComposePath(a:path,curword) + + call inputsave() + let newname= input("Moving ".oldname." to : ",substitute(oldname,'/*$','','e')) + call inputrestore() + + call rename(oldname,newname) + let ctr= ctr + 1 + endwhile + endif + + " refresh the directory + NetrwKeepj call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0)) + NetrwKeepj call winrestview(svpos) + let @@= ykeep +endfun + +" --------------------------------------------------------------------- +" s:NetrwLocalRm: {{{2 +fun! s:NetrwLocalRm(path) range + if !exists("w:netrw_bannercnt") + let w:netrw_bannercnt= b:netrw_bannercnt + endif + + " preparation for removing multiple files/directories + let ykeep = @@ + let ret = 0 + let all = 0 + let svpos = winsaveview() + + if exists("s:netrwmarkfilelist_{bufnr('%')}") + " remove all marked files + for fname in s:netrwmarkfilelist_{bufnr("%")} + let ok= s:NetrwLocalRmFile(a:path,fname,all) + if ok =~# 'q\%[uit]' || ok == "no" + break + elseif ok =~# '^a\%[ll]$' + let all= 1 + endif + endfor + call s:NetrwUnMarkFile(1) + + else + " remove (multiple) files and directories + + let keepsol= &l:sol + setl nosol + let ctr = a:firstline + while ctr <= a:lastline + exe "NetrwKeepj ".ctr + + " sanity checks + if line(".") < w:netrw_bannercnt + let ctr= ctr + 1 + continue + endif + let curword= s:NetrwGetWord() + if curword == "./" || curword == "../" + let ctr= ctr + 1 + continue + endif + let ok= s:NetrwLocalRmFile(a:path,curword,all) + if ok =~# 'q\%[uit]' || ok == "no" + break + elseif ok =~# '^a\%[ll]$' + let all= 1 + endif + let ctr= ctr + 1 + endwhile + let &l:sol= keepsol + endif + + " refresh the directory + if bufname("%") != "NetrwMessage" + NetrwKeepj call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0)) + NetrwKeepj call winrestview(svpos) + endif + let @@= ykeep +endfun + +" --------------------------------------------------------------------- +" s:NetrwLocalRmFile: remove file fname given the path {{{2 +" Give confirmation prompt unless all==1 +fun! s:NetrwLocalRmFile(path,fname,all) + " call Dfunc("s:NetrwLocalRmFile(path<".a:path."> fname<".a:fname."> all=".a:all) + + let all= a:all + let ok = "" + NetrwKeepj norm! 0 + let rmfile= s:NetrwFile(s:ComposePath(a:path,escape(a:fname, '\\'))) + " call Decho("rmfile<".rmfile.">",'~'.expand("<slnum>")) + + if rmfile !~ '^"' && (rmfile =~ '@$' || rmfile !~ '[\/]$') + " attempt to remove file + " call Decho("attempt to remove file<".rmfile.">",'~'.expand("<slnum>")) + if !all + echohl Statement + call inputsave() + let ok= input("Confirm deletion of file <".rmfile."> ","[{y(es)},n(o),a(ll),q(uit)] ") + call inputrestore() + echohl NONE + if ok == "" + let ok="no" + endif + " 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]$' + let all= 1 + endif + endif + + if all || ok =~# '^y\%[es]$' || ok == "" + let ret= s:NetrwDelete(rmfile) + " call Decho("errcode=".v:shell_error." ret=".ret,'~'.expand("<slnum>")) + endif + + else + " attempt to remove directory + if !all + echohl Statement + call inputsave() + let ok= input("Confirm *recursive* deletion of directory <".rmfile."> ","[{y(es)},n(o),a(ll),q(uit)] ") + call inputrestore() + let ok= substitute(ok,'\[{y(es)},n(o),a(ll),q(uit)]\s*','','e') + if ok == "" + let ok="no" + endif + if ok =~# '^a\%[ll]$' + let all= 1 + endif + endif + let rmfile= substitute(rmfile,'[\/]$','','e') + + if all || ok =~# '^y\%[es]$' || ok == "" + if delete(rmfile,"rf") + call netrw#ErrorMsg(s:ERROR,"unable to delete directory <".rmfile.">!",103) + endif + endif + endif + + " call Dret("s:NetrwLocalRmFile ".ok) + return ok +endfun + +" ===================================================================== +" Support Functions: {{{1 + +" --------------------------------------------------------------------- +" 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 +fun! netrw#Access(ilist) + if a:ilist == 0 + if exists("s:netrwmarkfilelist_".bufnr('%')) + return s:netrwmarkfilelist_{bufnr('%')} + else + return "no-list-buf#".bufnr('%') + endif + elseif a:ilist == 1 + return s:netrwmftgt + endif +endfun + +" --------------------------------------------------------------------- +" netrw#Call: allows user-specified mappings to call internal netrw functions {{{2 +fun! netrw#Call(funcname,...) + return call("s:".a:funcname,a:000) +endfun + +" --------------------------------------------------------------------- +" netrw#Expose: allows UserMaps and pchk to look at otherwise script-local variables {{{2 +" I expect this function to be used in +" :PChkAssert netrw#Expose("netrwmarkfilelist") +" for example. +fun! netrw#Expose(varname) + " call Dfunc("netrw#Expose(varname<".a:varname.">)") + if exists("s:".a:varname) + exe "let retval= s:".a:varname + " call Decho("retval=".retval,'~'.expand("<slnum>")) + if exists("g:netrw_pchk") + " call Decho("type(g:netrw_pchk=".g:netrw_pchk.")=".type(retval),'~'.expand("<slnum>")) + if type(retval) == 3 + let retval = copy(retval) + let i = 0 + while i < len(retval) + let retval[i]= substitute(retval[i],expand("$HOME"),'~','') + let i = i + 1 + endwhile + endif + " call Dret("netrw#Expose ".string(retval)),'~'.expand("<slnum>")) + return string(retval) + else + " call Decho("g:netrw_pchk doesn't exist",'~'.expand("<slnum>")) + endif + else + " call Decho("s:".a:varname." doesn't exist",'~'.expand("<slnum>")) + let retval= "n/a" + endif + + " call Dret("netrw#Expose ".string(retval)) + return retval +endfun + +" --------------------------------------------------------------------- +" netrw#Modify: allows UserMaps to set (modify) script-local variables {{{2 +fun! netrw#Modify(varname,newvalue) + " call Dfunc("netrw#Modify(varname<".a:varname.">,newvalue<".string(a:newvalue).">)") + exe "let s:".a:varname."= ".string(a:newvalue) + " call Dret("netrw#Modify") +endfun + +" --------------------------------------------------------------------- +" netrw#RFC2396: converts %xx into characters {{{2 +fun! netrw#RFC2396(fname) + " call Dfunc("netrw#RFC2396(fname<".a:fname.">)") + let fname = escape(substitute(a:fname,'%\(\x\x\)','\=printf("%c","0x".submatch(1))','ge')," \t") + " call Dret("netrw#RFC2396 ".fname) + return fname +endfun + +" --------------------------------------------------------------------- +" netrw#UserMaps: supports user-specified maps {{{2 +" see :help function() +" +" g:Netrw_UserMaps is a List with members such as: +" [[keymap sequence, function reference],...] +" +" The referenced function may return a string, +" refresh : refresh the display +" -other- : this string will be executed +" or it may return a List of strings. +" +" Each keymap-sequence will be set up with a nnoremap +" to invoke netrw#UserMaps(a:islocal). +" Related functions: +" netrw#Expose(varname) -- see s:varname variables +" netrw#Modify(varname,newvalue) -- modify value of s:varname variable +" netrw#Call(funcname,...) -- call internal netrw function with optional arguments +fun! netrw#UserMaps(islocal) + " call Dfunc("netrw#UserMaps(islocal=".a:islocal.")") + " call Decho("g:Netrw_UserMaps ".(exists("g:Netrw_UserMaps")? "exists" : "does NOT exist"),'~'.expand("<slnum>")) + + " set up usermaplist + if exists("g:Netrw_UserMaps") && type(g:Netrw_UserMaps) == 3 + " call Decho("g:Netrw_UserMaps has type 3<List>",'~'.expand("<slnum>")) + for umap in g:Netrw_UserMaps + " call Decho("type(umap[0]<".string(umap[0]).">)=".type(umap[0])." (should be 1=string)",'~'.expand("<slnum>")) + " call Decho("type(umap[1])=".type(umap[1])." (should be 1=string)",'~'.expand("<slnum>")) + " if umap[0] is a string and umap[1] is a string holding a function name + if type(umap[0]) == 1 && type(umap[1]) == 1 + " call Decho("nno <buffer> <silent> ".umap[0]." :call s:UserMaps(".a:islocal.",".string(umap[1]).")<cr>",'~'.expand("<slnum>")) + exe "nno <buffer> <silent> ".umap[0]." :call <SID>UserMaps(".a:islocal.",'".umap[1]."')<cr>" + else + call netrw#ErrorMsg(s:WARNING,"ignoring usermap <".string(umap[0])."> -- not a [string,funcref] entry",99) + endif + endfor + endif + " call Dret("netrw#UserMaps") +endfun + +" --------------------------------------------------------------------- +" netrw#WinPath: tries to insure that the path is windows-acceptable, whether cygwin is used or not {{{2 +fun! netrw#WinPath(path) + " call Dfunc("netrw#WinPath(path<".a:path.">)") + if (!g:netrw_cygwin || &shell !~ '\%(\<bash\>\|\<zsh\>\)\%(\.exe\)\=$') && has("win32") + " remove cygdrive prefix, if present + let path = substitute(a:path,g:netrw_cygdrive.'/\(.\)','\1:','') + " remove trailing slash (Win95) + let path = substitute(path, '\(\\\|/\)$', '', 'g') + " remove escaped spaces + let path = substitute(path, '\ ', ' ', 'g') + " convert slashes to backslashes + let path = substitute(path, '/', '\', 'g') + else + let path= a:path + endif + " call Dret("netrw#WinPath <".path.">") + return path +endfun + +" --------------------------------------------------------------------- +" s:StripTrailingSlash: removes trailing slashes from a path {{{2 +fun! s:StripTrailingSlash(path) + " remove trailing slash + return substitute(a:path, '[/\\]$', '', 'g') +endfun + +" --------------------------------------------------------------------- +" s:NetrwBadd: adds marked files to buffer list or vice versa {{{2 +" cb : bl2mf=0 add marked files to buffer list +" cB : bl2mf=1 use bufferlist to mark files +" (mnemonic: cb = copy (marked files) to buffer list) +fun! s:NetrwBadd(islocal,bl2mf) + " " call Dfunc("s:NetrwBadd(islocal=".a:islocal." mf2bl=".mf2bl.")") + if a:bl2mf + " cB: add buffer list to marked files + redir => bufl + ls + redir END + let bufl = map(split(bufl,"\n"),'substitute(v:val,''^.\{-}"\(.*\)".\{-}$'',''\1'','''')') + for fname in bufl + call s:NetrwMarkFile(a:islocal,fname) + endfor + else + " cb: add marked files to buffer list + for fname in s:netrwmarkfilelist_{bufnr("%")} + " " call Decho("badd ".fname,'~'.expand("<slnum>")) + exe "badd ".fnameescape(fname) + endfor + let curbufnr = bufnr("%") + let curdir = s:NetrwGetCurdir(a:islocal) + call s:NetrwUnmarkList(curbufnr,curdir) " remove markings from local buffer + endif + " call Dret("s:NetrwBadd") +endfun + +" --------------------------------------------------------------------- +" s:ComposePath: Appends a new part to a path taking different systems into consideration {{{2 +fun! s:ComposePath(base,subdir) + " call Dfunc("s:ComposePath(base<".a:base."> subdir<".a:subdir.">)") + + if has("amiga") + " call Decho("amiga",'~'.expand("<slnum>")) + let ec = a:base[s:Strlen(a:base)-1] + if ec != '/' && ec != ':' + let ret = a:base."/" . a:subdir + else + let ret = a:base.a:subdir + endif + + " COMBAK: test on windows with changing to root directory: :e C:/ + elseif a:subdir =~ '^\a:[/\\]\([^/\\]\|$\)' && has("win32") + " call Decho("windows",'~'.expand("<slnum>")) + let ret= a:subdir + + elseif a:base =~ '^\a:[/\\]\([^/\\]\|$\)' && has("win32") + " call Decho("windows",'~'.expand("<slnum>")) + if a:base =~ '[/\\]$' + let ret= a:base.a:subdir + else + let ret= a:base.'/'.a:subdir + endif + + elseif a:base =~ '^\a\{3,}://' + " call Decho("remote linux/macos",'~'.expand("<slnum>")) + let urlbase = substitute(a:base,'^\(\a\+://.\{-}/\)\(.*\)$','\1','') + let curpath = substitute(a:base,'^\(\a\+://.\{-}/\)\(.*\)$','\2','') + if a:subdir == '../' + if curpath =~ '[^/]/[^/]\+/$' + let curpath= substitute(curpath,'[^/]\+/$','','') + else + let curpath="" + endif + let ret= urlbase.curpath + else + let ret= urlbase.curpath.a:subdir + endif + " call Decho("urlbase<".urlbase.">",'~'.expand("<slnum>")) + " call Decho("curpath<".curpath.">",'~'.expand("<slnum>")) + " call Decho("ret<".ret.">",'~'.expand("<slnum>")) + + else + " call Decho("local linux/macos",'~'.expand("<slnum>")) + let ret = substitute(a:base."/".a:subdir,"//","/","g") + if a:base =~ '^//' + " keeping initial '//' for the benefit of network share listing support + let ret= '/'.ret + endif + let ret= simplify(ret) + endif + + " call Dret("s:ComposePath ".ret) + return ret +endfun + +" --------------------------------------------------------------------- +" s:DeleteBookmark: deletes a file/directory from Netrw's bookmark system {{{2 +" Related Functions: s:MakeBookmark() s:NetrwBookHistHandler() s:NetrwBookmark() +fun! s:DeleteBookmark(fname) + " call Dfunc("s:DeleteBookmark(fname<".a:fname.">)") + call s:MergeBookmarks() + + if exists("g:netrw_bookmarklist") + let indx= index(g:netrw_bookmarklist,a:fname) + if indx == -1 + let indx= 0 + while indx < len(g:netrw_bookmarklist) + if g:netrw_bookmarklist[indx] =~ a:fname + call remove(g:netrw_bookmarklist,indx) + let indx= indx - 1 + endif + let indx= indx + 1 + endwhile + else + " remove exact match + call remove(g:netrw_bookmarklist,indx) + endif + endif + + " call Dret("s:DeleteBookmark") +endfun + +" --------------------------------------------------------------------- +" s:FileReadable: o/s independent filereadable {{{2 +fun! s:FileReadable(fname) + " call Dfunc("s:FileReadable(fname<".a:fname.">)") + + if g:netrw_cygwin + let ret= filereadable(s:NetrwFile(substitute(a:fname,g:netrw_cygdrive.'/\(.\)','\1:/',''))) + else + let ret= filereadable(s:NetrwFile(a:fname)) + endif + + " call Dret("s:FileReadable ".ret) + return ret +endfun + +" --------------------------------------------------------------------- +" s:GetTempfile: gets a tempname that'll work for various o/s's {{{2 +" Places correct suffix on end of temporary filename, +" using the suffix provided with fname +fun! s:GetTempfile(fname) + " call Dfunc("s:GetTempfile(fname<".a:fname.">)") + + if !exists("b:netrw_tmpfile") + " get a brand new temporary filename + let tmpfile= tempname() + " call Decho("tmpfile<".tmpfile."> : from tempname()",'~'.expand("<slnum>")) + + let tmpfile= substitute(tmpfile,'\','/','ge') + " call Decho("tmpfile<".tmpfile."> : chgd any \\ -> /",'~'.expand("<slnum>")) + + " sanity check -- does the temporary file's directory exist? + if !isdirectory(s:NetrwFile(substitute(tmpfile,'[^/]\+$','','e'))) + " call Decho("ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) + NetrwKeepj call netrw#ErrorMsg(s:ERROR,"your <".substitute(tmpfile,'[^/]\+$','','e')."> directory is missing!",2) + " call Dret("s:GetTempfile getcwd<".getcwd().">") + return "" + endif + + " let netrw#NetSource() know about the tmpfile + let s:netrw_tmpfile= tmpfile " used by netrw#NetSource() and netrw#BrowseX() + " call Decho("tmpfile<".tmpfile."> s:netrw_tmpfile<".s:netrw_tmpfile.">",'~'.expand("<slnum>")) + + " o/s dependencies + if g:netrw_cygwin != 0 + let tmpfile = substitute(tmpfile,'^\(\a\):',g:netrw_cygdrive.'/\1','e') + elseif has("win32") + if !exists("+shellslash") || !&ssl + let tmpfile = substitute(tmpfile,'/','\','g') + endif + else + let tmpfile = tmpfile + endif + let b:netrw_tmpfile= tmpfile + " call Decho("o/s dependent fixed tempname<".tmpfile.">",'~'.expand("<slnum>")) + else + " re-use temporary filename + let tmpfile= b:netrw_tmpfile + " call Decho("tmpfile<".tmpfile."> re-using",'~'.expand("<slnum>")) + endif + + " use fname's suffix for the temporary file + if a:fname != "" + if a:fname =~ '\.[^./]\+$' + " call Decho("using fname<".a:fname.">'s suffix",'~'.expand("<slnum>")) + if a:fname =~ '\.tar\.gz$' || a:fname =~ '\.tar\.bz2$' || a:fname =~ '\.tar\.xz$' + let suffix = ".tar".substitute(a:fname,'^.*\(\.[^./]\+\)$','\1','e') + elseif a:fname =~ '.txz$' + let suffix = ".txz".substitute(a:fname,'^.*\(\.[^./]\+\)$','\1','e') + else + let suffix = substitute(a:fname,'^.*\(\.[^./]\+\)$','\1','e') + endif + " call Decho("suffix<".suffix.">",'~'.expand("<slnum>")) + let tmpfile= substitute(tmpfile,'\.tmp$','','e') + " call Decho("chgd tmpfile<".tmpfile."> (removed any .tmp suffix)",'~'.expand("<slnum>")) + let tmpfile .= suffix + " call Decho("chgd tmpfile<".tmpfile."> (added ".suffix." suffix) netrw_fname<".b:netrw_fname.">",'~'.expand("<slnum>")) + let s:netrw_tmpfile= tmpfile " supports netrw#NetSource() + endif + endif + + " 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:GetTempfile <".tmpfile.">") + return tmpfile +endfun + +" --------------------------------------------------------------------- +" s:MakeSshCmd: transforms input command using USEPORT HOSTNAME into {{{2 +" a correct command for use with a system() call +fun! s:MakeSshCmd(sshcmd) + " call Dfunc("s:MakeSshCmd(sshcmd<".a:sshcmd.">) user<".s:user."> machine<".s:machine.">") + if s:user == "" + let sshcmd = substitute(a:sshcmd,'\<HOSTNAME\>',s:machine,'') + else + let sshcmd = substitute(a:sshcmd,'\<HOSTNAME\>',s:user."@".s:machine,'') + endif + if exists("g:netrw_port") && g:netrw_port != "" + let sshcmd= substitute(sshcmd,"USEPORT",g:netrw_sshport.' '.g:netrw_port,'') + elseif exists("s:port") && s:port != "" + let sshcmd= substitute(sshcmd,"USEPORT",g:netrw_sshport.' '.s:port,'') + else + let sshcmd= substitute(sshcmd,"USEPORT ",'','') + endif + " call Dret("s:MakeSshCmd <".sshcmd.">") + return sshcmd +endfun + +" --------------------------------------------------------------------- +" s:MakeBookmark: enters a bookmark into Netrw's bookmark system {{{2 +fun! s:MakeBookmark(fname) + " call Dfunc("s:MakeBookmark(fname<".a:fname.">)") + + if !exists("g:netrw_bookmarklist") + let g:netrw_bookmarklist= [] + endif + + if index(g:netrw_bookmarklist,a:fname) == -1 + " curdir not currently in g:netrw_bookmarklist, so include it + if isdirectory(s:NetrwFile(a:fname)) && a:fname !~ '/$' + call add(g:netrw_bookmarklist,a:fname.'/') + elseif a:fname !~ '/' + call add(g:netrw_bookmarklist,getcwd()."/".a:fname) + else + call add(g:netrw_bookmarklist,a:fname) + endif + call sort(g:netrw_bookmarklist) + endif + + " call Dret("s:MakeBookmark") +endfun + +" --------------------------------------------------------------------- +" s:MergeBookmarks: merge current bookmarks with saved bookmarks {{{2 +fun! s:MergeBookmarks() + " call Dfunc("s:MergeBookmarks() : merge current bookmarks into .netrwbook") + " get bookmarks from .netrwbook file + let savefile= s:NetrwHome()."/.netrwbook" + if filereadable(s:NetrwFile(savefile)) + " call Decho("merge bookmarks (active and file)",'~'.expand("<slnum>")) + NetrwKeepj call s:NetrwBookHistSave() + " call Decho("bookmark delete savefile<".savefile.">",'~'.expand("<slnum>")) + NetrwKeepj call delete(savefile) + endif + " call Dret("s:MergeBookmarks") +endfun + +" --------------------------------------------------------------------- +" s:NetrwBMShow: {{{2 +fun! s:NetrwBMShow() + " call Dfunc("s:NetrwBMShow()") + redir => bmshowraw + menu + redir END + let bmshowlist = split(bmshowraw,'\n') + if bmshowlist != [] + let bmshowfuncs= filter(bmshowlist,'v:val =~# "<SNR>\\d\\+_BMShow()"') + if bmshowfuncs != [] + let bmshowfunc = substitute(bmshowfuncs[0],'^.*:\(call.*BMShow()\).*$','\1','') + if bmshowfunc =~# '^call.*BMShow()' + exe "sil! NetrwKeepj ".bmshowfunc + endif + endif + endif + " call Dret("s:NetrwBMShow : bmshowfunc<".(exists("bmshowfunc")? bmshowfunc : 'n/a').">") +endfun + +" --------------------------------------------------------------------- +" s:NetrwCursor: responsible for setting cursorline/cursorcolumn based upon g:netrw_cursor {{{2 +fun! s:NetrwCursor(editfile) + if !exists("w:netrw_liststyle") + let w:netrw_liststyle= g:netrw_liststyle + endif + " call Dfunc("s:NetrwCursor() ft<".&ft."> liststyle=".w:netrw_liststyle." g:netrw_cursor=".g:netrw_cursor." s:netrw_usercuc=".s:netrw_usercuc." s:netrw_usercul=".s:netrw_usercul) + + " call Decho("(s:NetrwCursor) COMBAK: cuc=".&l:cuc." cul=".&l:cul) + + if &ft != "netrw" + " if the current window isn't a netrw directory listing window, then use user cursorline/column + " settings. Affects when netrw is used to read/write a file using scp/ftp/etc. + " call Decho("case ft!=netrw: use user cul,cuc",'~'.expand("<slnum>")) + + elseif g:netrw_cursor == 8 + if w:netrw_liststyle == s:WIDELIST + setl cursorline + setl cursorcolumn + else + setl cursorline + endif + elseif g:netrw_cursor == 7 + setl cursorline + elseif g:netrw_cursor == 6 + if w:netrw_liststyle == s:WIDELIST + setl cursorline + endif + elseif g:netrw_cursor == 4 + " all styles: cursorline, cursorcolumn + " call Decho("case g:netrw_cursor==4: setl cul cuc",'~'.expand("<slnum>")) + setl cursorline + setl cursorcolumn + + elseif g:netrw_cursor == 3 + " thin-long-tree: cursorline, user's cursorcolumn + " wide : cursorline, cursorcolumn + if w:netrw_liststyle == s:WIDELIST + " call Decho("case g:netrw_cursor==3 and wide: setl cul cuc",'~'.expand("<slnum>")) + setl cursorline + setl cursorcolumn + else + " call Decho("case g:netrw_cursor==3 and not wide: setl cul (use user's cuc)",'~'.expand("<slnum>")) + setl cursorline + endif + + elseif g:netrw_cursor == 2 + " thin-long-tree: cursorline, user's cursorcolumn + " wide : cursorline, user's cursorcolumn + " call Decho("case g:netrw_cursor==2: setl cuc (use user's cul)",'~'.expand("<slnum>")) + setl cursorline + + elseif g:netrw_cursor == 1 + " thin-long-tree: user's cursorline, user's cursorcolumn + " wide : cursorline, user's cursorcolumn + if w:netrw_liststyle == s:WIDELIST + " call Decho("case g:netrw_cursor==2 and wide: setl cul (use user's cuc)",'~'.expand("<slnum>")) + setl cursorline + else + " call Decho("case g:netrw_cursor==2 and not wide: (use user's cul,cuc)",'~'.expand("<slnum>")) + endif + + else + " all styles: user's cursorline, user's cursorcolumn + " call Decho("default: (use user's cul,cuc)",'~'.expand("<slnum>")) + let &l:cursorline = s:netrw_usercul + let &l:cursorcolumn = s:netrw_usercuc + endif + + " call Decho("(s:NetrwCursor) COMBAK: cuc=".&l:cuc." cul=".&l:cul) + " call Dret("s:NetrwCursor : l:cursorline=".&l:cursorline." l:cursorcolumn=".&l:cursorcolumn) +endfun + +" --------------------------------------------------------------------- +" s:RestoreCursorline: restores cursorline/cursorcolumn to original user settings {{{2 +fun! s:RestoreCursorline() + " call Dfunc("s:RestoreCursorline() currently, cul=".&l:cursorline." cuc=".&l:cursorcolumn." win#".winnr()." buf#".bufnr("%")) + if exists("s:netrw_usercul") + let &l:cursorline = s:netrw_usercul + endif + if exists("s:netrw_usercuc") + let &l:cursorcolumn = s:netrw_usercuc + endif + " call Decho("(s:RestoreCursorline) COMBAK: cuc=".&l:cuc." cul=".&l:cul) + " call Dret("s:RestoreCursorline : restored cul=".&l:cursorline." cuc=".&l:cursorcolumn) +endfun + +" s:RestoreRegister: restores all registers given in the dict {{{2 +fun! s:RestoreRegister(dict) + for [key, val] in items(a:dict) + if key == 'unnamed' + let key = '' + endif + call setreg(key, val[0], val[1]) + endfor +endfun + +" --------------------------------------------------------------------- +" s:NetrwDelete: Deletes a file. {{{2 +" Uses Steve Hall's idea to insure that Windows paths stay +" acceptable. No effect on Unix paths. +" Examples of use: let result= s:NetrwDelete(path) +fun! s:NetrwDelete(path) + " call Dfunc("s:NetrwDelete(path<".a:path.">)") + + let path = netrw#WinPath(a:path) + if !g:netrw_cygwin && has("win32") + if exists("+shellslash") + let sskeep= &shellslash + setl noshellslash + let result = delete(path) + let &shellslash = sskeep + else + " call Decho("exe let result= ".a:cmd."('".path."')",'~'.expand("<slnum>")) + let result= delete(path) + endif + else + " call Decho("let result= delete(".path.")",'~'.expand("<slnum>")) + let result= delete(path) + endif + if result < 0 + NetrwKeepj call netrw#ErrorMsg(s:WARNING,"delete(".path.") failed!",71) + endif + + " call Dret("s:NetrwDelete ".result) + return result +endfun + +" --------------------------------------------------------------------- +" s:NetrwBufRemover: removes a buffer that: {{{2s +" has buffer-id > 1 +" is unlisted +" is unnamed +" does not appear in any window +fun! s:NetrwBufRemover(bufid) + " call Dfunc("s:NetrwBufRemover(".a:bufid.")") + " call Decho("buf#".a:bufid." ".((a:bufid > 1)? ">" : "≯")." must be >1 for removal","~".expand("<slnum>")) + " call Decho("buf#".a:bufid." is ".(buflisted(a:bufid)? "listed" : "unlisted"),"~".expand("<slnum>")) + " call Decho("buf#".a:bufid." has name <".bufname(a:bufid).">","~".expand("<slnum>")) + " call Decho("buf#".a:bufid." has winid#".bufwinid(a:bufid),"~".expand("<slnum>")) + + if a:bufid > 1 && !buflisted(a:bufid) && bufloaded(a:bufid) && bufname(a:bufid) == "" && bufwinid(a:bufid) == -1 + " call Decho("(s:NetrwBufRemover) removing buffer#".a:bufid,"~".expand("<slnum>")) + exe "sil! bd! ".a:bufid + endif + + " call Dret("s:NetrwBufRemover") +endfun + +" --------------------------------------------------------------------- +" s:NetrwEnew: opens a new buffer, passes netrw buffer variables through {{{2 +fun! s:NetrwEnew(...) + " call Dfunc("s:NetrwEnew() a:0=".a:0." win#".winnr()." winnr($)=".winnr("$")." bufnr($)=".bufnr("$")." expand(%)<".expand("%").">") + " call Decho("curdir<".((a:0>0)? a:1 : "")."> buf#".bufnr("%")."<".bufname("%").">",'~'.expand("<slnum>")) + + " Clean out the last buffer: + " Check if the last buffer has # > 1, is unlisted, is unnamed, and does not appear in a window + " If so, delete it. + call s:NetrwBufRemover(bufnr("$")) + + " grab a function-local-variable copy of buffer variables + " call Decho("make function-local copy of netrw variables",'~'.expand("<slnum>")) + if exists("b:netrw_bannercnt") |let netrw_bannercnt = b:netrw_bannercnt |endif + if exists("b:netrw_browser_active") |let netrw_browser_active = b:netrw_browser_active |endif + if exists("b:netrw_cpf") |let netrw_cpf = b:netrw_cpf |endif + if exists("b:netrw_curdir") |let netrw_curdir = b:netrw_curdir |endif + if exists("b:netrw_explore_bufnr") |let netrw_explore_bufnr = b:netrw_explore_bufnr |endif + if exists("b:netrw_explore_indx") |let netrw_explore_indx = b:netrw_explore_indx |endif + if exists("b:netrw_explore_line") |let netrw_explore_line = b:netrw_explore_line |endif + if exists("b:netrw_explore_list") |let netrw_explore_list = b:netrw_explore_list |endif + if exists("b:netrw_explore_listlen")|let netrw_explore_listlen = b:netrw_explore_listlen|endif + if exists("b:netrw_explore_mtchcnt")|let netrw_explore_mtchcnt = b:netrw_explore_mtchcnt|endif + if exists("b:netrw_fname") |let netrw_fname = b:netrw_fname |endif + if exists("b:netrw_lastfile") |let netrw_lastfile = b:netrw_lastfile |endif + if exists("b:netrw_liststyle") |let netrw_liststyle = b:netrw_liststyle |endif + if exists("b:netrw_method") |let netrw_method = b:netrw_method |endif + if exists("b:netrw_option") |let netrw_option = b:netrw_option |endif + if exists("b:netrw_prvdir") |let netrw_prvdir = b:netrw_prvdir |endif + + NetrwKeepj call s:NetrwOptionsRestore("w:") + " call Decho("generate a buffer with NetrwKeepj enew!",'~'.expand("<slnum>")) + " when tree listing uses file TreeListing... a new buffer is made. + " Want the old buffer to be unlisted. + " COMBAK: this causes a problem, see P43 + " setl nobl + let netrw_keepdiff= &l:diff + call s:NetrwEditFile("enew!","","") + let &l:diff= netrw_keepdiff + " call Decho("bufnr($)=".bufnr("$")."<".bufname(bufnr("$"))."> winnr($)=".winnr("$"),'~'.expand("<slnum>")) + NetrwKeepj call s:NetrwOptionsSave("w:") + + " copy function-local-variables to buffer variable equivalents + " call Decho("copy function-local variables back to buffer netrw variables",'~'.expand("<slnum>")) + if exists("netrw_bannercnt") |let b:netrw_bannercnt = netrw_bannercnt |endif + if exists("netrw_browser_active") |let b:netrw_browser_active = netrw_browser_active |endif + if exists("netrw_cpf") |let b:netrw_cpf = netrw_cpf |endif + if exists("netrw_curdir") |let b:netrw_curdir = netrw_curdir |endif + if exists("netrw_explore_bufnr") |let b:netrw_explore_bufnr = netrw_explore_bufnr |endif + if exists("netrw_explore_indx") |let b:netrw_explore_indx = netrw_explore_indx |endif + if exists("netrw_explore_line") |let b:netrw_explore_line = netrw_explore_line |endif + if exists("netrw_explore_list") |let b:netrw_explore_list = netrw_explore_list |endif + if exists("netrw_explore_listlen")|let b:netrw_explore_listlen = netrw_explore_listlen|endif + if exists("netrw_explore_mtchcnt")|let b:netrw_explore_mtchcnt = netrw_explore_mtchcnt|endif + if exists("netrw_fname") |let b:netrw_fname = netrw_fname |endif + if exists("netrw_lastfile") |let b:netrw_lastfile = netrw_lastfile |endif + if exists("netrw_liststyle") |let b:netrw_liststyle = netrw_liststyle |endif + if exists("netrw_method") |let b:netrw_method = netrw_method |endif + if exists("netrw_option") |let b:netrw_option = netrw_option |endif + if exists("netrw_prvdir") |let b:netrw_prvdir = netrw_prvdir |endif + + if a:0 > 0 + let b:netrw_curdir= a:1 + if b:netrw_curdir =~ '/$' + if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST + setl nobl + file NetrwTreeListing + setl nobl bt=nowrite bh=hide + nno <silent> <buffer> [ :sil call <SID>TreeListMove('[')<cr> + nno <silent> <buffer> ] :sil call <SID>TreeListMove(']')<cr> + else + call s:NetrwBufRename(b:netrw_curdir) + endif + endif + endif + if v:version >= 700 && has("balloon_eval") && !exists("s:initbeval") && !exists("g:netrw_nobeval") && has("syntax") && exists("g:syntax_on") + let &l:bexpr = "netrw#BalloonHelp()" + endif + + " call Dret("s:NetrwEnew : buf#".bufnr("%")."<".bufname("%")."> expand(%)<".expand("%")."> expand(#)<".expand("#")."> bh=".&bh." win#".winnr()." winnr($)#".winnr("$")) +endfun + +" --------------------------------------------------------------------- +" s:NetrwExe: executes a string using "!" {{{2 +fun! s:NetrwExe(cmd) + if has("win32") && exepath(&shell) !~? '\v[\/]?(cmd|pwsh|powershell)(\.exe)?$' && !g:netrw_cygwin + let savedShell=[&shell,&shellcmdflag,&shellxquote,&shellxescape,&shellquote,&shellpipe,&shellredir,&shellslash] + set shell& shellcmdflag& shellxquote& shellxescape& + set shellquote& shellpipe& shellredir& shellslash& + try + exe a:cmd + finally + let [&shell,&shellcmdflag,&shellxquote,&shellxescape,&shellquote,&shellpipe,&shellredir,&shellslash] = savedShell + endtry + else + exe a:cmd + endif + if v:shell_error + call netrw#ErrorMsg(s:WARNING,"shell signalled an error",106) + endif +endfun + +" --------------------------------------------------------------------- +" s:NetrwInsureWinVars: insure that a netrw buffer has its w: variables in spite of a wincmd v or s {{{2 +fun! s:NetrwInsureWinVars() + if !exists("w:netrw_liststyle") + " call Dfunc("s:NetrwInsureWinVars() win#".winnr()) + let curbuf = bufnr("%") + let curwin = winnr() + let iwin = 1 + while iwin <= winnr("$") + exe iwin."wincmd w" + if winnr() != curwin && bufnr("%") == curbuf && exists("w:netrw_liststyle") + " looks like ctrl-w_s or ctrl-w_v was used to split a netrw buffer + let winvars= w: + break + endif + let iwin= iwin + 1 + endwhile + exe "keepalt ".curwin."wincmd w" + if exists("winvars") + " call Decho("copying w#".iwin." window variables to w#".curwin,'~'.expand("<slnum>")) + for k in keys(winvars) + let w:{k}= winvars[k] + endfor + endif + " call Dret("s:NetrwInsureWinVars win#".winnr()) + endif +endfun + +" --------------------------------------------------------------------- +" s:NetrwLcd: handles changing the (local) directory {{{2 +" Returns: 0=success +" -1=failed +fun! s:NetrwLcd(newdir) + " call Dfunc("s:NetrwLcd(newdir<".a:newdir.">)") + " call Decho("changing local directory",'~'.expand("<slnum>")) + + let err472= 0 + try + exe 'NetrwKeepj sil lcd '.fnameescape(a:newdir) + catch /^Vim\%((\a\+)\)\=:E344/ + " Vim's lcd fails with E344 when attempting to go above the 'root' of a Windows share. + " Therefore, detect if a Windows share is present, and if E344 occurs, just settle at + " 'root' (ie. '\'). The share name may start with either backslashes ('\\Foo') or + " forward slashes ('//Foo'), depending on whether backslashes have been converted to + " forward slashes by earlier code; so check for both. + if has("win32") && !g:netrw_cygwin + if a:newdir =~ '^\\\\\w\+' || a:newdir =~ '^//\w\+' + let dirname = '\' + exe 'NetrwKeepj sil lcd '.fnameescape(dirname) + endif + endif + catch /^Vim\%((\a\+)\)\=:E472/ + let err472= 1 + endtry + + if err472 + call netrw#ErrorMsg(s:ERROR,"unable to change directory to <".a:newdir."> (permissions?)",61) + if exists("w:netrw_prvdir") + let a:newdir= w:netrw_prvdir + else + call s:NetrwOptionsRestore("w:") + " call Decho("setl noma nomod nowrap",'~'.expand("<slnum>")) + exe "setl ".g:netrw_bufsettings + " call Decho(" ro=".&l:ro." ma=".&l:ma." mod=".&l:mod." wrap=".&l:wrap." (filename<".expand("%")."> win#".winnr()." ft<".&ft.">)",'~'.expand("<slnum>")) + let a:newdir= dirname + endif + " call Dret("s:NetrwBrowse -1 : reusing buffer#".(exists("bufnum")? bufnum : 'N/A')."<".dirname."> getcwd<".getcwd().">") + return -1 + endif + + " call Decho("getcwd <".getcwd().">") + " call Decho("b:netrw_curdir<".b:netrw_curdir.">") + " call Dret("s:NetrwLcd 0") + return 0 +endfun + +" ------------------------------------------------------------------------ +" s:NetrwSaveWordPosn: used to keep cursor on same word after refresh, {{{2 +" changed sorting, etc. Also see s:NetrwRestoreWordPosn(). +fun! s:NetrwSaveWordPosn() + " call Dfunc("NetrwSaveWordPosn()") + let s:netrw_saveword= '^'.fnameescape(getline('.')).'$' + " call Dret("NetrwSaveWordPosn : saveword<".s:netrw_saveword.">") +endfun + +" --------------------------------------------------------------------- +" s:NetrwHumanReadable: takes a number and makes it "human readable" {{{2 +" 1000 -> 1K, 1000000 -> 1M, 1000000000 -> 1G +fun! s:NetrwHumanReadable(sz) + " call Dfunc("s:NetrwHumanReadable(sz=".a:sz.") type=".type(a:sz)." style=".g:netrw_sizestyle ) + + if g:netrw_sizestyle == 'h' + if a:sz >= 1000000000 + let sz = printf("%.1f",a:sz/1000000000.0)."g" + elseif a:sz >= 10000000 + let sz = printf("%d",a:sz/1000000)."m" + elseif a:sz >= 1000000 + let sz = printf("%.1f",a:sz/1000000.0)."m" + elseif a:sz >= 10000 + let sz = printf("%d",a:sz/1000)."k" + elseif a:sz >= 1000 + let sz = printf("%.1f",a:sz/1000.0)."k" + else + let sz= a:sz + endif + + elseif g:netrw_sizestyle == 'H' + if a:sz >= 1073741824 + let sz = printf("%.1f",a:sz/1073741824.0)."G" + elseif a:sz >= 10485760 + let sz = printf("%d",a:sz/1048576)."M" + elseif a:sz >= 1048576 + let sz = printf("%.1f",a:sz/1048576.0)."M" + elseif a:sz >= 10240 + let sz = printf("%d",a:sz/1024)."K" + elseif a:sz >= 1024 + let sz = printf("%.1f",a:sz/1024.0)."K" + else + let sz= a:sz + endif + + else + let sz= a:sz + endif + + " call Dret("s:NetrwHumanReadable ".sz) + return sz +endfun + +" --------------------------------------------------------------------- +" s:NetrwRestoreWordPosn: used to keep cursor on same word after refresh, {{{2 +" changed sorting, etc. Also see s:NetrwSaveWordPosn(). +fun! s:NetrwRestoreWordPosn() + " call Dfunc("NetrwRestoreWordPosn()") + sil! call search(s:netrw_saveword,'w') + " call Dret("NetrwRestoreWordPosn") +endfun + +" --------------------------------------------------------------------- +" s:RestoreBufVars: {{{2 +fun! s:RestoreBufVars() + " call Dfunc("s:RestoreBufVars()") + + if exists("s:netrw_curdir") |let b:netrw_curdir = s:netrw_curdir |endif + if exists("s:netrw_lastfile") |let b:netrw_lastfile = s:netrw_lastfile |endif + if exists("s:netrw_method") |let b:netrw_method = s:netrw_method |endif + if exists("s:netrw_fname") |let b:netrw_fname = s:netrw_fname |endif + if exists("s:netrw_machine") |let b:netrw_machine = s:netrw_machine |endif + if exists("s:netrw_browser_active")|let b:netrw_browser_active = s:netrw_browser_active|endif + + " call Dret("s:RestoreBufVars") +endfun + +" --------------------------------------------------------------------- +" s:RemotePathAnalysis: {{{2 +fun! s:RemotePathAnalysis(dirname) + " call Dfunc("s:RemotePathAnalysis(a:dirname<".a:dirname.">)") + + " method :// user @ machine :port /path + let dirpat = '^\(\w\{-}\)://\(\(\w\+\)@\)\=\([^/:#]\+\)\%([:#]\(\d\+\)\)\=/\(.*\)$' + let s:method = substitute(a:dirname,dirpat,'\1','') + let s:user = substitute(a:dirname,dirpat,'\3','') + let s:machine = substitute(a:dirname,dirpat,'\4','') + let s:port = substitute(a:dirname,dirpat,'\5','') + let s:path = substitute(a:dirname,dirpat,'\6','') + let s:fname = substitute(s:path,'^.*/\ze.','','') + if s:machine =~ '@' + let dirpat = '^\(.*\)@\(.\{-}\)$' + let s:user = s:user.'@'.substitute(s:machine,dirpat,'\1','') + let s:machine = substitute(s:machine,dirpat,'\2','') + endif + + " call Decho("set up s:method <".s:method .">",'~'.expand("<slnum>")) + " call Decho("set up s:user <".s:user .">",'~'.expand("<slnum>")) + " call Decho("set up s:machine<".s:machine.">",'~'.expand("<slnum>")) + " call Decho("set up s:port <".s:port.">",'~'.expand("<slnum>")) + " call Decho("set up s:path <".s:path .">",'~'.expand("<slnum>")) + " call Decho("set up s:fname <".s:fname .">",'~'.expand("<slnum>")) + + " call Dret("s:RemotePathAnalysis") +endfun + +" --------------------------------------------------------------------- +" s:RemoteSystem: runs a command on a remote host using ssh {{{2 +" Returns status +" Runs system() on +" [cd REMOTEDIRPATH;] a:cmd +" Note that it doesn't do s:ShellEscape(a:cmd)! +fun! s:RemoteSystem(cmd) + " call Dfunc("s:RemoteSystem(cmd<".a:cmd.">)") + if !executable(g:netrw_ssh_cmd) + NetrwKeepj call netrw#ErrorMsg(s:ERROR,"g:netrw_ssh_cmd<".g:netrw_ssh_cmd."> is not executable!",52) + elseif !exists("b:netrw_curdir") + NetrwKeepj call netrw#ErrorMsg(s:ERROR,"for some reason b:netrw_curdir doesn't exist!",53) + else + let cmd = s:MakeSshCmd(g:netrw_ssh_cmd." USEPORT HOSTNAME") + let remotedir= substitute(b:netrw_curdir,'^.*//[^/]\+/\(.*\)$','\1','') + if remotedir != "" + let cmd= cmd.' cd '.s:ShellEscape(remotedir).";" + else + let cmd= cmd.' ' + endif + let cmd= cmd.a:cmd + " call Decho("call system(".cmd.")",'~'.expand("<slnum>")) + let ret= system(cmd) + endif + " call Dret("s:RemoteSystem ".ret) + return ret +endfun + +" --------------------------------------------------------------------- +" s:RestoreWinVars: (used by Explore() and NetrwSplit()) {{{2 +fun! s:RestoreWinVars() + " call Dfunc("s:RestoreWinVars()") + if exists("s:bannercnt") |let w:netrw_bannercnt = s:bannercnt |unlet s:bannercnt |endif + if exists("s:col") |let w:netrw_col = s:col |unlet s:col |endif + if exists("s:curdir") |let w:netrw_curdir = s:curdir |unlet s:curdir |endif + if exists("s:explore_bufnr") |let w:netrw_explore_bufnr = s:explore_bufnr |unlet s:explore_bufnr |endif + if exists("s:explore_indx") |let w:netrw_explore_indx = s:explore_indx |unlet s:explore_indx |endif + if exists("s:explore_line") |let w:netrw_explore_line = s:explore_line |unlet s:explore_line |endif + if exists("s:explore_listlen")|let w:netrw_explore_listlen = s:explore_listlen|unlet s:explore_listlen|endif + if exists("s:explore_list") |let w:netrw_explore_list = s:explore_list |unlet s:explore_list |endif + if exists("s:explore_mtchcnt")|let w:netrw_explore_mtchcnt = s:explore_mtchcnt|unlet s:explore_mtchcnt|endif + if exists("s:fpl") |let w:netrw_fpl = s:fpl |unlet s:fpl |endif + if exists("s:hline") |let w:netrw_hline = s:hline |unlet s:hline |endif + if exists("s:line") |let w:netrw_line = s:line |unlet s:line |endif + if exists("s:liststyle") |let w:netrw_liststyle = s:liststyle |unlet s:liststyle |endif + if exists("s:method") |let w:netrw_method = s:method |unlet s:method |endif + if exists("s:prvdir") |let w:netrw_prvdir = s:prvdir |unlet s:prvdir |endif + if exists("s:treedict") |let w:netrw_treedict = s:treedict |unlet s:treedict |endif + if exists("s:treetop") |let w:netrw_treetop = s:treetop |unlet s:treetop |endif + if exists("s:winnr") |let w:netrw_winnr = s:winnr |unlet s:winnr |endif + " call Dret("s:RestoreWinVars") +endfun + +" --------------------------------------------------------------------- +" s:Rexplore: implements returning from a buffer to a netrw directory {{{2 +" +" s:SetRexDir() sets up <2-leftmouse> maps (if g:netrw_retmap +" is true) and a command, :Rexplore, which call this function. +" +" s:netrw_posn is set up by s:NetrwBrowseChgDir() +" +" s:rexposn_BUFNR used to save/restore cursor position +fun! s:NetrwRexplore(islocal,dirname) + if exists("s:netrwdrag") + return + endif + " call Dfunc("s:NetrwRexplore() w:netrw_rexlocal=".w:netrw_rexlocal." w:netrw_rexdir<".w:netrw_rexdir."> win#".winnr()) + " call Decho("currently in bufname<".bufname("%").">",'~'.expand("<slnum>")) + " call Decho("ft=".&ft." win#".winnr()." w:netrw_rexfile<".(exists("w:netrw_rexfile")? w:netrw_rexfile : 'n/a').">",'~'.expand("<slnum>")) + + if &ft == "netrw" && exists("w:netrw_rexfile") && w:netrw_rexfile != "" + " a :Rex while in a netrw buffer means: edit the file in w:netrw_rexfile + " call Decho("in netrw buffer, will edit file<".w:netrw_rexfile.">",'~'.expand("<slnum>")) + exe "NetrwKeepj e ".w:netrw_rexfile + unlet w:netrw_rexfile + " call Dret("s:NetrwRexplore returning from netrw to buf#".bufnr("%")."<".bufname("%")."> (ft=".&ft.")") + return + " else " Decho + " call Decho("treating as not-netrw-buffer: ft=".&ft.((&ft == "netrw")? " == netrw" : "!= netrw"),'~'.expand("<slnum>")) + " call Decho("treating as not-netrw-buffer: w:netrw_rexfile<".((exists("w:netrw_rexfile"))? w:netrw_rexfile : 'n/a').">",'~'.expand("<slnum>")) + endif + + " --------------------------- + " :Rex issued while in a file + " --------------------------- + + " record current file so :Rex can return to it from netrw + let w:netrw_rexfile= expand("%") + " call Decho("set w:netrw_rexfile<".w:netrw_rexfile."> (win#".winnr().")",'~'.expand("<slnum>")) + + if !exists("w:netrw_rexlocal") + " call Dret("s:NetrwRexplore w:netrw_rexlocal doesn't exist (".&ft." win#".winnr().")") + return + endif + " 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>")) + if w:netrw_rexlocal + NetrwKeepj call netrw#LocalBrowseCheck(w:netrw_rexdir) + else + NetrwKeepj call s:NetrwBrowse(0,w:netrw_rexdir) + endif + if exists("s:initbeval") + setl beval + endif + if exists("s:rexposn_".bufnr("%")) + " call Decho("restore posn, then unlet s:rexposn_".bufnr('%')."<".bufname("%").">",'~'.expand("<slnum>")) + " restore position in directory listing + " call Decho("restoring posn to s:rexposn_".bufnr('%')."<".string(s:rexposn_{bufnr('%')}).">",'~'.expand("<slnum>")) + NetrwKeepj call winrestview(s:rexposn_{bufnr('%')}) + if exists("s:rexposn_".bufnr('%')) + unlet s:rexposn_{bufnr('%')} + endif + else + " call Decho("s:rexposn_".bufnr('%')."<".bufname("%")."> doesn't exist",'~'.expand("<slnum>")) + endif + + if has("syntax") && exists("g:syntax_on") && g:syntax_on + if exists("s:explore_match") + exe "2match netrwMarkFile /".s:explore_match."/" + endif + endif + + " 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 Dret("s:NetrwRexplore : ft=".&ft) +endfun + +" --------------------------------------------------------------------- +" s:SaveBufVars: save selected b: variables to s: variables {{{2 +" use s:RestoreBufVars() to restore b: variables from s: variables +fun! s:SaveBufVars() + " call Dfunc("s:SaveBufVars() buf#".bufnr("%")) + + if exists("b:netrw_curdir") |let s:netrw_curdir = b:netrw_curdir |endif + if exists("b:netrw_lastfile") |let s:netrw_lastfile = b:netrw_lastfile |endif + if exists("b:netrw_method") |let s:netrw_method = b:netrw_method |endif + if exists("b:netrw_fname") |let s:netrw_fname = b:netrw_fname |endif + if exists("b:netrw_machine") |let s:netrw_machine = b:netrw_machine |endif + if exists("b:netrw_browser_active")|let s:netrw_browser_active = b:netrw_browser_active|endif + + " call Dret("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("%").">") + + if !exists("a:posndict[bufnr('%')]") + let a:posndict[bufnr("%")]= [] + endif + " call Decho("before push: a:posndict[buf#".bufnr("%")."]=".string(a:posndict[bufnr('%')])) + call add(a:posndict[bufnr("%")],winsaveview()) + " call Decho("after push: a:posndict[buf#".bufnr("%")."]=".string(a:posndict[bufnr('%')])) + + " 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 exists("a:posndict") + if has_key(a:posndict,bufnr("%")) + " call Decho("before pop: a:posndict[buf#".bufnr("%")."]=".string(a:posndict[bufnr('%')])) + let posnlen= len(a:posndict[bufnr("%")]) + if posnlen > 0 + let posnlen= posnlen - 1 + " call Decho("restoring posn posndict[".bufnr("%")."][".posnlen."]=".string(a:posndict[bufnr("%")][posnlen]),'~'.expand("<slnum>")) + call winrestview(a:posndict[bufnr("%")][posnlen]) + call remove(a:posndict[bufnr("%")],posnlen) + " call Decho("after pop: a:posndict[buf#".bufnr("%")."]=".string(a:posndict[bufnr('%')])) + endif + endif + endif + " call Dret("s:RestorePosn") +endfun + +" --------------------------------------------------------------------- +" s:SaveWinVars: (used by Explore() and NetrwSplit()) {{{2 +fun! s:SaveWinVars() + " call Dfunc("s:SaveWinVars() win#".winnr()) + if exists("w:netrw_bannercnt") |let s:bannercnt = w:netrw_bannercnt |endif + if exists("w:netrw_col") |let s:col = w:netrw_col |endif + if exists("w:netrw_curdir") |let s:curdir = w:netrw_curdir |endif + if exists("w:netrw_explore_bufnr") |let s:explore_bufnr = w:netrw_explore_bufnr |endif + if exists("w:netrw_explore_indx") |let s:explore_indx = w:netrw_explore_indx |endif + if exists("w:netrw_explore_line") |let s:explore_line = w:netrw_explore_line |endif + if exists("w:netrw_explore_listlen")|let s:explore_listlen = w:netrw_explore_listlen|endif + if exists("w:netrw_explore_list") |let s:explore_list = w:netrw_explore_list |endif + if exists("w:netrw_explore_mtchcnt")|let s:explore_mtchcnt = w:netrw_explore_mtchcnt|endif + if exists("w:netrw_fpl") |let s:fpl = w:netrw_fpl |endif + if exists("w:netrw_hline") |let s:hline = w:netrw_hline |endif + if exists("w:netrw_line") |let s:line = w:netrw_line |endif + if exists("w:netrw_liststyle") |let s:liststyle = w:netrw_liststyle |endif + if exists("w:netrw_method") |let s:method = w:netrw_method |endif + if exists("w:netrw_prvdir") |let s:prvdir = w:netrw_prvdir |endif + if exists("w:netrw_treedict") |let s:treedict = w:netrw_treedict |endif + if exists("w:netrw_treetop") |let s:treetop = w:netrw_treetop |endif + if exists("w:netrw_winnr") |let s:winnr = w:netrw_winnr |endif + " call Dret("s:SaveWinVars") +endfun + +" --------------------------------------------------------------------- +" s:SetBufWinVars: (used by NetrwBrowse() and LocalBrowseCheck()) {{{2 +" To allow separate windows to have their own activities, such as +" Explore **/pattern, several variables have been made window-oriented. +" However, when the user splits a browser window (ex: ctrl-w s), these +" variables are not inherited by the new window. SetBufWinVars() and +" UseBufWinVars() get around that. +fun! s:SetBufWinVars() + " call Dfunc("s:SetBufWinVars() win#".winnr()) + if exists("w:netrw_liststyle") |let b:netrw_liststyle = w:netrw_liststyle |endif + if exists("w:netrw_bannercnt") |let b:netrw_bannercnt = w:netrw_bannercnt |endif + if exists("w:netrw_method") |let b:netrw_method = w:netrw_method |endif + if exists("w:netrw_prvdir") |let b:netrw_prvdir = w:netrw_prvdir |endif + if exists("w:netrw_explore_indx") |let b:netrw_explore_indx = w:netrw_explore_indx |endif + if exists("w:netrw_explore_listlen")|let b:netrw_explore_listlen= w:netrw_explore_listlen|endif + if exists("w:netrw_explore_mtchcnt")|let b:netrw_explore_mtchcnt= w:netrw_explore_mtchcnt|endif + if exists("w:netrw_explore_bufnr") |let b:netrw_explore_bufnr = w:netrw_explore_bufnr |endif + if exists("w:netrw_explore_line") |let b:netrw_explore_line = w:netrw_explore_line |endif + if exists("w:netrw_explore_list") |let b:netrw_explore_list = w:netrw_explore_list |endif + " call Dret("s:SetBufWinVars") +endfun + +" --------------------------------------------------------------------- +" s:SetRexDir: set directory for :Rexplore {{{2 +fun! s:SetRexDir(islocal,dirname) + " call Dfunc("s:SetRexDir(islocal=".a:islocal." dirname<".a:dirname.">) win#".winnr()) + 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,'~'.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 ".string(winsaveview()),'~'.expand("<slnum>")) + " call Dret("s:SetRexDir : win#".winnr()." ".(a:islocal? "local" : "remote")." dir: ".a:dirname) +endfun + +" --------------------------------------------------------------------- +" s:ShowLink: used to modify thin and tree listings to show links {{{2 +fun! s:ShowLink() + if exists("b:netrw_curdir") + keepp :norm! $?\a + "call histdel("/",-1) + if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treetop") + let basedir = s:NetrwTreePath(w:netrw_treetop) + else + let basedir = b:netrw_curdir.'/' + endif + let fname = basedir.s:NetrwGetWord() + let resname = resolve(fname) + if resname =~ '^\M'.basedir + let dirlen = strlen(basedir) + let resname = strpart(resname,dirlen) + endif + let modline = getline(".")."\t --> ".resname + setl noro ma + call setline(".",modline) + setl ro noma nomod + endif +endfun + +" --------------------------------------------------------------------- +" s:ShowStyle: {{{2 +fun! s:ShowStyle() + if !exists("w:netrw_liststyle") + let liststyle= g:netrw_liststyle + else + let liststyle= w:netrw_liststyle + endif + if liststyle == s:THINLIST + return s:THINLIST.":thin" + elseif liststyle == s:LONGLIST + return s:LONGLIST.":long" + elseif liststyle == s:WIDELIST + return s:WIDELIST.":wide" + elseif liststyle == s:TREELIST + return s:TREELIST.":tree" + else + return 'n/a' + endif +endfun + +" --------------------------------------------------------------------- +" s:Strlen: this function returns the length of a string, even if its using multi-byte characters. {{{2 +" Solution from Nicolai Weibull, vim docs (:help strlen()), +" Tony Mechelynck, and my own invention. +fun! s:Strlen(x) + " "" call Dfunc("s:Strlen(x<".a:x."> g:Align_xstrlen=".g:Align_xstrlen.")") + + if v:version >= 703 && exists("*strdisplaywidth") + let ret= strdisplaywidth(a:x) + + elseif type(g:Align_xstrlen) == 1 + " allow user to specify a function to compute the string length (ie. let g:Align_xstrlen="mystrlenfunc") + exe "let ret= ".g:Align_xstrlen."('".substitute(a:x,"'","''","g")."')" + + elseif g:Align_xstrlen == 1 + " number of codepoints (Latin a + combining circumflex is two codepoints) + " (comment from TM, solution from NW) + let ret= strlen(substitute(a:x,'.','c','g')) + + elseif g:Align_xstrlen == 2 + " number of spacing codepoints (Latin a + combining circumflex is one spacing + " codepoint; a hard tab is one; wide and narrow CJK are one each; etc.) + " (comment from TM, solution from TM) + let ret=strlen(substitute(a:x, '.\Z', 'x', 'g')) + + elseif g:Align_xstrlen == 3 + " virtual length (counting, for instance, tabs as anything between 1 and + " 'tabstop', wide CJK as 2 rather than 1, Arabic alif as zero when immediately + " preceded by lam, one otherwise, etc.) + " (comment from TM, solution from me) + let modkeep= &l:mod + exe "norm! o\<esc>" + call setline(line("."),a:x) + let ret= virtcol("$") - 1 + d + NetrwKeepj norm! k + let &l:mod= modkeep + + else + " at least give a decent default + let ret= strlen(a:x) + endif + " "" call Dret("s:Strlen ".ret) + return ret +endfun + +" --------------------------------------------------------------------- +" s:ShellEscape: shellescape(), or special windows handling {{{2 +fun! s:ShellEscape(s, ...) + if has('win32') && $SHELL == '' && &shellslash + return printf('"%s"', substitute(a:s, '"', '""', 'g')) + endif + let f = a:0 > 0 ? a:1 : 0 + return shellescape(a:s, f) +endfun + +" --------------------------------------------------------------------- +" s:TreeListMove: supports [[, ]], [], and ][ in tree mode {{{2 +fun! s:TreeListMove(dir) + " call Dfunc("s:TreeListMove(dir<".a:dir.">)") + let curline = getline('.') + let prvline = (line(".") > 1)? getline(line(".")-1) : '' + let nxtline = (line(".") < line("$"))? getline(line(".")+1) : '' + let curindent = substitute(getline('.'),'^\(\%('.s:treedepthstring.'\)*\)[^'.s:treedepthstring.'].\{-}$','\1','e') + let indentm1 = substitute(curindent,'^'.s:treedepthstring,'','') + let treedepthchr = substitute(s:treedepthstring,' ','','g') + let stopline = exists("w:netrw_bannercnt")? w:netrw_bannercnt : 1 + " call Decho("prvline <".prvline."> #".(line(".")-1), '~'.expand("<slnum>")) + " call Decho("curline <".curline."> #".line(".") , '~'.expand("<slnum>")) + " call Decho("nxtline <".nxtline."> #".(line(".")+1), '~'.expand("<slnum>")) + " call Decho("curindent<".curindent.">" , '~'.expand("<slnum>")) + " call Decho("indentm1 <".indentm1.">" , '~'.expand("<slnum>")) + " COMBAK : need to handle when on a directory + " COMBAK : need to handle ]] and ][. In general, needs work!!! + if curline !~ '/$' + if a:dir == '[[' && prvline != '' + NetrwKeepj norm! 0 + let nl = search('^'.indentm1.'\%('.s:treedepthstring.'\)\@!','bWe',stopline) " search backwards + " call Decho("regfile srch back: ".nl,'~'.expand("<slnum>")) + elseif a:dir == '[]' && nxtline != '' + NetrwKeepj norm! 0 + " call Decho('srchpat<'.'^\%('.curindent.'\)\@!'.'>','~'.expand("<slnum>")) + let nl = search('^\%('.curindent.'\)\@!','We') " search forwards + if nl != 0 + NetrwKeepj norm! k + else + NetrwKeepj norm! G + endif + " call Decho("regfile srch fwd: ".nl,'~'.expand("<slnum>")) + endif + endif + + " call Dret("s:TreeListMove") +endfun + +" --------------------------------------------------------------------- +" s:UpdateBuffersMenu: does emenu Buffers.Refresh (but due to locale, the menu item may not be called that) {{{2 +" The Buffers.Refresh menu calls s:BMShow(); unfortunately, that means that that function +" can't be called except via emenu. But due to locale, that menu line may not be called +" Buffers.Refresh; hence, s:NetrwBMShow() utilizes a "cheat" to call that function anyway. +fun! s:UpdateBuffersMenu() + " call Dfunc("s:UpdateBuffersMenu()") + if has("gui") && has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu + try + sil emenu Buffers.Refresh\ menu + catch /^Vim\%((\a\+)\)\=:E/ + let v:errmsg= "" + sil NetrwKeepj call s:NetrwBMShow() + endtry + endif + " call Dret("s:UpdateBuffersMenu") +endfun + +" --------------------------------------------------------------------- +" s:UseBufWinVars: (used by NetrwBrowse() and LocalBrowseCheck() {{{2 +" Matching function to s:SetBufWinVars() +fun! s:UseBufWinVars() + " call Dfunc("s:UseBufWinVars()") + if exists("b:netrw_liststyle") && !exists("w:netrw_liststyle") |let w:netrw_liststyle = b:netrw_liststyle |endif + if exists("b:netrw_bannercnt") && !exists("w:netrw_bannercnt") |let w:netrw_bannercnt = b:netrw_bannercnt |endif + if exists("b:netrw_method") && !exists("w:netrw_method") |let w:netrw_method = b:netrw_method |endif + if exists("b:netrw_prvdir") && !exists("w:netrw_prvdir") |let w:netrw_prvdir = b:netrw_prvdir |endif + if exists("b:netrw_explore_indx") && !exists("w:netrw_explore_indx") |let w:netrw_explore_indx = b:netrw_explore_indx |endif + if exists("b:netrw_explore_listlen") && !exists("w:netrw_explore_listlen")|let w:netrw_explore_listlen = b:netrw_explore_listlen|endif + if exists("b:netrw_explore_mtchcnt") && !exists("w:netrw_explore_mtchcnt")|let w:netrw_explore_mtchcnt = b:netrw_explore_mtchcnt|endif + if exists("b:netrw_explore_bufnr") && !exists("w:netrw_explore_bufnr") |let w:netrw_explore_bufnr = b:netrw_explore_bufnr |endif + if exists("b:netrw_explore_line") && !exists("w:netrw_explore_line") |let w:netrw_explore_line = b:netrw_explore_line |endif + if exists("b:netrw_explore_list") && !exists("w:netrw_explore_list") |let w:netrw_explore_list = b:netrw_explore_list |endif + " call Dret("s:UseBufWinVars") +endfun + +" --------------------------------------------------------------------- +" s:UserMaps: supports user-defined UserMaps {{{2 +" * calls a user-supplied funcref(islocal,curdir) +" * interprets result +" See netrw#UserMaps() +fun! s:UserMaps(islocal,funcname) + if !exists("b:netrw_curdir") + let b:netrw_curdir= getcwd() + endif + let Funcref = function(a:funcname) + let result = Funcref(a:islocal) + + if type(result) == 1 + " if result from user's funcref is a string... + if result == "refresh" + call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) + elseif result != "" + exe result + endif + + elseif type(result) == 3 + " if result from user's funcref is a List... + for action in result + if action == "refresh" + call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) + elseif action != "" + exe action + endif + endfor + endif +endfun + +" ========================== +" Settings Restoration: {{{1 +" ========================== +let &cpo= s:keepcpo +unlet s:keepcpo + +" =============== +" Modelines: {{{1 +" =============== +" vim:ts=8 sts=2 sw=2 et fdm=marker diff --git a/runtime/pack/dist/opt/netrw/autoload/netrwSettings.vim b/runtime/pack/dist/opt/netrw/autoload/netrwSettings.vim new file mode 100644 index 0000000000..884d9ce081 --- /dev/null +++ b/runtime/pack/dist/opt/netrw/autoload/netrwSettings.vim @@ -0,0 +1,242 @@ +" Maintainer: Luca Saccarola <github.e41mv@aleeas.com> +" Former Maintainer: Charles E Campbell +" Upstream: <https://github.com/saccarosium/netrw.vim> +" Copyright: Copyright (C) 1999-2007 Charles E. Campbell {{{ +" Permission is hereby granted to use and distribute this code, +" with or without modifications, provided that this copyright +" notice is copied with it. Like anything else that's free, +" netrwSettings.vim is provided *as is* and comes with no +" warranty of any kind, either expressed or implied. By using +" this plugin, you agree that in no event will the copyright +" holder be liable for any damages resulting from the use +" of this software. }}} + +if &cp || exists("g:loaded_netrwSettings") + finish +endif + +let g:loaded_netrwSettings = "v175" +if v:version < 700 + echohl WarningMsg + echo "***warning*** this version of netrwSettings needs vim 7.0" + echohl Normal + finish +endif + +" NetrwSettings: {{{ + +function! netrwSettings#NetrwSettings() + " this call is here largely just to insure that netrw has been loaded + call netrw#WinPath("") + if !exists("g:loaded_netrw") + echohl WarningMsg + echomsg "***sorry*** netrw needs to be loaded prior to using NetrwSettings" + echohl None + return + endif + + above wincmd s + enew + setlocal noswapfile bh=wipe + set ft=vim + file Netrw\ Settings + + " these variables have the following default effects when they don't + " exist (ie. have not been set by the user in his/her .vimrc) + if !exists("g:netrw_liststyle") + let g:netrw_liststyle= 0 + let g:netrw_list_cmd= "ssh HOSTNAME ls -FLa" + endif + if !exists("g:netrw_silent") + let g:netrw_silent= 0 + endif + if !exists("g:netrw_use_nt_rcp") + let g:netrw_use_nt_rcp= 0 + endif + if !exists("g:netrw_ftp") + let g:netrw_ftp= 0 + endif + if !exists("g:netrw_ignorenetrc") + let g:netrw_ignorenetrc= 0 + endif + + put ='+ ---------------------------------------------' + put ='+ NetrwSettings: by Charles E. Campbell' + put ='+ Press <F1> with cursor atop any line for help' + put ='+ ---------------------------------------------' + let s:netrw_settings_stop= line(".") + + put ='' + put ='+ Netrw Protocol Commands' + put = 'let g:netrw_dav_cmd = '.g:netrw_dav_cmd + put = 'let g:netrw_fetch_cmd = '.g:netrw_fetch_cmd + put = 'let g:netrw_ftp_cmd = '.g:netrw_ftp_cmd + put = 'let g:netrw_http_cmd = '.g:netrw_http_cmd + put = 'let g:netrw_rcp_cmd = '.g:netrw_rcp_cmd + put = 'let g:netrw_rsync_cmd = '.g:netrw_rsync_cmd + put = 'let g:netrw_scp_cmd = '.g:netrw_scp_cmd + put = 'let g:netrw_sftp_cmd = '.g:netrw_sftp_cmd + put = 'let g:netrw_ssh_cmd = '.g:netrw_ssh_cmd + let s:netrw_protocol_stop= line(".") + put = '' + + put ='+Netrw Transfer Control' + put = 'let g:netrw_cygwin = '.g:netrw_cygwin + put = 'let g:netrw_ftp = '.g:netrw_ftp + put = 'let g:netrw_ftpmode = '.g:netrw_ftpmode + put = 'let g:netrw_ignorenetrc = '.g:netrw_ignorenetrc + put = 'let g:netrw_sshport = '.g:netrw_sshport + put = 'let g:netrw_silent = '.g:netrw_silent + put = 'let g:netrw_use_nt_rcp = '.g:netrw_use_nt_rcp + let s:netrw_xfer_stop= line(".") + put ='' + put ='+ Netrw Messages' + put ='let g:netrw_use_errorwindow = '.g:netrw_use_errorwindow + + put = '' + put ='+ Netrw Browser Control' + if exists("g:netrw_altfile") + put = 'let g:netrw_altfile = '.g:netrw_altfile + else + put = 'let g:netrw_altfile = 0' + endif + put = 'let g:netrw_alto = '.g:netrw_alto + put = 'let g:netrw_altv = '.g:netrw_altv + put = 'let g:netrw_banner = '.g:netrw_banner + if exists("g:netrw_bannerbackslash") + put = 'let g:netrw_bannerbackslash = '.g:netrw_bannerbackslash + else + put = '\" let g:netrw_bannerbackslash = (not defined)' + endif + put = 'let g:netrw_browse_split = '.g:netrw_browse_split + if exists("g:netrw_browsex_viewer") + put = 'let g:netrw_browsex_viewer = '.g:netrw_browsex_viewer + else + put = '\" let g:netrw_browsex_viewer = (not defined)' + endif + put = 'let g:netrw_compress = '.g:netrw_compress + if exists("g:Netrw_corehandler") + put = 'let g:Netrw_corehandler = '.g:Netrw_corehandler + else + put = '\" let g:Netrw_corehandler = (not defined)' + endif + put = 'let g:netrw_ctags = '.g:netrw_ctags + put = 'let g:netrw_cursor = '.g:netrw_cursor + let decompressline= line("$") + put = 'let g:netrw_decompress = '.string(g:netrw_decompress) + if exists("g:netrw_dynamic_maxfilenamelen") + put = 'let g:netrw_dynamic_maxfilenamelen='.g:netrw_dynamic_maxfilenamelen + else + put = '\" let g:netrw_dynamic_maxfilenamelen= (not defined)' + endif + put = 'let g:netrw_dirhistmax = '.g:netrw_dirhistmax + put = 'let g:netrw_errorlvl = '.g:netrw_errorlvl + put = 'let g:netrw_fastbrowse = '.g:netrw_fastbrowse + let fnameescline= line("$") + put = 'let g:netrw_fname_escape = '.string(g:netrw_fname_escape) + put = 'let g:netrw_ftp_browse_reject = '.g:netrw_ftp_browse_reject + put = 'let g:netrw_ftp_list_cmd = '.g:netrw_ftp_list_cmd + put = 'let g:netrw_ftp_sizelist_cmd = '.g:netrw_ftp_sizelist_cmd + put = 'let g:netrw_ftp_timelist_cmd = '.g:netrw_ftp_timelist_cmd + let globescline= line("$") + put = 'let g:netrw_glob_escape = '.string(g:netrw_glob_escape) + put = 'let g:netrw_hide = '.g:netrw_hide + if exists("g:netrw_home") + put = 'let g:netrw_home = '.g:netrw_home + else + put = '\" let g:netrw_home = (not defined)' + endif + put = 'let g:netrw_keepdir = '.g:netrw_keepdir + put = 'let g:netrw_list_cmd = '.g:netrw_list_cmd + put = 'let g:netrw_list_hide = '.g:netrw_list_hide + put = 'let g:netrw_liststyle = '.g:netrw_liststyle + put = 'let g:netrw_localcopycmd = '.g:netrw_localcopycmd + put = 'let g:netrw_localcopycmdopt = '.g:netrw_localcopycmdopt + put = 'let g:netrw_localmkdir = '.g:netrw_localmkdir + put = 'let g:netrw_localmkdiropt = '.g:netrw_localmkdiropt + put = 'let g:netrw_localmovecmd = '.g:netrw_localmovecmd + put = 'let g:netrw_localmovecmdopt = '.g:netrw_localmovecmdopt + put = 'let g:netrw_maxfilenamelen = '.g:netrw_maxfilenamelen + put = 'let g:netrw_menu = '.g:netrw_menu + put = 'let g:netrw_mousemaps = '.g:netrw_mousemaps + put = 'let g:netrw_mkdir_cmd = '.g:netrw_mkdir_cmd + if exists("g:netrw_nobeval") + put = 'let g:netrw_nobeval = '.g:netrw_nobeval + else + put = '\" let g:netrw_nobeval = (not defined)' + endif + put = 'let g:netrw_remote_mkdir = '.g:netrw_remote_mkdir + put = 'let g:netrw_preview = '.g:netrw_preview + put = 'let g:netrw_rename_cmd = '.g:netrw_rename_cmd + put = 'let g:netrw_retmap = '.g:netrw_retmap + put = 'let g:netrw_rm_cmd = '.g:netrw_rm_cmd + put = 'let g:netrw_rmdir_cmd = '.g:netrw_rmdir_cmd + put = 'let g:netrw_rmf_cmd = '.g:netrw_rmf_cmd + put = 'let g:netrw_sort_by = '.g:netrw_sort_by + put = 'let g:netrw_sort_direction = '.g:netrw_sort_direction + put = 'let g:netrw_sort_options = '.g:netrw_sort_options + put = 'let g:netrw_sort_sequence = '.g:netrw_sort_sequence + put = 'let g:netrw_servername = '.g:netrw_servername + put = 'let g:netrw_special_syntax = '.g:netrw_special_syntax + put = 'let g:netrw_ssh_browse_reject = '.g:netrw_ssh_browse_reject + put = 'let g:netrw_ssh_cmd = '.g:netrw_ssh_cmd + put = 'let g:netrw_scpport = '.g:netrw_scpport + put = 'let g:netrw_sepchr = '.g:netrw_sepchr + put = 'let g:netrw_sshport = '.g:netrw_sshport + put = 'let g:netrw_timefmt = '.g:netrw_timefmt + let tmpfileescline= line("$") + put ='let g:netrw_tmpfile_escape...' + put = 'let g:netrw_use_noswf = '.g:netrw_use_noswf + put = 'let g:netrw_xstrlen = '.g:netrw_xstrlen + put = 'let g:netrw_winsize = '.g:netrw_winsize + + put ='' + put ='+ For help, place cursor on line and press <F1>' + + 1d + silent %s/^+/"/e + res 99 + silent %s/= \([^0-9].*\)$/= '\1'/e + silent %s/= $/= ''/e + 1 + + call setline(decompressline, "let g:netrw_decompress = ".substitute(string(g:netrw_decompress),"^'\\(.*\\)'$",'\1','')) + call setline(fnameescline, "let g:netrw_fname_escape = '".escape(g:netrw_fname_escape,"'")."'") + call setline(globescline, "let g:netrw_glob_escape = '".escape(g:netrw_glob_escape,"'")."'") + call setline(tmpfileescline, "let g:netrw_tmpfile_escape = '".escape(g:netrw_tmpfile_escape,"'")."'") + + set nomod + + nmap <buffer> <silent> <F1> :call NetrwSettingHelp()<cr> + nnoremap <buffer> <silent> <leftmouse> <leftmouse> :call NetrwSettingHelp()<cr> + let tmpfile= tempname() + exe 'au BufWriteCmd Netrw\ Settings silent w! '.tmpfile.'|so '.tmpfile.'|call delete("'.tmpfile.'")|set nomod' +endfunction + +" }}} +" NetrwSettingHelp: {{{ + +function! NetrwSettingHelp() + let curline = getline(".") + if curline =~ '=' + let varhelp = substitute(curline,'^\s*let ','','e') + let varhelp = substitute(varhelp,'\s*=.*$','','e') + try + exe "he ".varhelp + catch /^Vim\%((\a\+)\)\=:E149/ + echo "***sorry*** no help available for <".varhelp.">" + endtry + elseif line(".") < s:netrw_settings_stop + he netrw-settings + elseif line(".") < s:netrw_protocol_stop + he netrw-externapp + elseif line(".") < s:netrw_xfer_stop + he netrw-variables + else + he netrw-browse-var + endif +endfunction + +" }}} + +" vim:ts=8 sts=4 sw=4 et fdm=marker diff --git a/runtime/autoload/netrw_gitignore.vim b/runtime/pack/dist/opt/netrw/autoload/netrw_gitignore.vim index 1b55e2488a..c76d28db04 100644 --- a/runtime/autoload/netrw_gitignore.vim +++ b/runtime/pack/dist/opt/netrw/autoload/netrw_gitignore.vim @@ -1,3 +1,7 @@ +" Maintainer: Luca Saccarola <github.e41mv@aleeas.com> +" Former Maintainer: Bruno Sutic +" Upstream: <https://github.com/saccarosium/netrw.vim> + " netrw_gitignore#Hide: gitignore-based hiding " Function returns a string of comma separated patterns convenient for " assignment to `g:netrw_list_hide` option. @@ -8,7 +12,7 @@ " let g:netrw_list_hide = netrw_gitignore#Hide() " let g:netrw_list_hide = netrw_gitignore#Hide() . 'more,hide,patterns' " -" Copyright: Copyright (C) 2013 Bruno Sutic {{{1 +" Copyright: Copyright (C) 2013 Bruno Sutic {{{ " Permission is hereby granted to use and distribute this code, " with or without modifications, provided that this copyright " notice is copied with it. Like anything else that's free, @@ -16,7 +20,10 @@ " warranty of any kind, either expressed or implied. By using " this plugin, you agree that in no event will the copyright " holder be liable for any damages resulting from the use -" of this software. +" of this software. }}} + function! netrw_gitignore#Hide(...) - return substitute(substitute(system('git ls-files --other --ignored --exclude-standard --directory'), '\n', ',', 'g'), ',$', '', '') + return substitute(substitute(system('git ls-files --other --ignored --exclude-standard --directory'), '\n', ',', 'g'), ',$', '', '') endfunction + +" vim:ts=8 sts=4 sw=4 et fdm=marker diff --git a/runtime/doc/pi_netrw.txt b/runtime/pack/dist/opt/netrw/doc/netrw.txt index 09d1369d46..9fc7b42bb7 100644 --- a/runtime/doc/pi_netrw.txt +++ b/runtime/pack/dist/opt/netrw/doc/netrw.txt @@ -1,14 +1,12 @@ -*pi_netrw.txt* For Vim version 8.2. Last change: 2020 Aug 15 +*netrw.txt* Nvim ------------------------------------------------ NETRW REFERENCE MANUAL by Charles E. Campbell ------------------------------------------------ -Author: Charles E. Campbell <NcampObell@SdrPchip.AorgM-NOSPAM> - (remove NOSPAM from Campbell's email first) - +Original Author: Charles E. Campbell Copyright: Copyright (C) 2017 Charles E Campbell *netrw-copyright* The VIM LICENSE applies to the files in this package, including - netrw.vim, pi_netrw.txt, netrwFileHandlers.vim, netrwSettings.vim, and + netrw.vim, netrw.txt, netrwSettings.vim, and syntax/netrw.vim. Like anything else that's free, netrw.vim and its associated files are provided *as is* and comes with no warranty of any kind, either expressed or implied. No guarantees of @@ -110,10 +108,7 @@ Copyright: Copyright (C) 2017 Charles E Campbell *netrw-copyright* Selecting Sorting Style.............................|netrw-s| Setting Editing Window..............................|netrw-C| 10. Problems and Fixes....................................|netrw-problems| -11. Debugging Netrw Itself................................|netrw-debug| -12. History...............................................|netrw-history| -13. Todo..................................................|netrw-todo| -14. Credits...............................................|netrw-credits| +11. Credits...............................................|netrw-credits| ============================================================================== 2. Starting With Netrw *netrw-start* {{{1 @@ -2645,10 +2640,34 @@ your browsing preferences. (see also: |netrw-settings|) netrw last saw |g:netrw_cursor| >= 5 or when netrw was initially run. - *g:netrw_decompress* = { ".gz" : "gunzip" , - ".bz2" : "bunzip2" , - ".zip" : "unzip" , - ".tar" : "tar -xf"} + *g:netrw_decompress* = { ".lz4": "lz4 -d", + ".lzo": "lzop -d", + ".lz": "lzip -dk", + ".7z": "7za x", + ".001": "7za x", + ".tar.bz": "tar -xvjf", + ".tar.bz2": "tar -xvjf", + ".tbz": "tar -xvjf", + ".tbz2": "tar -xvjf", + ".tar.gz": "tar -xvzf", + ".tgz": "tar -xvzf", + ".tar.zst": "tar --use-compress-program=unzstd -xvf", + ".tzst": "tar --use-compress-program=unzstd -xvf", + ".tar": "tar -xvf", + ".zip": "unzip", + ".bz": "bunzip2 -k", + ".bz2": "bunzip2 -k", + ".gz": "gunzip -k", + ".lzma": "unlzma -T0 -k", + ".xz": "unxz -T0 -k", + ".zst": "zstd -T0 -d", + ".Z": "uncompress -k", + ".rar": "unrar x -ad", + ".tar.lzma": "tar --lzma -xvf", + ".tlz": "tar --lzma -xvf", + ".tar.xz": "tar -xvJf", + ".txz": "tar -xvJf"} + A dictionary mapping suffices to decompression programs. @@ -3376,7 +3395,6 @@ Example: Clear netrw's marked file list via a mapping on gu > 10. Problems and Fixes *netrw-problems* {{{1 (This section is likely to grow as I get feedback) - (also see |netrw-debug|) *netrw-p1* P1. I use Windows, and my network browsing with ftp doesn't sort by {{{2 time or size! -or- The remote system is a Windows server; why @@ -3739,613 +3757,8 @@ Example: Clear netrw's marked file list via a mapping on gu > directory, which may not be the same as the browsing directory shown by netrw (see |g:netrw_keepdir|). - -============================================================================== -11. Debugging Netrw Itself *netrw-debug* {{{1 - -Step 1: check that the problem you've encountered hasn't already been resolved -by obtaining a copy of the latest (often developmental) netrw at: - - http://www.drchip.org/astronaut/vim/index.html#NETRW - -The <netrw.vim> script is typically installed on systems as something like: -> - /usr/local/share/vim/vim8x/plugin/netrwPlugin.vim - /usr/local/share/vim/vim8x/autoload/netrw.vim - (see output of :echo &rtp) -< -which is loaded automatically at startup (assuming :set nocp). If you -installed a new netrw, then it will be located at > - - $HOME/.vim/plugin/netrwPlugin.vim - $HOME/.vim/autoload/netrw.vim -< -Step 2: assuming that you've installed the latest version of netrw, -check that your problem is really due to netrw. Create a file -called netrw.vimrc with the following contents: > - - set nocp - so $HOME/.vim/plugin/netrwPlugin.vim -< -Then run netrw as follows: > - - vim -u netrw.vimrc --noplugins -i NONE [some path here] -< -Perform whatever netrw commands you need to, and check that the problem is -still present. This procedure sidesteps any issues due to personal .vimrc -settings, .viminfo file, and other plugins. If the problem does not appear, -then you need to determine which setting in your .vimrc is causing the -conflict with netrw or which plugin(s) is/are involved. - -Step 3: If the problem still is present, then get a debugging trace from -netrw: - - 1. Get the <Decho.vim> script, available as: - - http://www.drchip.org/astronaut/vim/index.html#DECHO - or - http://vim.sourceforge.net/scripts/script.php?script_id=120 - - Decho.vim is provided as a "vimball". You - should edit the Decho.vba.gz file and source it in: > - - vim Decho.vba.gz - :so % - :q -< - 2. To turn on debug tracing in netrw, then edit the <netrw.vim> - file by typing: > - - vim netrw.vim - :DechoOn - :wq -< - To restore to normal non-debugging behavior, re-edit <netrw.vim> - and type > - - vim netrw.vim - :DechoOff - :wq -< - This command, provided by <Decho.vim>, will comment out all - Decho-debugging statements (Dfunc(), Dret(), Decho(), Dredir()). - - 3. Then bring up vim and attempt to evoke the problem by doing a - transfer or doing some browsing. A set of messages should appear - concerning the steps that <netrw.vim> took in attempting to - read/write your file over the network in a separate tab or - server vim window. - - Change the netrw.vimrc file to include the Decho plugin: > - - set nocp - so $HOME/.vim/plugin/Decho.vim - so $HOME/.vim/plugin/netrwPlugin.vim -< - You should continue to run vim with > - - vim -u netrw.vimrc --noplugins -i NONE [some path here] -< - to avoid entanglements with options and other plugins. - - To save the file: under linux, the output will be in a separate - remote server window; in it, just save the file with > - - :w! DBG - -< Under a vim that doesn't support clientserver, your debugging - output will appear in another tab: > - - :tabnext - :set bt= - :w! DBG -< - Furthermore, it'd be helpful if you would type > - - :Dsep <command> - -< where <command> is the command you're about to type next, - thereby making it easier to associate which part of the - debugging trace is due to which command. - - Please send that information to <netrw.vim>'s maintainer along - with the o/s you're using and the vim version that you're using - (see |:version|) (remove the embedded NOSPAM first) > - - NcampObell@SdrPchip.AorgM-NOSPAM -< -============================================================================== -12. History *netrw-history* {{{1 - - v172: Sep 02, 2021 * (Bram Moolenaar) Changed "l:go" to "go" - * (Bram Moolenaar) no need for "b" in - netrw-safe guioptions - Nov 15, 2021 * removed netrw_localrm and netrw_localrmdir - references - Aug 18, 2022 * (Miguel Barro) improving compatibility with - powershell - v171: Oct 09, 2020 * included code in s:NetrwOptionsSafe() - to allow |'bh'| to be set to delete when - rather than hide when g:netrw_fastbrowse - was zero. - * Installed |g:netrw_clipboard| setting - * Installed option bypass for |'guioptions'| - a/A settings - * Changed popup_beval() to popup_atcursor() - in netrw#ErrorMsg (lacygoill). Apparently - popup_beval doesn't reliably close the - popup when the mouse is moved. - * VimEnter() now using win_execute to examine - buffers for an attempt to open a directory. - Avoids issues with popups/terminal from - command line. (lacygoill) - Jun 28, 2021 * (zeertzjq) provided a patch for use of - xmap,xno instead of vmap,vno in - netrwPlugin.vim. Avoids entanglement with - select mode. - Jul 14, 2021 * Fixed problem addressed by tst976; opening - a file using tree mode, going up a - directory, and opening a file there was - opening the file in the wrong directory. - Jul 28, 2021 * (Ingo Karkat) provided a patch fixing an - E488 error with netrwPlugin.vim - (occurred for vim versions < 8.02) - v170: Mar 11, 2020 * (reported by Reiner Herrmann) netrw+tree - would not hide with the ^\..* pattern - correctly. - * (Marcin Szamotulski) NetrwOptionRestore - did not restore options correctly that - had a single quote in the option string. - Apr 13, 2020 * implemented error handling via popup - windows (see popup_beval()) - Apr 30, 2020 * (reported by Manatsu Takahashi) while - using Lexplore, a modified file could - be overwritten. Sol'n: will not overwrite, - but will emit an |E37| (although one cannot - add an ! to override) - Jun 07, 2020 * (reported by Jo Totland) repeatedly invoking - :Lexplore and quitting it left unused - hidden buffers. Netrw will now set netrw - buffers created by :Lexplore to |'bh'|=wipe. - v169: Dec 20, 2019 * (reported by amkarthik) that netrw's x - (|netrw-x|) would throw an error when - attempting to open a local directory. - v168: Dec 12, 2019 * scp timeout error message not reported, - hopefully now fixed (Shane Xb Qian) - v167: Nov 29, 2019 * netrw does a save&restore on @* and @+. - That causes problems with the clipboard. - Now restores occurs only if @* or @+ have - been changed. - * netrw will change @* or @+ less often. - Never if I happen to have caught all the - operations that modify the unnamed - register (which also writes @*). - * Modified hiding behavior so that "s" - will not ignore hiding. - v166: Nov 06, 2019 * Removed a space from a nmap for "-" - * Numerous debugging statement changes - v163: Dec 05, 2017 * (Cristi Balan) reported that a setting ('sel') - was left changed - * (Holger Mitschke) reported a problem with - saving and restoring history. Fixed. - * Hopefully I fixed a nasty bug that caused a - file rename to wipe out a buffer that it - should not have wiped out. - * (Holger Mitschke) amended this help file - with additional |g:netrw_special_syntax| - items - * Prioritized wget over curl for - g:netrw_http_cmd - v162: Sep 19, 2016 * (haya14busa) pointed out two syntax errors - with a patch; these are now fixed. - Oct 26, 2016 * I started using mate-terminal and found that - x and gx (|netrw-x| and |netrw-gx|) were no - longer working. Fixed (using atril when - $DESKTOP_SESSION is "mate"). - Nov 04, 2016 * (Martin Vuille) pointed out that @+ was - being restored with keepregstar rather than - keepregplus. - Nov 09, 2016 * Broke apart the command from the options, - mostly for Windows. Introduced new netrw - settings: |g:netrw_localcopycmdopt| - |g:netrw_localcopydircmdopt| - |g:netrw_localmkdiropt| - |g:netrw_localmovecmdopt| - Nov 21, 2016 * (mattn) provided a patch for preview; swapped - winwidth() with winheight() - Nov 22, 2016 * (glacambre) reported that files containing - spaces weren't being obtained properly via - scp. Fix: apparently using single quotes - such as with "file name" wasn't enough; the - spaces inside the quotes also had to be - escaped (ie. "file\ name"). - * Also fixed obtain (|netrw-O|) to be able to - obtain files with spaces in their names - Dec 20, 2016 * (xc1427) Reported that using "I" (|netrw-I|) - when atop "Hiding" in the banner also caused - the active-banner hiding control to occur - Jan 03, 2017 * (Enno Nagel) reported that attempting to - apply netrw to a directory that was without - read permission caused a syntax error. - Jan 13, 2017 * (Ingo Karkat) provided a patch which makes - using netrw#Call() better. Now returns - value of internal routines return, for example. - Jan 13, 2017 * (Ingo Karkat) changed netrw#FileUrlRead to - use |:edit| instead of |:read|. I also - changed the routine name to netrw#FileUrlEdit. - Jan 16, 2017 * (Sayem) reported a problem where :Lexplore - could generate a new listing buffer and - window instead of toggling the netrw display. - Unfortunately, the directions for eliciting - the problem weren't complete, so I may or - may not have fixed that issue. - Feb 06, 2017 * Implemented cb and cB. Changed "c" to "cd". - (see |netrw-cb|, |netrw-cB|, and |netrw-cd|) - Mar 21, 2017 * previously, netrw would specify (safe) settings - even when the setting was already safe for - netrw. Netrw now attempts to leave such - already-netrw-safe settings alone. - (affects s:NetrwOptionRestore() and - s:NetrwSafeOptions(); also introduced - s:NetrwRestoreSetting()) - Jun 26, 2017 * (Christian Brabandt) provided a patch to - allow curl to follow redirects (ie. -L - option) - Jun 26, 2017 * (Callum Howard) reported a problem with - :Lexpore not removing the Lexplore window - after a change-directory - Aug 30, 2017 * (Ingo Karkat) one cannot switch to the - previously edited file (e.g. with CTRL-^) - after editing a file:// URL. Patch to - have a "keepalt" included. - Oct 17, 2017 * (Adam Faryna) reported that gn (|netrw-gn|) - did not work on directories in the current - tree - v157: Apr 20, 2016 * (Nicola) had set up a "nmap <expr> ..." with - a function that returned a 0 while silently - invoking a shell command. The shell command - activated a ShellCmdPost event which in turn - called s:LocalBrowseRefresh(). That looks - over all netrw buffers for changes needing - refreshes. However, inside a |:map-<expr>|, - tab and window changes are disallowed. Fixed. - (affects netrw's s:LocalBrowseRefresh()) - * g:netrw_localrmdir not used any more, but - the relevant patch that causes |delete()| to - take over was #1107 (not #1109). - * |expand()| is now used on |g:netrw_home|; - consequently, g:netrw_home may now use - environment variables - * s:NetrwLeftmouse and s:NetrwCLeftmouse will - return without doing anything if invoked - when inside a non-netrw window - Jun 15, 2016 * gx now calls netrw#GX() which returns - the word under the cursor. The new - wrinkle: if one is in a netrw buffer, - then netrw's s:NetrwGetWord(). - Jun 22, 2016 * Netrw was executing all its associated - Filetype commands silently; I'm going - to try doing that "noisily" and see if - folks have a problem with that. - Aug 12, 2016 * Changed order of tool selection for - handling http://... viewing. - (Nikolay Aleksandrovich Pavlov) - Aug 21, 2016 * Included hiding/showing/all for tree - listings - * Fixed refresh (^L) for tree listings - 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 - window. - Nov 05, 2015 * Improved s:TreeSqueezeDir() to use search() - instead of a loop - * NetrwBrowse() will return line to - w:netrw_bannercnt if cursor ended up in - banner - Nov 16, 2015 * Added a <Plug>NetrwTreeSqueeze (|netrw-s-cr|) - Nov 17, 2015 * Commented out imaps -- perhaps someone can - tell me how they're useful and should be - retained? - Nov 20, 2015 * Added |netrw-ma| and |netrw-mA| support - Nov 20, 2015 * gx (|netrw-gx|) on a URL downloaded the - file in addition to simply bringing up the - URL in a browser. Fixed. - Nov 23, 2015 * Added |g:netrw_sizestyle| support - Nov 27, 2015 * Inserted a lot of <c-u>s into various netrw - maps. - Jan 05, 2016 * |netrw-qL| implemented to mark files based - upon |location-list|s; similar to |netrw-qF|. - Jan 19, 2016 * using - call delete(directoryname,"d") - - instead of using g:netrw_localrmdir if - v7.4 + patch#1107 is available - Jan 28, 2016 * changed to using |winsaveview()| and - |winrestview()| - Jan 28, 2016 * s:NetrwTreePath() now does a save and - restore of view - Feb 08, 2016 * Fixed a tree-listing problem with remote - directories - v154: Feb 26, 2015 * (Yuri Kanivetsky) reported a situation where - a file was not treated properly as a file - due to g:netrw_keepdir == 1 - Mar 25, 2015 * (requested by Ben Friz) one may now sort by - extension - Mar 28, 2015 * (requested by Matt Brooks) netrw has a lot - of buffer-local mappings; however, some - plugins (such as vim-surround) set up - conflicting mappings that cause vim to wait. - The "<nowait>" modifier has been included - with most of netrw's mappings to avoid that - delay. - Jun 26, 2015 * |netrw-gn| mapping implemented - * :Ntree NotADir resulted in having - the tree listing expand in the error messages - window. Fixed. - Jun 29, 2015 * Attempting to delete a file remotely caused - an error with "keepsol" mentioned; fixed. - Jul 08, 2015 * Several changes to keep the |:jumps| table - correct when working with - |g:netrw_fastbrowse| set to 2 - * wide listing with accented characters fixed - (using %-S instead of %-s with a |printf()| - Jul 13, 2015 * (Daniel Hahler) CheckIfKde() could be true - but kfmclient not installed. Changed order - in netrw#BrowseX(): checks if kde and - kfmclient, then will use xdg-open on a unix - system (if xdg-open is executable) - Aug 11, 2015 * (McDonnell) tree listing mode wouldn't - select a file in a open subdirectory. - * (McDonnell) when multiple subdirectories - were concurrently open in tree listing - mode, a ctrl-L wouldn't refresh properly. - * The netrw:target menu showed duplicate - entries - Oct 13, 2015 * (mattn) provided an exception to handle - windows with shellslash set but no shell - Oct 23, 2015 * if g:netrw_usetab and <c-tab> now used - to control whether NetrwShrink is used - (see |netrw-c-tab|) - v153: May 13, 2014 * added another |g:netrw_ffkeep| usage {{{2 - May 14, 2014 * changed s:PerformListing() so that it - always sets ft=netrw for netrw buffers - (ie. even when syntax highlighting is - off, not available, etc) - May 16, 2014 * introduced the |netrw-ctrl-r| functionality - May 17, 2014 * introduced the |netrw-:NetrwMB| functionality - * mb and mB (|netrw-mb|, |netrw-mB|) will - add/remove marked files from bookmark list - May 20, 2014 * (Enno Nagel) reported that :Lex <dirname> - wasn't working. Fixed. - May 26, 2014 * restored test to prevent leftmouse window - resizing from causing refresh. - (see s:NetrwLeftmouse()) - * fixed problem where a refresh caused cursor - to go just under the banner instead of - staying put - May 28, 2014 * (László Bimba) provided a patch for opening - the |:Lexplore| window 100% high, optionally - on the right, and will work with remote - files. - May 29, 2014 * implemented :NetrwC (see |netrw-:NetrwC|) - Jun 01, 2014 * Removed some "silent"s from commands used - to implemented scp://... and pscp://... - directory listing. Permits request for - password to appear. - Jun 05, 2014 * (Enno Nagel) reported that user maps "/" - caused problems with "b" and "w", which - are mapped (for wide listings only) to - skip over files rather than just words. - Jun 10, 2014 * |g:netrw_gx| introduced to allow users to - override default "<cfile>" with the gx - (|netrw-gx|) map - Jun 11, 2014 * gx (|netrw-gx|), with |'autowrite'| set, - will write modified files. s:NetrwBrowseX() - will now save, turn off, and restore the - |'autowrite'| setting. - Jun 13, 2014 * added visual map for gx use - Jun 15, 2014 * (Enno Nagel) reported that with having hls - set and wide listing style in use, that the - b and w maps caused unwanted highlighting. - Jul 05, 2014 * |netrw-mv| and |netrw-mX| commands included - Jul 09, 2014 * |g:netrw_keepj| included, allowing optional - keepj - Jul 09, 2014 * fixing bugs due to previous update - Jul 21, 2014 * (Bruno Sutic) provided an updated - netrw_gitignore.vim - Jul 30, 2014 * (Yavuz Yetim) reported that editing two - remote files of the same name caused the - second instance to have a "temporary" - name. Fixed: now they use the same buffer. - Sep 18, 2014 * (Yasuhiro Matsumoto) provided a patch which - allows scp and windows local paths to work. - Oct 07, 2014 * gx (see |netrw-gx|) when atop a directory, - will now do |gf| instead - Nov 06, 2014 * For cygwin: cygstart will be available for - netrw#BrowseX() to use if its executable. - Nov 07, 2014 * Began support for file://... urls. Will use - |g:netrw_file_cmd| (typically elinks or links) - Dec 02, 2014 * began work on having mc (|netrw-mc|) copy - directories. Works for linux machines, - cygwin+vim, but not for windows+gvim. - Dec 02, 2014 * in tree mode, netrw was not opening - directories via symbolic links. - Dec 02, 2014 * added resolved link information to - thin and tree modes - Dec 30, 2014 * (issue#231) |:ls| was not showing - remote-file buffers reliably. Fixed. - v152: Apr 08, 2014 * uses the |'noswapfile'| option (requires {{{2 - vim 7.4 with patch 213) - * (Enno Nagel) turn |'rnu'| off in netrw - buffers. - * (Quinn Strahl) suggested that netrw - allow regular window splitting to occur, - thereby allowing |'equalalways'| to take - effect. - * (qingtian zhao) normally, netrw will - save and restore the |'fileformat'|; - however, sometimes that isn't wanted - Apr 14, 2014 * whenever netrw marks a buffer as ro, - it will also mark it as nomod. - Apr 16, 2014 * sftp protocol now supported by - netrw#Obtain(); this means that one - may use "mc" to copy a remote file - to a local file using sftp, and that - the |netrw-O| command can obtain remote - files via sftp. - * added [count]C support (see |netrw-C|) - Apr 18, 2014 * when |g:netrw_chgwin| is one more than - the last window, then vertically split - the last window and use it as the - chgwin window. - May 09, 2014 * SavePosn was "saving filename under cursor" - from a non-netrw window when using :Rex. - v151: Jan 22, 2014 * extended :Rexplore to return to buffer {{{2 - prior to Explore or editing a directory - * (Ken Takata) netrw gave error when - clipboard was disabled. Sol'n: Placed - several if has("clipboard") tests in. - * Fixed ftp://X@Y@Z// problem; X@Y now - part of user id, and only Z is part of - hostname. - * (A Loumiotis) reported that completion - using a directory name containing spaces - did not work. Fixed with a retry in - netrw#Explore() which removes the - backslashes vim inserted. - Feb 26, 2014 * :Rexplore now records the current file - using w:netrw_rexfile when returning via - |:Rexplore| - Mar 08, 2014 * (David Kotchan) provided some patches - allowing netrw to work properly with - windows shares. - * Multiple one-liner help messages available - by pressing <cr> while atop the "Quick - Help" line - * worked on ShellCmdPost, FocusGained event - handling. - * |:Lexplore| path: will be used to update - a left-side netrw browsing directory. - Mar 12, 2014 * |netrw-s-cr|: use <s-cr> to close - tree directory implemented - Mar 13, 2014 * (Tony Mechylynck) reported that using - the browser with ftp on a directory, - and selecting a gzipped txt file, that - an E19 occurred (which was issued by - gzip.vim). Fixed. - Mar 14, 2014 * Implemented :MF and :MT (see |netrw-:MF| - and |netrw-:MT|, respectively) - Mar 17, 2014 * |:Ntree| [dir] wasn't working properly; fixed - Mar 18, 2014 * Changed all uses of set to setl - Mar 18, 2014 * Commented the netrw_btkeep line in - s:NetrwOptionSave(); the effect is that - netrw buffers will remain as |'bt'|=nofile. - This should prevent swapfiles being created - for netrw buffers. - Mar 20, 2014 * Changed all uses of lcd to use s:NetrwLcd() - instead. Consistent error handling results - and it also handles Window's shares - * Fixed |netrw-d| command when applied with ftp - * https: support included for netrw#NetRead() - v150: Jul 12, 2013 * removed a "keepalt" to allow ":e #" to {{{2 - return to the netrw directory listing - Jul 13, 2013 * (Jonas Diemer) suggested changing - a <cWORD> to <cfile>. - Jul 21, 2013 * (Yuri Kanivetsky) reported that netrw's - use of mkdir did not produce directories - following the user's umask. - Aug 27, 2013 * introduced |g:netrw_altfile| option - Sep 05, 2013 * s:Strlen() now uses |strdisplaywidth()| - when available, by default - Sep 12, 2013 * (Selyano Baldo) reported that netrw wasn't - opening some directories properly from the - command line. - Nov 09, 2013 * |:Lexplore| introduced - * (Ondrej Platek) reported an issue with - netrw's trees (P15). Fixed. - * (Jorge Solis) reported that "t" in - tree mode caused netrw to forget its - line position. - Dec 05, 2013 * Added <s-leftmouse> file marking - (see |netrw-mf|) - Dec 05, 2013 * (Yasuhiro Matsumoto) Explore should use - strlen() instead s:Strlen() when handling - multibyte chars with strpart() - (ie. strpart() is byte oriented, not - display-width oriented). - Dec 09, 2013 * (Ken Takata) Provided a patch; File sizes - and a portion of timestamps were wrongly - highlighted with the directory color when - setting `:let g:netrw_liststyle=1` on Windows. - * (Paul Domaskis) noted that sometimes - cursorline was activating in non-netrw - windows. All but one setting of cursorline - was done via setl; there was one that was - overlooked. Fixed. - Dec 24, 2013 * (esquifit) asked that netrw allow the - /cygdrive prefix be a user-alterable - parameter. - Jan 02, 2014 * Fixed a problem with netrw-based balloon - evaluation (ie. netrw#NetrwBalloonHelp() - not having been loaded error messages) - Jan 03, 2014 * Fixed a problem with tree listings - * New command installed: |:Ntree| - Jan 06, 2014 * (Ivan Brennan) reported a problem with - |netrw-P|. Fixed. - Jan 06, 2014 * Fixed a problem with |netrw-P| when the - modified file was to be abandoned. - Jan 15, 2014 * (Matteo Cavalleri) reported that when the - banner is suppressed and tree listing is - used, a blank line was left at the top of - the display. Fixed. - Jan 20, 2014 * (Gideon Go) reported that, in tree listing - style, with a previous window open, that - the wrong directory was being used to open - a file. Fixed. (P21) - v149: Apr 18, 2013 * in wide listing format, now have maps for {{{2 - w and b to move to next/previous file - Apr 26, 2013 * one may now copy files in the same - directory; netrw will issue requests for - what names the files should be copied under - Apr 29, 2013 * Trying Benzinger's problem again. Seems - that commenting out the BufEnter and - installing VimEnter (only) works. Weird - problem! (tree listing, vim -O Dir1 Dir2) - May 01, 2013 * :Explore ftp://... wasn't working. Fixed. - May 02, 2013 * introduced |g:netrw_bannerbackslash| as - requested by Paul Domaskis. - Jul 03, 2013 * Explore now avoids splitting when a buffer - will be hidden. - v148: Apr 16, 2013 * changed Netrw's Style menu to allow direct {{{2 - choice of listing style, hiding style, and - sorting style - -============================================================================== -13. Todo *netrw-todo* {{{1 - -07/29/09 : banner :|g:netrw_banner| can be used to suppress the - suppression banner. This feature is new and experimental, - so its in the process of being debugged. -09/04/09 : "gp" : See if it can be made to work for remote systems. - : See if it can be made to work with marked files. - ============================================================================== -14. Credits *netrw-credits* {{{1 +11. Credits *netrw-credits* {{{1 Vim editor by Bram Moolenaar (Thanks, Bram!) dav support by C Campbell diff --git a/runtime/pack/dist/opt/netrw/plugin/netrwPlugin.vim b/runtime/pack/dist/opt/netrw/plugin/netrwPlugin.vim new file mode 100644 index 0000000000..8d10c00153 --- /dev/null +++ b/runtime/pack/dist/opt/netrw/plugin/netrwPlugin.vim @@ -0,0 +1,214 @@ +" Maintainer: Luca Saccarola <github.e41mv@aleeas.com> +" Former Maintainer: Charles E Campbell +" Upstream: <https://github.com/saccarosium/netrw.vim> +" Copyright: Copyright (C) 1999-2021 Charles E. Campbell {{{ +" Permission is hereby granted to use and distribute this code, +" with or without modifications, provided that this copyright +" notice is copied with it. Like anything else that's free, +" netrw.vim, netrwPlugin.vim, and netrwSettings.vim are provided +" *as is* and comes with no warranty of any kind, either +" expressed or implied. By using this plugin, you agree that +" in no event will the copyright holder be liable for any damages +" resulting from the use of this software. }}} + +if &cp || exists("g:loaded_netrwPlugin") + finish +endif + +let g:loaded_netrwPlugin = "v175" + +let s:keepcpo = &cpo +set cpo&vim + +" Commands Launch/URL: {{{ + +command -complete=shellcmd -nargs=1 Launch call netrw#Launch(trim(<q-args>)) +command -complete=file -nargs=1 Open call netrw#Open(trim(<q-args>)) + +" }}} +" Local Browsing Autocmds: {{{ + +augroup FileExplorer + au! + au BufLeave * if &ft != "netrw"|let w:netrw_prvfile= expand("%:p")|endif + au BufEnter * sil call s:LocalBrowse(expand("<amatch>")) + au VimEnter * sil call s:VimEnter(expand("<amatch>")) + if has("win32") + au BufEnter .* sil call s:LocalBrowse(expand("<amatch>")) + endif +augroup END + +" }}} +" Network Browsing Reading Writing: {{{ + +augroup Network + au! + au BufReadCmd file://* call netrw#FileUrlEdit(expand("<amatch>")) + au BufReadCmd ftp://*,rcp://*,scp://*,http://*,https://*,dav://*,davs://*,rsync://*,sftp://* exe "sil doau BufReadPre ".fnameescape(expand("<amatch>"))|call netrw#Nread(2,expand("<amatch>"))|exe "sil doau BufReadPost ".fnameescape(expand("<amatch>")) + au FileReadCmd ftp://*,rcp://*,scp://*,http://*,file://*,https://*,dav://*,davs://*,rsync://*,sftp://* exe "sil doau FileReadPre ".fnameescape(expand("<amatch>"))|call netrw#Nread(1,expand("<amatch>"))|exe "sil doau FileReadPost ".fnameescape(expand("<amatch>")) + au BufWriteCmd ftp://*,rcp://*,scp://*,http://*,file://*,dav://*,davs://*,rsync://*,sftp://* exe "sil doau BufWritePre ".fnameescape(expand("<amatch>"))|exe 'Nwrite '.fnameescape(expand("<amatch>"))|exe "sil doau BufWritePost ".fnameescape(expand("<amatch>")) + au FileWriteCmd ftp://*,rcp://*,scp://*,http://*,file://*,dav://*,davs://*,rsync://*,sftp://* exe "sil doau FileWritePre ".fnameescape(expand("<amatch>"))|exe "'[,']".'Nwrite '.fnameescape(expand("<amatch>"))|exe "sil doau FileWritePost ".fnameescape(expand("<amatch>")) + try + au SourceCmd ftp://*,rcp://*,scp://*,http://*,file://*,https://*,dav://*,davs://*,rsync://*,sftp://* exe 'Nsource '.fnameescape(expand("<amatch>")) + catch /^Vim\%((\a\+)\)\=:E216/ + au SourcePre ftp://*,rcp://*,scp://*,http://*,file://*,https://*,dav://*,davs://*,rsync://*,sftp://* exe 'Nsource '.fnameescape(expand("<amatch>")) + endtry +augroup END + +" }}} +" Commands: :Nread, :Nwrite, :NetUserPass {{{ + +command! -count=1 -nargs=* Nread let s:svpos= winsaveview()<bar>call netrw#NetRead(<count>,<f-args>)<bar>call winrestview(s:svpos) +command! -range=% -nargs=* Nwrite let s:svpos= winsaveview()<bar><line1>,<line2>call netrw#NetWrite(<f-args>)<bar>call winrestview(s:svpos) +command! -nargs=* NetUserPass call NetUserPass(<f-args>) +command! -nargs=* Nsource let s:svpos= winsaveview()<bar>call netrw#NetSource(<f-args>)<bar>call winrestview(s:svpos) +command! -nargs=? Ntree call netrw#SetTreetop(1,<q-args>) + +" }}} +" Commands: :Explore, :Sexplore, Hexplore, Vexplore, Lexplore {{{ + +command! -nargs=* -bar -bang -count=0 -complete=dir Explore call netrw#Explore(<count>, 0, 0+<bang>0, <q-args>) +command! -nargs=* -bar -bang -count=0 -complete=dir Sexplore call netrw#Explore(<count>, 1, 0+<bang>0, <q-args>) +command! -nargs=* -bar -bang -count=0 -complete=dir Hexplore call netrw#Explore(<count>, 1, 2+<bang>0, <q-args>) +command! -nargs=* -bar -bang -count=0 -complete=dir Vexplore call netrw#Explore(<count>, 1, 4+<bang>0, <q-args>) +command! -nargs=* -bar -count=0 -complete=dir Texplore call netrw#Explore(<count>, 0, 6, <q-args>) +command! -nargs=* -bar -bang -count=0 -complete=dir Lexplore call netrw#Lexplore(<count>, <bang>0, <q-args>) +command! -nargs=* -bar -bang Nexplore call netrw#Explore(-1, 0, 0, <q-args>) +command! -nargs=* -bar -bang Pexplore call netrw#Explore(-2, 0, 0, <q-args>) + +" }}} +" Commands: NetrwSettings {{{ + +command! -nargs=0 NetrwSettings call netrwSettings#NetrwSettings() +command! -bang NetrwClean call netrw#Clean(<bang>0) + +" }}} +" Maps: {{{ + +if !exists("g:netrw_nogx") + if maparg('gx','n') == "" + if !hasmapto('<Plug>NetrwBrowseX') + nmap <unique> gx <Plug>NetrwBrowseX + endif + nno <silent> <Plug>NetrwBrowseX :call netrw#BrowseX(netrw#GX(),netrw#CheckIfRemote(netrw#GX()))<cr> + endif + if maparg('gx','x') == "" + if !hasmapto('<Plug>NetrwBrowseXVis') + xmap <unique> gx <Plug>NetrwBrowseXVis + endif + xno <silent> <Plug>NetrwBrowseXVis :<c-u>call netrw#BrowseXVis()<cr> + endif +endif + +if exists("g:netrw_usetab") && g:netrw_usetab + if maparg('<c-tab>','n') == "" + nmap <unique> <c-tab> <Plug>NetrwShrink + endif + nno <silent> <Plug>NetrwShrink :call netrw#Shrink()<cr> +endif + +" }}} +" LocalBrowse: invokes netrw#LocalBrowseCheck() on directory buffers {{{ + +function! s:LocalBrowse(dirname) + " do not trigger in the terminal + " https://github.com/vim/vim/issues/16463 + if &buftype ==# 'terminal' + return + endif + + if !exists("s:vimentered") + " If s:vimentered doesn't exist, then the VimEnter event hasn't fired. It will, + " and so s:VimEnter() will then be calling this routine, but this time with s:vimentered defined. + return + endif + + if has("amiga") + " The check against '' is made for the Amiga, where the empty + " string is the current directory and not checking would break + " things such as the help command. + if a:dirname != '' && isdirectory(a:dirname) + sil! call netrw#LocalBrowseCheck(a:dirname) + if exists("w:netrw_bannercnt") && line('.') < w:netrw_bannercnt + exe w:netrw_bannercnt + endif + endif + elseif isdirectory(a:dirname) + " Jul 13, 2021: for whatever reason, preceding the following call with + " a sil! causes an unbalanced if-endif vim error + call netrw#LocalBrowseCheck(a:dirname) + if exists("w:netrw_bannercnt") && line('.') < w:netrw_bannercnt + exe w:netrw_bannercnt + endif + endif +endfunction + +" }}} +" s:VimEnter: after all vim startup stuff is done, this function is called. {{{ +" Its purpose: to look over all windows and run s:LocalBrowse() on +" them, which checks if they're directories and will create a directory +" listing when appropriate. +" It also sets s:vimentered, letting s:LocalBrowse() know that s:VimEnter() +" has already been called. +function! s:VimEnter(dirname) + if has('nvim') || v:version < 802 + " Johann Höchtl: reported that the call range... line causes an E488: Trailing characters + " error with neovim. I suspect its because neovim hasn't updated with recent + " vim patches. As is, this code will have problems with popup terminals + " instantiated before the VimEnter event runs. + " Ingo Karkat : E488 also in Vim 8.1.1602 + let curwin = winnr() + let s:vimentered = 1 + windo call s:LocalBrowse(expand("%:p")) + exe curwin."wincmd w" + else + " the following complicated expression comes courtesy of lacygoill; largely does the same thing as the windo and + " wincmd which are commented out, but avoids some side effects. Allows popup terminal before VimEnter. + let s:vimentered = 1 + call range(1, winnr('$'))->map({_, v -> win_execute(win_getid(v), 'call expand("%:p")->s:LocalBrowse()')}) + endif +endfunction + +" }}} +" NetrwStatusLine: {{{ + +function! NetrwStatusLine() + if !exists("w:netrw_explore_bufnr") || w:netrw_explore_bufnr != bufnr("%") || !exists("w:netrw_explore_line") || w:netrw_explore_line != line(".") || !exists("w:netrw_explore_list") + let &stl= s:netrw_explore_stl + unlet! w:netrw_explore_bufnr w:netrw_explore_line + return "" + else + return "Match ".w:netrw_explore_mtchcnt." of ".w:netrw_explore_listlen + endif +endfunction + +" }}} +" NetUserPass: set username and password for subsequent ftp transfer {{{ +" Usage: :call NetUserPass() -- will prompt for userid and password +" :call NetUserPass("uid") -- will prompt for password +" :call NetUserPass("uid","password") -- sets global userid and password +function! NetUserPass(...) + " get/set userid + if a:0 == 0 + if !exists("g:netrw_uid") || g:netrw_uid == "" + " via prompt + let g:netrw_uid= input('Enter username: ') + endif + else " from command line + let g:netrw_uid= a:1 + endif + + " get password + if a:0 <= 1 " via prompt + let g:netrw_passwd= inputsecret("Enter Password: ") + else " from command line + let g:netrw_passwd=a:2 + endif +endfunction + +" }}} + +let &cpo= s:keepcpo +unlet s:keepcpo + +" vim:ts=8 sts=4 sw=4 et fdm=marker diff --git a/runtime/pack/dist/opt/netrw/syntax/netrw.vim b/runtime/pack/dist/opt/netrw/syntax/netrw.vim new file mode 100644 index 0000000000..8042854a12 --- /dev/null +++ b/runtime/pack/dist/opt/netrw/syntax/netrw.vim @@ -0,0 +1,149 @@ +" Maintainer: Luca Saccarola <github.e41mv@aleeas.com> +" Former Maintainer: Charles E Campbell +" Upstream: <https://github.com/saccarosium/netrw.vim> +" Language: Netrw Listing Syntax + +if exists("b:current_syntax") + finish +endif + +let b:current_syntax = "netrwlist" + +" Directory List Syntax Highlighting: {{{ + +syn cluster NetrwGroup contains=netrwHide,netrwSortBy,netrwSortSeq,netrwQuickHelp,netrwVersion,netrwCopyTgt +syn cluster NetrwTreeGroup contains=netrwDir,netrwSymLink,netrwExe + +syn match netrwPlain "\(\S\+ \)*\S\+" contains=netrwLink,@NoSpell +syn match netrwSpecial "\%(\S\+ \)*\S\+[*|=]\ze\%(\s\{2,}\|$\)" contains=netrwClassify,@NoSpell +syn match netrwDir "\.\{1,2}/" contains=netrwClassify,@NoSpell +syn match netrwDir "\%(\S\+ \)*\S\+/\ze\%(\s\{2,}\|$\)" contains=netrwClassify,@NoSpell +syn match netrwSizeDate "\<\d\+\s\d\{1,2}/\d\{1,2}/\d\{4}\s" skipwhite contains=netrwDateSep,@NoSpell nextgroup=netrwTime +syn match netrwSymLink "\%(\S\+ \)*\S\+@\ze\%(\s\{2,}\|$\)" contains=netrwClassify,@NoSpell +syn match netrwExe "\%(\S\+ \)*\S*[^~]\*\ze\%(\s\{2,}\|$\)" contains=netrwClassify,@NoSpell +if has("gui_running") && (&enc == 'utf-8' || &enc == 'utf-16' || &enc == 'ucs-4') + syn match netrwTreeBar "^\%([-+|│] \)\+" contains=netrwTreeBarSpace nextgroup=@netrwTreeGroup +else + syn match netrwTreeBar "^\%([-+|] \)\+" contains=netrwTreeBarSpace nextgroup=@netrwTreeGroup +endif +syn match netrwTreeBarSpace " " contained + +syn match netrwClassify "[*=|@/]\ze\%(\s\{2,}\|$\)" contained +syn match netrwDateSep "/" contained +syn match netrwTime "\d\{1,2}:\d\{2}:\d\{2}" contained contains=netrwTimeSep +syn match netrwTimeSep ":" + +syn match netrwComment '".*\%(\t\|$\)' contains=@NetrwGroup,@NoSpell +syn match netrwHide '^"\s*\(Hid\|Show\)ing:' skipwhite contains=@NoSpell nextgroup=netrwHidePat +syn match netrwSlash "/" contained +syn match netrwHidePat "[^,]\+" contained skipwhite contains=@NoSpell nextgroup=netrwHideSep +syn match netrwHideSep "," contained skipwhite nextgroup=netrwHidePat +syn match netrwSortBy "Sorted by" contained transparent skipwhite nextgroup=netrwList +syn match netrwSortSeq "Sort sequence:" contained transparent skipwhite nextgroup=netrwList +syn match netrwCopyTgt "Copy/Move Tgt:" contained transparent skipwhite nextgroup=netrwList +syn match netrwList ".*$" contained contains=netrwComma,@NoSpell +syn match netrwComma "," contained +syn region netrwQuickHelp matchgroup=Comment start="Quick Help:\s\+" end="$" contains=netrwHelpCmd,netrwQHTopic,@NoSpell keepend contained +syn match netrwHelpCmd "\S\+\ze:" contained skipwhite contains=@NoSpell nextgroup=netrwCmdSep +syn match netrwQHTopic "([a-zA-Z &]\+)" contained skipwhite +syn match netrwCmdSep ":" contained nextgroup=netrwCmdNote +syn match netrwCmdNote ".\{-}\ze " contained contains=@NoSpell +syn match netrwVersion "(netrw.*)" contained contains=@NoSpell +syn match netrwLink "-->" contained skipwhite + +" }}} +" Special filetype highlighting {{{ + +if exists("g:netrw_special_syntax") && g:netrw_special_syntax + if exists("+suffixes") && &suffixes != "" + let suflist= join(split(&suffixes,',')) + let suflist= escape(substitute(suflist," ",'\\|','g'),'.~') + exe "syn match netrwSpecFile '\\(\\S\\+ \\)*\\S*\\(".suflist."\\)\\>' contains=netrwTreeBar,@NoSpell" + endif + syn match netrwBak "\(\S\+ \)*\S\+\.bak\>" contains=netrwTreeBar,@NoSpell + syn match netrwCompress "\(\S\+ \)*\S\+\.\%(gz\|bz2\|Z\|zip\)\>" contains=netrwTreeBar,@NoSpell + if has("unix") + syn match netrwCoreDump "\<core\%(\.\d\+\)\=\>" contains=netrwTreeBar,@NoSpell + endif + syn match netrwLex "\(\S\+ \)*\S\+\.\%(l\|lex\)\>" contains=netrwTreeBar,@NoSpell + syn match netrwYacc "\(\S\+ \)*\S\+\.y\>" contains=netrwTreeBar,@NoSpell + syn match netrwData "\(\S\+ \)*\S\+\.dat\>" contains=netrwTreeBar,@NoSpell + syn match netrwDoc "\(\S\+ \)*\S\+\.\%(doc\|txt\|pdf\|ps\|docx\)\>" contains=netrwTreeBar,@NoSpell + syn match netrwHdr "\(\S\+ \)*\S\+\.\%(h\|hpp\)\>" contains=netrwTreeBar,@NoSpell + syn match netrwLib "\(\S\+ \)*\S*\.\%(a\|so\|lib\|dll\)\>" contains=netrwTreeBar,@NoSpell + syn match netrwMakeFile "\<[mM]akefile\>\|\(\S\+ \)*\S\+\.mak\>" contains=netrwTreeBar,@NoSpell + syn match netrwObj "\(\S\+ \)*\S*\.\%(o\|obj\)\>" contains=netrwTreeBar,@NoSpell + syn match netrwPix "\c\(\S\+ \)*\S*\.\%(bmp\|fits\=\|gif\|je\=pg\|pcx\|ppc\|pgm\|png\|ppm\|psd\|rgb\|tif\|xbm\|xcf\)\>" contains=netrwTreeBar,@NoSpell + syn match netrwTags "\<\(ANmenu\|ANtags\)\>" contains=netrwTreeBar,@NoSpell + syn match netrwTags "\<tags\>" contains=netrwTreeBar,@NoSpell + syn match netrwTilde "\(\S\+ \)*\S\+\~\*\=\>" contains=netrwTreeBar,@NoSpell + syn match netrwTmp "\<tmp\(\S\+ \)*\S\+\>\|\(\S\+ \)*\S*tmp\>" contains=netrwTreeBar,@NoSpell +endif + +" }}} +" Highlighting Links: {{{ + +if !exists("did_drchip_netrwlist_syntax") + let did_drchip_netrwlist_syntax= 1 + hi default link netrwClassify Function + hi default link netrwCmdSep Delimiter + hi default link netrwComment Comment + hi default link netrwDir Directory + hi default link netrwHelpCmd Function + hi default link netrwQHTopic Number + hi default link netrwHidePat Statement + hi default link netrwHideSep netrwComment + hi default link netrwList Statement + hi default link netrwVersion Identifier + hi default link netrwSymLink Question + hi default link netrwExe PreProc + hi default link netrwDateSep Delimiter + + hi default link netrwTreeBar Special + hi default link netrwTimeSep netrwDateSep + hi default link netrwComma netrwComment + hi default link netrwHide netrwComment + hi default link netrwMarkFile TabLineSel + hi default link netrwLink Special + + " special syntax highlighting (see :he g:netrw_special_syntax) + hi default link netrwCoreDump WarningMsg + hi default link netrwData Folded + hi default link netrwHdr netrwPlain + hi default link netrwLex netrwPlain + hi default link netrwLib DiffChange + hi default link netrwMakefile DiffChange + hi default link netrwYacc netrwPlain + hi default link netrwPix Special + + hi default link netrwBak netrwGray + hi default link netrwCompress netrwGray + hi default link netrwSpecFile netrwGray + hi default link netrwObj netrwGray + hi default link netrwTags netrwGray + hi default link netrwTilde netrwGray + hi default link netrwTmp netrwGray +endif + +" set up netrwGray to be understated (but not Ignore'd or Conceal'd, as those +" can be hard/impossible to read). Users may override this in a colorscheme by +" specifying netrwGray highlighting. +redir => s:netrwgray +sil hi netrwGray +redir END + +if s:netrwgray !~ 'guifg' + if has("gui") && has("gui_running") + if &bg == "dark" + exe "hi netrwGray gui=NONE guifg=gray30" + else + exe "hi netrwGray gui=NONE guifg=gray70" + endif + else + hi link netrwGray Folded + endif +endif + +" }}} + +" vim:ts=8 sts=4 sw=4 et fdm=marker diff --git a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim index fd8c4b8817..b6da91a833 100644 --- a/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim +++ b/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -38,7 +38,7 @@ " NEOVIM COMPATIBILITY " " The vim specific functionalities were replaced with neovim specific calls: -" - term_start -> termopen +" - term_start -> `jobstart(…, {'term': v:true})` " - term_sendkeys -> chansend " - term_getline -> getbufline " - job_info && term_getjob -> nvim_get_chan_info @@ -251,7 +251,7 @@ endfunc " Open a terminal window without a job, to run the debugged program in. func s:StartDebug_term(dict) execute s:vertical ? 'vnew' : 'new' - let s:pty_job_id = termopen('tail -f /dev/null;#gdb program') + let s:pty_job_id = jobstart('tail -f /dev/null;#gdb program', {'term': v:true}) if s:pty_job_id == 0 call s:Echoerr('Invalid argument (or job table is full) while opening terminal window') return @@ -323,7 +323,7 @@ func s:StartDebug_term(dict) execute 'new' " call ch_log($'executing "{join(gdb_cmd)}"') - let s:gdb_job_id = termopen(gdb_cmd, {'on_exit': function('s:EndTermDebug')}) + let s:gdb_job_id = jobstart(gdb_cmd, {'term': v:true, 'on_exit': function('s:EndTermDebug')}) if s:gdb_job_id == 0 call s:Echoerr('Invalid argument (or job table is full) while opening gdb terminal window') exe 'bwipe! ' . s:ptybufnr @@ -491,7 +491,7 @@ func s:StartDebug_prompt(dict) " Unix: Run the debugged program in a terminal window. Open it below the " gdb window. belowright new - let s:pty_job_id = termopen('tail -f /dev/null;#gdb program') + let s:pty_job_id = jobstart('tail -f /dev/null;#gdb program', {'term': v:true}) if s:pty_job_id == 0 call s:Echoerr('Invalid argument (or job table is full) while opening terminal window') return diff --git a/runtime/plugin/editorconfig.lua b/runtime/plugin/editorconfig.lua index a96919e1fe..357146cabd 100644 --- a/runtime/plugin/editorconfig.lua +++ b/runtime/plugin/editorconfig.lua @@ -1,4 +1,4 @@ -local group = vim.api.nvim_create_augroup('editorconfig', {}) +local group = vim.api.nvim_create_augroup('nvim.editorconfig', {}) vim.api.nvim_create_autocmd({ 'BufNewFile', 'BufRead', 'BufFilePost' }, { group = group, callback = function(args) diff --git a/runtime/plugin/man.lua b/runtime/plugin/man.lua index 512b1f63e8..e707b68859 100644 --- a/runtime/plugin/man.lua +++ b/runtime/plugin/man.lua @@ -8,9 +8,9 @@ vim.api.nvim_create_user_command('Man', function(params) if params.bang then man.init_pager() else - local ok, err = pcall(man.open_page, params.count, params.smods, params.fargs) - if not ok then - vim.notify(man.errormsg or err, vim.log.levels.ERROR) + local err = man.open_page(params.count, params.smods, params.fargs) + if err then + vim.notify('man.lua: ' .. err, vim.log.levels.ERROR) end end end, { @@ -24,13 +24,16 @@ end, { end, }) -local augroup = vim.api.nvim_create_augroup('man', {}) +local augroup = vim.api.nvim_create_augroup('nvim.man', {}) vim.api.nvim_create_autocmd('BufReadCmd', { group = augroup, pattern = 'man://*', nested = true, callback = function(params) - require('man').read_page(vim.fn.matchstr(params.match, 'man://\\zs.*')) + local err = require('man').read_page(assert(params.match:match('man://(.*)'))) + if err then + vim.notify('man.lua: ' .. err, vim.log.levels.ERROR) + end end, }) diff --git a/runtime/plugin/matchparen.vim b/runtime/plugin/matchparen.vim index 661a34b578..13d1b6824f 100644 --- a/runtime/plugin/matchparen.vim +++ b/runtime/plugin/matchparen.vim @@ -109,6 +109,10 @@ func s:Highlight_Matching_Pair() if !has("syntax") || !exists("g:syntax_on") let s_skip = "0" + elseif exists("b:ts_highlight") && &syntax != 'on' + let s_skip = "match(v:lua.vim.treesitter.get_captures_at_cursor(), '" + \ .. 'string\|character\|singlequote\|escape\|symbol\|comment' + \ .. "') != -1" else " Build an expression that detects whether the current cursor position is " in certain syntax types (string, comment, etc.), for use as diff --git a/runtime/plugin/netrwPlugin.vim b/runtime/plugin/netrwPlugin.vim index 775b650e71..6d7a8660ff 100644 --- a/runtime/plugin/netrwPlugin.vim +++ b/runtime/plugin/netrwPlugin.vim @@ -1,233 +1,9 @@ -" netrwPlugin.vim: Handles file transfer and remote directory listing across a network -" PLUGIN SECTION -" Maintainer: This runtime file is looking for a new maintainer. -" Date: Sep 09, 2021 -" Last Change: -" 2024 May 08 by Vim Project: cleanup legacy Win9X checks -" 2024 Oct 27 by Vim Project: cleanup gx mapping -" 2024 Oct 28 by Vim Project: further improvements -" 2024 Oct 31 by Vim Project: use autoloaded functions -" Former Maintainer: Charles E Campbell -" GetLatestVimScripts: 1075 1 :AutoInstall: netrw.vim -" Copyright: Copyright (C) 1999-2021 Charles E. Campbell {{{1 -" Permission is hereby granted to use and distribute this code, -" with or without modifications, provided that this copyright -" notice is copied with it. Like anything else that's free, -" netrw.vim, netrwPlugin.vim, and netrwSettings.vim are provided -" *as is* and comes with no warranty of any kind, either -" expressed or implied. By using this plugin, you agree that -" in no event will the copyright holder be liable for any damages -" resulting from the use of this software. -" -" But be doers of the Word, and not only hearers, deluding your own selves {{{1 -" (James 1:22 RSV) -" =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -" Load Once: {{{1 -if &cp || exists("g:loaded_netrwPlugin") - finish -endif -let g:loaded_netrwPlugin = "v173" -let s:keepcpo = &cpo -set cpo&vim -"DechoRemOn - -" --------------------------------------------------------------------- -" Public Interface: {{{1 - -" Commands Launch/URL {{{2 -command -complete=shellcmd -nargs=1 Launch call netrw#Launch(trim(<q-args>)) -command -complete=file -nargs=1 Open call netrw#Open(trim(<q-args>)) -" " }}} -" Local Browsing Autocmds: {{{2 -augroup FileExplorer - au! - au BufLeave * if &ft != "netrw"|let w:netrw_prvfile= expand("%:p")|endif - au BufEnter * sil call s:LocalBrowse(expand("<amatch>")) - au VimEnter * sil call s:VimEnter(expand("<amatch>")) - if has("win32") - au BufEnter .* sil call s:LocalBrowse(expand("<amatch>")) - endif -augroup END - -" Network Browsing Reading Writing: {{{2 -augroup Network - au! - au BufReadCmd file://* call netrw#FileUrlEdit(expand("<amatch>")) - au BufReadCmd ftp://*,rcp://*,scp://*,http://*,https://*,dav://*,davs://*,rsync://*,sftp://* exe "sil doau BufReadPre ".fnameescape(expand("<amatch>"))|call netrw#Nread(2,expand("<amatch>"))|exe "sil doau BufReadPost ".fnameescape(expand("<amatch>")) - au FileReadCmd ftp://*,rcp://*,scp://*,http://*,file://*,https://*,dav://*,davs://*,rsync://*,sftp://* exe "sil doau FileReadPre ".fnameescape(expand("<amatch>"))|call netrw#Nread(1,expand("<amatch>"))|exe "sil doau FileReadPost ".fnameescape(expand("<amatch>")) - au BufWriteCmd ftp://*,rcp://*,scp://*,http://*,file://*,dav://*,davs://*,rsync://*,sftp://* exe "sil doau BufWritePre ".fnameescape(expand("<amatch>"))|exe 'Nwrite '.fnameescape(expand("<amatch>"))|exe "sil doau BufWritePost ".fnameescape(expand("<amatch>")) - au FileWriteCmd ftp://*,rcp://*,scp://*,http://*,file://*,dav://*,davs://*,rsync://*,sftp://* exe "sil doau FileWritePre ".fnameescape(expand("<amatch>"))|exe "'[,']".'Nwrite '.fnameescape(expand("<amatch>"))|exe "sil doau FileWritePost ".fnameescape(expand("<amatch>")) - try - au SourceCmd ftp://*,rcp://*,scp://*,http://*,file://*,https://*,dav://*,davs://*,rsync://*,sftp://* exe 'Nsource '.fnameescape(expand("<amatch>")) - catch /^Vim\%((\a\+)\)\=:E216/ - au SourcePre ftp://*,rcp://*,scp://*,http://*,file://*,https://*,dav://*,davs://*,rsync://*,sftp://* exe 'Nsource '.fnameescape(expand("<amatch>")) - endtry -augroup END - -" Commands: :Nread, :Nwrite, :NetUserPass {{{2 -com! -count=1 -nargs=* Nread let s:svpos= winsaveview()<bar>call netrw#NetRead(<count>,<f-args>)<bar>call winrestview(s:svpos) -com! -range=% -nargs=* Nwrite let s:svpos= winsaveview()<bar><line1>,<line2>call netrw#NetWrite(<f-args>)<bar>call winrestview(s:svpos) -com! -nargs=* NetUserPass call NetUserPass(<f-args>) -com! -nargs=* Nsource let s:svpos= winsaveview()<bar>call netrw#NetSource(<f-args>)<bar>call winrestview(s:svpos) -com! -nargs=? Ntree call netrw#SetTreetop(1,<q-args>) +" Load the netrw package. -" Commands: :Explore, :Sexplore, Hexplore, Vexplore, Lexplore {{{2 -com! -nargs=* -bar -bang -count=0 -complete=dir Explore call netrw#Explore(<count>,0,0+<bang>0,<q-args>) -com! -nargs=* -bar -bang -count=0 -complete=dir Sexplore call netrw#Explore(<count>,1,0+<bang>0,<q-args>) -com! -nargs=* -bar -bang -count=0 -complete=dir Hexplore call netrw#Explore(<count>,1,2+<bang>0,<q-args>) -com! -nargs=* -bar -bang -count=0 -complete=dir Vexplore call netrw#Explore(<count>,1,4+<bang>0,<q-args>) -com! -nargs=* -bar -count=0 -complete=dir Texplore call netrw#Explore(<count>,0,6 ,<q-args>) -com! -nargs=* -bar -bang Nexplore call netrw#Explore(-1,0,0,<q-args>) -com! -nargs=* -bar -bang Pexplore call netrw#Explore(-2,0,0,<q-args>) -com! -nargs=* -bar -bang -count=0 -complete=dir Lexplore call netrw#Lexplore(<count>,<bang>0,<q-args>) - -" Commands: NetrwSettings {{{2 -com! -nargs=0 NetrwSettings call netrwSettings#NetrwSettings() -com! -bang NetrwClean call netrw#Clean(<bang>0) - -" Maps: -if !exists("g:netrw_nogx") - if maparg('gx','n') == "" - if !hasmapto('<Plug>NetrwBrowseX') - nmap <unique> gx <Plug>NetrwBrowseX - endif - nno <silent> <Plug>NetrwBrowseX :call netrw#BrowseX(netrw#GX(),netrw#CheckIfRemote(netrw#GX()))<cr> - endif - if maparg('gx','x') == "" - if !hasmapto('<Plug>NetrwBrowseXVis') - xmap <unique> gx <Plug>NetrwBrowseXVis - endif - xno <silent> <Plug>NetrwBrowseXVis :<c-u>call netrw#BrowseXVis()<cr> - endif -endif -if exists("g:netrw_usetab") && g:netrw_usetab - if maparg('<c-tab>','n') == "" - nmap <unique> <c-tab> <Plug>NetrwShrink - endif - nno <silent> <Plug>NetrwShrink :call netrw#Shrink()<cr> +if &cp || exists("g:loaded_netrw") || exists("g:loaded_netrwPlugin") + finish endif -" --------------------------------------------------------------------- -" LocalBrowse: invokes netrw#LocalBrowseCheck() on directory buffers {{{2 -fun! s:LocalBrowse(dirname) - " Unfortunate interaction -- only DechoMsg debugging calls can be safely used here. - " Otherwise, the BufEnter event gets triggered when attempts to write to - " the DBG buffer are made. - - if !exists("s:vimentered") - " If s:vimentered doesn't exist, then the VimEnter event hasn't fired. It will, - " and so s:VimEnter() will then be calling this routine, but this time with s:vimentered defined. -" call Dfunc("s:LocalBrowse(dirname<".a:dirname.">) (s:vimentered doesn't exist)") -" call Dret("s:LocalBrowse") - return - endif - -" call Dfunc("s:LocalBrowse(dirname<".a:dirname.">) (s:vimentered=".s:vimentered.")") - - if has("amiga") - " The check against '' is made for the Amiga, where the empty - " string is the current directory and not checking would break - " things such as the help command. -" call Decho("(LocalBrowse) dirname<".a:dirname."> (isdirectory, amiga)") - if a:dirname != '' && isdirectory(a:dirname) - sil! call netrw#LocalBrowseCheck(a:dirname) - if exists("w:netrw_bannercnt") && line('.') < w:netrw_bannercnt - exe w:netrw_bannercnt - endif - endif - - elseif isdirectory(a:dirname) -" call Decho("(LocalBrowse) dirname<".a:dirname."> ft=".&ft." (isdirectory, not amiga)") -" call Dredir("LocalBrowse ft last set: ","verbose set ft") - " Jul 13, 2021: for whatever reason, preceding the following call with - " a sil! causes an unbalanced if-endif vim error - call netrw#LocalBrowseCheck(a:dirname) - if exists("w:netrw_bannercnt") && line('.') < w:netrw_bannercnt - exe w:netrw_bannercnt - endif - - else - " not a directory, ignore it -" call Decho("(LocalBrowse) dirname<".a:dirname."> not a directory, ignoring...") - endif - -" call Dret("s:LocalBrowse") -endfun - -" --------------------------------------------------------------------- -" s:VimEnter: after all vim startup stuff is done, this function is called. {{{2 -" Its purpose: to look over all windows and run s:LocalBrowse() on -" them, which checks if they're directories and will create a directory -" listing when appropriate. -" It also sets s:vimentered, letting s:LocalBrowse() know that s:VimEnter() -" has already been called. -fun! s:VimEnter(dirname) -" call Dfunc("s:VimEnter(dirname<".a:dirname.">) expand(%)<".expand("%").">") - if has('nvim') || v:version < 802 - " Johann Höchtl: reported that the call range... line causes an E488: Trailing characters - " error with neovim. I suspect its because neovim hasn't updated with recent - " vim patches. As is, this code will have problems with popup terminals - " instantiated before the VimEnter event runs. - " Ingo Karkat : E488 also in Vim 8.1.1602 - let curwin = winnr() - let s:vimentered = 1 - windo call s:LocalBrowse(expand("%:p")) - exe curwin."wincmd w" - else - " the following complicated expression comes courtesy of lacygoill; largely does the same thing as the windo and - " wincmd which are commented out, but avoids some side effects. Allows popup terminal before VimEnter. - let s:vimentered = 1 - call range(1, winnr('$'))->map({_, v -> win_execute(win_getid(v), 'call expand("%:p")->s:LocalBrowse()')}) - endif -" call Dret("s:VimEnter") -endfun - -" --------------------------------------------------------------------- -" NetrwStatusLine: {{{1 -fun! NetrwStatusLine() -" let g:stlmsg= "Xbufnr=".w:netrw_explore_bufnr." bufnr=".bufnr("%")." Xline#".w:netrw_explore_line." line#".line(".") - if !exists("w:netrw_explore_bufnr") || w:netrw_explore_bufnr != bufnr("%") || !exists("w:netrw_explore_line") || w:netrw_explore_line != line(".") || !exists("w:netrw_explore_list") - let &stl= s:netrw_explore_stl - if exists("w:netrw_explore_bufnr")|unlet w:netrw_explore_bufnr|endif - if exists("w:netrw_explore_line")|unlet w:netrw_explore_line|endif - return "" - else - return "Match ".w:netrw_explore_mtchcnt." of ".w:netrw_explore_listlen - endif -endfun - -" ------------------------------------------------------------------------ -" NetUserPass: set username and password for subsequent ftp transfer {{{1 -" Usage: :call NetUserPass() -- will prompt for userid and password -" :call NetUserPass("uid") -- will prompt for password -" :call NetUserPass("uid","password") -- sets global userid and password -fun! NetUserPass(...) - - " get/set userid - if a:0 == 0 -" call Dfunc("NetUserPass(a:0<".a:0.">)") - if !exists("g:netrw_uid") || g:netrw_uid == "" - " via prompt - let g:netrw_uid= input('Enter username: ') - endif - else " from command line -" call Dfunc("NetUserPass(a:1<".a:1.">) {") - let g:netrw_uid= a:1 - endif - - " get password - if a:0 <= 1 " via prompt -" call Decho("a:0=".a:0." case <=1:") - let g:netrw_passwd= inputsecret("Enter Password: ") - else " from command line -" call Decho("a:0=".a:0." case >1: a:2<".a:2.">") - let g:netrw_passwd=a:2 - endif -" call Dret("NetUserPass") -endfun +packadd netrw -" ------------------------------------------------------------------------ -" Modelines And Restoration: {{{1 -let &cpo= s:keepcpo -unlet s:keepcpo -" vim:ts=8 fdm=marker +" vim:ts=8 sts=2 sw=2 et diff --git a/runtime/plugin/osc52.lua b/runtime/plugin/osc52.lua index 7ffd64342e..c7f1cbe2e3 100644 --- a/runtime/plugin/osc52.lua +++ b/runtime/plugin/osc52.lua @@ -6,7 +6,15 @@ for _, ui in ipairs(vim.api.nvim_list_uis()) do end end -if not tty or vim.g.clipboard ~= nil or vim.o.clipboard ~= '' or not os.getenv('SSH_TTY') then +-- Do not query when any of the following is true: +-- * TUI is not attached +-- * OSC 52 support is explicitly disabled via g:termfeatures +-- * Using a badly behaved terminal +if + not tty + or (vim.g.termfeatures ~= nil and vim.g.termfeatures.osc52 == false) + or vim.env.TERM_PROGRAM == 'Apple_Terminal' +then return end @@ -17,28 +25,13 @@ require('vim.termcap').query('Ms', function(cap, found, seq) assert(cap == 'Ms') - -- Check 'clipboard' and g:clipboard again to avoid a race condition - if vim.o.clipboard ~= '' or vim.g.clipboard ~= nil then - return - end - -- If the terminal reports a sequence other than OSC 52 for the Ms capability -- then ignore it. We only support OSC 52 (for now) if not seq or not seq:match('^\027%]52') then return end - local osc52 = require('vim.ui.clipboard.osc52') - - vim.g.clipboard = { - name = 'OSC 52', - copy = { - ['+'] = osc52.copy('+'), - ['*'] = osc52.copy('*'), - }, - paste = { - ['+'] = osc52.paste('+'), - ['*'] = osc52.paste('*'), - }, - } + local termfeatures = vim.g.termfeatures or {} + termfeatures.osc52 = true + vim.g.termfeatures = termfeatures end) diff --git a/runtime/queries/c/highlights.scm b/runtime/queries/c/highlights.scm index eba272d5c9..bd6857fd17 100644 --- a/runtime/queries/c/highlights.scm +++ b/runtime/queries/c/highlights.scm @@ -252,13 +252,22 @@ ; Preproc def / undef (preproc_def - name: (_) @constant) + name: (_) @constant.macro) (preproc_call directive: (preproc_directive) @_u - argument: (_) @constant + argument: (_) @constant.macro (#eq? @_u "#undef")) +(preproc_ifdef + name: (identifier) @constant.macro) + +(preproc_elifdef + name: (identifier) @constant.macro) + +(preproc_defined + (identifier) @constant.macro) + (call_expression function: (identifier) @function.call) diff --git a/runtime/queries/lua/highlights.scm b/runtime/queries/lua/highlights.scm index 01c280f2d5..79ab165aa7 100644 --- a/runtime/queries/lua/highlights.scm +++ b/runtime/queries/lua/highlights.scm @@ -151,8 +151,6 @@ ((identifier) @constant (#lua-match? @constant "^[A-Z][A-Z_0-9]*$")) -(vararg_expression) @constant - (nil) @constant.builtin [ diff --git a/runtime/queries/markdown_inline/highlights.scm b/runtime/queries/markdown_inline/highlights.scm index 148ef0fad0..5fb9e911dd 100644 --- a/runtime/queries/markdown_inline/highlights.scm +++ b/runtime/queries/markdown_inline/highlights.scm @@ -40,14 +40,12 @@ (image_description) ] @markup.link.label -(inline_link - (link_text) @_label - (link_destination) @_url +((inline_link + (link_destination) @_url) @_label (#set! @_label url @_url)) -(image - (image_description) @_label - (link_destination) @_url +((image + (link_destination) @_url) @_label (#set! @_label url @_url)) ; Conceal image links @@ -93,9 +91,6 @@ (email_autolink) ] @markup.link.url @nospell -((link_destination) @_url - (#set! @_url url @_url)) - ((uri_autolink) @_url (#offset! @_url 0 1 0 -1) (#set! @_url url @_url)) diff --git a/runtime/queries/query/highlights.scm b/runtime/queries/query/highlights.scm index e459b44602..cbd192a8ff 100644 --- a/runtime/queries/query/highlights.scm +++ b/runtime/queries/query/highlights.scm @@ -11,6 +11,9 @@ (named_node name: (identifier) @variable) +(missing_node + name: (identifier) @variable) + (field_definition name: (identifier) @variable.member) @@ -43,7 +46,12 @@ "#" ] @punctuation.special -"_" @constant +(predicate + "." @punctuation.special) + +"_" @character.special + +"MISSING" @keyword ((parameters (identifier) @number) diff --git a/runtime/queries/vim/highlights.scm b/runtime/queries/vim/highlights.scm index 14e5a8128f..df7b3cf483 100644 --- a/runtime/queries/vim/highlights.scm +++ b/runtime/queries/vim/highlights.scm @@ -287,6 +287,7 @@ "=~" "!~" "=" + "^=" "+=" "-=" "*=" diff --git a/runtime/syntax/apache.vim b/runtime/syntax/apache.vim index e73045e4c8..65317fd36a 100644 --- a/runtime/syntax/apache.vim +++ b/runtime/syntax/apache.vim @@ -3,8 +3,8 @@ " Maintainer: David Necas (Yeti) <yeti@physics.muni.cz> " License: This file can be redistribued and/or modified under the same terms " as Vim itself. -" Last Change: 2022 Apr 25 -" Notes: Last synced with apache-2.2.3, version 1.x is no longer supported +" Last Change: 2024 Nov 24 +" Notes: Last synced with apache-2.4.62, version 1.x is no longer supported " TODO: see particular FIXME's scattered through the file " make it really linewise? " + add `display' where appropriate @@ -14,6 +14,7 @@ if exists("b:current_syntax") finish endif +syn iskeyword @,48-57,_,192-255,- syn case ignore " Base constructs @@ -45,9 +46,9 @@ syn keyword apacheMethodOption GET POST PUT DELETE CONNECT OPTIONS TRACE PATCH P " Added as suggested by Mikko Koivunalho syn keyword apacheMethodOption BASELINE-CONTROL CHECKIN CHECKOUT LABEL MERGE MKACTIVITY MKWORKSPACE REPORT UNCHECKOUT UPDATE VERSION-CONTROL contained syn case ignore -syn match apacheSection "<\/\=\(Directory\|DirectoryMatch\|Files\|FilesMatch\|IfModule\|IfDefine\|Location\|LocationMatch\|VirtualHost\)[^>]*>" contains=apacheAnything +syn match apacheSection "<\/\=\(Directory\|Files\|If\|Else\|Location\|VirtualHost\)[^>]*>" contains=apacheAnything syn match apacheSection "<\/\=\(RequireAll\|RequireAny\|RequireNone\)>" contains=apacheAnything -syn match apacheLimitSection "<\/\=\(Limit\|LimitExcept\)[^>]*>" contains=apacheLimitSectionKeyword,apacheMethodOption,apacheError +syn match apacheLimitSection "<\/\=Limit[^>]*>" contains=apacheLimitSectionKeyword,apacheMethodOption,apacheError syn keyword apacheLimitSectionKeyword Limit LimitExcept contained syn match apacheAuthType "AuthType\s.*$" contains=apacheAuthTypeValue syn keyword apacheAuthTypeValue Basic Digest @@ -68,7 +69,7 @@ syn keyword apacheDeclaration AuthAuthoritative AuthGroupFile AuthUserFile syn keyword apacheDeclaration AuthBasicAuthoritative AuthBasicProvider syn keyword apacheDeclaration AuthDigestAlgorithm AuthDigestDomain AuthDigestNcCheck AuthDigestNonceFormat AuthDigestNonceLifetime AuthDigestProvider AuthDigestQop AuthDigestShmemSize syn keyword apacheOption none auth auth-int MD5 MD5-sess -syn match apacheSection "<\/\=\(<AuthnProviderAlias\)[^>]*>" contains=apacheAnything +syn match apacheSection "<\/\=Auth[ntz]ProviderAlias[^>]*>" contains=apacheAnything syn keyword apacheDeclaration Anonymous Anonymous_Authoritative Anonymous_LogEmail Anonymous_MustGiveEmail Anonymous_NoUserID Anonymous_VerifyEmail syn keyword apacheDeclaration AuthDBDUserPWQuery AuthDBDUserRealmQuery syn keyword apacheDeclaration AuthDBMGroupFile AuthDBMAuthoritative @@ -155,7 +156,7 @@ syn keyword apacheDeclaration PerlCleanupHandler PerlChildInitHandler PerlChildE syn keyword apacheDeclaration PerlRestartHandler PerlDispatchHandler syn keyword apacheDeclaration PerlFreshRestart PerlSendHeader syn keyword apacheDeclaration php_value php_flag php_admin_value php_admin_flag -syn match apacheSection "<\/\=\(Proxy\|ProxyMatch\)[^>]*>" contains=apacheAnything +syn match apacheSection "<\/\=\(Macro\|MDomain\|Proxy\)[^>]*>" contains=apacheAnything syn keyword apacheDeclaration AllowCONNECT NoProxy ProxyBadHeader ProxyBlock ProxyDomain ProxyErrorOverride ProxyIOBufferSize ProxyMaxForwards ProxyPass ProxyPassMatch ProxyPassReverse ProxyPassReverseCookieDomain ProxyPassReverseCookiePath ProxyPreserveHost ProxyReceiveBufferSize ProxyRemote ProxyRemoteMatch ProxyRequests ProxyTimeout ProxyVia syn keyword apacheDeclaration RewriteBase RewriteCond RewriteEngine RewriteLock RewriteLog RewriteLogLevel RewriteMap RewriteOptions RewriteRule syn keyword apacheOption inherit @@ -173,8 +174,8 @@ syn keyword apacheDeclaration SuexecUserGroup syn keyword apacheDeclaration UserDir syn keyword apacheDeclaration CookieDomain CookieExpires CookieName CookieStyle CookieTracking syn keyword apacheOption Netscape Cookie Cookie2 RFC2109 RFC2965 -syn match apacheSection "<\/\=\(<IfVersion\)[^>]*>" contains=apacheAnything syn keyword apacheDeclaration VirtualDocumentRoot VirtualDocumentRootIP VirtualScriptAlias VirtualScriptAliasIP +syn keyword apacheDeclaration AcceptErrorsNonFatal AsyncFilter AsyncRequestWorkerFactor AuthBasicFake AuthBasicUseDigestAlgorithm AuthBearerAuthoritative AuthBearerProvider AuthBearerProxy AuthDBMType AuthDBMUserFile AuthFormAuthoritative AuthFormBody AuthFormDisableNoStore AuthFormFakeBasicAuth AuthFormLocation AuthFormLoginRequiredLocation AuthFormLoginSuccessLocation AuthFormLogoutLocation AuthFormMethod AuthFormMimetype AuthFormPassword AuthFormProvider AuthFormSitePassphrase AuthFormSize AuthFormUsername AuthLDAPAuthorizePrefix AuthLDAPBindAuthoritative AuthLDAPCompareAsUser AuthLDAPInitialBindAsUser AuthLDAPInitialBindPattern AuthLDAPMaxSubGroupDepth AuthLDAPRemoteUserAttribute AuthLDAPSearchAsUser AuthLDAPSubGroupAttribute AuthLDAPSubGroupClass AuthLDAPURL AuthMerging AuthnCacheContext AuthnCacheEnable AuthnCacheProvideFor AuthnCacheSOCache AuthnCacheTimeout AuthnzFcgiCheckAuthnProvider AuthnzFcgiDefineProvider AuthtJwtClaim AuthtJwtDriver AuthtJwtSign AuthtJwtVerify AuthzDBDLoginToReferer AuthzDBDQuery AuthzDBDRedirectQuery AuthzSendForbiddenOnFailure BalancerGrowth BalancerInherit BalancerMember BalancerPersist BrotliAlterETag BrotliCompressionMaxInputBlock BrotliCompressionQuality BrotliCompressionWindow BrotliFilterNote BufferSize CacheDetailHeader CacheHeader CacheIgnoreQueryString CacheIgnoreURLSessionIdentifiers CacheKeyBaseURL CacheLock CacheLockMaxAge CacheLockPath CacheMinExpire CacheQuickHandler CacheReadSize CacheReadTime CacheSocache CacheSocacheMaxSize CacheSocacheMaxTime CacheSocacheMinTime CacheSocacheReadSize CacheSocacheReadTime CacheStaleOnError CacheStoreExpired CGIDScriptTimeout CGIPassAuth CGIScriptTimeout CGIVar CheckBasenameMatch ChrootDir CookieHTTPOnly CookieSameSite CookieSecure CryptoCipher CryptoDriver CryptoIV CryptoKey CryptoSize CTAuditStorage CTLogClient CTLogConfigDB CTMaxSCTAge CTProxyAwareness CTSCTStorage CTServerHelloSCTLimit CTStaticLogConfig CTStaticSCTs DBDInitSQL DefaultRuntimeDir DefaultStateDir DeflateAlterETag DeflateInflateLimitRequestBody DeflateInflateRatioBurst DeflateInflateRatioLimit DirectoryCheckHandler DTracePrivileges FallbackResource Files FilesMatch FirehoseConnectionInput FirehoseConnectionOutput FirehoseProxyConnectionInput FirehoseProxyConnectionOutput FirehoseRequestInput FirehoseRequestOutput FlushMaxPipelined FlushMaxThreshold GlobalLog GprofDir H2CopyFiles H2Direct H2EarlyHint H2EarlyHints H2MaxDataFrameLen H2MaxSessionStreams H2MaxWorkerIdleSeconds H2MaxWorkers H2MinWorkers H2ModernTLSOnly H2OutputBuffering H2Padding H2ProxyRequests H2Push H2PushDiarySize H2PushPriority H2PushResource H2SerializeHeaders H2StreamMaxMemSize H2StreamTimeout H2TLSCoolDownSecs H2TLSWarmUpSize H2Upgrade H2WebSockets H2WindowSize HeartbeatAddress HeartbeatListen HeartbeatMaxServers HeartbeatStorage HeartbeatStorage HostnameLookups HttpProtocolOptions IndexForbiddenReturn404 IndexHeadInsert InputSed ISAPIFakeAsync KeptBodySize LDAPConnectionPoolTTL LDAPLibraryDebug LDAPReferralHopLimit LDAPReferrals LDAPRetries LDAPRetryDelay LDAPTimeout Location LocationMatch LogIOTrackTTFB LogIOTrackTTFU LogMessage LuaAuthzProvider LuaCodeCache LuaHookAccessChecker LuaHookAuthChecker LuaHookCheckUserID LuaHookFixups LuaHookInsertFilter LuaHookLog LuaHookMapToStorage LuaHookPreTranslate LuaHookTranslateName LuaHookTypeChecker LuaInherit LuaInputFilter LuaMapHandler LuaOutputFilter LuaPackageCPath LuaPackagePath LuaQuickHandler LuaRoot LuaScope MacroIgnoreBadNesting MacroIgnoreEmptyArgs MaxConnectionsPerChild MaxRangeOverlaps MaxRangeReversals MaxRanges MaxRequestWorkers MDActivationDelay MDBaseServer MDCAChallenges MDCertificateAgreement MDCertificateAuthority MDCertificateCheck MDCertificateFile MDCertificateKeyFile MDCertificateMonitor MDCertificateProtocol MDCertificateStatus MDChallengeDns01 MDChallengeDns01Version MDCheckInterval MDContactEmail MDDriveMode MDExternalAccountBinding MDHttpProxy MDMatchNames MDMember MDMembers MDMessageCmd MDMustStaple MDNotifyCmd MDomain MDPortMap MDPrivateKeys MDRenewMode MDRenewWindow MDRequireHttps MDRetryDelay MDRetryFailover MDServerStatus MDStapleOthers MDStapling MDStaplingKeepResponse MDStaplingRenewWindow MDStoreDir MDStoreLocks MDWarnWindow MemcacheConnTTL MergeSlashes MergeTrailers MimeOptions ModemStandard Mutex Order OutputSed PolicyConditional PolicyConditionalURL PolicyEnvironment PolicyFilter PolicyKeepalive PolicyKeepaliveURL PolicyLength PolicyLengthURL PolicyMaxage PolicyMaxageURL PolicyNocache PolicyNocacheURL PolicyType PolicyTypeURL PolicyValidation PolicyValidationURL PolicyVary PolicyVaryURL PolicyVersion PolicyVersionURL PrivilegesMode Protocol Protocols ProtocolsHonorOrder Proxy100Continue ProxyAddHeaders ProxyExpressDBMFile ProxyExpressDBMType ProxyExpressEnable ProxyFCGIBackendType ProxyFCGISetEnvIf ProxyFtpDirCharset ProxyFtpEscapeWildcards ProxyFtpListOnWildcard ProxyHCExpr ProxyHCTemplate ProxyHCTPsize ProxyHTMLBufSize ProxyHTMLCharsetOut ProxyHTMLDocType ProxyHTMLEnable ProxyHTMLEvents ProxyHTMLExtended ProxyHTMLFixups ProxyHTMLInterp ProxyHTMLLinks ProxyHTMLMeta ProxyHTMLStripComments ProxyHTMLURLMap ProxySCGIInternalRedirect ProxySCGISendfile ProxySet ProxySourceAddress ProxyStatus ProxyWebsocketAsync ProxyWebsocketAsyncDelay ProxyWebsocketFallbackToProxyHttp ProxyWebsocketIdleTimeout QualifyRedirectURL ReadBufferSize ReceiveBufferSize RedisConnPoolTTL RedisTimeout ReflectorHeader RegexDefaultOptions RegisterHttpMethod RemoteIPHeader RemoteIPInternalProxy RemoteIPInternalProxyList RemoteIPProxiesHeader RemoteIPProxyProtocol RemoteIPProxyProtocolExceptions RemoteIPTrustedProxy RemoteIPTrustedProxyList RemoveLanguage RequestReadTimeout SeeRequestTail Session SessionCookieMaxAge SessionCookieName SessionCookieName2 SessionCookieRemove SessionCryptoCipher SessionCryptoDriver SessionCryptoPassphrase SessionCryptoPassphraseFile SessionDBDCookieName SessionDBDCookieName2 SessionDBDCookieRemove SessionDBDDeleteLabel SessionDBDInsertLabel SessionDBDPerUser SessionDBDSelectLabel SessionDBDUpdateLabel SessionEnv SessionExclude SessionExpiryUpdateInterval SessionHeader SessionInclude SessionMaxAge SSIETag SSILastModified SSILegacyExprParser SSLCARevocationCheck SSLClientHelloVars SSLOCSPDefaultResponder SSLOCSPEnable SSLOCSPNoverify SSLOCSPOverrideResponder SSLOCSPProxyURL SSLOCSPResponderCertificateFile SSLOCSPResponderTimeout SSLOCSPResponseMaxAge SSLOCSPResponseTimeSkew SSLOCSPUseRequestNonce SSLOpenSSLConfCmd SSLPolicy SSLProxyCARevocationCheck SSLProxyCheckPeerName SSLSRPUnknownUserSeed SSLSRPVerifierFile SSLStaplingCache SSLStaplingErrorCacheTimeout SSLStaplingFakeTryLater SSLStaplingForceURL SSLStaplingResponderTimeout SSLStaplingResponseMaxAge SSLStaplingResponseTimeSkew SSLStaplingReturnResponderErrors SSLStaplingStandardCacheTimeout SSLUseStapling StrictHostCheck Substitute SubstituteInheritBefore SubstituteMaxLineLength Suexec UNCList UnDefine UndefMacro Use UseCanonicalPhysicalPort VHostCGIMode VHostCGIPrivs VHostGroup VHostPrivs VHostSecure VHostUser Warning WatchdogInterval xml2EncAlias xml2EncDefault xml2StartParse " Define the default highlighting diff --git a/runtime/syntax/apkbuild.vim b/runtime/syntax/apkbuild.vim new file mode 100644 index 0000000000..f969ff0e2e --- /dev/null +++ b/runtime/syntax/apkbuild.vim @@ -0,0 +1,17 @@ +" Vim syntax file +" Language: apkbuild +" Maintainer: The Vim Project <https://github.com/vim/vim> +" Last Change: 2024 Dec 22 + +" quit when a syntax file was already loaded +if exists("b:current_syntax") + finish +endif + +" The actual syntax is in sh.vim and controlled by buffer-local variables. +unlet! b:is_bash b:is_kornshell +let b:is_sh = 1 + +runtime! syntax/sh.vim + +let b:current_syntax = 'apkbuild' diff --git a/runtime/syntax/asm.vim b/runtime/syntax/asm.vim index 73f283a4a7..18f3de1c63 100644 --- a/runtime/syntax/asm.vim +++ b/runtime/syntax/asm.vim @@ -3,8 +3,8 @@ " Maintainer: Doug Kearns dougkearns@gmail.com " Previous Maintainers: Erik Wognsen <erik.wognsen@gmail.com> " Kevin Dahlhausen <kdahlhaus@yahoo.com> -" Contributors: Ori Avtalion, Lakshay Garg -" Last Change: 2020 Oct 31 +" Contributors: Ori Avtalion, Lakshay Garg, Nir Lichtman +" Last Change: 2025 Jan 26 " quit when a syntax file was already loaded if exists("b:current_syntax") @@ -32,6 +32,9 @@ syn match asmType "\.single" syn match asmType "\.space" syn match asmType "\.string" syn match asmType "\.word" +syn match asmType "\.2byte" +syn match asmType "\.4byte" +syn match asmType "\.8byte" syn match asmIdentifier "[a-z_][a-z0-9_]*" syn match asmLabel "[a-z_][a-z0-9_]*:"he=e-1 diff --git a/runtime/syntax/c.vim b/runtime/syntax/c.vim index 30db9438d0..68b6778c73 100644 --- a/runtime/syntax/c.vim +++ b/runtime/syntax/c.vim @@ -1,7 +1,7 @@ " Vim syntax file -" Language: C -" Maintainer: The Vim Project <https://github.com/vim/vim> -" Last Change: 2023 Aug 10 +" Language: C +" Maintainer: The Vim Project <https://github.com/vim/vim> +" Last Change: 2025 Jan 18 " Former Maintainer: Bram Moolenaar <Bram@vim.org> " Quit when a (custom) syntax file was already loaded @@ -111,6 +111,20 @@ if (s:ft ==# "c" && !exists("c_no_c11")) || (s:in_cpp_family && !exists("cpp_no_ syn match cSpecialCharacter display "[Uu]'\\x\x\+'" endif +if (s:ft ==# "c" && !exists("c_no_c23")) || (s:in_cpp_family && !exists("cpp_no_cpp17")) + syn match cCharacter "u8'[^\\]'" + syn match cCharacter "u8'[^']*'" contains=cSpecial + if exists("c_gnu") + syn match cSpecialError "u8'\\[^'\"?\\abefnrtv]'" + syn match cSpecialCharacter "u8'\\['\"?\\abefnrtv]'" + else + syn match cSpecialError "u8'\\[^'\"?\\abfnrtv]'" + syn match cSpecialCharacter "u8'\\['\"?\\abfnrtv]'" + endif + syn match cSpecialCharacter display "u8'\\\o\{1,3}'" + syn match cSpecialCharacter display "u8'\\x\x\+'" +endif + "when wanted, highlight trailing white space if exists("c_space_errors") if !exists("c_no_trail_space_error") @@ -191,12 +205,25 @@ syn case ignore syn match cNumbers display transparent "\<\d\|\.\d" contains=cNumber,cFloat,cOctalError,cOctal " Same, but without octal error (for comments) syn match cNumbersCom display contained transparent "\<\d\|\.\d" contains=cNumber,cFloat,cOctal -syn match cNumber display contained "\d\+\%(u\=l\{0,2}\|ll\=u\)\>" -"hex number -syn match cNumber display contained "0x\x\+\%(u\=l\{0,2}\|ll\=u\)\>" -" Flag the first zero of an octal number as something special -syn match cOctal display contained "0\o\+\%(u\=l\{0,2}\|ll\=u\)\>" contains=cOctalZero -syn match cOctalZero display contained "\<0" + +" cpp.vim handles these +if !exists("c_no_c23") && !s:in_cpp_family + syn match cNumber display contained "\d\%('\=\d\+\)*\%(u\=l\{0,2}\|ll\=u\|u\=wb\|wbu\=\)\>" + "hex number + syn match cNumber display contained "0x\x\%('\=\x\+\)*\%(u\=l\{0,2}\|ll\=u\|u\=wb\|wbu\=\)\>" + " Flag the first zero of an octal number as something special + syn match cOctal display contained "0\o\%('\=\o\+\)*\%(u\=l\{0,2}\|ll\=u\|u\=wb\|wbu\=\)\>" contains=cOctalZero + "binary number + syn match cNumber display contained "0b[01]\%('\=[01]\+\)*\%(u\=l\{0,2}\|ll\=u\|u\=wb\|wbu\=\)\>" +else + syn match cNumber display contained "\d\+\%(u\=l\{0,2}\|ll\=u\)\>" + "hex number + syn match cNumber display contained "0x\x\+\%(u\=l\{0,2}\|ll\=u\)\>" + " Flag the first zero of an octal number as something special + syn match cOctal display contained "0\o\+\%(u\=l\{0,2}\|ll\=u\)\>" contains=cOctalZero + syn match cOctalZero display contained "\<0" +endif + "floating point number, with dot, optional exponent syn match cFloat display contained "\d\+\.\d*\%(e[-+]\=\d\+\)\=[fl]\=" "floating point number, starting with a dot, optional exponent @@ -277,6 +304,13 @@ if !exists("c_no_c99") " ISO C99 syn keyword cType intptr_t uintptr_t syn keyword cType intmax_t uintmax_t endif +if !exists("c_no_c23") && !s:in_cpp_family + syn keyword cOperator typeof typeof_unqual + syn keyword cType _BitInt _Decimal32 _Decimal64 _Decimal128 +endif +if (s:ft ==# "c" && !exists("c_no_c23")) || (s:in_cpp_family && !exists("cpp_no_cpp11")) + syn keyword cType nullptr_t +endif syn keyword cTypedef typedef syn keyword cStructure struct union enum @@ -284,6 +318,9 @@ syn keyword cStorageClass static register auto volatile extern const if !exists("c_no_c99") && !s:in_cpp_family syn keyword cStorageClass inline restrict endif +if (s:ft ==# "c" && !exists("c_no_c23")) || (s:in_cpp_family && !exists("cpp_no_cpp11")) + syn keyword cStorageClass constexpr +endif if !exists("c_no_c11") syn keyword cStorageClass _Alignas alignas syn keyword cOperator _Alignof alignof @@ -312,10 +349,15 @@ if !exists("c_no_c11") syn keyword cType atomic_intmax_t atomic_uintmax_t endif +if (s:ft ==# "c" && !exists("c_no_c23")) || (s:in_cpp_family && !exists("cpp_no_cpp20")) + syn keyword cType char8_t +endif + if !exists("c_no_ansi") || exists("c_ansi_constants") || exists("c_gnu") if exists("c_gnu") syn keyword cConstant __GNUC__ __FUNCTION__ __PRETTY_FUNCTION__ __func__ endif + " TODO: __STDC_HOSTED__ is C99 and C++11 syn keyword cConstant __LINE__ __FILE__ __DATE__ __TIME__ __STDC__ __STDC_VERSION__ __STDC_HOSTED__ syn keyword cConstant CHAR_BIT MB_LEN_MAX MB_CUR_MAX syn keyword cConstant UCHAR_MAX UINT_MAX ULONG_MAX USHRT_MAX @@ -324,6 +366,8 @@ if !exists("c_no_ansi") || exists("c_ansi_constants") || exists("c_gnu") syn keyword cConstant SCHAR_MIN SINT_MIN SLONG_MIN SSHRT_MIN syn keyword cConstant SCHAR_MAX SINT_MAX SLONG_MAX SSHRT_MAX if !exists("c_no_c99") + syn keyword cConstant __STDC_ISO_10646__ __STDC_IEC_559_COMPLEX__ + syn keyword cConstant __STDC_MB_MIGHT_NEQ_WC__ syn keyword cConstant __func__ __VA_ARGS__ syn keyword cConstant LLONG_MIN LLONG_MAX ULLONG_MAX syn keyword cConstant INT8_MIN INT16_MIN INT32_MIN INT64_MIN @@ -340,6 +384,26 @@ if !exists("c_no_ansi") || exists("c_ansi_constants") || exists("c_gnu") syn keyword cConstant PTRDIFF_MIN PTRDIFF_MAX SIG_ATOMIC_MIN SIG_ATOMIC_MAX syn keyword cConstant SIZE_MAX WCHAR_MIN WCHAR_MAX WINT_MIN WINT_MAX endif + if !exists("c_no_c11") + syn keyword cConstant __STDC_UTF_16__ __STDC_UTF_32__ __STDC_ANALYZABLE__ + syn keyword cConstant __STDC_LIB_EXT1__ __STDC_NO_ATOMICS__ + syn keyword cConstant __STDC_NO_COMPLEX__ __STDC_NO_THREADS__ + syn keyword cConstant __STDC_NO_VLA__ + endif + if !exists("c_no_c23") + syn keyword cConstant __STDC_UTF_16__ __STDC_UTF_32__ + syn keyword cConstant __STDC_EMBED_NOT_FOUND__ __STDC_EMBED_FOUND__ + syn keyword cConstant __STDC_EMBED_EMPTY__ __STDC_IEC_60559_BFP__ + syn keyword cConstant __STDC_IEC_60559_DFP__ __STDC_IEC_60559_COMPLEX__ + syn keyword cConstant __STDC_IEC_60559_TYPES__ + syn keyword cConstant BITINT_MAXWIDTH + endif + if (s:ft ==# "c" && !exists("c_no_c23")) || (s:in_cpp_family && !exists("cpp_no_cpp20")) + syn keyword cConstant __VA_OPT__ + endif + if (s:ft ==# "c" && !exists("c_no_c23")) || (s:in_cpp_family && !exists("cpp_no_cpp11")) + syn keyword cConstant nullptr + endif syn keyword cConstant FLT_RADIX FLT_ROUNDS FLT_DIG FLT_MANT_DIG FLT_EPSILON DBL_DIG DBL_MANT_DIG DBL_EPSILON syn keyword cConstant LDBL_DIG LDBL_MANT_DIG LDBL_EPSILON FLT_MIN FLT_MAX FLT_MIN_EXP FLT_MAX_EXP FLT_MIN_10_EXP FLT_MAX_10_EXP syn keyword cConstant DBL_MIN DBL_MAX DBL_MIN_EXP DBL_MAX_EXP DBL_MIN_10_EXP DBL_MAX_10_EXP LDBL_MIN LDBL_MAX LDBL_MIN_EXP LDBL_MAX_EXP @@ -377,37 +441,77 @@ if !exists("c_no_ansi") || exists("c_ansi_constants") || exists("c_gnu") endif if !exists("c_no_c99") " ISO C99 syn keyword cConstant true false + syn keyword cConstant INFINITY NAN + " math.h + syn keyword cConstant HUGE_VAL HUGE_VALF HUGE_VALL + syn keyword cConstant FP_FAST_FMAF FP_FAST_FMA FP_FAST_FMAL + syn keyword cConstant FP_ILOGB0 FP_ILOGBNAN + syn keyword cConstant math_errhandling MATH_ERRNO MATH_ERREXCEPT + syn keyword cConstant FP_NORMAL FP_SUBNORMAL FP_ZERO FP_INFINITE FP_NAN endif " Accept %: for # (C99) -syn region cPreCondit start="^\s*\zs\%(%:\|#\)\s*\%(if\|ifdef\|ifndef\|elif\)\>" skip="\\$" end="$" keepend contains=cComment,cCommentL,cCppString,cCharacter,cCppParen,cParenError,cNumbers,cCommentError,cSpaceError +syn cluster cPreProcGroup contains=cPreCondit,cIncluded,cInclude,cDefine,cErrInParen,cErrInBracket,cUserLabel,cSpecial,cOctalZero,cCppOutWrapper,cCppInWrapper,@cCppOutInGroup,cFormat,cNumber,cFloat,cOctal,cOctalError,cNumbersCom,cString,cCommentSkip,cCommentString,cComment2String,@cCommentGroup,cCommentStartError,cParen,cBracket,cMulti,cBadBlock +if !exists("c_no_c23") + syn region cPreCondit start="^\s*\zs\%(%:\|#\)\s*\%(el\)\=\%(if\|ifdef\|ifndef\)\>" skip="\\$" end="$" keepend contains=cComment,cCommentL,cCppString,cCharacter,cCppParen,cParenError,cNumbers,cCommentError,cSpaceError +else + syn region cPreCondit start="^\s*\zs\%(%:\|#\)\s*\%(if\|ifdef\|ifndef\|elif\)\>" skip="\\$" end="$" keepend contains=cComment,cCommentL,cCppString,cCharacter,cCppParen,cParenError,cNumbers,cCommentError,cSpaceError +endif syn match cPreConditMatch display "^\s*\zs\%(%:\|#\)\s*\%(else\|endif\)\>" if !exists("c_no_if0") syn cluster cCppOutInGroup contains=cCppInIf,cCppInElse,cCppInElse2,cCppOutIf,cCppOutIf2,cCppOutElse,cCppInSkip,cCppOutSkip syn region cCppOutWrapper start="^\s*\zs\%(%:\|#\)\s*if\s\+0\+\s*\%($\|//\|/\*\|&\)" end=".\@=\|$" contains=cCppOutIf,cCppOutElse,@NoSpell fold syn region cCppOutIf contained start="0\+" matchgroup=cCppOutWrapper end="^\s*\%(%:\|#\)\s*endif\>" contains=cCppOutIf2,cCppOutElse if !exists("c_no_if0_fold") - syn region cCppOutIf2 contained matchgroup=cCppOutWrapper start="0\+" end="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0\+\s*\%($\|//\|/\*\|&\)\)\@!\|endif\>\)"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell fold + if !exists("c_no_c23") + syn region cCppOutIf2 contained matchgroup=cCppOutWrapper start="0\+" end="^\s*\%(%:\|#\)\s*\%(else\>\|el\%(if\|ifdef\|ifndef\)\s\+\%(0\+\s*\%($\|//\|/\*\|&\)\)\@!\|endif\>\)"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell fold + else + syn region cCppOutIf2 contained matchgroup=cCppOutWrapper start="0\+" end="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0\+\s*\%($\|//\|/\*\|&\)\)\@!\|endif\>\)"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell fold + endif + else + if !exists("c_no_c23") + syn region cCppOutIf2 contained matchgroup=cCppOutWrapper start="0\+" end="^\s*\%(%:\|#\)\s*\%(else\>\|el\%(if\|ifdef\|ifndef\)\s\+\%(0\+\s*\%($\|//\|/\*\|&\)\)\@!\|endif\>\)"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell + else + syn region cCppOutIf2 contained matchgroup=cCppOutWrapper start="0\+" end="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0\+\s*\%($\|//\|/\*\|&\)\)\@!\|endif\>\)"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell + endif + endif + if !exists("c_no_c23") + syn region cCppOutElse contained matchgroup=cCppOutWrapper start="^\s*\%(%:\|#\)\s*\%(else\|el\%(if\|ifdef\|ifndef\)\)" end="^\s*\%(%:\|#\)\s*endif\>"me=s-1 contains=TOP,cPreCondit else - syn region cCppOutIf2 contained matchgroup=cCppOutWrapper start="0\+" end="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0\+\s*\%($\|//\|/\*\|&\)\)\@!\|endif\>\)"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell + syn region cCppOutElse contained matchgroup=cCppOutWrapper start="^\s*\%(%:\|#\)\s*\%(else\|elif\)" end="^\s*\%(%:\|#\)\s*endif\>"me=s-1 contains=TOP,cPreCondit endif - syn region cCppOutElse contained matchgroup=cCppOutWrapper start="^\s*\%(%:\|#\)\s*\%(else\|elif\)" end="^\s*\%(%:\|#\)\s*endif\>"me=s-1 contains=TOP,cPreCondit syn region cCppInWrapper start="^\s*\zs\%(%:\|#\)\s*if\s\+0*[1-9]\d*\s*\%($\|//\|/\*\||\)" end=".\@=\|$" contains=cCppInIf,cCppInElse fold syn region cCppInIf contained matchgroup=cCppInWrapper start="\d\+" end="^\s*\%(%:\|#\)\s*endif\>" contains=TOP,cPreCondit if !exists("c_no_if0_fold") - syn region cCppInElse contained start="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0*[1-9]\d*\s*\%($\|//\|/\*\||\)\)\@!\)" end=".\@=\|$" containedin=cCppInIf contains=cCppInElse2 fold + if !exists("c_no_c23") + syn region cCppInElse contained start="^\s*\%(%:\|#\)\s*\%(else\>\|el\%(if\|ifdef\|ifndef\)\s\+\%(0*[1-9]\d*\s*\%($\|//\|/\*\||\)\)\@!\)" end=".\@=\|$" containedin=cCppInIf contains=cCppInElse2 fold + else + syn region cCppInElse contained start="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0*[1-9]\d*\s*\%($\|//\|/\*\||\)\)\@!\)" end=".\@=\|$" containedin=cCppInIf contains=cCppInElse2 fold + endif else - syn region cCppInElse contained start="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0*[1-9]\d*\s*\%($\|//\|/\*\||\)\)\@!\)" end=".\@=\|$" containedin=cCppInIf contains=cCppInElse2 + if !exists("c_no_c23") + syn region cCppInElse contained start="^\s*\%(%:\|#\)\s*\%(else\>\|el\%(if\|ifdef\|ifndef\)\s\+\%(0*[1-9]\d*\s*\%($\|//\|/\*\||\)\)\@!\)" end=".\@=\|$" containedin=cCppInIf contains=cCppInElse2 + else + syn region cCppInElse contained start="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0*[1-9]\d*\s*\%($\|//\|/\*\||\)\)\@!\)" end=".\@=\|$" containedin=cCppInIf contains=cCppInElse2 + endif + endif + if !exists("c_no_c23") + syn region cCppInElse2 contained matchgroup=cCppInWrapper start="^\s*\%(%:\|#\)\s*\%(else\|el\%(if\|ifdef\|ifndef\)\)\%([^/]\|/[^/*]\)*" end="^\s*\%(%:\|#\)\s*endif\>"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell + else + syn region cCppInElse2 contained matchgroup=cCppInWrapper start="^\s*\%(%:\|#\)\s*\%(else\|elif\)\%([^/]\|/[^/*]\)*" end="^\s*\%(%:\|#\)\s*endif\>"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell endif - syn region cCppInElse2 contained matchgroup=cCppInWrapper start="^\s*\%(%:\|#\)\s*\%(else\|elif\)\%([^/]\|/[^/*]\)*" end="^\s*\%(%:\|#\)\s*endif\>"me=s-1 contains=cSpaceError,cCppOutSkip,@Spell syn region cCppOutSkip contained start="^\s*\%(%:\|#\)\s*\%(if\>\|ifdef\>\|ifndef\>\)" skip="\\$" end="^\s*\%(%:\|#\)\s*endif\>" contains=cSpaceError,cCppOutSkip syn region cCppInSkip contained matchgroup=cCppInWrapper start="^\s*\%(%:\|#\)\s*\%(if\s\+\%(\d\+\s*\%($\|//\|/\*\||\|&\)\)\@!\|ifdef\>\|ifndef\>\)" skip="\\$" end="^\s*\%(%:\|#\)\s*endif\>" containedin=cCppOutElse,cCppInIf,cCppInSkip contains=TOP,cPreProc endif syn region cIncluded display contained start=+"+ skip=+\\\\\|\\"+ end=+"+ syn match cIncluded display contained "<[^>]*>" syn match cInclude display "^\s*\zs\%(%:\|#\)\s*include\>\s*["<]" contains=cIncluded +if !exists("c_no_c23") && !s:in_cpp_family + syn region cInclude start="^\s*\zs\%(%:\|#\)\s*embed\>" skip="\\$" end="$" keepend contains=cEmbed,cComment,cCommentL,cCppString,cCharacter,cCppParen,cParenError,cNumbers,cCommentError,cSpaceError + syn match cEmbed contained "\%(%:\|#\)\s*embed\>" nextgroup=cIncluded skipwhite transparent + syn cluster cPreProcGroup add=cEmbed +endif "syn match cLineSkip "\\$" -syn cluster cPreProcGroup contains=cPreCondit,cIncluded,cInclude,cDefine,cErrInParen,cErrInBracket,cUserLabel,cSpecial,cOctalZero,cCppOutWrapper,cCppInWrapper,@cCppOutInGroup,cFormat,cNumber,cFloat,cOctal,cOctalError,cNumbersCom,cString,cCommentSkip,cCommentString,cComment2String,@cCommentGroup,cCommentStartError,cParen,cBracket,cMulti,cBadBlock syn region cDefine start="^\s*\zs\%(%:\|#\)\s*\%(define\|undef\)\>" skip="\\$" end="$" keepend contains=ALLBUT,@cPreProcGroup,@Spell syn region cPreProc start="^\s*\zs\%(%:\|#\)\s*\%(pragma\>\|line\>\|warning\>\|warn\>\|error\>\)" skip="\\$" end="$" keepend contains=ALLBUT,@cPreProcGroup,@Spell diff --git a/runtime/syntax/checkhealth.vim b/runtime/syntax/checkhealth.vim index a4f6e016cb..14c80640ba 100644 --- a/runtime/syntax/checkhealth.vim +++ b/runtime/syntax/checkhealth.vim @@ -1,6 +1,5 @@ " Vim syntax file " Language: Nvim :checkhealth buffer -" Last Change: 2022 Nov 10 if exists("b:current_syntax") finish diff --git a/runtime/syntax/chordpro.vim b/runtime/syntax/chordpro.vim index 41a0a1e9d1..02c34b8466 100644 --- a/runtime/syntax/chordpro.vim +++ b/runtime/syntax/chordpro.vim @@ -2,6 +2,7 @@ " Language: ChordPro 6 (https://www.chordpro.org) " Maintainer: Niels Bo Andersen <niels@niboan.dk> " Last Change: 2022-04-15 +" 2024 Dec 31: add "keys" as syntax keyword (via: https://groups.google.com/g/vim_dev/c/vP4epus0euM/m/mNoDY6hsCQAJ) " Quit when a syntax file was already loaded if exists("b:current_syntax") @@ -104,7 +105,7 @@ syn match chordproStandardMetadata /instrument\.description/ contained syn match chordproStandardMetadata /user\.name/ contained syn match chordproStandardMetadata /user\.fullname/ contained -syn keyword chordproDefineKeyword contained frets fingers +syn keyword chordproDefineKeyword contained frets fingers keys syn match chordproDefineKeyword /base-fret/ contained syn match chordproArgumentsNumber /\d\+/ contained diff --git a/runtime/syntax/cmacro.vim b/runtime/syntax/cmacro.vim new file mode 100644 index 0000000000..1d448f0d1b --- /dev/null +++ b/runtime/syntax/cmacro.vim @@ -0,0 +1,77 @@ +" Vim syntax file +" Language: C macro for C preprocessor +" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu> +" Last Change: 2024 Dec 31 +" modified from syntax/c.vim + +" C compiler has a preprocessor: `cpp -P test.txt` +" test.txt doesn't need to be a C file +if exists("b:current_syntax") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +" Accept %: for # (C99) +syn region cmacroPreCondit start="^\s*\zs\%(%:\|#\)\s*\%(if\|ifdef\|ifndef\|elif\)\>" skip="\\$" end="$" keepend contains=cmacroCppParen,cmacroNumbers +syn match cmacroPreConditMatch display "^\s*\zs\%(%:\|#\)\s*\%(else\|endif\)\>" +if !exists("c_no_if0") + syn cluster cmacroCppOutInGroup contains=cmacroCppInIf,cmacroCppInElse,cmacroCppInElse2,cmacroCppOutIf,cmacroCppOutIf2,cmacroCppOutElse,cmacroCppInSkip,cmacroCppOutSkip + syn region cmacroCppOutWrapper start="^\s*\zs\%(%:\|#\)\s*if\s\+0\+\s*\%($\|//\|/\*\|&\)" end=".\@=\|$" contains=cmacroCppOutIf,cmacroCppOutElse,@NoSpell fold + syn region cmacroCppOutIf contained start="0\+" matchgroup=cmacroCppOutWrapper end="^\s*\%(%:\|#\)\s*endif\>" contains=cmacroCppOutIf2,cmacroCppOutElse + if !exists("c_no_if0_fold") + syn region cmacroCppOutIf2 contained matchgroup=cmacroCppOutWrapper start="0\+" end="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0\+\s*\%($\|//\|/\*\|&\)\)\@!\|endif\>\)"me=s-1 contains=cmacroCppOutSkip,@Spell fold + else + syn region cmacroCppOutIf2 contained matchgroup=cmacroCppOutWrapper start="0\+" end="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0\+\s*\%($\|//\|/\*\|&\)\)\@!\|endif\>\)"me=s-1 contains=cmacroCppOutSkip,@Spell + endif + syn region cmacroCppOutElse contained matchgroup=cmacroCppOutWrapper start="^\s*\%(%:\|#\)\s*\%(else\|elif\)" end="^\s*\%(%:\|#\)\s*endif\>"me=s-1 contains=TOP,cmacroPreCondit + syn region cmacroCppInWrapper start="^\s*\zs\%(%:\|#\)\s*if\s\+0*[1-9]\d*\s*\%($\|//\|/\*\||\)" end=".\@=\|$" contains=cmacroCppInIf,cmacroCppInElse fold + syn region cmacroCppInIf contained matchgroup=cmacroCppInWrapper start="\d\+" end="^\s*\%(%:\|#\)\s*endif\>" contains=TOP,cmacroPreCondit + if !exists("c_no_if0_fold") + syn region cmacroCppInElse contained start="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0*[1-9]\d*\s*\%($\|//\|/\*\||\)\)\@!\)" end=".\@=\|$" containedin=cmacroCppInIf contains=cmacroCppInElse2 fold + else + syn region cmacroCppInElse contained start="^\s*\%(%:\|#\)\s*\%(else\>\|elif\s\+\%(0*[1-9]\d*\s*\%($\|//\|/\*\||\)\)\@!\)" end=".\@=\|$" containedin=cmacroCppInIf contains=cmacroCppInElse2 + endif + syn region cmacroCppInElse2 contained matchgroup=cmacroCppInWrapper start="^\s*\%(%:\|#\)\s*\%(else\|elif\)\%([^/]\|/[^/*]\)*" end="^\s*\%(%:\|#\)\s*endif\>"me=s-1 contains=cmacroCppOutSkip,@Spell + syn region cmacroCppOutSkip contained start="^\s*\%(%:\|#\)\s*\%(if\>\|ifdef\>\|ifndef\>\)" skip="\\$" end="^\s*\%(%:\|#\)\s*endif\>" contains=cmacroCppOutSkip + syn region cmacroCppInSkip contained matchgroup=cmacroCppInWrapper start="^\s*\%(%:\|#\)\s*\%(if\s\+\%(\d\+\s*\%($\|//\|/\*\||\|&\)\)\@!\|ifdef\>\|ifndef\>\)" skip="\\$" end="^\s*\%(%:\|#\)\s*endif\>" containedin=cmacroCppOutElse,cmacroCppInIf,cmacroCppInSkip contains=TOP,cmacroPreProc +endif +syn region cmacroIncluded display contained start=+"+ skip=+\\\\\|\\"+ end=+"+ +syn match cmacroIncluded display contained "<[^>]*>" +syn match cmacroInclude display "^\s*\zs\%(%:\|#\)\s*include\>\s*["<]" contains=cmacroIncluded +"syn match cmacroLineSkip "\\$" +syn cluster cmacroPreProcmacroGroup contains=cmacroPreCondit,cmacroIncluded,cmacroInclude,cmacroDefine,cmacroCppOutWrapper,cmacroCppInWrapper,@cmacroCppOutInGroup,cmacroNumbersCom,@cmacroCommentGroup,cmacroParen,cmacroBracket,cmacroMulti,cmacroBadBlock +syn region cmacroDefine start="^\s*\zs\%(%:\|#\)\s*\%(define\|undef\)\>" skip="\\$" end="$" keepend contains=ALLBUT,@cmacroPreProcmacroGroup,@Spell +syn region cmacroPreProc start="^\s*\zs\%(%:\|#\)\s*\%(pragma\>\|line\>\|warning\>\|warn\>\|error\>\)" skip="\\$" end="$" keepend contains=ALLBUT,@cmacroPreProcmacroGroup,@Spell + +" be able to fold #pragma regions +syn region cmacroPragma start="^\s*#pragma\s\+region\>" end="^\s*#pragma\s\+endregion\>" transparent keepend extend fold + +syn keyword cmacroTodo contained TODO FIXME XXX NOTE +syn region cmacroComment start='/\*' end='\*/' contains=cmacroTodo,@Spell +syn match cmacroCommentError "\*/" +syn region cmacroComment start='//' end='$' contains=cmacroTodo,@Spell + +" Define the default highlighting. +" Only used when an item doesn't have highlighting yet +hi def link cmacroInclude Include +hi def link cmacroPreProc PreProc +hi def link cmacroDefine Macro +hi def link cmacroIncluded cmacroString +hi def link cmacroCppInWrapper cmacroCppOutWrapper +hi def link cmacroCppOutWrapper cmacroPreCondit +hi def link cmacroPreConditMatch cmacroPreCondit +hi def link cmacroPreCondit PreCondit +hi def link cmacroCppOutSkip cmacroCppOutIf2 +hi def link cmacroCppInElse2 cmacroCppOutIf2 +hi def link cmacroCppOutIf2 cmacroCppOut +hi def link cmacroCppOut Comment +hi def link cmacroTodo Todo +hi def link cmacroComment Comment +hi def link cmacroCommentError Error + +let b:current_syntax = "cmacro" + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/syntax/dircolors.vim b/runtime/syntax/dircolors.vim index 74a7068488..d968ed8fdd 100644 --- a/runtime/syntax/dircolors.vim +++ b/runtime/syntax/dircolors.vim @@ -85,6 +85,9 @@ endfunction function! s:get_hi_str(color, place) abort if a:color >= 0 && a:color <= 255 if has('gui_running') || &termguicolors + if ! exists("s:termguicolors") + call s:set_guicolors() + endif return ' gui' . a:place . '=' . s:termguicolors[a:color] elseif a:color <= 7 || &t_Co == 256 || &t_Co == 88 return ' cterm' . a:place . '=' . a:color diff --git a/runtime/syntax/dockerfile.vim b/runtime/syntax/dockerfile.vim index 6ec71fcdb6..f1d612f4ad 100644 --- a/runtime/syntax/dockerfile.vim +++ b/runtime/syntax/dockerfile.vim @@ -1,6 +1,6 @@ " dockerfile.vim - Syntax highlighting for Dockerfiles " Maintainer: Honza Pokorny <https://honza.ca> -" Last Change: 2024 Jul 03 +" Last Change: 2024 Dec 20 " License: BSD " https://docs.docker.com/engine/reference/builder/ @@ -35,7 +35,6 @@ syntax region dockerfileShell contained keepend start=/\v/ skip=/\v\\\_./ end=/ syntax region dockerfileValue contained keepend start=/\v/ skip=/\v\\\_./ end=/\v$/ contains=dockerfileString syntax region dockerfileComment start=/\v^\s*#/ end=/\v$/ contains=@Spell -set commentstring=#\ %s hi def link dockerfileString String hi def link dockerfileKeyword Keyword diff --git a/runtime/syntax/gel.vim b/runtime/syntax/gel.vim new file mode 100644 index 0000000000..5f3800273c --- /dev/null +++ b/runtime/syntax/gel.vim @@ -0,0 +1,19 @@ +" Vim syntax file +" Language: TI Code Composer Studio General Extension Language +" Document: https://downloads.ti.com/ccs/esd/documents/users_guide/ccs_debug-gel.html +" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu> +" Last Change: 2024 Dec 25 + +if exists("b:current_syntax") + finish +endif + +runtime! syntax/cpp.vim + +syn keyword gelStatement StartUp GEL_AddInputFile GEL_AddOutputFile GEL_AdvancedReset GEL_AsmStepInto GEL_AsmStepOver GEL_BreakPtAdd GEL_BreakPtDel GEL_BreakPtDisable GEL_BreakPtReset GEL_CancelTimer GEL_Connect GEL_Dialog GEL_DisableFileOutput GEL_DisableRealtime GEL_Disconnect GEL_EnableClock GEL_EnableFileOutput GEL_EnableRealtime GEL_EnableZeroFill GEL_EvalOnTarget GEL_GetBoolDebugProperty GEL_GetBoolDriverProperty GEL_GetBoolTargetDbProperty GEL_GetNumericDebugProperty GEL_GetNumericDriverProperty GEL_GetNumericTargetDbProperty GEL_GetStringDebugProperty GEL_GetStringDriverProperty GEL_GetStringTargetDbProperty GEL_Go GEL_Halt GEL_HandleTargetError GEL_HWBreakPtAdd GEL_HWBreakPtDel GEL_HWBreakPtDisable GEL_HWBreakPtReset GEL_IsConnected GEL_IsHalted GEL_IsInRealtimeMode GEL_IsResetSupported GEL_IsTimerSet GEL_Load GEL_LoadBin GEL_LoadGel GEL_LoadProgramOnly GEL_MapAdd GEL_MapAddStr GEL_MapDelete GEL_MapOff GEL_MapOn GEL_MapReset GEL_MatchesConnection GEL_MemoryFill GEL_MemoryListSupportedTypes GEL_MemoryLoad GEL_MemoryLoadData GEL_MemorySave GEL_MemorySaveBin GEL_MemorySaveCoff GEL_MemorySaveData GEL_MemorySaveHex GEL_PatchAssembly GEL_ProbePtAdd GEL_ProbePtDel GEL_ProbePtDisable GEL_ProbePtReset GEL_ReConnect GEL_RefreshWindows GEL_Reload GEL_RemoveDebugState GEL_RemoveInputFile GEL_RemoveOutputFile GEL_Reset GEL_Restart GEL_RestoreDebugState GEL_Run GEL_RunF GEL_SetBlockResetMode GEL_SetBoolDebugProperty GEL_SetClockEvent GEL_SetNumericDebugProperty GEL_SetSemihostingMainArgs GEL_SetStringDebugProperty GEL_SetTimer GEL_SetWaitInResetMode GEL_SrcStepInto GEL_SrcStepOver GEL_StepInto GEL_StepOut GEL_StepOver GEL_StrCat GEL_StrLen GEL_SubStr GEL_SymbolAdd GEL_SymbolAddOffset GEL_SymbolAddRel GEL_SymbolDisable GEL_SymbolEnable GEL_SymbolHideSection GEL_SymbolLoad GEL_SymbolLoadOffset GEL_SymbolLoadRel GEL_SymbolRemove GEL_SymbolShowSection GEL_SyncHalt GEL_SyncRun GEL_SyncStepInto GEL_SyncStepOut GEL_SyncStepOver GEL_System GEL_TargetTextOut GEL_TextOut GEL_Trace GEL_UnloadAllGels GEL_UnloadAllSymbols GEL_UnloadGel GEL_VerifyBinProgram GEL_VerifyProgram OnChildRunning OnFileLoaded OnHalt OnPreFileLoaded OnPreReset OnPreTargetConnect OnReset OnResetDetected OnRestart OnTargetConnect +syn keyword gelModifier hotmenu menuitem + +hi def link gelStatement Statement +hi def link gelModifier Type + +let b:current_syntax = "gel" diff --git a/runtime/syntax/graphql.vim b/runtime/syntax/graphql.vim new file mode 100644 index 0000000000..01d5ca25ff --- /dev/null +++ b/runtime/syntax/graphql.vim @@ -0,0 +1,90 @@ +" Vim syntax file +" Language: graphql +" Maintainer: Jon Parise <jon@indelible.org> +" Filenames: *.graphql *.graphqls *.gql +" URL: https://github.com/jparise/vim-graphql +" License: MIT <https://opensource.org/license/mit> +" Last Change: 2024 Dec 21 + +if !exists('main_syntax') + if exists('b:current_syntax') + finish + endif + let main_syntax = 'graphql' +endif + +syn case match + +syn match graphqlComment "#.*$" contains=@Spell + +syn match graphqlOperator "=" display +syn match graphqlOperator "!" display +syn match graphqlOperator "|" display +syn match graphqlOperator "&" display +syn match graphqlOperator "\M..." display + +syn keyword graphqlBoolean true false +syn keyword graphqlNull null +syn match graphqlNumber "-\=\<\%(0\|[1-9]\d*\)\%(\.\d\+\)\=\%([eE][-+]\=\d\+\)\=\>" display +syn region graphqlString start=+"+ skip=+\\\\\|\\"+ end=+"\|$+ +syn region graphqlString start=+"""+ skip=+\\"""+ end=+"""+ + +syn keyword graphqlKeyword repeatable nextgroup=graphqlKeyword skipwhite +syn keyword graphqlKeyword on nextgroup=graphqlType,graphqlDirectiveLocation skipwhite + +syn keyword graphqlStructure enum scalar type union nextgroup=graphqlType skipwhite +syn keyword graphqlStructure input interface subscription nextgroup=graphqlType skipwhite +syn keyword graphqlStructure implements nextgroup=graphqlType skipwhite +syn keyword graphqlStructure query mutation fragment nextgroup=graphqlName skipwhite +syn keyword graphqlStructure directive nextgroup=graphqlDirective skipwhite +syn keyword graphqlStructure extend nextgroup=graphqlStructure skipwhite +syn keyword graphqlStructure schema nextgroup=graphqlFold skipwhite + +syn match graphqlDirective "\<@\h\w*\>" display +syn match graphqlVariable "\<\$\h\w*\>" display +syn match graphqlName "\<\h\w*\>" display +syn match graphqlType "\<_*\u\w*\>" display + +" https://spec.graphql.org/October2021/#ExecutableDirectiveLocation +syn keyword graphqlDirectiveLocation QUERY MUTATION SUBSCRIPTION FIELD +syn keyword graphqlDirectiveLocation FRAGMENT_DEFINITION FRAGMENT_SPREAD +syn keyword graphqlDirectiveLocation INLINE_FRAGMENT VARIABLE_DEFINITION +" https://spec.graphql.org/October2021/#TypeSystemDirectiveLocation +syn keyword graphqlDirectiveLocation SCHEMA SCALAR OBJECT FIELD_DEFINITION +syn keyword graphqlDirectiveLocation ARGUMENT_DEFINITION INTERFACE UNION +syn keyword graphqlDirectiveLocation ENUM ENUM_VALUE INPUT_OBJECT +syn keyword graphqlDirectiveLocation INPUT_FIELD_DEFINITION + +syn keyword graphqlMetaFields __schema __type __typename + +syn region graphqlFold matchgroup=graphqlBraces start="{" end="}" transparent fold contains=ALLBUT,graphqlStructure +syn region graphqlList matchgroup=graphqlBraces start="\[" end="]" transparent contains=ALLBUT,graphqlDirective,graphqlStructure + +if main_syntax ==# 'graphql' + syn sync minlines=500 +endif + +hi def link graphqlComment Comment +hi def link graphqlOperator Operator + +hi def link graphqlBraces Delimiter + +hi def link graphqlBoolean Boolean +hi def link graphqlNull Keyword +hi def link graphqlNumber Number +hi def link graphqlString String + +hi def link graphqlDirective PreProc +hi def link graphqlDirectiveLocation Special +hi def link graphqlName Identifier +hi def link graphqlMetaFields Special +hi def link graphqlKeyword Keyword +hi def link graphqlStructure Structure +hi def link graphqlType Type +hi def link graphqlVariable Identifier + +let b:current_syntax = 'graphql' + +if main_syntax ==# 'graphql' + unlet main_syntax +endif diff --git a/runtime/syntax/hyprlang.vim b/runtime/syntax/hyprlang.vim new file mode 100644 index 0000000000..cde504d9ca --- /dev/null +++ b/runtime/syntax/hyprlang.vim @@ -0,0 +1,59 @@ +" Vim syntax file +" Language: hyprlang +" Maintainer: Luca Saccarola <github.e41mv@aleeas.com> +" Last Change: 2025 Jan 29 + +if exists("b:current_syntax") + finish +endif +let b:current_syntax = "hyprlang" + +syn case ignore + +syn match hyprCommand '^\s*\zs\S\+\ze\s*=' contains=hyprVariable +syn match hyprValue '=\s*\zs.\+\ze$' contains=hyprNumber,hyprFloat,hyprBoolean,hyprString,hyprColor,hyprModifier,hyprVariable,hyprComment + +syn match hyprVariable '\$\w\+' contained + +" Category +syn region hyprCategory matchgroup=hyprCategoryD start='^\s*\k\+\s*{' end='^\s*}' contains=hyprCommand,hyprValue,hyprComment,hyprCategory,hyprCategoryD + +" Variables Types +syn match hyprNumber '\%[-+]\<\d\+\>\%[%]' contained +syn match hyprFloat '\%[-+]\<\d\+\.\d\+\>\%[%]' contained +syn match hyprString "'[^']*'" contained +syn match hyprString '"[^"]*"' contained +syn match hyprColor 'rgb(\(\w\|\d\)\{6})' contained +syn match hyprColor 'rgba(\(\w\|\d\)\{8})' contained +syn match hyprColor '0x\(\w\|\d\)\{8}' contained +syn keyword hyprBoolean true false yes no on off contained + +" Super Shift Alt Ctrl Control +syn keyword hyprModifier contained + \ super supershift superalt superctrl supercontrol + \ super_shift super_alt super_ctrl super_control + \ shift shiftsuper shiftalt shiftctrl shiftcontrol + \ shift_super shift_alt shift_ctrl shift_control + \ alt altsuper altshift altctrl altcontrol + \ alt_super alt_shift alt_ctrl alt_control + \ ctrl ctrlsuper ctrlshift ctrlalt ctrlcontrol + \ ctrl_super ctrl_shift ctrl_alt ctrl_control + \ control controlsuper controlshift controlalt controlctrl + \ control_super control_shift control_alt control_ctrl + +" Comments +syn match hyprComment '#.*$' + +" Link to default groups +hi def link hyprVariable Identifier +hi def link hyprCategoryD Special +hi def link hyprComment Comment +hi def link hyprNumber Constant +hi def link hyprModifier Constant +hi def link hyprFloat hyprNumber +hi def link hyprBoolean Boolean +hi def link hyprString String +hi def link hyprColor Structure +hi def link hyprCommand Keyword + +" vim: ts=8 sts=2 sw=2 et diff --git a/runtime/syntax/java.vim b/runtime/syntax/java.vim index b3e17b55f6..9b38ccd4dc 100644 --- a/runtime/syntax/java.vim +++ b/runtime/syntax/java.vim @@ -3,7 +3,7 @@ " Maintainer: Aliaksei Budavei <0x000c70 AT gmail DOT com> " Former Maintainer: Claudio Fleiner <claudio@fleiner.com> " Repository: https://github.com/zzzyxwvut/java-vim.git -" Last Change: 2024 Oct 10 +" Last Change: 2025 Jan 02 " Please check ":help java.vim" for comments on some of the options " available. @@ -391,18 +391,32 @@ if !exists("g:java_ignore_javadoc") && (s:with_html || s:with_markdown) && g:mai if s:with_markdown try syntax include @javaMarkdown syntax/markdown.vim - let s:ff.WithMarkdown = s:ff.LeftConstant + + try + syn clear markdownId markdownLineStart markdownH1 markdownH2 markdownHeadingRule markdownRule markdownCode markdownCodeBlock markdownIdDeclaration + let s:ff.WithMarkdown = s:ff.LeftConstant + catch /\<E28:/ + call s:ReportOnce(v:exception) + let s:no_support = 1 + unlet! g:java_ignore_markdown + let g:java_ignore_markdown = 28 + endtry catch /\<E48[45]:/ call s:ReportOnce(v:exception) - unlockvar s:with_markdown - let s:with_markdown = 0 - lockvar s:with_markdown - hi clear markdownCode - hi clear markdownCodeBlock - hi clear markdownCodeDelimiter - hi clear markdownLinkDelimiter + let s:no_support = 1 finally unlet! b:current_syntax + + if exists("s:no_support") + unlet s:no_support + unlockvar s:with_markdown + let s:with_markdown = 0 + lockvar s:with_markdown + hi clear markdownCode + hi clear markdownCodeBlock + hi clear markdownCodeDelimiter + hi clear markdownLinkDelimiter + endif endtry endif @@ -422,7 +436,6 @@ if !exists("g:java_ignore_javadoc") && (s:with_html || s:with_markdown) && g:mai exec 'syn region javaMarkdownCommentTitle contained matchgroup=javaMarkdownComment start="\%(///.*\r\=\n\s*\)\@' . s:ff.Peek('80', '') . '<!///\s*\%({@return\>\)\@=" matchgroup=javaMarkdownCommentTitle end="}\%(\s*\.*\)*" contains=javaMarkdownShortcutLink,@javaMarkdown,javaMarkdownCommentMask,javaTodo,@Spell,@javaDocTags,javaTitleSkipBlock' exec 'syn region javaMarkdownCommentTitle contained matchgroup=javaMarkdownComment start="\%(///.*\r\=\n\s*\)\@' . s:ff.Peek('80', '') . '<!///\s*\%({@summary\>\)\@=" matchgroup=javaMarkdownCommentTitle end="}" contains=javaMarkdownShortcutLink,@javaMarkdown,javaMarkdownCommentMask,javaTodo,@Spell,@javaDocTags,javaTitleSkipBlock' - syn clear markdownId markdownLineStart markdownH1 markdownH2 markdownHeadingRule markdownRule markdownCode markdownCodeBlock markdownIdDeclaration " REDEFINE THE MARKDOWN ITEMS ANCHORED WITH "^", OBSERVING THE " DEFINITION ORDER. syn match markdownLineStart contained "^\s*///\s*[<@]\@!" contains=@markdownBlock,javaMarkdownCommentTitle,javaMarkdownCommentMask nextgroup=@markdownBlock,htmlSpecialChar diff --git a/runtime/syntax/jj.vim b/runtime/syntax/jjdescription.vim index a2911a0268..04848bcb3b 100644 --- a/runtime/syntax/jj.vim +++ b/runtime/syntax/jjdescription.vim @@ -6,7 +6,6 @@ if exists('b:current_syntax') finish endif -let b:current_syntax = 'jj' syn match jjAdded "A .*" contained syn match jjRemoved "D .*" contained @@ -14,7 +13,12 @@ syn match jjChanged "M .*" contained syn region jjComment start="^JJ: " end="$" contains=jjAdded,jjRemoved,jjChanged +syn include @jjCommitDiff syntax/diff.vim +syn region jjCommitDiff start=/\%(^diff --\%(git\|cc\|combined\) \)\@=/ end=/^\%(diff --\|$\|@@\@!\|[^[:alnum:]\ +-]\S\@!\)\@=/ fold contains=@jjCommitDiff + hi def link jjComment Comment hi def link jjAdded Added hi def link jjRemoved Removed hi def link jjChanged Changed + +let b:current_syntax = 'jjdescription' diff --git a/runtime/syntax/just.vim b/runtime/syntax/just.vim new file mode 100644 index 0000000000..79c81d0f9c --- /dev/null +++ b/runtime/syntax/just.vim @@ -0,0 +1,406 @@ +" Vim syntax file +" Language: Justfile +" Maintainer: Peter Benjamin <@pbnj> +" Last Change: 2025 Jan 25 +" Credits: The original author, Noah Bogart <https://github.com/NoahTheDuke/vim-just/> + +if exists('b:current_syntax') + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +let b:current_syntax = 'just' + +" syncing fromstart prevents mismatched highlighting when jumping around in a justfile +" linebreaks= keeps multi-line constructs highlighted correctly while typing +syn sync fromstart linebreaks=10 + +" a-zA-Z0-9_- +syn iskeyword @,48-57,_,- + +syn match justComment "#.*$" contains=@Spell,justCommentTodo +syn match justCommentInBody '#.*$' contained contains=justCommentTodo,justInterpolation,@justOtherCurlyBraces +syn keyword justCommentTodo TODO FIXME XXX contained +syn match justShebang "^\s*#!.*$" contains=justInterpolation,@justOtherCurlyBraces +syn match justName "\h\k*" contained +syn match justFunction "\h\k*" contained + +syn match justPreBodyComment "\v%(\s|\\\n)*%([^\\]\n)@3<!#%([^!].*)?\n%(\t+| +)@=" transparent contained contains=justComment + \ nextgroup=@justBodies skipnl + +syn region justBacktick start=/`/ end=/`/ +syn region justBacktick start=/```/ end=/```/ +syn region justRawString start=/'/ end=/'/ +syn region justRawString start=/'''/ end=/'''/ +syn region justString start=/"/ skip=/\\\\\|\\"/ end=/"/ contains=justStringEscapeSequence,justStringUEscapeSequence,justStringEscapeError +syn region justString start=/"""/ skip=/\\\\\|\\"/ end=/"""/ contains=justStringEscapeSequence,justStringUEscapeSequence,justStringEscapeError + +syn region justShellExpandRawString start=/\v\k@1<!x'/ end=/'/ + \ contains=justShellExpandVarRaw,justDollarEscape +syn region justShellExpandRawString start=/\v\k@1<!x'''/ end=/'''/ + \ contains=justShellExpandVarRaw,justDollarEscape +syn region justShellExpandString + \ start=/\v\k@1<!x"/ skip=/\\\\\|\\"/ end=/"/ + \ contains=justStringEscapeSequence,justStringUEscapeSequence,justStringEscapeError,justShellExpandVar,justDollarEscape,justDollarEscapeSplit +syn region justShellExpandString + \ start=/\v\k@1<!x"""/ skip=/\\\\\|\\"/ end=/"""/ + \ contains=justStringEscapeSequence,justStringUEscapeSequence,justStringEscapeError,justShellExpandVar,justDollarEscape,justDollarEscapeSplit + +syn cluster justStringLiterals + \ contains=justRawString,justString,justShellExpandRawString,justShellExpandString +syn cluster justAllStrings contains=justBacktick,@justStringLiterals + +syn match justRegexReplacement + \ /\v,%(\_s|\\\n)*%('\_[^']*'|'''%(\_.%(''')@!)*\_.?''')%(\_s|\\\n)*%(,%(\_s|\\\n)*)?\)/me=e-1 + \ transparent contained contains=@justExpr,@justStringsWithRegexCapture +syn match justRegexReplacement + \ /\v,%(\_s|\\\n)*%("%(\_[^"]|\\")*"|"""%(\_.%(""")@!)*\_.?""")%(\_s|\\\n)*%(,%(\_s|\\\n)*)?\)/me=e-1 + \ transparent contained contains=@justExpr,@justStringsWithRegexCapture + +syn region justRawStrRegexRepl start=/\v'/ end=/'/ contained contains=justRegexCapture,justDollarEscape +syn region justRawStrRegexRepl start=/\v'''/ end=/'''/ contained contains=justRegexCapture,justDollarEscape +syn region justStringRegexRepl start=/\v"/ skip=/\\\\\|\\"/ end=/"/ contained contains=justStringEscapeSequence,justStringUEscapeSequence,justStringEscapeError,justRegexCapture,justDollarEscape,justDollarEscapeSplit +syn region justStringRegexRepl start=/\v"""/ skip=/\\\\\|\\"/ end=/"""/ contained contains=justStringEscapeSequence,justStringUEscapeSequence,justStringEscapeError,justRegexCapture,justDollarEscape,justDollarEscapeSplit +syn match justRegexCapture '\v\$%(\w+|\{\w+\})' contained +syn cluster justStringsWithRegexCapture contains=justRawStrRegexRepl,justStringRegexRepl + +syn cluster justRawStrings contains=justRawString,justRawStrRegexRepl + +syn region justStringInsideBody start=/\v\\@1<!'/ end=/'/ contained contains=justInterpolation,@justOtherCurlyBraces,justIndentError +syn region justStringInsideBody start=/\v\\@1<!"/ skip=/\v\\@1<!\\"/ end=/"/ contained contains=justInterpolation,@justOtherCurlyBraces,justIndentError +syn region justStringInShebangBody start=/\v\\@1<!'/ end=/'/ contained contains=justInterpolation,@justOtherCurlyBraces,justShebangIndentError +syn region justStringInShebangBody start=/\v\\@1<!"/ skip=/\v\\@1<!\\"/ end=/"/ contained contains=justInterpolation,@justOtherCurlyBraces,justShebangIndentError + +syn match justStringEscapeError '\\.' contained +syn match justStringEscapeSequence '\v\\[tnr"\\]' contained +syn match justStringUEscapeSequence '\v\\u\{[0-9A-Fa-f]{1,6}\}' contained + +syn match justAssignmentOperator "\V:=" contained + +syn region justExprParen start='\V(' end='\V)' transparent contains=@justExpr +syn region justExprParenInInterp start='\V(' end='\V)' transparent contained contains=@justExprInInterp + +syn match justRecipeAt "^@" contained +syn match justRecipeColon ":" contained + +syn region justRecipeAttributes + \ matchgroup=justRecipeAttr start='\v^%(\\\n)@3<!\[' end='\V]' + \ contains=justRecipeAttr,justRecipeAttrSep,justRecipeAttrArgs,justRecipeAttrArgError,justRecipeAttrValueShort + +syn keyword justRecipeAttr + \ confirm doc extension group linux macos no-cd no-exit-message no-quiet openbsd positional-arguments private script unix windows working-directory + \ contained +syn match justRecipeAttrSep ',' contained +syn match justRecipeAttrValueShort '\v:%(\_s|\\\n)*' transparent contained + \ contains=justRecipeAttrValueColon nextgroup=@justStringLiterals,justInvalidAttrValue +syn match justRecipeAttrValueColon '\V:' contained +syn region justRecipeAttrArgs matchgroup=justRecipeAttr start='\V(' end='\V)' contained + \ contains=@justStringLiterals +syn match justRecipeAttrArgError '\v\(%(\s|\\?\n)*\)' contained + +syn match justInvalidAttrValue '\v[^"',]["']@![^,\]]*' contained + +syn match justRecipeDeclSimple "\v^\@?\h\k*%(%(\s|\\\n)*:\=@!)@=" + \ transparent contains=justRecipeName + \ nextgroup=justRecipeNoDeps,justRecipeDeps + +syn region justRecipeDeclComplex start="\v^\@?\h\k*%(\s|\\\n)+%([+*$]+%(\s|\\\n)*)*\h" end="\v%(:\=@!)@=|$" + \ transparent + \ contains=justRecipeName,justParameter + \ nextgroup=justRecipeNoDeps,justRecipeDeps + +syn match justRecipeName "\v^\@?\h\k*" transparent contained contains=justRecipeAt,justFunction + +syn match justParameter "\v%(\s|\\\n)@3<=%(%([*+]%(\s|\\\n)*)?%(\$%(\s|\\\n)*)?|\$%(\s|\\\n)*[*+]%(\s|\\\n)*)\h\k*" + \ transparent contained + \ contains=justName,justVariadicPrefix,justParamExport,justVariadicPrefixError + \ nextgroup=justPreParamValue + +syn match justPreParamValue '\v%(\s|\\\n)*\=%(\s|\\\n)*' + \ contained transparent + \ contains=justParameterOperator + \ nextgroup=justParamValue + +syn region justParamValue contained transparent + \ start="\v\S" + \ skip="\\\n" + \ end="\v%(\s|^)%([*+$:]|\h)@=|:@=|$" + \ contains=@justAllStrings,justRecipeParenDefault,@justExprFunc + \ nextgroup=justParameterError +syn match justParameterOperator "\V=" contained + +syn match justVariadicPrefix "\v%(\s|\\\n)@3<=[*+]%(%(\s|\\\n)*\$?%(\s|\\\n)*\h)@=" contained +syn match justParamExport '\V$' contained +syn match justVariadicPrefixError "\v\$%(\s|\\\n)*[*+]" contained + +syn match justParameterError "\v%(%([+*$]+%(\s|\\\n)*)*\h\k*)@>%(%(\s|\\\n)*\=)@!" contained + +syn region justRecipeParenDefault + \ matchgroup=justRecipeDepParamsParen start='\v%(\=%(\s|\\\n)*)@<=\(' end='\V)' + \ contained + \ contains=@justExpr +syn match justRecipeSubsequentDeps '\V&&' contained + +syn match justRecipeNoDeps '\v:%(\s|\\\n)*\n|:#@=|:%(\s|\\\n)+#@=' + \ transparent contained + \ contains=justRecipeColon + \ nextgroup=justPreBodyComment,@justBodies +syn region justRecipeDeps start="\v:%(\s|\\\n)*%([a-zA-Z_(]|\&\&)" skip='\\\n' end="\v#@=|\\@1<!\n" + \ transparent contained + \ contains=justFunction,justRecipeColon,justRecipeSubsequentDeps,justRecipeParamDep + \ nextgroup=justPreBodyComment,@justBodies + +syn region justRecipeParamDep contained transparent + \ matchgroup=justRecipeDepParamsParen + \ start="\V(" + \ end="\V)" + \ contains=justRecipeDepParenName,@justExpr + +syn keyword justBoolean true false contained + +syn match justAssignment "\v^\h\k*%(\s|\\\n)*:\=" transparent contains=justAssignmentOperator + +syn match justSet '\v^set' contained +syn keyword justSetKeywords + \ allow-duplicate-recipes allow-duplicate-variables dotenv-load dotenv-filename dotenv-path dotenv-required export fallback ignore-comments positional-arguments quiet script-interpreter shell tempdir unstable windows-shell working-directory + \ contained +syn keyword justSetDeprecatedKeywords windows-powershell contained +syn match justBooleanSet "\v^set%(\s|\\\n)+%(allow-duplicate-%(recip|variabl)es|dotenv-%(loa|require)d|export|fallback|ignore-comments|positional-arguments|quiet|unstable|windows-powershell)%(%(\s|\\\n)*:\=%(\s|\\\n)*%(true|false))?%(\s|\\\n)*%($|#@=)" + \ contains=justSet,justSetKeywords,justSetDeprecatedKeywords,justAssignmentOperator,justBoolean + \ transparent + +syn match justStringSet '\v^set%(\s|\\\n)+\k+%(\s|\\\n)*:\=%(\s|\\\n)*%(x?['"])@=' transparent contains=justSet,justSetKeywords,justAssignmentOperator + +syn match justShellSet + \ "\v^set%(\s|\\\n)+%(s%(hell|cript-interpreter)|windows-shell)%(\s|\\\n)*:\=%(\s|\\\n)*\[@=" + \ contains=justSet,justSetKeywords,justAssignmentOperator + \ transparent skipwhite + \ nextgroup=justShellSetValue +syn region justShellSetValue + \ start='\V[' end='\V]' + \ contained + \ contains=@justStringLiterals,justShellSetError + +syn match justShellSetError '\v\k+['"]@!' contained + +syn match justAlias '\v^alias' contained +syn match justAliasDecl "\v^alias%(\s|\\\n)+\h\k*%(\s|\\\n)*:\=%(\s|\\\n)*" + \ transparent + \ contains=justAlias,justFunction,justAssignmentOperator + \ nextgroup=justAliasRes +syn match justAliasRes '\v\h\k*%(\s|\\\n)*%(#@=|$)' contained transparent contains=justFunction + +syn match justExportedAssignment "\v^export%(\s|\\\n)+\h\k*%(\s|\\\n)*:\=" transparent + \ contains=justExport,justAssignmentOperator + +syn match justExport '\v^export' contained + +syn match justUnexportStatement '\v^unexport%(\s|\\\n)+\w+\s*$' contains=justUnexport +syn match justUnexport '\v^unexport' contained + +syn keyword justConditional if else +syn region justConditionalBraces start="\v\{\{@!" end="\v\}@=" transparent contains=@justExpr +syn region justConditionalBracesInInterp start="\v\{\{@!" end="\v\}@=" transparent contained contains=@justExprInInterp + +syn match justLineLeadingSymbol "\v^%(\\\n)@3<!\s+\zs%(\@-|-\@|\@|-)" + +syn match justLineContinuation "\\$" + \ containedin=ALLBUT,justComment,justCommentInBody,justShebang,@justRawStrings,justRecipeAttrArgError,justShellExpandRawDefaultValue + +syn region justBody + \ start=/\v^\z( +|\t+)%(#!)@!\S/ + \ skip='\v\\\n|\n\s*$' + \ end="\v\n\z1@!|%(^\S)@2<=\_.@=" + \ contains=justInterpolation,@justOtherCurlyBraces,justLineLeadingSymbol,justCommentInBody,justStringInsideBody,justIndentError + \ contained + +syn region justShebangBody + \ start="\v^\z( +|\t+)#!" + \ skip='\v\\\n|\n\s*$' + \ end="\v\n\z1@!|%(^\S)@2<=\_.@=" + \ contains=justInterpolation,@justOtherCurlyBraces,justCommentInBody,justShebang,justStringInShebangBody,justShebangIndentError + \ contained + +syn cluster justBodies contains=justBody,justShebangBody + +syn match justIndentError '\v^%(\\\n)@3<!%( +\zs\t|\t+\zs )\s*\S@=' +syn match justShebangIndentError '\v^ +\zs\t\s*\S@=' + +syn region justInterpolation + \ matchgroup=justInterpolationDelim + \ start="\v\{\{\{@!" end="\v%(%(\\\n\s|\S)\s*)@<=\}\}|$" + \ matchgroup=justInterpError end='^\S' + \ contained + \ contains=@justExprInInterp + +syn match justBadCurlyBraces '\v\{{3}\ze[^{]' contained +syn match justCurlyBraces '\v\{{4}' contained +syn match justBadCurlyBraces '\v\{{5}\ze[^{]' contained +syn cluster justOtherCurlyBraces contains=justCurlyBraces,justBadCurlyBraces + +syn match justFunctionCall "\v\w+%(\s|\\\n)*\(@=" transparent contains=justBuiltInFunction + +" error() is intentionally not included in this list +syn keyword justBuiltInFunction + \ absolute_path append arch blake3 blake3_file cache_dir cache_directory canonicalize capitalize choose clean config_dir config_directory config_local_dir config_local_directory data_dir data_directory data_local_dir data_local_directory datetime datetime_utc encode_uri_component env env_var env_var_or_default executable_dir executable_directory extension file_name file_stem home_dir home_directory invocation_dir invocation_dir_native invocation_directory invocation_directory_native is_dependency join just_executable just_pid justfile justfile_dir justfile_directory kebabcase lowercamelcase lowercase module_dir module_directory module_file num_cpus os os_family parent_dir parent_directory path_exists prepend quote replace replace_regex semver_matches sha256 sha256_file shell shoutykebabcase shoutysnakecase snakecase source_dir source_directory source_file style titlecase trim trim_end trim_end_match trim_end_matches trim_start trim_start_match trim_start_matches uppercamelcase uppercase uuid without_extension + \ contained + +syn match justUserDefinedError "\v%(assert|error)%(%(\s|\\\n)*\()@=" + +syn match justReplaceRegex '\vreplace_regex%(\s|\\\n)*\(@=' transparent contains=justBuiltInFunction nextgroup=justReplaceRegexCall +syn match justReplaceRegexInInterp '\vreplace_regex%(\s|\\\n)*\(@=' transparent contained contains=justBuiltInFunction nextgroup=justReplaceRegexCallInInterp + +syn region justReplaceRegexCall + \ matchgroup=justReplaceRegexCall + \ start='\V(' end='\V)' + \ transparent contained + \ contains=@justExpr,justRegexReplacement +syn region justReplaceRegexCallInInterp + \ matchgroup=justReplaceRegexCall + \ start='\V(' end='\V)' + \ transparent contained + \ contains=@justExprInInterp,justRegexReplacement + +syn match justParameterLineContinuation '\v%(\s|\\\n)*' contained nextgroup=justParameterError + +syn match justRecipeDepParenName '\v%(\(\n?)@3<=%(\_s|\\\n)*\h\k*' + \ transparent contained + \ contains=justFunction + +syn cluster justBuiltInFunctions contains=justFunctionCall,justUserDefinedError + +syn match justConditionalOperator "\V==" +syn match justConditionalOperator "\V!=" +syn match justConditionalOperator "\V=~" + +syn match justOperator "\V+" +syn match justOperator "\V/" +syn match justOperator "\V&&" +syn match justOperator "\V||" + +syn keyword justConstant + \ HEX HEXLOWER HEXUPPER + \ CLEAR NORMAL BOLD ITALIC UNDERLINE INVERT HIDE STRIKETHROUGH + \ BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE + \ BG_BLACK BG_RED BG_GREEN BG_YELLOW BG_BLUE BG_MAGENTA BG_CYAN BG_WHITE + +syn match justShellExpandVarRaw '\v\$%(\{\_[^}]*\}|\w+)' contained contains=justShellExpandRawDefaultDelimiter +syn match justShellExpandRawDefaultDelimiter '\V:-' contained nextgroup=justShellExpandRawDefaultValue +syn match justShellExpandRawDefaultValue '\v\_[^}]*' contained +syn match justShellExpandVar '\v\$%(\w|\\\n\s*)+' contained +syn region justShellExpandVar start='\v\$%(\\\n\s*)*\{' end='\V}' contains=justShellExpandDefaultDelimiter,justStringEscapeSequence,justStringUEscapeSequence,justStringEscapeError +syn match justShellExpandDefaultDelimiter '\v:%(\\\n\s*)*-@=' contained nextgroup=justShellExpandDefault +syn region justShellExpandDefault + \ matchgroup=justShellExpandDefaultDelimiter start='\V-' end='\v\}@=' + \ contained + \ contains=justStringEscapeSequence,justStringUEscapeSequence,justStringEscapeError + +syn match justDollarEscape '\V$$' contained +syn match justDollarEscapeSplit '\v\$%(\\\n\s*)*\$' contained + +syn cluster justExprBase contains=@justAllStrings,@justBuiltInFunctions,justConditional,justConditionalOperator,justOperator,justConstant +syn cluster justExpr contains=@justExprBase,justExprParen,justConditionalBraces,justReplaceRegex +syn cluster justExprInInterp contains=@justExprBase,justName,justExprParenInInterp,justConditionalBracesInInterp,justReplaceRegexInInterp + +syn cluster justExprFunc contains=@justBuiltInFunctions,justReplaceRegex,justExprParen + +syn match justImport /\v^import%(%(\s|\\\n)*\?|%(\s|\\\n)+%(x?['"])@=)/ transparent + \ contains=justImportStatement,justOptionalFile +syn match justImportStatement '^import' contained + +syn match justOldInclude "^!include" + +syn match justModule /\v^mod%(%(\s|\\\n)*\?)?%(\s|\\\n)+\h\k*\s*%($|%(\s|\\\n)*%(x?['"]|#)@=)/ + \ transparent contains=justModStatement,justName,justOptionalFile +syn match justModStatement '^mod' contained + +syn match justOptionalFile '\V?' contained + +" Most linked colorscheme colors are chosen based on semantics of the color name. +" Some are for parity with other syntax files (for example, Number for recipe body highlighting +" is to align with the make.vim distributed with Vim). +" Deprecated `just` syntaxes are highlighted as Underlined. +" +" Colors are linked 'def'(ault) so that users who prefer other colors +" can override them, e.g. in ~/.vim/after/syntax/just.vim +" +" Note that vim-just's highlight groups are an implementation detail and may be subject to change. + +" The list of highlight links is sorted alphabetically. + +hi def link justAlias Statement +hi def link justAssignmentOperator Operator +hi def link justBacktick Special +hi def link justBadCurlyBraces Error +hi def link justBody Number +hi def link justBoolean Boolean +hi def link justBuiltInFunction Function +hi def link justComment Comment +hi def link justCommentInBody Comment +hi def link justCommentTodo Todo +hi def link justConditional Conditional +hi def link justConditionalOperator Conditional +hi def link justConstant Constant +hi def link justCurlyBraces Special +hi def link justDollarEscape Special +hi def link justDollarEscapeSplit Special +hi def link justExport Statement +hi def link justFunction Function +hi def link justImportStatement Include +hi def link justIndentError Error +hi def link justInterpError Error +hi def link justInterpolation Normal +hi def link justInterpolationDelim Delimiter +hi def link justInvalidAttrValue Error +hi def link justLineContinuation Special +hi def link justLineLeadingSymbol Special +hi def link justModStatement Keyword +hi def link justName Identifier +hi def link justOldInclude Error +hi def link justOperator Operator +hi def link justOptionalFile Conditional +hi def link justParameterError Error +hi def link justParameterOperator Operator +hi def link justParamExport Statement +hi def link justRawString String +hi def link justRawStrRegexRepl String +hi def link justRecipeAt Special +hi def link justRecipeAttr Type +hi def link justRecipeAttrArgError Error +hi def link justRecipeAttrSep Operator +hi def link justRecipeAttrValueColon Operator +hi def link justRecipeColon Operator +hi def link justRecipeDepParamsParen Delimiter +hi def link justRecipeSubsequentDeps Delimiter +hi def link justRegexCapture Identifier +hi def link justSet Statement +hi def link justSetDeprecatedKeywords Underlined +hi def link justSetKeywords Keyword +hi def link justShebang SpecialComment +hi def link justShebangBody Number +hi def link justShebangIndentError Error +hi def link justShellExpandDefault Character +hi def link justShellExpandDefaultDelimiter Operator +hi def link justShellExpandRawDefaultDelimiter Operator +hi def link justShellExpandRawDefaultValue Character +hi def link justShellExpandRawString String +hi def link justShellExpandString String +hi def link justShellExpandVar PreProc +hi def link justShellExpandVarRaw PreProc +hi def link justShellSetError Error +hi def link justString String +hi def link justStringEscapeError Error +hi def link justStringEscapeSequence Special +hi def link justStringInShebangBody String +hi def link justStringInsideBody String +hi def link justStringRegexRepl String +hi def link justStringUEscapeSequence Special +hi def link justUnexport Statement +hi def link justUserDefinedError Exception +hi def link justVariadicPrefix Statement +hi def link justVariadicPrefixError Error + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/syntax/karel.vim b/runtime/syntax/karel.vim new file mode 100644 index 0000000000..85c78529e6 --- /dev/null +++ b/runtime/syntax/karel.vim @@ -0,0 +1,112 @@ +" Vim syntax file +" Language: KAREL +" Last Change: 2024-11-17 +" Maintainer: Kirill Morozov <kirill@robotix.pro> +" Credits: Jay Strybis for the initial implementation and Patrick Knosowski +" for a couple of fixes. + +if exists("b:current_syntax") + finish +endif + +" KAREL is case-insensitive +syntax case ignore + +" Identifiers +syn match karelIdentifier /[a-zA-Z0-9_]\+/ +hi def link karelIdentifier Identifier + +" Constants +syn keyword karelConstant CR +syn region karelString start="'" end="'" +syn match karelReal /\d\+\.\d\+/ +syn match karelInteger /\d\+/ +syn keyword karelBoolean true false +hi def link karelConstant Constant +hi def link karelString String +hi def link karelInteger Number +hi def link karelReal Float +hi def link karelBoolean Boolean + +" Directives +syn match karelDirective /%[a-zA-Z]\+/ +hi def link karelDirective PreProc + +" Operators +syn keyword karelOperator AND OR NOT DIV MOD +syn match karelOperator /[\+\-\*\/\<\=\>\:\#\@]/ +syn match karelOperator /<=/ +syn match karelOperator />=/ +syn match karelOperator /<>/ +syn match karelOperator />=</ +hi def link karelOperator Operator + +" Types +syn keyword karelType ARRAY BOOLEAN BYTE CONFIG DISP_DAT_T FILE INTEGER JOINTPOS PATH POSITION QUEUE_TYPE REAL SHORT STD_PTH_NODE STRING VECTOR XYZWPR XYZWPREXT +syn keyword karelStructure STRUCTURE ENDSTRUCTURE +hi def link karelType Type +hi def link karelStructure Typedef + +syn keyword karelAction NOABORT NOMESSAGE NOPAUSE PAUSE PULSE RESUME STOP UNHOLD UNPAUSE +syn match karelAction /SIGNAL EVENT/ +syn match karelAction /SIGNAL SEMAPHORE/ +hi def link karelAction Keyword + +syn keyword karelFunction ABS ACOS APPROACH ARRAY_LEN ASIN ATAN2 ATTACH BYNAME BYTES_LEFT CHR COS CURJPOS CURPOS CURR_PROG EXP +syn keyword karelFunction FRAME GET_FILE_POS GET_JPOS_REG GET_JPOS_TPE GET_PORT_ATR GET_POS_REG GET_POS_TPE GET_USEC_TIM INDEX +syn keyword karelFunction IN_RANGE INV IO_STATUS J_IN_RANGE JOINT2POS LN MIRROR MOTION_CTL NODE_SIZE ORD ORIENT PATH_LEN POS POS2JOINT +syn keyword karelFunction ROUND SEMA_COUNT SIN SQRT STR_LEN SUB_STR TAN TRUNC UNINIT +hi def link karelFunction Function + +syn keyword karelClause EVAL FROM IN WHEN WITH +hi def link karelClause Keyword + +syn keyword karelConditional IF THEN ELSE ENDIF SELECT ENDSELECT CASE +hi def link karelConditional Conditional + +syn keyword karelRepeat WHILE DO ENDWHILE FOR +hi def link karelRepeat Repeat + +syn keyword karelProcedure ABORT_TASK ACT_SCREEN ACT_TBL ADD_BYNAMEPC ADD_DICT ADD_INTPC ADD_REALPC ADD_STRINGPC APPEND_NODE APPEND_QUEUE +syn keyword karelProcedure ATT_WINDOW_D ATT_WINDOW_S AVL_POS_NUM +syn keyword karelProcedure BYTES_AHEAD +syn keyword karelProcedure CALL_PROG CALL_PROGLIN CHECK_DICT CHECK_EPOS CHECK_NAME CLEAR CLEAR_SEMA CLOSE_TEP CLR_IO_STAT CLR_PORT_SIM CLR_POS_REG +syn keyword karelProcedure CNC_DYN_DISB CNC_DYN_DISE CNC_DYN_DISI CNC_DYN_DISP CNC_DYN_DISR CNC_DYN_DISS CNCL_STP_MTN CNV_CNF_STRG CNV_CONF_STR CNV_INT_STR CNV_JPOS_REL CNV_REAL_STR CNV_REL_JPOS CNV_STR_CONF CNV_STR_INT CNV_STR_REAL CNV_STR_TIME CNV_TIME_STR +syn keyword karelProcedure COMPARE_FILE CONT_TASK COPY_FILE COPY_PATH COPY_QUEUE COPY_TPE CREATE_TPE CREATE_VAR +syn keyword karelProcedure DAQ_CHECKP DAQ_REGPIPE DAQ_START DAQ_STOP DAQ_UNREG DAQ_WRITE DEF_SCREEN DEF_WINDOW +syn keyword karelProcedure DELETE_FILE DELETE_NODE DELETE_QUEUE DEL_INST_TPE DET_WINDOW DISCTRL_ALPH DISCTRL_FORM DISCTRL_LIST DISCTRL_PLMN DISCTRL_SBMN DISCTRL_TBL DISMOUNT_DEV DOSFILE_INF +syn keyword karelProcedure ERR_DATA FILE_LIST FORCE_SPMENU FORMAT_DEV GET_ATTR_PRG GET_PORT_ASG GET_PORT_CMT GET_PORT_MOD GET_PORT_SIM GET_PORT_VAL GET_POS_FRM GET_POS_TYP GET_PREG_CMT GET_QUEUE +syn keyword karelProcedure GET_REG GET_REG_CMT GET_SREG_CMT GET_STR_REG GET_TIME GET_TPE_CMT GET_TPE_PRM GET_TSK_INFO GET_USEC_SUB GET_VAR +syn keyword karelProcedure INI_DYN_DISB INI_DYN_DISE INI_DYN_DISI INI_DYN_DISP INI_DYN_DISR INI_DYN_DISS INIT_QUEUE INIT_TBL INSERT_NODE INSERT_QUEUE IO_MOD_TYPE +syn keyword karelProcedure KCL KCL_NO_WAIT KCL_STATUS LOAD LOAD_STATUS LOCK_GROUP MODIFY_QUEUE MOUNT_DEV MOVE_FILE MSG_CONNECT MSG_DISO MSG_PING +syn keyword karelProcedure OPEN_TPE PAUSE_TASK PEND_SEMA PIPE_CONFIG POP_KEY_RD POS_REG_TYPE POST_ERR POST_ERR_L POST_SEMA PRINT_FILE PROG_BACKUP PROG_CLEAR PROG_RESTORE PROG_LIST +syn keyword karelProcedure PURGE_DEV PUSH_KEY_RD READ_DICT READ_DICT_V READ_KB REMOVE_DICT RENAME_FILE RENAME_VAR RENAME_VARS RESET RUN_TASK SAVE SAVE_DRAM SELECT_TPE SEND_DATAPC SEND_EVENTPC SET_ATTR_PRG SET_CURSOR SET_EPOS_REG SET_EPOS_TPE +syn keyword karelProcedure SET_FILE_ATR SET_FILE_POS SET_INT_REG SET_JPOS_REG SET_JPOS_TPE SET_LANG SET_PERCH SET_PORT_ASG SET_PORT_ATR SET_PORT_CMT SET_PORT_MOD SET_PORT_SIM SET_PORT_VAL SET_POS_REG SET_POS_TPE SET_PREG_CMT SET_REAL_REG SET_REG CMT SET_SREG_CMT SET_STR_REG SET_TIME SET_TPE_CMT SET_TRNS_TPE SET_TSK_ATTR SET_TSK_NAME SET_VAR +syn keyword karelProcedure TRANSLATE UNLOCK_GROUP UNPOS V_CAM_CALIB V_GET_OFFSET V_GET_PASSFL V_GET_QUEUE V_INIT_QUEUE V_RALC_QUEUE V_RUN_FIND V_SET_REF V_START_VTRK V_STOP_VTRK VAR_INFO VAR_LIST VOL_SPACE VREG_FND_POS VREG_OFFSET +syn keyword karelProcedure WRITE_DICT WRITE_DICT_V XML_ADDTAG XML_GETDATA XML_REMTAG XML_SCAN XML_SETVAR +hi def link karelProcedure Function + +syn keyword karelStatement ABORT CONDITION ENDCONDITION CONTINUE DELAY ERROR EVENT FOR ENDFOR HOLD READ RELEASE REPEAT RETURN SEMAPHORE UNTIL USING ENDUSING WRITE +syn match karelStatement /CANCEL FILE/ +syn match karelStatement /CLOSE FILE/ +syn match karelStatement /CLOSE HAND/ +syn match karelStatement /CONNECT TIMER/ +syn match karelStatement /DISABLE CONDITION/ +syn match karelStatement /DISCONNECT TIMER/ +syn match karelStatement /ENABLE CONDITION/ +syn match karelStatement /GO TO/ +syn match karelStatement /OPEN FILE/ +syn match karelStatement /OPEN HAND/ +syn match karelStatement /PURGE CONDITION/ +syn match karelStatement /RELAX HAND/ +syn match karelStatement /WAIT FOR/ +hi def link karelStatement Statement + +syn keyword karelKeyword BEGIN CONST END PROGRAM ROUTINE STRUCT TYPE VAR +hi def link karelKeyword Keyword + +" Comments +syn region karelComment start="--" end="$" +hi def link karelComment Comment + +let b:current_syntax = "karel" diff --git a/runtime/syntax/kconfig.vim b/runtime/syntax/kconfig.vim index 0aecc00060..64a47a5995 100644 --- a/runtime/syntax/kconfig.vim +++ b/runtime/syntax/kconfig.vim @@ -1,7 +1,7 @@ " Vim syntax file " Maintainer: Christian Brabandt <cb@256bit.org> " Previous Maintainer: Nikolai Weibull <now@bitwi.se> -" Latest Revision: 2024-07-19 +" Latest Revision: 2025 Jan 20 " License: Vim (see :h license) " Repository: https://github.com/chrisbra/vim-kconfig @@ -12,722 +12,722 @@ endif let s:cpo_save = &cpo set cpo&vim +exe "syn sync minlines=" . get(g:, 'kconfig_minlines', 50) + if exists("g:kconfig_syntax_heavy") -syn match kconfigBegin '^' nextgroup=kconfigKeyword - \ skipwhite - -syn keyword kconfigTodo contained TODO FIXME XXX NOTE - -syn match kconfigComment display '#.*$' contains=kconfigTodo - -syn keyword kconfigKeyword config nextgroup=kconfigSymbol - \ skipwhite - -syn keyword kconfigKeyword menuconfig nextgroup=kconfigSymbol - \ skipwhite - -syn keyword kconfigKeyword comment menu mainmenu - \ nextgroup=kconfigKeywordPrompt - \ skipwhite - -syn keyword kconfigKeyword choice - \ nextgroup=@kconfigConfigOptions - \ skipwhite skipnl - -syn keyword kconfigKeyword endmenu endchoice - -syn keyword kconfigPreProc source - \ nextgroup=kconfigPath - \ skipwhite - -" TODO: This is a hack. The who .*Expr stuff should really be generated so -" that we can reuse it for various nextgroups. -syn keyword kconfigConditional if endif - \ nextgroup=@kconfigConfigOptionIfExpr - \ skipwhite - -syn match kconfigKeywordPrompt '"[^"\\]*\%(\\.[^"\\]*\)*"' - \ contained - \ nextgroup=@kconfigConfigOptions - \ skipwhite skipnl - -syn match kconfigPath '"[^"\\]*\%(\\.[^"\\]*\)*"\|\S\+' - \ contained - -syn match kconfigSymbol '\<\k\+\>' - \ contained - \ nextgroup=@kconfigConfigOptions - \ skipwhite skipnl - -" FIXME: There is – probably – no reason to cluster these instead of just -" defining them in the same group. -syn cluster kconfigConfigOptions contains=kconfigTypeDefinition, - \ kconfigInputPrompt, - \ kconfigDefaultValue, - \ kconfigDependencies, - \ kconfigReverseDependencies, - \ kconfigNumericalRanges, - \ kconfigHelpText, - \ kconfigDefBool, - \ kconfigOptional - -syn keyword kconfigTypeDefinition bool boolean tristate string hex int - \ contained - \ nextgroup=kconfigTypeDefPrompt, - \ @kconfigConfigOptions - \ skipwhite skipnl - -syn match kconfigTypeDefPrompt '"[^"\\]*\%(\\.[^"\\]*\)*"' - \ contained - \ nextgroup=kconfigConfigOptionIf, - \ @kconfigConfigOptions - \ skipwhite skipnl - -syn match kconfigTypeDefPrompt "'[^'\\]*\%(\\.[^'\\]*\)*'" - \ contained - \ nextgroup=kconfigConfigOptionIf, - \ @kconfigConfigOptions - \ skipwhite skipnl - -syn keyword kconfigInputPrompt prompt - \ contained - \ nextgroup=kconfigPromptPrompt - \ skipwhite - -syn match kconfigPromptPrompt '"[^"\\]*\%(\\.[^"\\]*\)*"' - \ contained - \ nextgroup=kconfigConfigOptionIf, - \ @kconfigConfigOptions - \ skipwhite skipnl - -syn match kconfigPromptPrompt "'[^'\\]*\%(\\.[^'\\]*\)*'" - \ contained - \ nextgroup=kconfigConfigOptionIf, - \ @kconfigConfigOptions - \ skipwhite skipnl - -syn keyword kconfigDefaultValue default - \ contained - \ nextgroup=@kconfigConfigOptionExpr - \ skipwhite - -syn match kconfigDependencies 'depends on\|requires' - \ contained - \ nextgroup=@kconfigConfigOptionIfExpr - \ skipwhite - -syn keyword kconfigReverseDependencies select - \ contained - \ nextgroup=@kconfigRevDepSymbol - \ skipwhite - -syn cluster kconfigRevDepSymbol contains=kconfigRevDepCSymbol, - \ kconfigRevDepNCSymbol - -syn match kconfigRevDepCSymbol '"[^"\\]*\%(\\.[^"\\]*\)*"' - \ contained - \ nextgroup=kconfigConfigOptionIf, - \ @kconfigConfigOptions - \ skipwhite skipnl - -syn match kconfigRevDepCSymbol "'[^'\\]*\%(\\.[^'\\]*\)*'" - \ contained - \ nextgroup=kconfigConfigOptionIf, - \ @kconfigConfigOptions - \ skipwhite skipnl - -syn match kconfigRevDepNCSymbol '\<\k\+\>' - \ contained - \ nextgroup=kconfigConfigOptionIf, - \ @kconfigConfigOptions - \ skipwhite skipnl - -syn keyword kconfigNumericalRanges range - \ contained - \ nextgroup=@kconfigRangeSymbol - \ skipwhite - -syn cluster kconfigRangeSymbol contains=kconfigRangeCSymbol, - \ kconfigRangeNCSymbol - -syn match kconfigRangeCSymbol '"[^"\\]*\%(\\.[^"\\]*\)*"' - \ contained - \ nextgroup=@kconfigRangeSymbol2 - \ skipwhite skipnl - -syn match kconfigRangeCSymbol "'[^'\\]*\%(\\.[^'\\]*\)*'" - \ contained - \ nextgroup=@kconfigRangeSymbol2 - \ skipwhite skipnl - -syn match kconfigRangeNCSymbol '\<\k\+\>' - \ contained - \ nextgroup=@kconfigRangeSymbol2 - \ skipwhite skipnl - -syn cluster kconfigRangeSymbol2 contains=kconfigRangeCSymbol2, - \ kconfigRangeNCSymbol2 - -syn match kconfigRangeCSymbol2 "'[^'\\]*\%(\\.[^'\\]*\)*'" - \ contained - \ nextgroup=kconfigConfigOptionIf, - \ @kconfigConfigOptions - \ skipwhite skipnl - -syn match kconfigRangeNCSymbol2 '\<\k\+\>' - \ contained - \ nextgroup=kconfigConfigOptionIf, - \ @kconfigConfigOptions - \ skipwhite skipnl - -syn region kconfigHelpText contained - \ matchgroup=kconfigConfigOption - \ start='\%(help\|---help---\)\ze\s*\n\z(\s\+\)' - \ skip='^$' - \ end='^\z1\@!' - \ nextgroup=@kconfigConfigOptions - \ skipwhite skipnl - -" XXX: Undocumented -syn keyword kconfigDefBool def_bool - \ contained - \ nextgroup=@kconfigDefBoolSymbol - \ skipwhite - -syn cluster kconfigDefBoolSymbol contains=kconfigDefBoolCSymbol, - \ kconfigDefBoolNCSymbol - -syn match kconfigDefBoolCSymbol '"[^"\\]*\%(\\.[^"\\]*\)*"' - \ contained - \ nextgroup=kconfigConfigOptionIf, - \ @kconfigConfigOptions - \ skipwhite skipnl - -syn match kconfigDefBoolCSymbol "'[^'\\]*\%(\\.[^'\\]*\)*'" - \ contained - \ nextgroup=kconfigConfigOptionIf, - \ @kconfigConfigOptions - \ skipwhite skipnl - -syn match kconfigDefBoolNCSymbol '\<\k\+\>' - \ contained - \ nextgroup=kconfigConfigOptionIf, - \ @kconfigConfigOptions - \ skipwhite skipnl - -" XXX: This is actually only a valid option for “choice”, but treating it -" specially would require a lot of extra groups. -syn keyword kconfigOptional optional - \ contained - \ nextgroup=@kconfigConfigOptions - \ skipwhite skipnl - -syn keyword kconfigConfigOptionIf if - \ contained - \ nextgroup=@kconfigConfigOptionIfExpr - \ skipwhite - -syn cluster kconfigConfigOptionIfExpr contains=@kconfigConfOptIfExprSym, - \ kconfigConfOptIfExprNeg, - \ kconfigConfOptIfExprGroup - -syn cluster kconfigConfOptIfExprSym contains=kconfigConfOptIfExprCSym, - \ kconfigConfOptIfExprNCSym - -syn match kconfigConfOptIfExprCSym '"[^"\\]*\%(\\.[^"\\]*\)*"' - \ contained - \ nextgroup=@kconfigConfigOptions, - \ kconfigConfOptIfExprAnd, - \ kconfigConfOptIfExprOr, - \ kconfigConfOptIfExprEq, - \ kconfigConfOptIfExprNEq - \ skipwhite skipnl - -syn match kconfigConfOptIfExprCSym "'[^'\\]*\%(\\.[^'\\]*\)*'" - \ contained - \ nextgroup=@kconfigConfigOptions, - \ kconfigConfOptIfExprAnd, - \ kconfigConfOptIfExprOr, - \ kconfigConfOptIfExprEq, - \ kconfigConfOptIfExprNEq - \ skipwhite skipnl - -syn match kconfigConfOptIfExprNCSym '\<\k\+\>' - \ contained - \ nextgroup=@kconfigConfigOptions, - \ kconfigConfOptIfExprAnd, - \ kconfigConfOptIfExprOr, - \ kconfigConfOptIfExprEq, - \ kconfigConfOptIfExprNEq - \ skipwhite skipnl - -syn cluster kconfigConfOptIfExprSym2 contains=kconfigConfOptIfExprCSym2, - \ kconfigConfOptIfExprNCSym2 - -syn match kconfigConfOptIfExprEq '=' - \ contained - \ nextgroup=@kconfigConfOptIfExprSym2 - \ skipwhite - -syn match kconfigConfOptIfExprNEq '!=' - \ contained - \ nextgroup=@kconfigConfOptIfExprSym2 - \ skipwhite - -syn match kconfigConfOptIfExprCSym2 "'[^'\\]*\%(\\.[^'\\]*\)*'" - \ contained - \ nextgroup=@kconfigConfigOptions, - \ kconfigConfOptIfExprAnd, - \ kconfigConfOptIfExprOr - \ skipwhite skipnl - -syn match kconfigConfOptIfExprNCSym2 '\<\k\+\>' - \ contained - \ nextgroup=@kconfigConfigOptions, - \ kconfigConfOptIfExprAnd, - \ kconfigConfOptIfExprOr - \ skipwhite skipnl - -syn match kconfigConfOptIfExprNeg '!' - \ contained - \ nextgroup=@kconfigConfigOptionIfExpr - \ skipwhite - -syn match kconfigConfOptIfExprAnd '&&' - \ contained - \ nextgroup=@kconfigConfigOptionIfExpr - \ skipwhite - -syn match kconfigConfOptIfExprOr '||' - \ contained - \ nextgroup=@kconfigConfigOptionIfExpr - \ skipwhite - -syn match kconfigConfOptIfExprGroup '(' - \ contained - \ nextgroup=@kconfigConfigOptionIfGExp - \ skipwhite - -" TODO: hm, this kind of recursion doesn't work right. We need another set of -" expressions that have kconfigConfigOPtionIfGExp as nextgroup and a matcher -" for '(' that sets it all off. -syn cluster kconfigConfigOptionIfGExp contains=@kconfigConfOptIfGExpSym, - \ kconfigConfOptIfGExpNeg, - \ kconfigConfOptIfExprGroup - -syn cluster kconfigConfOptIfGExpSym contains=kconfigConfOptIfGExpCSym, - \ kconfigConfOptIfGExpNCSym - -syn match kconfigConfOptIfGExpCSym '"[^"\\]*\%(\\.[^"\\]*\)*"' - \ contained - \ nextgroup=@kconfigConfigIf, - \ kconfigConfOptIfGExpAnd, - \ kconfigConfOptIfGExpOr, - \ kconfigConfOptIfGExpEq, - \ kconfigConfOptIfGExpNEq - \ skipwhite skipnl - -syn match kconfigConfOptIfGExpCSym "'[^'\\]*\%(\\.[^'\\]*\)*'" - \ contained - \ nextgroup=@kconfigConfigIf, - \ kconfigConfOptIfGExpAnd, - \ kconfigConfOptIfGExpOr, - \ kconfigConfOptIfGExpEq, - \ kconfigConfOptIfGExpNEq - \ skipwhite skipnl - -syn match kconfigConfOptIfGExpNCSym '\<\k\+\>' - \ contained - \ nextgroup=kconfigConfOptIfExprGrpE, - \ kconfigConfOptIfGExpAnd, - \ kconfigConfOptIfGExpOr, - \ kconfigConfOptIfGExpEq, - \ kconfigConfOptIfGExpNEq - \ skipwhite skipnl - -syn cluster kconfigConfOptIfGExpSym2 contains=kconfigConfOptIfGExpCSym2, - \ kconfigConfOptIfGExpNCSym2 - -syn match kconfigConfOptIfGExpEq '=' - \ contained - \ nextgroup=@kconfigConfOptIfGExpSym2 - \ skipwhite - -syn match kconfigConfOptIfGExpNEq '!=' - \ contained - \ nextgroup=@kconfigConfOptIfGExpSym2 - \ skipwhite - -syn match kconfigConfOptIfGExpCSym2 '"[^"\\]*\%(\\.[^"\\]*\)*"' - \ contained - \ nextgroup=kconfigConfOptIfExprGrpE, - \ kconfigConfOptIfGExpAnd, - \ kconfigConfOptIfGExpOr - \ skipwhite skipnl - -syn match kconfigConfOptIfGExpCSym2 "'[^'\\]*\%(\\.[^'\\]*\)*'" - \ contained - \ nextgroup=kconfigConfOptIfExprGrpE, - \ kconfigConfOptIfGExpAnd, - \ kconfigConfOptIfGExpOr - \ skipwhite skipnl - -syn match kconfigConfOptIfGExpNCSym2 '\<\k\+\>' - \ contained - \ nextgroup=kconfigConfOptIfExprGrpE, - \ kconfigConfOptIfGExpAnd, - \ kconfigConfOptIfGExpOr - \ skipwhite skipnl - -syn match kconfigConfOptIfGExpNeg '!' - \ contained - \ nextgroup=@kconfigConfigOptionIfGExp - \ skipwhite - -syn match kconfigConfOptIfGExpAnd '&&' - \ contained - \ nextgroup=@kconfigConfigOptionIfGExp - \ skipwhite - -syn match kconfigConfOptIfGExpOr '||' - \ contained - \ nextgroup=@kconfigConfigOptionIfGExp - \ skipwhite - -syn match kconfigConfOptIfExprGrpE ')' - \ contained - \ nextgroup=@kconfigConfigOptions, - \ kconfigConfOptIfExprAnd, - \ kconfigConfOptIfExprOr - \ skipwhite skipnl - - -syn cluster kconfigConfigOptionExpr contains=@kconfigConfOptExprSym, - \ kconfigConfOptExprNeg, - \ kconfigConfOptExprGroup - -syn cluster kconfigConfOptExprSym contains=kconfigConfOptExprCSym, - \ kconfigConfOptExprNCSym - -syn match kconfigConfOptExprCSym '"[^"\\]*\%(\\.[^"\\]*\)*"' - \ contained - \ nextgroup=kconfigConfigOptionIf, - \ kconfigConfOptExprAnd, - \ kconfigConfOptExprOr, - \ kconfigConfOptExprEq, - \ kconfigConfOptExprNEq, - \ @kconfigConfigOptions - \ skipwhite skipnl - -syn match kconfigConfOptExprCSym "'[^'\\]*\%(\\.[^'\\]*\)*'" - \ contained - \ nextgroup=kconfigConfigOptionIf, - \ kconfigConfOptExprAnd, - \ kconfigConfOptExprOr, - \ kconfigConfOptExprEq, - \ kconfigConfOptExprNEq, - \ @kconfigConfigOptions - \ skipwhite skipnl - -syn match kconfigConfOptExprNCSym '\<\k\+\>' - \ contained - \ nextgroup=kconfigConfigOptionIf, - \ kconfigConfOptExprAnd, - \ kconfigConfOptExprOr, - \ kconfigConfOptExprEq, - \ kconfigConfOptExprNEq, - \ @kconfigConfigOptions - \ skipwhite skipnl - -syn cluster kconfigConfOptExprSym2 contains=kconfigConfOptExprCSym2, - \ kconfigConfOptExprNCSym2 - -syn match kconfigConfOptExprEq '=' - \ contained - \ nextgroup=@kconfigConfOptExprSym2 - \ skipwhite - -syn match kconfigConfOptExprNEq '!=' - \ contained - \ nextgroup=@kconfigConfOptExprSym2 - \ skipwhite - -syn match kconfigConfOptExprCSym2 '"[^"\\]*\%(\\.[^"\\]*\)*"' - \ contained - \ nextgroup=kconfigConfigOptionIf, - \ kconfigConfOptExprAnd, - \ kconfigConfOptExprOr, - \ @kconfigConfigOptions - \ skipwhite skipnl - -syn match kconfigConfOptExprCSym2 "'[^'\\]*\%(\\.[^'\\]*\)*'" - \ contained - \ nextgroup=kconfigConfigOptionIf, - \ kconfigConfOptExprAnd, - \ kconfigConfOptExprOr, - \ @kconfigConfigOptions - \ skipwhite skipnl - -syn match kconfigConfOptExprNCSym2 '\<\k\+\>' - \ contained - \ nextgroup=kconfigConfigOptionIf, - \ kconfigConfOptExprAnd, - \ kconfigConfOptExprOr, - \ @kconfigConfigOptions - \ skipwhite skipnl - -syn match kconfigConfOptExprNeg '!' - \ contained - \ nextgroup=@kconfigConfigOptionExpr - \ skipwhite - -syn match kconfigConfOptExprAnd '&&' - \ contained - \ nextgroup=@kconfigConfigOptionExpr - \ skipwhite - -syn match kconfigConfOptExprOr '||' - \ contained - \ nextgroup=@kconfigConfigOptionExpr - \ skipwhite - -syn match kconfigConfOptExprGroup '(' - \ contained - \ nextgroup=@kconfigConfigOptionGExp - \ skipwhite - -syn cluster kconfigConfigOptionGExp contains=@kconfigConfOptGExpSym, - \ kconfigConfOptGExpNeg, - \ kconfigConfOptGExpGroup - -syn cluster kconfigConfOptGExpSym contains=kconfigConfOptGExpCSym, - \ kconfigConfOptGExpNCSym - -syn match kconfigConfOptGExpCSym '"[^"\\]*\%(\\.[^"\\]*\)*"' - \ contained - \ nextgroup=kconfigConfOptExprGrpE, - \ kconfigConfOptGExpAnd, - \ kconfigConfOptGExpOr, - \ kconfigConfOptGExpEq, - \ kconfigConfOptGExpNEq - \ skipwhite skipnl - -syn match kconfigConfOptGExpCSym "'[^'\\]*\%(\\.[^'\\]*\)*'" - \ contained - \ nextgroup=kconfigConfOptExprGrpE, - \ kconfigConfOptGExpAnd, - \ kconfigConfOptGExpOr, - \ kconfigConfOptGExpEq, - \ kconfigConfOptGExpNEq - \ skipwhite skipnl - -syn match kconfigConfOptGExpNCSym '\<\k\+\>' - \ contained - \ nextgroup=kconfigConfOptExprGrpE, - \ kconfigConfOptGExpAnd, - \ kconfigConfOptGExpOr, - \ kconfigConfOptGExpEq, - \ kconfigConfOptGExpNEq - \ skipwhite skipnl - -syn cluster kconfigConfOptGExpSym2 contains=kconfigConfOptGExpCSym2, - \ kconfigConfOptGExpNCSym2 - -syn match kconfigConfOptGExpEq '=' - \ contained - \ nextgroup=@kconfigConfOptGExpSym2 - \ skipwhite - -syn match kconfigConfOptGExpNEq '!=' - \ contained - \ nextgroup=@kconfigConfOptGExpSym2 - \ skipwhite - -syn match kconfigConfOptGExpCSym2 '"[^"\\]*\%(\\.[^"\\]*\)*"' - \ contained - \ nextgroup=kconfigConfOptExprGrpE, - \ kconfigConfOptGExpAnd, - \ kconfigConfOptGExpOr - \ skipwhite skipnl - -syn match kconfigConfOptGExpCSym2 "'[^'\\]*\%(\\.[^'\\]*\)*'" - \ contained - \ nextgroup=kconfigConfOptExprGrpE, - \ kconfigConfOptGExpAnd, - \ kconfigConfOptGExpOr - \ skipwhite skipnl - -syn match kconfigConfOptGExpNCSym2 '\<\k\+\>' - \ contained - \ nextgroup=kconfigConfOptExprGrpE, - \ kconfigConfOptGExpAnd, - \ kconfigConfOptGExpOr - \ skipwhite skipnl - -syn match kconfigConfOptGExpNeg '!' - \ contained - \ nextgroup=@kconfigConfigOptionGExp - \ skipwhite - -syn match kconfigConfOptGExpAnd '&&' - \ contained - \ nextgroup=@kconfigConfigOptionGExp - \ skipwhite - -syn match kconfigConfOptGExpOr '||' - \ contained - \ nextgroup=@kconfigConfigOptionGExp - \ skipwhite - -syn match kconfigConfOptExprGrpE ')' - \ contained - \ nextgroup=kconfigConfigOptionIf, - \ kconfigConfOptExprAnd, - \ kconfigConfOptExprOr - \ skipwhite skipnl - -syn sync minlines=50 - -hi def link kconfigTodo Todo -hi def link kconfigComment Comment -hi def link kconfigKeyword Keyword -hi def link kconfigPreProc PreProc -hi def link kconfigConditional Conditional -hi def link kconfigPrompt String -hi def link kconfigKeywordPrompt kconfigPrompt -hi def link kconfigPath String -hi def link kconfigSymbol String -hi def link kconfigConstantSymbol Constant -hi def link kconfigConfigOption Type -hi def link kconfigTypeDefinition kconfigConfigOption -hi def link kconfigTypeDefPrompt kconfigPrompt -hi def link kconfigInputPrompt kconfigConfigOption -hi def link kconfigPromptPrompt kconfigPrompt -hi def link kconfigDefaultValue kconfigConfigOption -hi def link kconfigDependencies kconfigConfigOption -hi def link kconfigReverseDependencies kconfigConfigOption -hi def link kconfigRevDepCSymbol kconfigConstantSymbol -hi def link kconfigRevDepNCSymbol kconfigSymbol -hi def link kconfigNumericalRanges kconfigConfigOption -hi def link kconfigRangeCSymbol kconfigConstantSymbol -hi def link kconfigRangeNCSymbol kconfigSymbol -hi def link kconfigRangeCSymbol2 kconfigConstantSymbol -hi def link kconfigRangeNCSymbol2 kconfigSymbol -hi def link kconfigHelpText Normal -hi def link kconfigDefBool kconfigConfigOption -hi def link kconfigDefBoolCSymbol kconfigConstantSymbol -hi def link kconfigDefBoolNCSymbol kconfigSymbol -hi def link kconfigOptional kconfigConfigOption -hi def link kconfigConfigOptionIf Conditional -hi def link kconfigConfOptIfExprCSym kconfigConstantSymbol -hi def link kconfigConfOptIfExprNCSym kconfigSymbol -hi def link kconfigOperator Operator -hi def link kconfigConfOptIfExprEq kconfigOperator -hi def link kconfigConfOptIfExprNEq kconfigOperator -hi def link kconfigConfOptIfExprCSym2 kconfigConstantSymbol -hi def link kconfigConfOptIfExprNCSym2 kconfigSymbol -hi def link kconfigConfOptIfExprNeg kconfigOperator -hi def link kconfigConfOptIfExprAnd kconfigOperator -hi def link kconfigConfOptIfExprOr kconfigOperator -hi def link kconfigDelimiter Delimiter -hi def link kconfigConfOptIfExprGroup kconfigDelimiter -hi def link kconfigConfOptIfGExpCSym kconfigConstantSymbol -hi def link kconfigConfOptIfGExpNCSym kconfigSymbol -hi def link kconfigConfOptIfGExpEq kconfigOperator -hi def link kconfigConfOptIfGExpNEq kconfigOperator -hi def link kconfigConfOptIfGExpCSym2 kconfigConstantSymbol -hi def link kconfigConfOptIfGExpNCSym2 kconfigSymbol -hi def link kconfigConfOptIfGExpNeg kconfigOperator -hi def link kconfigConfOptIfGExpAnd kconfigOperator -hi def link kconfigConfOptIfGExpOr kconfigOperator -hi def link kconfigConfOptIfExprGrpE kconfigDelimiter -hi def link kconfigConfOptExprCSym kconfigConstantSymbol -hi def link kconfigConfOptExprNCSym kconfigSymbol -hi def link kconfigConfOptExprEq kconfigOperator -hi def link kconfigConfOptExprNEq kconfigOperator -hi def link kconfigConfOptExprCSym2 kconfigConstantSymbol -hi def link kconfigConfOptExprNCSym2 kconfigSymbol -hi def link kconfigConfOptExprNeg kconfigOperator -hi def link kconfigConfOptExprAnd kconfigOperator -hi def link kconfigConfOptExprOr kconfigOperator -hi def link kconfigConfOptExprGroup kconfigDelimiter -hi def link kconfigConfOptGExpCSym kconfigConstantSymbol -hi def link kconfigConfOptGExpNCSym kconfigSymbol -hi def link kconfigConfOptGExpEq kconfigOperator -hi def link kconfigConfOptGExpNEq kconfigOperator -hi def link kconfigConfOptGExpCSym2 kconfigConstantSymbol -hi def link kconfigConfOptGExpNCSym2 kconfigSymbol -hi def link kconfigConfOptGExpNeg kconfigOperator -hi def link kconfigConfOptGExpAnd kconfigOperator -hi def link kconfigConfOptGExpOr kconfigOperator -hi def link kconfigConfOptExprGrpE kconfigConfOptIfExprGroup + syn match kconfigBegin '^' nextgroup=kconfigKeyword + \ skipwhite + + syn keyword kconfigTodo contained TODO FIXME XXX NOTE + + syn match kconfigComment display '#.*$' contains=kconfigTodo + + syn keyword kconfigKeyword config nextgroup=kconfigSymbol + \ skipwhite + + syn keyword kconfigKeyword menuconfig nextgroup=kconfigSymbol + \ skipwhite + + syn keyword kconfigKeyword comment menu mainmenu + \ nextgroup=kconfigKeywordPrompt + \ skipwhite + + syn keyword kconfigKeyword choice + \ nextgroup=@kconfigConfigOptions + \ skipwhite skipnl + + syn keyword kconfigKeyword endmenu endchoice + + syn keyword kconfigPreProc source + \ nextgroup=kconfigPath + \ skipwhite + + " TODO: This is a hack. The who .*Expr stuff should really be generated so + " that we can reuse it for various nextgroups. + syn keyword kconfigConditional if endif + \ nextgroup=@kconfigConfigOptionIfExpr + \ skipwhite + + syn match kconfigKeywordPrompt '"[^"\\]*\%(\\.[^"\\]*\)*"' + \ contained + \ nextgroup=@kconfigConfigOptions + \ skipwhite skipnl + + syn match kconfigPath '"[^"\\]*\%(\\.[^"\\]*\)*"\|\S\+' + \ contained + + syn match kconfigSymbol '\<\k\+\>' + \ contained + \ nextgroup=@kconfigConfigOptions + \ skipwhite skipnl + + " FIXME: There is – probably – no reason to cluster these instead of just + " defining them in the same group. + syn cluster kconfigConfigOptions contains=kconfigTypeDefinition, + \ kconfigInputPrompt, + \ kconfigDefaultValue, + \ kconfigDependencies, + \ kconfigReverseDependencies, + \ kconfigNumericalRanges, + \ kconfigHelpText, + \ kconfigDefBool, + \ kconfigOptional + + syn keyword kconfigTypeDefinition bool boolean tristate string hex int + \ contained + \ nextgroup=kconfigTypeDefPrompt, + \ @kconfigConfigOptions + \ skipwhite skipnl + + syn match kconfigTypeDefPrompt '"[^"\\]*\%(\\.[^"\\]*\)*"' + \ contained + \ nextgroup=kconfigConfigOptionIf, + \ @kconfigConfigOptions + \ skipwhite skipnl + + syn match kconfigTypeDefPrompt "'[^'\\]*\%(\\.[^'\\]*\)*'" + \ contained + \ nextgroup=kconfigConfigOptionIf, + \ @kconfigConfigOptions + \ skipwhite skipnl + + syn keyword kconfigInputPrompt prompt + \ contained + \ nextgroup=kconfigPromptPrompt + \ skipwhite + + syn match kconfigPromptPrompt '"[^"\\]*\%(\\.[^"\\]*\)*"' + \ contained + \ nextgroup=kconfigConfigOptionIf, + \ @kconfigConfigOptions + \ skipwhite skipnl + + syn match kconfigPromptPrompt "'[^'\\]*\%(\\.[^'\\]*\)*'" + \ contained + \ nextgroup=kconfigConfigOptionIf, + \ @kconfigConfigOptions + \ skipwhite skipnl + + syn keyword kconfigDefaultValue default + \ contained + \ nextgroup=@kconfigConfigOptionExpr + \ skipwhite + + syn match kconfigDependencies 'depends on\|requires' + \ contained + \ nextgroup=@kconfigConfigOptionIfExpr + \ skipwhite + + syn keyword kconfigReverseDependencies select + \ contained + \ nextgroup=@kconfigRevDepSymbol + \ skipwhite + + syn cluster kconfigRevDepSymbol contains=kconfigRevDepCSymbol, + \ kconfigRevDepNCSymbol + + syn match kconfigRevDepCSymbol '"[^"\\]*\%(\\.[^"\\]*\)*"' + \ contained + \ nextgroup=kconfigConfigOptionIf, + \ @kconfigConfigOptions + \ skipwhite skipnl + + syn match kconfigRevDepCSymbol "'[^'\\]*\%(\\.[^'\\]*\)*'" + \ contained + \ nextgroup=kconfigConfigOptionIf, + \ @kconfigConfigOptions + \ skipwhite skipnl + + syn match kconfigRevDepNCSymbol '\<\k\+\>' + \ contained + \ nextgroup=kconfigConfigOptionIf, + \ @kconfigConfigOptions + \ skipwhite skipnl + + syn keyword kconfigNumericalRanges range + \ contained + \ nextgroup=@kconfigRangeSymbol + \ skipwhite + + syn cluster kconfigRangeSymbol contains=kconfigRangeCSymbol, + \ kconfigRangeNCSymbol + + syn match kconfigRangeCSymbol '"[^"\\]*\%(\\.[^"\\]*\)*"' + \ contained + \ nextgroup=@kconfigRangeSymbol2 + \ skipwhite skipnl + + syn match kconfigRangeCSymbol "'[^'\\]*\%(\\.[^'\\]*\)*'" + \ contained + \ nextgroup=@kconfigRangeSymbol2 + \ skipwhite skipnl + + syn match kconfigRangeNCSymbol '\<\k\+\>' + \ contained + \ nextgroup=@kconfigRangeSymbol2 + \ skipwhite skipnl + + syn cluster kconfigRangeSymbol2 contains=kconfigRangeCSymbol2, + \ kconfigRangeNCSymbol2 + + syn match kconfigRangeCSymbol2 "'[^'\\]*\%(\\.[^'\\]*\)*'" + \ contained + \ nextgroup=kconfigConfigOptionIf, + \ @kconfigConfigOptions + \ skipwhite skipnl + + syn match kconfigRangeNCSymbol2 '\<\k\+\>' + \ contained + \ nextgroup=kconfigConfigOptionIf, + \ @kconfigConfigOptions + \ skipwhite skipnl + + syn region kconfigHelpText contained + \ matchgroup=kconfigConfigOption + \ start='\%(help\|---help---\)\ze\s*\n\z(\s\+\)' + \ skip='^$' + \ end='^\z1\@!' + \ nextgroup=@kconfigConfigOptions + \ skipwhite skipnl + + " XXX: Undocumented + syn keyword kconfigDefBool def_bool + \ contained + \ nextgroup=@kconfigDefBoolSymbol + \ skipwhite + + syn cluster kconfigDefBoolSymbol contains=kconfigDefBoolCSymbol, + \ kconfigDefBoolNCSymbol + + syn match kconfigDefBoolCSymbol '"[^"\\]*\%(\\.[^"\\]*\)*"' + \ contained + \ nextgroup=kconfigConfigOptionIf, + \ @kconfigConfigOptions + \ skipwhite skipnl + + syn match kconfigDefBoolCSymbol "'[^'\\]*\%(\\.[^'\\]*\)*'" + \ contained + \ nextgroup=kconfigConfigOptionIf, + \ @kconfigConfigOptions + \ skipwhite skipnl + + syn match kconfigDefBoolNCSymbol '\<\k\+\>' + \ contained + \ nextgroup=kconfigConfigOptionIf, + \ @kconfigConfigOptions + \ skipwhite skipnl + + " XXX: This is actually only a valid option for “choice”, but treating it + " specially would require a lot of extra groups. + syn keyword kconfigOptional optional + \ contained + \ nextgroup=@kconfigConfigOptions + \ skipwhite skipnl + + syn keyword kconfigConfigOptionIf if + \ contained + \ nextgroup=@kconfigConfigOptionIfExpr + \ skipwhite + + syn cluster kconfigConfigOptionIfExpr contains=@kconfigConfOptIfExprSym, + \ kconfigConfOptIfExprNeg, + \ kconfigConfOptIfExprGroup + + syn cluster kconfigConfOptIfExprSym contains=kconfigConfOptIfExprCSym, + \ kconfigConfOptIfExprNCSym + + syn match kconfigConfOptIfExprCSym '"[^"\\]*\%(\\.[^"\\]*\)*"' + \ contained + \ nextgroup=@kconfigConfigOptions, + \ kconfigConfOptIfExprAnd, + \ kconfigConfOptIfExprOr, + \ kconfigConfOptIfExprEq, + \ kconfigConfOptIfExprNEq + \ skipwhite skipnl + + syn match kconfigConfOptIfExprCSym "'[^'\\]*\%(\\.[^'\\]*\)*'" + \ contained + \ nextgroup=@kconfigConfigOptions, + \ kconfigConfOptIfExprAnd, + \ kconfigConfOptIfExprOr, + \ kconfigConfOptIfExprEq, + \ kconfigConfOptIfExprNEq + \ skipwhite skipnl + + syn match kconfigConfOptIfExprNCSym '\<\k\+\>' + \ contained + \ nextgroup=@kconfigConfigOptions, + \ kconfigConfOptIfExprAnd, + \ kconfigConfOptIfExprOr, + \ kconfigConfOptIfExprEq, + \ kconfigConfOptIfExprNEq + \ skipwhite skipnl + + syn cluster kconfigConfOptIfExprSym2 contains=kconfigConfOptIfExprCSym2, + \ kconfigConfOptIfExprNCSym2 + + syn match kconfigConfOptIfExprEq '=' + \ contained + \ nextgroup=@kconfigConfOptIfExprSym2 + \ skipwhite + + syn match kconfigConfOptIfExprNEq '!=' + \ contained + \ nextgroup=@kconfigConfOptIfExprSym2 + \ skipwhite + + syn match kconfigConfOptIfExprCSym2 "'[^'\\]*\%(\\.[^'\\]*\)*'" + \ contained + \ nextgroup=@kconfigConfigOptions, + \ kconfigConfOptIfExprAnd, + \ kconfigConfOptIfExprOr + \ skipwhite skipnl + + syn match kconfigConfOptIfExprNCSym2 '\<\k\+\>' + \ contained + \ nextgroup=@kconfigConfigOptions, + \ kconfigConfOptIfExprAnd, + \ kconfigConfOptIfExprOr + \ skipwhite skipnl + + syn match kconfigConfOptIfExprNeg '!' + \ contained + \ nextgroup=@kconfigConfigOptionIfExpr + \ skipwhite + + syn match kconfigConfOptIfExprAnd '&&' + \ contained + \ nextgroup=@kconfigConfigOptionIfExpr + \ skipwhite + + syn match kconfigConfOptIfExprOr '||' + \ contained + \ nextgroup=@kconfigConfigOptionIfExpr + \ skipwhite + + syn match kconfigConfOptIfExprGroup '(' + \ contained + \ nextgroup=@kconfigConfigOptionIfGExp + \ skipwhite + + " TODO: hm, this kind of recursion doesn't work right. We need another set of + " expressions that have kconfigConfigOPtionIfGExp as nextgroup and a matcher + " for '(' that sets it all off. + syn cluster kconfigConfigOptionIfGExp contains=@kconfigConfOptIfGExpSym, + \ kconfigConfOptIfGExpNeg, + \ kconfigConfOptIfExprGroup + + syn cluster kconfigConfOptIfGExpSym contains=kconfigConfOptIfGExpCSym, + \ kconfigConfOptIfGExpNCSym + + syn match kconfigConfOptIfGExpCSym '"[^"\\]*\%(\\.[^"\\]*\)*"' + \ contained + \ nextgroup=@kconfigConfigIf, + \ kconfigConfOptIfGExpAnd, + \ kconfigConfOptIfGExpOr, + \ kconfigConfOptIfGExpEq, + \ kconfigConfOptIfGExpNEq + \ skipwhite skipnl + + syn match kconfigConfOptIfGExpCSym "'[^'\\]*\%(\\.[^'\\]*\)*'" + \ contained + \ nextgroup=@kconfigConfigIf, + \ kconfigConfOptIfGExpAnd, + \ kconfigConfOptIfGExpOr, + \ kconfigConfOptIfGExpEq, + \ kconfigConfOptIfGExpNEq + \ skipwhite skipnl + + syn match kconfigConfOptIfGExpNCSym '\<\k\+\>' + \ contained + \ nextgroup=kconfigConfOptIfExprGrpE, + \ kconfigConfOptIfGExpAnd, + \ kconfigConfOptIfGExpOr, + \ kconfigConfOptIfGExpEq, + \ kconfigConfOptIfGExpNEq + \ skipwhite skipnl + + syn cluster kconfigConfOptIfGExpSym2 contains=kconfigConfOptIfGExpCSym2, + \ kconfigConfOptIfGExpNCSym2 + + syn match kconfigConfOptIfGExpEq '=' + \ contained + \ nextgroup=@kconfigConfOptIfGExpSym2 + \ skipwhite + + syn match kconfigConfOptIfGExpNEq '!=' + \ contained + \ nextgroup=@kconfigConfOptIfGExpSym2 + \ skipwhite + + syn match kconfigConfOptIfGExpCSym2 '"[^"\\]*\%(\\.[^"\\]*\)*"' + \ contained + \ nextgroup=kconfigConfOptIfExprGrpE, + \ kconfigConfOptIfGExpAnd, + \ kconfigConfOptIfGExpOr + \ skipwhite skipnl + + syn match kconfigConfOptIfGExpCSym2 "'[^'\\]*\%(\\.[^'\\]*\)*'" + \ contained + \ nextgroup=kconfigConfOptIfExprGrpE, + \ kconfigConfOptIfGExpAnd, + \ kconfigConfOptIfGExpOr + \ skipwhite skipnl + + syn match kconfigConfOptIfGExpNCSym2 '\<\k\+\>' + \ contained + \ nextgroup=kconfigConfOptIfExprGrpE, + \ kconfigConfOptIfGExpAnd, + \ kconfigConfOptIfGExpOr + \ skipwhite skipnl + + syn match kconfigConfOptIfGExpNeg '!' + \ contained + \ nextgroup=@kconfigConfigOptionIfGExp + \ skipwhite + + syn match kconfigConfOptIfGExpAnd '&&' + \ contained + \ nextgroup=@kconfigConfigOptionIfGExp + \ skipwhite + + syn match kconfigConfOptIfGExpOr '||' + \ contained + \ nextgroup=@kconfigConfigOptionIfGExp + \ skipwhite + + syn match kconfigConfOptIfExprGrpE ')' + \ contained + \ nextgroup=@kconfigConfigOptions, + \ kconfigConfOptIfExprAnd, + \ kconfigConfOptIfExprOr + \ skipwhite skipnl + + + syn cluster kconfigConfigOptionExpr contains=@kconfigConfOptExprSym, + \ kconfigConfOptExprNeg, + \ kconfigConfOptExprGroup + + syn cluster kconfigConfOptExprSym contains=kconfigConfOptExprCSym, + \ kconfigConfOptExprNCSym + + syn match kconfigConfOptExprCSym '"[^"\\]*\%(\\.[^"\\]*\)*"' + \ contained + \ nextgroup=kconfigConfigOptionIf, + \ kconfigConfOptExprAnd, + \ kconfigConfOptExprOr, + \ kconfigConfOptExprEq, + \ kconfigConfOptExprNEq, + \ @kconfigConfigOptions + \ skipwhite skipnl + + syn match kconfigConfOptExprCSym "'[^'\\]*\%(\\.[^'\\]*\)*'" + \ contained + \ nextgroup=kconfigConfigOptionIf, + \ kconfigConfOptExprAnd, + \ kconfigConfOptExprOr, + \ kconfigConfOptExprEq, + \ kconfigConfOptExprNEq, + \ @kconfigConfigOptions + \ skipwhite skipnl + + syn match kconfigConfOptExprNCSym '\<\k\+\>' + \ contained + \ nextgroup=kconfigConfigOptionIf, + \ kconfigConfOptExprAnd, + \ kconfigConfOptExprOr, + \ kconfigConfOptExprEq, + \ kconfigConfOptExprNEq, + \ @kconfigConfigOptions + \ skipwhite skipnl + + syn cluster kconfigConfOptExprSym2 contains=kconfigConfOptExprCSym2, + \ kconfigConfOptExprNCSym2 + + syn match kconfigConfOptExprEq '=' + \ contained + \ nextgroup=@kconfigConfOptExprSym2 + \ skipwhite + + syn match kconfigConfOptExprNEq '!=' + \ contained + \ nextgroup=@kconfigConfOptExprSym2 + \ skipwhite + + syn match kconfigConfOptExprCSym2 '"[^"\\]*\%(\\.[^"\\]*\)*"' + \ contained + \ nextgroup=kconfigConfigOptionIf, + \ kconfigConfOptExprAnd, + \ kconfigConfOptExprOr, + \ @kconfigConfigOptions + \ skipwhite skipnl + + syn match kconfigConfOptExprCSym2 "'[^'\\]*\%(\\.[^'\\]*\)*'" + \ contained + \ nextgroup=kconfigConfigOptionIf, + \ kconfigConfOptExprAnd, + \ kconfigConfOptExprOr, + \ @kconfigConfigOptions + \ skipwhite skipnl + + syn match kconfigConfOptExprNCSym2 '\<\k\+\>' + \ contained + \ nextgroup=kconfigConfigOptionIf, + \ kconfigConfOptExprAnd, + \ kconfigConfOptExprOr, + \ @kconfigConfigOptions + \ skipwhite skipnl + + syn match kconfigConfOptExprNeg '!' + \ contained + \ nextgroup=@kconfigConfigOptionExpr + \ skipwhite + + syn match kconfigConfOptExprAnd '&&' + \ contained + \ nextgroup=@kconfigConfigOptionExpr + \ skipwhite + + syn match kconfigConfOptExprOr '||' + \ contained + \ nextgroup=@kconfigConfigOptionExpr + \ skipwhite + + syn match kconfigConfOptExprGroup '(' + \ contained + \ nextgroup=@kconfigConfigOptionGExp + \ skipwhite + + syn cluster kconfigConfigOptionGExp contains=@kconfigConfOptGExpSym, + \ kconfigConfOptGExpNeg, + \ kconfigConfOptGExpGroup + + syn cluster kconfigConfOptGExpSym contains=kconfigConfOptGExpCSym, + \ kconfigConfOptGExpNCSym + + syn match kconfigConfOptGExpCSym '"[^"\\]*\%(\\.[^"\\]*\)*"' + \ contained + \ nextgroup=kconfigConfOptExprGrpE, + \ kconfigConfOptGExpAnd, + \ kconfigConfOptGExpOr, + \ kconfigConfOptGExpEq, + \ kconfigConfOptGExpNEq + \ skipwhite skipnl + + syn match kconfigConfOptGExpCSym "'[^'\\]*\%(\\.[^'\\]*\)*'" + \ contained + \ nextgroup=kconfigConfOptExprGrpE, + \ kconfigConfOptGExpAnd, + \ kconfigConfOptGExpOr, + \ kconfigConfOptGExpEq, + \ kconfigConfOptGExpNEq + \ skipwhite skipnl + + syn match kconfigConfOptGExpNCSym '\<\k\+\>' + \ contained + \ nextgroup=kconfigConfOptExprGrpE, + \ kconfigConfOptGExpAnd, + \ kconfigConfOptGExpOr, + \ kconfigConfOptGExpEq, + \ kconfigConfOptGExpNEq + \ skipwhite skipnl + + syn cluster kconfigConfOptGExpSym2 contains=kconfigConfOptGExpCSym2, + \ kconfigConfOptGExpNCSym2 + + syn match kconfigConfOptGExpEq '=' + \ contained + \ nextgroup=@kconfigConfOptGExpSym2 + \ skipwhite + + syn match kconfigConfOptGExpNEq '!=' + \ contained + \ nextgroup=@kconfigConfOptGExpSym2 + \ skipwhite + + syn match kconfigConfOptGExpCSym2 '"[^"\\]*\%(\\.[^"\\]*\)*"' + \ contained + \ nextgroup=kconfigConfOptExprGrpE, + \ kconfigConfOptGExpAnd, + \ kconfigConfOptGExpOr + \ skipwhite skipnl + + syn match kconfigConfOptGExpCSym2 "'[^'\\]*\%(\\.[^'\\]*\)*'" + \ contained + \ nextgroup=kconfigConfOptExprGrpE, + \ kconfigConfOptGExpAnd, + \ kconfigConfOptGExpOr + \ skipwhite skipnl + + syn match kconfigConfOptGExpNCSym2 '\<\k\+\>' + \ contained + \ nextgroup=kconfigConfOptExprGrpE, + \ kconfigConfOptGExpAnd, + \ kconfigConfOptGExpOr + \ skipwhite skipnl + + syn match kconfigConfOptGExpNeg '!' + \ contained + \ nextgroup=@kconfigConfigOptionGExp + \ skipwhite + + syn match kconfigConfOptGExpAnd '&&' + \ contained + \ nextgroup=@kconfigConfigOptionGExp + \ skipwhite + + syn match kconfigConfOptGExpOr '||' + \ contained + \ nextgroup=@kconfigConfigOptionGExp + \ skipwhite + + syn match kconfigConfOptExprGrpE ')' + \ contained + \ nextgroup=kconfigConfigOptionIf, + \ kconfigConfOptExprAnd, + \ kconfigConfOptExprOr + \ skipwhite skipnl + + hi def link kconfigTodo Todo + hi def link kconfigComment Comment + hi def link kconfigKeyword Keyword + hi def link kconfigPreProc PreProc + hi def link kconfigConditional Conditional + hi def link kconfigPrompt String + hi def link kconfigKeywordPrompt kconfigPrompt + hi def link kconfigPath String + hi def link kconfigSymbol String + hi def link kconfigConstantSymbol Constant + hi def link kconfigConfigOption Type + hi def link kconfigTypeDefinition kconfigConfigOption + hi def link kconfigTypeDefPrompt kconfigPrompt + hi def link kconfigInputPrompt kconfigConfigOption + hi def link kconfigPromptPrompt kconfigPrompt + hi def link kconfigDefaultValue kconfigConfigOption + hi def link kconfigDependencies kconfigConfigOption + hi def link kconfigReverseDependencies kconfigConfigOption + hi def link kconfigRevDepCSymbol kconfigConstantSymbol + hi def link kconfigRevDepNCSymbol kconfigSymbol + hi def link kconfigNumericalRanges kconfigConfigOption + hi def link kconfigRangeCSymbol kconfigConstantSymbol + hi def link kconfigRangeNCSymbol kconfigSymbol + hi def link kconfigRangeCSymbol2 kconfigConstantSymbol + hi def link kconfigRangeNCSymbol2 kconfigSymbol + hi def link kconfigHelpText Normal + hi def link kconfigDefBool kconfigConfigOption + hi def link kconfigDefBoolCSymbol kconfigConstantSymbol + hi def link kconfigDefBoolNCSymbol kconfigSymbol + hi def link kconfigOptional kconfigConfigOption + hi def link kconfigConfigOptionIf Conditional + hi def link kconfigConfOptIfExprCSym kconfigConstantSymbol + hi def link kconfigConfOptIfExprNCSym kconfigSymbol + hi def link kconfigOperator Operator + hi def link kconfigConfOptIfExprEq kconfigOperator + hi def link kconfigConfOptIfExprNEq kconfigOperator + hi def link kconfigConfOptIfExprCSym2 kconfigConstantSymbol + hi def link kconfigConfOptIfExprNCSym2 kconfigSymbol + hi def link kconfigConfOptIfExprNeg kconfigOperator + hi def link kconfigConfOptIfExprAnd kconfigOperator + hi def link kconfigConfOptIfExprOr kconfigOperator + hi def link kconfigDelimiter Delimiter + hi def link kconfigConfOptIfExprGroup kconfigDelimiter + hi def link kconfigConfOptIfGExpCSym kconfigConstantSymbol + hi def link kconfigConfOptIfGExpNCSym kconfigSymbol + hi def link kconfigConfOptIfGExpEq kconfigOperator + hi def link kconfigConfOptIfGExpNEq kconfigOperator + hi def link kconfigConfOptIfGExpCSym2 kconfigConstantSymbol + hi def link kconfigConfOptIfGExpNCSym2 kconfigSymbol + hi def link kconfigConfOptIfGExpNeg kconfigOperator + hi def link kconfigConfOptIfGExpAnd kconfigOperator + hi def link kconfigConfOptIfGExpOr kconfigOperator + hi def link kconfigConfOptIfExprGrpE kconfigDelimiter + hi def link kconfigConfOptExprCSym kconfigConstantSymbol + hi def link kconfigConfOptExprNCSym kconfigSymbol + hi def link kconfigConfOptExprEq kconfigOperator + hi def link kconfigConfOptExprNEq kconfigOperator + hi def link kconfigConfOptExprCSym2 kconfigConstantSymbol + hi def link kconfigConfOptExprNCSym2 kconfigSymbol + hi def link kconfigConfOptExprNeg kconfigOperator + hi def link kconfigConfOptExprAnd kconfigOperator + hi def link kconfigConfOptExprOr kconfigOperator + hi def link kconfigConfOptExprGroup kconfigDelimiter + hi def link kconfigConfOptGExpCSym kconfigConstantSymbol + hi def link kconfigConfOptGExpNCSym kconfigSymbol + hi def link kconfigConfOptGExpEq kconfigOperator + hi def link kconfigConfOptGExpNEq kconfigOperator + hi def link kconfigConfOptGExpCSym2 kconfigConstantSymbol + hi def link kconfigConfOptGExpNCSym2 kconfigSymbol + hi def link kconfigConfOptGExpNeg kconfigOperator + hi def link kconfigConfOptGExpAnd kconfigOperator + hi def link kconfigConfOptGExpOr kconfigOperator + hi def link kconfigConfOptExprGrpE kconfigConfOptIfExprGroup else -syn keyword kconfigTodo contained TODO FIXME XXX NOTE + syn keyword kconfigTodo contained TODO FIXME XXX NOTE -syn match kconfigComment display '#.*$' contains=kconfigTodo + syn match kconfigComment display '#.*$' contains=kconfigTodo -syn keyword kconfigKeyword config menuconfig comment mainmenu + syn keyword kconfigKeyword config menuconfig comment mainmenu -syn keyword kconfigConditional menu endmenu choice endchoice if endif + syn keyword kconfigConditional menu endmenu choice endchoice if endif -syn keyword kconfigPreProc source - \ nextgroup=kconfigPath - \ skipwhite + syn keyword kconfigPreProc source + \ nextgroup=kconfigPath + \ skipwhite -syn keyword kconfigTriState y m n + syn keyword kconfigTriState y m n -syn match kconfigSpecialChar contained '\\.' -syn match kconfigSpecialChar '\\$' + syn match kconfigSpecialChar contained '\\.' + syn match kconfigSpecialChar '\\$' -syn region kconfigPath matchgroup=kconfigPath - \ start=+"+ skip=+\\\\\|\\\"+ end=+"+ - \ contains=kconfigSpecialChar + syn region kconfigPath matchgroup=kconfigPath + \ start=+"+ skip=+\\\\\|\\\"+ end=+"+ + \ contains=kconfigSpecialChar -syn region kconfigPath matchgroup=kconfigPath - \ start=+'+ skip=+\\\\\|\\\'+ end=+'+ - \ contains=kconfigSpecialChar + syn region kconfigPath matchgroup=kconfigPath + \ start=+'+ skip=+\\\\\|\\\'+ end=+'+ + \ contains=kconfigSpecialChar -syn match kconfigPath '\S\+' - \ contained + syn match kconfigPath '\S\+' + \ contained -syn region kconfigString matchgroup=kconfigString - \ start=+"+ skip=+\\\\\|\\\"+ end=+"+ - \ contains=kconfigSpecialChar + syn region kconfigString matchgroup=kconfigString + \ start=+"+ skip=+\\\\\|\\\"+ end=+"+ + \ contains=kconfigSpecialChar -syn region kconfigString matchgroup=kconfigString - \ start=+'+ skip=+\\\\\|\\\'+ end=+'+ - \ contains=kconfigSpecialChar + syn region kconfigString matchgroup=kconfigString + \ start=+'+ skip=+\\\\\|\\\'+ end=+'+ + \ contains=kconfigSpecialChar -syn keyword kconfigType bool boolean tristate string hex int + syn keyword kconfigType bool boolean tristate string hex int -syn keyword kconfigOption prompt default requires select range - \ optional -syn match kconfigOption 'depends\%( on\)\=' + syn keyword kconfigOption prompt default requires select range + \ optional + syn match kconfigOption 'depends\%( on\)\=' -syn keyword kconfigMacro def_bool def_tristate + syn keyword kconfigMacro def_bool def_tristate -syn region kconfigHelpText - \ matchgroup=kconfigOption - \ start='\%(help\|---help---\)\ze\s*\n\z(\s\+\)' - \ skip='^$' - \ end='^\z1\@!' + syn region kconfigHelpText + \ matchgroup=kconfigOption + \ start='\%(help\|---help---\)\ze\s*\n\z(\s\+\)' + \ skip='^$' + \ end='^\z1\@!' -hi def link kconfigTodo Todo -hi def link kconfigComment Comment -hi def link kconfigKeyword Keyword -hi def link kconfigConditional Conditional -hi def link kconfigPreProc PreProc -hi def link kconfigTriState Boolean -hi def link kconfigSpecialChar SpecialChar -hi def link kconfigPath String -hi def link kconfigString String -hi def link kconfigType Type -hi def link kconfigOption Identifier -hi def link kconfigHelpText Normal -hi def link kconfigmacro Macro + hi def link kconfigTodo Todo + hi def link kconfigComment Comment + hi def link kconfigKeyword Keyword + hi def link kconfigConditional Conditional + hi def link kconfigPreProc PreProc + hi def link kconfigTriState Boolean + hi def link kconfigSpecialChar SpecialChar + hi def link kconfigPath String + hi def link kconfigString String + hi def link kconfigType Type + hi def link kconfigOption Identifier + hi def link kconfigHelpText Normal + hi def link kconfigmacro Macro endif diff --git a/runtime/syntax/lnk.vim b/runtime/syntax/lnk.vim new file mode 100644 index 0000000000..45ca382aca --- /dev/null +++ b/runtime/syntax/lnk.vim @@ -0,0 +1,40 @@ +" Vim syntax file +" Language: TI linker command file +" Document: https://downloads.ti.com/docs/esd/SPRUI03A/Content/SPRUI03A_HTML/linker_description.html +" Document: https://software-dl.ti.com/ccs/esd/documents/sdto_cgt_Linker-Command-File-Primer.html +" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu> +" Last Change: 2024 Dec 31 + +if exists("b:current_syntax") + finish +endif + +runtime! syntax/cmacro.vim + +syn case ignore +syn match lnkNumber "0x[0-9a-f]\+" +" Linker command files are ASCII files that contain one or more of the following: +" Input filenames, which specify object files, archive libraries, or other command files. +" Linker options, which can be used in the command file in the same manner that they are used on the command line +syn match lnkOption "^[-+][-_a-zA-Z#@]\+" +syn match lnkOption "^--[^ \t$=`'"|);]\+" +syn match lnkFile '[^ =]\+\%(\.\S\+\)\+\>' +syn match lnkLibFile '[^ =]\+\.lib\>' +" The MEMORY and SECTIONS linker directives. The MEMORY directive defines the target memory configuration (see Section 8.5.4). The SECTIONS directive controls how sections are built and allocated (see Section 8.5.5.) +syn keyword lnkKeyword ADDRESS_MASK f LOAD ORIGIN START ALGORITHM FILL LOAD_END PAGE TABLE ALIGN GROUP LOAD_SIZE PALIGN TYPE ATTR HAMMING_MASK LOAD_START PARITY_MASK UNION BLOCK HIGH MEMORY RUN UNORDERED COMPRESSION INPUT_PAGE MIRRORING RUN_END VFILL COPY INPUT_RANGE NOINIT RUN_SIZE DSECT l NOLOAD RUN_START ECC LEN o SECTIONS END LENGTH ORG SIZE +syn region lnkLibrary start=+<+ end=+>+ +syn match lnkAttrib '\<[RWXI]\+\>' +syn match lnkSections '\<\.\k\+' +" Assignment statements, which define and assign values to global symbols +syn case match + +hi def link lnkNumber Number +hi def link lnkOption Special +hi def link lnkKeyword Keyword +hi def link lnkLibrary String +hi def link lnkFile String +hi def link lnkLibFile Special +hi def link lnkAttrib Type +hi def link lnkSections Macro + +let b:current_syntax = "lnk" diff --git a/runtime/syntax/lnkmap.vim b/runtime/syntax/lnkmap.vim new file mode 100644 index 0000000000..dc097f412f --- /dev/null +++ b/runtime/syntax/lnkmap.vim @@ -0,0 +1,35 @@ +" Vim syntax file +" Language: TI Linker map +" Document: https://downloads.ti.com/docs/esd/SPRUI03A/Content/SPRUI03A_HTML/linker_description.html +" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu> +" Last Change: 2024 Dec 30 + +if exists("b:current_syntax") + finish +endif + +syn match lnkmapTime ">> .*$" +syn region lnkmapHeadline start="^\*\+$" end="^\*\+$" +syn match lnkmapHeadline "^[A-Z][-A-Z0-9 ']*\ze\%(:\|$\)" +syn match lnkmapSectionDelim "^=\+$" +syn match lnkmapTableDelim "\%(^\|\s\)\zs---*\ze\%($\|\s\)" +syn match lnkmapNumber "\%(^\|\s\)\zs[0-9a-f]\+\ze\%($\|\s\)" +syn match lnkmapSections '\<\.\k\+\>' +syn match lnkmapFile '[^ =]\+\%(\.\S\+\)\+\>' +syn match lnkmapLibFile '[^ =]\+\.lib\>' +syn match lnkmapAttrib '\<[RWIX]\+\>' +syn match lnkmapAttrib '\s\zs--HOLE--\ze\%\(\s\|$\)' +syn keyword lnkmapAttrib UNINITIALIZED DESCT + + +hi def link lnkmapTime Comment +hi def link lnkmapHeadline Title +hi def link lnkmapSectionDelim PreProc +hi def link lnkmapTableDelim PreProc +hi def link lnkmapNumber Number +hi def link lnkmapSections Macro +hi def link lnkmapFile String +hi def link lnkmapLibFile Special +hi def link lnkmapAttrib Type + +let b:current_syntax = "lnkmap" diff --git a/runtime/syntax/lyrics.vim b/runtime/syntax/lyrics.vim index fd127988f2..48a5b1171c 100644 --- a/runtime/syntax/lyrics.vim +++ b/runtime/syntax/lyrics.vim @@ -2,7 +2,7 @@ " Language: LyRiCs " Maintainer: ObserverOfTime <chronobserver@disroot.org> " Filenames: *.lrc -" Last Change: 2024 Sep 20 +" Last Change: 2025 Jan 13 if exists('b:current_syntax') finish @@ -23,7 +23,7 @@ syn match lrcTagName contained nextgroup=lrcTagValue syn match lrcTagValue /:\zs.\+\ze\]/ contained " Lyrics -syn match lrcLyricTime /^\s*\(\[\d\d:\d\d\.\d\d\]\)\+/ +syn match lrcLyricTime /^\s*\(\[\d\d:\d\d\.\d\d\d\?\]\)\+/ \ contains=lrcNumber nextgroup=lrcLyricLine syn match lrcLyricLine /.*$/ contained contains=lrcWordTime,@Spell syn match lrcWordTime /<\d\d:\d\d\.\d\d>/ contained contains=lrcNumber,@NoSpell diff --git a/runtime/syntax/netrw.vim b/runtime/syntax/netrw.vim deleted file mode 100644 index f5b7fdc2c6..0000000000 --- a/runtime/syntax/netrw.vim +++ /dev/null @@ -1,148 +0,0 @@ -" Language : Netrw Listing Syntax -" Maintainer: This runtime file is looking for a new maintainer. -" Former Maintainer: Charles E. Campbell -" Last Change: Nov 07, 2019 -" 2024 Feb 19 by Vim Project (announce adoption) -" Version : 20 -" --------------------------------------------------------------------- -if exists("b:current_syntax") - finish -endif - -" --------------------------------------------------------------------- -" Directory List Syntax Highlighting: {{{1 -syn cluster NetrwGroup contains=netrwHide,netrwSortBy,netrwSortSeq,netrwQuickHelp,netrwVersion,netrwCopyTgt -syn cluster NetrwTreeGroup contains=netrwDir,netrwSymLink,netrwExe - -syn match netrwPlain "\(\S\+ \)*\S\+" contains=netrwLink,@NoSpell -syn match netrwSpecial "\%(\S\+ \)*\S\+[*|=]\ze\%(\s\{2,}\|$\)" contains=netrwClassify,@NoSpell -syn match netrwDir "\.\{1,2}/" contains=netrwClassify,@NoSpell -syn match netrwDir "\%(\S\+ \)*\S\+/\ze\%(\s\{2,}\|$\)" contains=netrwClassify,@NoSpell -syn match netrwSizeDate "\<\d\+\s\d\{1,2}/\d\{1,2}/\d\{4}\s" skipwhite contains=netrwDateSep,@NoSpell nextgroup=netrwTime -syn match netrwSymLink "\%(\S\+ \)*\S\+@\ze\%(\s\{2,}\|$\)" contains=netrwClassify,@NoSpell -syn match netrwExe "\%(\S\+ \)*\S*[^~]\*\ze\%(\s\{2,}\|$\)" contains=netrwClassify,@NoSpell -if has("gui_running") && (&enc == 'utf-8' || &enc == 'utf-16' || &enc == 'ucs-4') -syn match netrwTreeBar "^\%([-+|│] \)\+" contains=netrwTreeBarSpace nextgroup=@netrwTreeGroup -else -syn match netrwTreeBar "^\%([-+|] \)\+" contains=netrwTreeBarSpace nextgroup=@netrwTreeGroup -endif -syn match netrwTreeBarSpace " " contained - -syn match netrwClassify "[*=|@/]\ze\%(\s\{2,}\|$\)" contained -syn match netrwDateSep "/" contained -syn match netrwTime "\d\{1,2}:\d\{2}:\d\{2}" contained contains=netrwTimeSep -syn match netrwTimeSep ":" - -syn match netrwComment '".*\%(\t\|$\)' contains=@NetrwGroup,@NoSpell -syn match netrwHide '^"\s*\(Hid\|Show\)ing:' skipwhite contains=@NoSpell nextgroup=netrwHidePat -syn match netrwSlash "/" contained -syn match netrwHidePat "[^,]\+" contained skipwhite contains=@NoSpell nextgroup=netrwHideSep -syn match netrwHideSep "," contained skipwhite nextgroup=netrwHidePat -syn match netrwSortBy "Sorted by" contained transparent skipwhite nextgroup=netrwList -syn match netrwSortSeq "Sort sequence:" contained transparent skipwhite nextgroup=netrwList -syn match netrwCopyTgt "Copy/Move Tgt:" contained transparent skipwhite nextgroup=netrwList -syn match netrwList ".*$" contained contains=netrwComma,@NoSpell -syn match netrwComma "," contained -syn region netrwQuickHelp matchgroup=Comment start="Quick Help:\s\+" end="$" contains=netrwHelpCmd,netrwQHTopic,@NoSpell keepend contained -syn match netrwHelpCmd "\S\+\ze:" contained skipwhite contains=@NoSpell nextgroup=netrwCmdSep -syn match netrwQHTopic "([a-zA-Z &]\+)" contained skipwhite -syn match netrwCmdSep ":" contained nextgroup=netrwCmdNote -syn match netrwCmdNote ".\{-}\ze " contained contains=@NoSpell -syn match netrwVersion "(netrw.*)" contained contains=@NoSpell -syn match netrwLink "-->" contained skipwhite - -" ----------------------------- -" Special filetype highlighting {{{1 -" ----------------------------- -if exists("g:netrw_special_syntax") && g:netrw_special_syntax - if exists("+suffixes") && &suffixes != "" - let suflist= join(split(&suffixes,',')) - let suflist= escape(substitute(suflist," ",'\\|','g'),'.~') - exe "syn match netrwSpecFile '\\(\\S\\+ \\)*\\S*\\(".suflist."\\)\\>' contains=netrwTreeBar,@NoSpell" - endif - syn match netrwBak "\(\S\+ \)*\S\+\.bak\>" contains=netrwTreeBar,@NoSpell - syn match netrwCompress "\(\S\+ \)*\S\+\.\%(gz\|bz2\|Z\|zip\)\>" contains=netrwTreeBar,@NoSpell - if has("unix") - syn match netrwCoreDump "\<core\%(\.\d\+\)\=\>" contains=netrwTreeBar,@NoSpell - endif - syn match netrwLex "\(\S\+ \)*\S\+\.\%(l\|lex\)\>" contains=netrwTreeBar,@NoSpell - syn match netrwYacc "\(\S\+ \)*\S\+\.y\>" contains=netrwTreeBar,@NoSpell - syn match netrwData "\(\S\+ \)*\S\+\.dat\>" contains=netrwTreeBar,@NoSpell - syn match netrwDoc "\(\S\+ \)*\S\+\.\%(doc\|txt\|pdf\|ps\|docx\)\>" contains=netrwTreeBar,@NoSpell - syn match netrwHdr "\(\S\+ \)*\S\+\.\%(h\|hpp\)\>" contains=netrwTreeBar,@NoSpell - syn match netrwLib "\(\S\+ \)*\S*\.\%(a\|so\|lib\|dll\)\>" contains=netrwTreeBar,@NoSpell - syn match netrwMakeFile "\<[mM]akefile\>\|\(\S\+ \)*\S\+\.mak\>" contains=netrwTreeBar,@NoSpell - syn match netrwObj "\(\S\+ \)*\S*\.\%(o\|obj\)\>" contains=netrwTreeBar,@NoSpell - syn match netrwPix "\c\(\S\+ \)*\S*\.\%(bmp\|fits\=\|gif\|je\=pg\|pcx\|ppc\|pgm\|png\|ppm\|psd\|rgb\|tif\|xbm\|xcf\)\>" contains=netrwTreeBar,@NoSpell - syn match netrwTags "\<\(ANmenu\|ANtags\)\>" contains=netrwTreeBar,@NoSpell - syn match netrwTags "\<tags\>" contains=netrwTreeBar,@NoSpell - syn match netrwTilde "\(\S\+ \)*\S\+\~\*\=\>" contains=netrwTreeBar,@NoSpell - syn match netrwTmp "\<tmp\(\S\+ \)*\S\+\>\|\(\S\+ \)*\S*tmp\>" contains=netrwTreeBar,@NoSpell -endif - -" --------------------------------------------------------------------- -" Highlighting Links: {{{1 -if !exists("did_drchip_netrwlist_syntax") - let did_drchip_netrwlist_syntax= 1 - hi default link netrwClassify Function - hi default link netrwCmdSep Delimiter - hi default link netrwComment Comment - hi default link netrwDir Directory - hi default link netrwHelpCmd Function - hi default link netrwQHTopic Number - hi default link netrwHidePat Statement - hi default link netrwHideSep netrwComment - hi default link netrwList Statement - hi default link netrwVersion Identifier - hi default link netrwSymLink Question - hi default link netrwExe PreProc - hi default link netrwDateSep Delimiter - - hi default link netrwTreeBar Special - hi default link netrwTimeSep netrwDateSep - hi default link netrwComma netrwComment - hi default link netrwHide netrwComment - hi default link netrwMarkFile TabLineSel - hi default link netrwLink Special - - " special syntax highlighting (see :he g:netrw_special_syntax) - hi default link netrwCoreDump WarningMsg - hi default link netrwData Folded - hi default link netrwHdr netrwPlain - hi default link netrwLex netrwPlain - hi default link netrwLib DiffChange - hi default link netrwMakefile DiffChange - hi default link netrwYacc netrwPlain - hi default link netrwPix Special - - hi default link netrwBak netrwGray - hi default link netrwCompress netrwGray - hi default link netrwSpecFile netrwGray - hi default link netrwObj netrwGray - hi default link netrwTags netrwGray - hi default link netrwTilde netrwGray - hi default link netrwTmp netrwGray -endif - - " set up netrwGray to be understated (but not Ignore'd or Conceal'd, as those - " can be hard/impossible to read). Users may override this in a colorscheme by - " specifying netrwGray highlighting. - redir => s:netrwgray - sil hi netrwGray - redir END - if s:netrwgray !~ 'guifg' - if has("gui") && has("gui_running") - if &bg == "dark" - exe "hi netrwGray gui=NONE guifg=gray30" - else - exe "hi netrwGray gui=NONE guifg=gray70" - endif - else - hi link netrwGray Folded - endif - endif - -" Current Syntax: {{{1 -let b:current_syntax = "netrwlist" -" --------------------------------------------------------------------- -" vim: ts=8 fdm=marker diff --git a/runtime/syntax/opencl.vim b/runtime/syntax/opencl.vim new file mode 100644 index 0000000000..c237aa30f9 --- /dev/null +++ b/runtime/syntax/opencl.vim @@ -0,0 +1,13 @@ +" Vim syntax file +" Language: OpenCL +" Last Change: 2024 Nov 19 +" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu> + +if exists("b:current_syntax") + finish +endif + +" TODO: support openCL specific keywords +runtime! syntax/c.vim + +let current_syntax = "opencl" diff --git a/runtime/syntax/po.vim b/runtime/syntax/po.vim index 08d6baec27..6da27f639d 100644 --- a/runtime/syntax/po.vim +++ b/runtime/syntax/po.vim @@ -1,10 +1,11 @@ " Vim syntax file " Language: po (gettext) " Maintainer: Dwayne Bailey <dwayne@translate.org.za> -" Last Change: 2015 Jun 07 +" Last Change: 2024 Nov 28 " Contributors: Dwayne Bailey (Most advanced syntax highlighting) " Leonardo Fontenelle (Spell checking) " Nam SungHyun <namsh@kldp.org> (Original maintainer) +" Eisuke Kawashima (add format-flags: #16132) " quit when a syntax file was already loaded if exists("b:current_syntax") @@ -32,9 +33,9 @@ syn region poMsgCTxt matchgroup=poStatementMsgCTxt start=+^msgctxt "+rs=e-1 syn region poMsgID matchgroup=poStatementMsgid start=+^msgid "+rs=e-1 matchgroup=poStringID end=+^msgstr\(\|\[[\]0\[]\]\) "+me=s-1 contains=poStringID,poStatementMsgidplural,poStatementMsgid syn region poMsgSTR matchgroup=poStatementMsgstr start=+^msgstr\(\|\[[\]0\[]\]\) "+rs=e-1 matchgroup=poStringSTR end=+\n\n+me=s-1 contains=poStringSTR,poStatementMsgstr syn region poStringCTxt start=+"+ skip=+\\\\\|\\"+ end=+"+ -syn region poStringID start=+"+ skip=+\\\\\|\\"+ end=+"+ contained +syn region poStringID start=+"+ skip=+\\\\\|\\"+ end=+"+ contained \ contains=poSpecial,poFormat,poCommentKDE,poPluralKDE,poKDEdesktopFile,poHtml,poAcceleratorId,poHtmlNot,poVariable -syn region poStringSTR start=+"+ skip=+\\\\\|\\"+ end=+"+ contained +syn region poStringSTR start=+"+ skip=+\\\\\|\\"+ end=+"+ contained \ contains=@Spell,poSpecial,poFormat,poHeaderItem,poCommentKDEError,poHeaderUndefined,poPluralKDEError,poMsguniqError,poKDEdesktopFile,poHtml,poAcceleratorStr,poHtmlNot,poVariable " Header and Copyright @@ -45,13 +46,43 @@ syn match poCopyrightUnset "SOME DESCRIPTIVE TITLE\|FIRST AUTHOR <EMAIL@ADDR " Translation comment block including: translator comment, automatic comments, flags and locations syn match poComment "^#.*$" syn keyword poFlagFuzzy fuzzy contained + +syn match poFlagFormat /\<\%(no-\)\?awk-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?boost-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?c++-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?c-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?csharp-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?elisp-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?gcc-internal-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?gfc-internal-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?java-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?java-printf-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?javascript-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?kde-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?librep-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?lisp-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?lua-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?objc-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?object-pascal-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?perl-brace-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?perl-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?php-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?python-brace-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?python-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?qt-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?qt-plural-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?ruby-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?scheme-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?sh-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?smalltalk-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?tcl-format\>/ contained +syn match poFlagFormat /\<\%(no-\)\?ycp-format\>/ contained + syn match poCommentTranslator "^# .*$" contains=poCopyrightUnset -syn match poCommentAutomatic "^#\..*$" +syn match poCommentAutomatic "^#\..*$" syn match poCommentSources "^#:.*$" -syn match poCommentFlags "^#,.*$" contains=poFlagFuzzy -syn match poDiffOld '\(^#| "[^{]*+}\|{+[^}]*+}\|{+[^}]*\|"$\)' contained -syn match poDiffNew '\(^#| "[^{]*-}\|{-[^}]*-}\|{-[^}]*\|"$\)' contained -syn match poCommentDiff "^#|.*$" contains=poDiffOld,poDiffNew +syn match poCommentFlags "^#,.*$" contains=poFlagFuzzy,poFlagFormat +syn match poCommentPrevious "^#|.*$" " Translations (also includes header fields as they appear in a translation msgstr) syn region poCommentKDE start=+"_: +ms=s+1 end="\\n" end="\"\n^msgstr"me=s-1 contained @@ -66,13 +97,13 @@ syn match poFormat "%%" contained syn region poMsguniqError matchgroup=poMsguniqErrorMarkers start="#-#-#-#-#" end='#\("\n"\|\)-\("\n"\|\)#\("\n"\|\)-\("\n"\|\)#\("\n"\|\)-\("\n"\|\)#\("\n"\|\)-\("\n"\|\)#\("\n"\|\)\\n' contained " Obsolete messages -syn match poObsolete "^#\~.*$" +syn match poObsolete "^#\~.*$" " KDE Name= handling syn match poKDEdesktopFile "\"\(Name\|Comment\|GenericName\|Description\|Keywords\|About\)="ms=s+1,me=e-1 " Accelerator keys - this messes up if the preceding or following char is a multibyte unicode char -syn match poAcceleratorId contained "[^&_~][&_~]\(\a\|\d\)[^:]"ms=s+1,me=e-1 +syn match poAcceleratorId contained "[^&_~][&_~]\(\a\|\d\)[^:]"ms=s+1,me=e-1 syn match poAcceleratorStr contained "[^&_~][&_~]\(\a\|\d\)[^:]"ms=s+1,me=e-1 contains=@Spell " Variables simple @@ -86,11 +117,10 @@ hi def link poComment Comment hi def link poCommentAutomatic Comment hi def link poCommentTranslator Comment hi def link poCommentFlags Special -hi def link poCommentDiff Comment +hi def link poCommentPrevious Comment hi def link poCopyrightUnset Todo hi def link poFlagFuzzy Todo -hi def link poDiffOld Todo -hi def link poDiffNew Special +hi def link poFlagFormat Todo hi def link poObsolete Comment hi def link poStatementMsgid Statement diff --git a/runtime/syntax/ptx.vim b/runtime/syntax/ptx.vim new file mode 100644 index 0000000000..98de4ff6d3 --- /dev/null +++ b/runtime/syntax/ptx.vim @@ -0,0 +1,52 @@ +" Vim syntax file +" Language: Nvidia PTX (Parallel Thread Execution) +" Maintainer: Yinzuo Jiang <jiangyinzuo@foxmail.com> +" Latest Revision: 2024-12-05 + +if exists("b:current_syntax") + finish +endif + +let s:cpo_save = &cpo +set cpo&vim + +syntax iskeyword .,_,a-z,48-57 + +" https://docs.nvidia.com/cuda/parallel-thread-execution/#directives +syntax keyword ptxFunction .entry .func +syntax keyword ptxDirective .branchtargets .file .loc .secion .maxnctapersm .maxnreg .minnctapersm .noreturn .pragma .reqntid .target .version .weak +syntax keyword ptxOperator .address_size .alias .align .callprototype .calltargets +syntax keyword ptxStorageClass .common .const .extern .global .local .param .reg .sreg .shared .tex .visible +syntax keyword ptxType .explicitcluster .maxclusterrank .reqnctapercluster + +" https://docs.nvidia.com/cuda/parallel-thread-execution/#fundamental-types +" signed integer +syntax keyword ptxType .s8 .s16 .s32 .s64 +" unsigned integer +syntax keyword ptxType .u8 .u16 .u32 .u64 +" floating-point +syntax keyword ptxType .f16 .f16x2 .f32 .f64 +" bits (untyped) +syntax keyword ptxType .b8 .b16 .b32 .b64 .b128 +" predicate +syntax keyword ptxType .pred + +" https://docs.nvidia.com/cuda/parallel-thread-execution/#instruction-statements +syntax keyword ptxStatement ret + +syntax region ptxCommentL start="//" skip="\\$" end="$" keepend +syntax region ptxComment matchgroup=ptxCommentStart start="/\*" end="\*/" extend + +hi def link ptxFunction Function +hi def link ptxDirective Keyword +hi def link ptxOperator Operator +hi def link ptxStorageClass StorageClass +hi def link ptxType Type +hi def link ptxStatement Statement + +hi def link ptxCommentL ptxComment +hi def link ptxCommentStart ptxComment +hi def link ptxComment Comment + +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/runtime/syntax/query.lua b/runtime/syntax/query.lua index 2dfe29f69b..0de08b4dfb 100644 --- a/runtime/syntax/query.lua +++ b/runtime/syntax/query.lua @@ -1,6 +1,5 @@ -- Neovim syntax file -- Language: Treesitter query --- Last Change: 2024 Jul 03 -- it's a lisp! vim.cmd([[runtime! syntax/lisp.vim]]) diff --git a/runtime/syntax/sh.vim b/runtime/syntax/sh.vim index 97e74d205f..67268cdfe3 100644 --- a/runtime/syntax/sh.vim +++ b/runtime/syntax/sh.vim @@ -4,6 +4,9 @@ " Previous Maintainers: Charles E. Campbell " Lennart Schultz <Lennart.Schultz@ecmwf.int> " Last Change: 2024 Mar 04 by Vim Project +" 2024 Nov 03 by Aliaksei Budavei <0x000c70 AT gmail DOT com> (improved bracket expressions, #15941) +" 2025 Jan 06 add $PS0 to bashSpecialVariables (#16394) +" 2025 Jan 18 add bash coproc, remove duplicate syn keywords (#16467) " Version: 208 " Former URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_SH " For options and settings, please use: :help ft-sh-syntax @@ -22,7 +25,7 @@ elseif getline(1) =~ '\<bash\>' elseif getline(1) =~ '\<dash\>' let b:is_dash = 1 elseif !exists("g:is_kornshell") && !exists("g:is_bash") && !exists("g:is_posix") && !exists("g:is_sh") && !exists("g:is_dash") - " user did not specify which shell to use, and + " user did not specify which shell to use, and " the script itself does not specify which shell to use. FYI: /bin/sh is ambiguous. " Assuming /bin/sh is executable, and if its a link, find out what it links to. let s:shell = "" @@ -127,6 +130,50 @@ else com! -nargs=* ShFoldIfDoFor <args> endif +" Generate bracket expression items {{{1 +" ================================= +" Note that the following function can be invoked as many times as necessary +" provided that these constraints hold for the passed dictionary argument: +" - every time a unique group-name value is assigned to the "itemGroup" key; +" - only ONCE either the "extraArgs" key is not entered or it is entered and +" its value does not have "contained" among other optional arguments (":help +" :syn-arguments"). +fun! s:GenerateBracketExpressionItems(dict) abort + let itemGroup = a:dict.itemGroup + let bracketGroup = a:dict.bracketGroup + let invGroup = itemGroup . 'Inv' + let skipLeftBracketGroup = itemGroup . 'SkipLeftBracket' + let skipRightBracketGroup = itemGroup . 'SkipRightBracket' + let extraArgs = has_key(a:dict, 'extraArgs') ? a:dict.extraArgs : '' + + " Make the leading "[!^]" stand out in a NON-matching expression. + exec 'syn match ' . invGroup . ' contained "\[\@<=[!^]"' + + " Set up indirections for unbalanced-bracket highlighting. + exec 'syn region ' . skipRightBracketGroup . ' contained matchgroup=' . bracketGroup . ' start="\[\%([!^]\=\\\=\]\)\@=" matchgroup=shCollSymb end="\[\.[^]]\{-}\][^]]\{-}\.\]" matchgroup=' . itemGroup . ' end="\]" contains=@shBracketExprList,shDoubleQuote,' . invGroup + exec 'syn region ' . skipLeftBracketGroup . ' contained matchgroup=' . bracketGroup . ' start="\[\%([!^]\=\\\=\]\)\@=" skip="[!^]\=\\\=\]\%(\[[^]]\+\]\|[^]]\)\{-}\%(\[[:.=]\@!\)\@=" matchgroup=' . itemGroup . ' end="\[[:.=]\@!" contains=@shBracketExprList,shDoubleQuote,' . invGroup + + " Look for a general matching expression. + exec 'syn region ' . itemGroup . ' matchgroup=' . bracketGroup . ' start="\[\S\@=" end="\]" contains=@shBracketExprList,shDoubleQuote ' . extraArgs + " Look for a general NON-matching expression. + exec 'syn region ' . itemGroup . ' matchgroup=' . bracketGroup . ' start="\[[!^]\@=" end="\]" contains=@shBracketExprList,shDoubleQuote,' . invGroup . ' ' . extraArgs + + " Accommodate unbalanced brackets in bracket expressions. The supported + " syntax for a plain "]" can be: "[]ws]" and "[^]ws]"; or, "[ws[.xs]ys.]zs]" + " and "[^ws[.xs]ys.]zs]"; see §9.3.5 RE Bracket Expression (in XBD). + exec 'syn region ' . itemGroup . ' matchgroup=NONE start="\[[!^]\=\\\=\]" matchgroup=' . bracketGroup . ' end="\]" contains=@shBracketExprList,shDoubleQuote,' . skipRightBracketGroup . ' ' . extraArgs + " Strive to handle "[]...[]" etc. + exec 'syn region ' . itemGroup . ' matchgroup=NONE start="\[[!^]\=\\\=\]\%(\[[^]]\+\]\|[^]]\)\{-}\[[:.=]\@!" matchgroup=' . bracketGroup . ' end="\]" contains=@shBracketExprList,shDoubleQuote,' . skipLeftBracketGroup . ' ' . extraArgs + + if !exists("g:skip_sh_syntax_inits") + exec 'hi def link ' . skipLeftBracketGroup . ' ' . itemGroup + exec 'hi def link ' . skipRightBracketGroup . ' ' . itemGroup + exec 'hi def link ' . invGroup . ' Underlined' + endif +endfun + +call s:GenerateBracketExpressionItems({'itemGroup': 'shBracketExpr', 'bracketGroup': 'shBracketExprDelim'}) + " sh syntax is case sensitive {{{1 syn case match @@ -136,23 +183,24 @@ syn cluster shErrorList contains=shDoError,shIfError,shInError,shCaseError,shEsa if exists("b:is_kornshell") || exists("b:is_bash") syn cluster ErrorList add=shDTestError endif -syn cluster shArithParenList contains=shArithmetic,shArithParen,shCaseEsac,shComment,shDeref,shDo,shDerefSimple,shEcho,shEscape,shNumber,shOperator,shPosnParm,shExSingleQuote,shExDoubleQuote,shHereString,shRedir,shSingleQuote,shDoubleQuote,shStatement,shVariable,shAlias,shTest,shCtrlSeq,shSpecial,shParen,bashSpecialVariables,bashStatement,shIf,shFor,shFunctionKey,shFunctionOne,shFunctionTwo +syn cluster shArithParenList contains=shArithmetic,shArithParen,shCaseEsac,shComment,shDeref,shDerefVarArray,shDo,shDerefSimple,shEcho,shEscape,shExpr,shNumber,shOperator,shPosnParm,shExSingleQuote,shExDoubleQuote,shHereString,shRedir,shSingleQuote,shDoubleQuote,shStatement,shVariable,shAlias,shTest,shCtrlSeq,shSpecial,shParen,bashSpecialVariables,bashStatement,shIf,shFor,shFunctionKey,shFunctionOne,shFunctionTwo syn cluster shArithList contains=@shArithParenList,shParenError +syn cluster shBracketExprList contains=shCharClassOther,shCharClass,shCollSymb,shEqClass syn cluster shCaseEsacList contains=shCaseStart,shCaseLabel,shCase,shCaseBar,shCaseIn,shComment,shDeref,shDerefSimple,shCaseCommandSub,shCaseExSingleQuote,shCaseSingleQuote,shCaseDoubleQuote,shCtrlSeq,@shErrorList,shStringSpecial,shCaseRange syn cluster shCaseList contains=@shCommandSubList,shCaseEsac,shColon,shCommandSub,shCommandSubBQ,shSubshare,shValsub,shComment,shDblBrace,shDo,shEcho,shExpr,shFor,shHereDoc,shIf,shHereString,shRedir,shSetList,shSource,shStatement,shVariable,shCtrlSeq if exists("b:is_kornshell") || exists("b:is_bash") syn cluster shCaseList add=shForPP,shDblParen endif -syn cluster shCommandSubList contains=shAlias,shArithmetic,shCmdParenRegion,shCommandSub,shComment,shCtrlSeq,shDeref,shDerefSimple,shDoubleQuote,shEcho,shEscape,shExDoubleQuote,shExpr,shExSingleQuote,shHereDoc,shNumber,shOperator,shOption,shPosnParm,shHereString,shRedir,shSingleQuote,shSpecial,shStatement,shSubSh,shTest,shVariable +syn cluster shCommandSubList contains=shAlias,shArithmetic,shBracketExpr,shCmdParenRegion,shCommandSub,shComment,shCtrlSeq,shDeref,shDerefSimple,shDoubleQuote,shEcho,shEscape,shExDoubleQuote,shExpr,shExSingleQuote,shHereDoc,shNumber,shOperator,shOption,shPosnParm,shHereString,shRedir,shSingleQuote,shSpecial,shStatement,shSubSh,shTest,shVariable syn cluster shCurlyList contains=shNumber,shComma,shDeref,shDerefSimple,shDerefSpecial " COMBAK: removing shEscape from shDblQuoteList fails ksh04:43 -- Jun 09, 2022: I don't see the problem with ksh04, so am reinstating shEscape syn cluster shDblQuoteList contains=shArithmetic,shCommandSub,shCommandSubBQ,shSubshare,shValsub,shDeref,shDerefSimple,shEscape,shPosnParm,shCtrlSeq,shSpecial,shSpecialDQ syn cluster shDerefList contains=shDeref,shDerefSimple,shDerefVar,shDerefSpecial,shDerefWordError,shDerefPSR,shDerefPPS syn cluster shDerefVarList contains=shDerefOffset,shDerefOp,shDerefVarArray,shDerefOpError -syn cluster shEchoList contains=shArithmetic,shCommandSub,shCommandSubBQ,shSubshare,shValsub,shDeref,shDerefSimple,shEscape,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shCtrlSeq,shEchoQuote -syn cluster shExprList1 contains=shCharClass,shNumber,shOperator,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shExpr,shDblBrace,shDeref,shDerefSimple,shCtrlSeq +syn cluster shEchoList contains=shArithmetic,shBracketExpr,shCommandSub,shCommandSubBQ,shDerefVarArray,shSubshare,shValsub,shDeref,shDerefSimple,shEscape,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shCtrlSeq,shEchoQuote +syn cluster shExprList1 contains=shBracketExpr,shNumber,shOperator,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shExpr,shDblBrace,shDeref,shDerefSimple,shCtrlSeq syn cluster shExprList2 contains=@shExprList1,@shCaseList,shTest -syn cluster shFunctionList contains=@shCommandSubList,shCaseEsac,shColon,shComment,shDo,shEcho,shExpr,shFor,shHereDoc,shIf,shOption,shHereString,shRedir,shSetList,shSource,shStatement,shVariable,shOperator,shCtrlSeq +syn cluster shFunctionList contains=shBracketExpr,@shCommandSubList,shCaseEsac,shColon,shComment,shDo,shEcho,shExpr,shFor,shHereDoc,shIf,shOption,shHereString,shRedir,shSetList,shSource,shStatement,shVariable,shOperator,shCtrlSeq if exists("b:is_kornshell") || exists("b:is_bash") syn cluster shFunctionList add=shRepeat,shDblBrace,shDblParen,shForPP syn cluster shDerefList add=shCommandSubList,shEchoDeref @@ -160,16 +208,16 @@ endif syn cluster shHereBeginList contains=@shCommandSubList syn cluster shHereList contains=shBeginHere,shHerePayload syn cluster shHereListDQ contains=shBeginHere,@shDblQuoteList,shHerePayload -syn cluster shIdList contains=shArithmetic,shCommandSub,shCommandSubBQ,shSubshare,shValsub,shWrapLineOperator,shSetOption,shComment,shDeref,shDerefSimple,shHereString,shNumber,shOperator,shRedir,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shExpr,shCtrlSeq,shStringSpecial,shAtExpr +syn cluster shIdList contains=shArithmetic,shCommandSub,shCommandSubBQ,shDerefVarArray,shSubshare,shValsub,shWrapLineOperator,shSetOption,shComment,shDeref,shDerefSimple,shHereString,shNumber,shOperator,shRedir,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shExpr,shCtrlSeq,shStringSpecial,shAtExpr syn cluster shIfList contains=@shLoopList,shDblBrace,shDblParen,shFunctionKey,shFunctionOne,shFunctionTwo syn cluster shLoopList contains=@shCaseList,@shErrorList,shCaseEsac,shConditional,shDblBrace,shExpr,shFor,shIf,shOption,shSet,shTest,shTestOpr,shTouch if exists("b:is_kornshell") || exists("b:is_bash") syn cluster shLoopList add=shForPP,shDblParen endif -syn cluster shPPSLeftList contains=shAlias,shArithmetic,shCmdParenRegion,shCommandSub,shSubshare,shValsub,shCtrlSeq,shDeref,shDerefSimple,shDoubleQuote,shEcho,shEscape,shExDoubleQuote,shExpr,shExSingleQuote,shHereDoc,shNumber,shOperator,shOption,shPosnParm,shHereString,shRedir,shSingleQuote,shSpecial,shStatement,shSubSh,shTest,shVariable +syn cluster shPPSLeftList contains=shAlias,shArithmetic,shBracketExpr,shCmdParenRegion,shCommandSub,shSubshare,shValsub,shCtrlSeq,shDeref,shDerefSimple,shDoubleQuote,shEcho,shEscape,shExDoubleQuote,shExpr,shExSingleQuote,shHereDoc,shNumber,shOperator,shOption,shPosnParm,shHereString,shRedir,shSingleQuote,shSpecial,shStatement,shSubSh,shTest,shVariable syn cluster shPPSRightList contains=shDeref,shDerefSimple,shEscape,shPosnParm -syn cluster shSubShList contains=@shCommandSubList,shCommandSubBQ,shSubshare,shValsub,shCaseEsac,shColon,shCommandSub,shComment,shDo,shEcho,shExpr,shFor,shIf,shHereString,shRedir,shSetList,shSource,shStatement,shVariable,shCtrlSeq,shOperator -syn cluster shTestList contains=shArithmetic,shCharClass,shCommandSub,shCommandSubBQ,shSubshare,shValsub,shCtrlSeq,shDeref,shDerefSimple,shDoubleQuote,shSpecialDQ,shExDoubleQuote,shExpr,shExSingleQuote,shNumber,shOperator,shSingleQuote,shTest,shTestOpr +syn cluster shSubShList contains=shBracketExpr,@shCommandSubList,shCommandSubBQ,shSubshare,shValsub,shCaseEsac,shColon,shCommandSub,shComment,shDo,shEcho,shExpr,shFor,shIf,shHereString,shRedir,shSetList,shSource,shStatement,shVariable,shCtrlSeq,shOperator +syn cluster shTestList contains=shArithmetic,shBracketExpr,shCommandSub,shCommandSubBQ,shSubshare,shValsub,shCtrlSeq,shDeref,shDerefSimple,shDoubleQuote,shSpecialDQ,shExDoubleQuote,shExpr,shExSingleQuote,shNumber,shOperator,shSingleQuote,shTest,shTestOpr syn cluster shNoZSList contains=shSpecialNoZS syn cluster shForList contains=shTestOpr,shNumber,shDerefSimple,shDeref,shCommandSub,shCommandSubBQ,shSubshare,shValsub,shArithmetic @@ -190,7 +238,7 @@ endif syn match shEchoQuote contained '\%(\\\\\)*\\["`'()]' " This must be after the strings, so that ... \" will be correct -syn region shEmbeddedEcho contained matchgroup=shStatement start="\<print\>" skip="\\$" matchgroup=shEchoDelim end="$" matchgroup=NONE end="[<>;&|`)]"me=e-1 end="\d[<>]"me=e-2 end="\s#"me=e-2 contains=shNumber,shExSingleQuote,shSingleQuote,shDeref,shDerefSimple,shSpecialVar,shOperator,shExDoubleQuote,shDoubleQuote,shCharClass,shCtrlSeq +syn region shEmbeddedEcho contained matchgroup=shStatement start="\<print\>" skip="\\$" matchgroup=shEchoDelim end="$" matchgroup=NONE end="[<>;&|`)]"me=e-1 end="\d[<>]"me=e-2 end="\s#"me=e-2 contains=shBracketExpr,shNumber,shExSingleQuote,shSingleQuote,shDeref,shDerefSimple,shSpecialVar,shOperator,shExDoubleQuote,shDoubleQuote,shCtrlSeq " Alias: {{{1 " ===== @@ -240,7 +288,6 @@ syn match shRedir "\d<<-\=" " ========== syn match shOperator "<<\|>>" contained syn match shOperator "[!&;|]" contained -syn match shOperator "\[[[^:]\|\]]" contained syn match shOperator "[-=/*+%]\==" skipwhite nextgroup=shPattern syn match shPattern "\<\S\+\())\)\@=" contained contains=shExSingleQuote,shSingleQuote,shExDoubleQuote,shDoubleQuote,shDeref @@ -251,9 +298,9 @@ syn region shSubSh transparent matchgroup=shSubShRegion start="[^(]\zs(" end=")" " Tests: {{{1 "======= -syn region shExpr matchgroup=shRange start="\[" skip=+\\\\\|\\$\|\[+ end="\]" contains=@shTestList,shSpecial +syn region shExpr matchgroup=shRange start="\[\s\@=" skip=+\\\\\|\\$\|\[+ end="\]" contains=@shTestList,shSpecial syn region shTest transparent matchgroup=shStatement start="\<test\s" skip=+\\\\\|\\$+ matchgroup=NONE end="[;&|]"me=e-1 end="$" contains=@shExprList1 -syn region shNoQuote start='\S' skip='\%(\\\\\)*\\.' end='\ze\s' end="\ze['"]" contained contains=shDerefSimple,shDeref +syn region shNoQuote start='\S' skip='\%(\\\\\)*\\.' end='\ze\s' end="\ze['"]" contained contains=shBracketExpr,shDerefSimple,shDeref syn match shAstQuote contained '\*\ze"' nextgroup=shString syn match shTestOpr contained '[^-+/%]\zs=' skipwhite nextgroup=shTestDoubleQuote,shTestSingleQuote,shTestPattern syn match shTestOpr contained "<=\|>=\|!=\|==\|=\~\|-.\>\|-\(nt\|ot\|ef\|eq\|ne\|lt\|le\|gt\|ge\)\>\|[!<>]" @@ -262,13 +309,16 @@ syn region shTestDoubleQuote contained start='\%(\%(\\\\\)*\\\)\@<!"' skip=+\\\\ syn match shTestSingleQuote contained '\\.' nextgroup=shTestSingleQuote syn match shTestSingleQuote contained "'[^']*'" if exists("b:is_kornshell") || exists("b:is_bash") - syn region shDblBrace matchgroup=Delimiter start="\[\[" skip=+\%(\\\\\)*\\$+ end="\]\]" contains=@shTestList,shAstQuote,shNoQuote,shComment + syn region shDblBrace matchgroup=Delimiter start="\[\[\s\@=" skip=+\%(\\\\\)*\\$+ end="\]\]" contains=@shTestList,shAstQuote,shNoQuote,shComment syn region shDblParen matchgroup=Delimiter start="((" skip=+\%(\\\\\)*\\$+ end="))" contains=@shTestList,shComment endif " Character Class In Range: {{{1 " ========================= +syn match shCharClassOther contained "\[:\w\{-}:\]" syn match shCharClass contained "\[:\(backspace\|escape\|return\|xdigit\|alnum\|alpha\|blank\|cntrl\|digit\|graph\|lower\|print\|punct\|space\|upper\|tab\):\]" +syn match shCollSymb contained "\[\..\{-}\.\]" +syn match shEqClass contained "\[=.\{-}=\]" " Loops: do, if, while, until {{{1 " ====== @@ -298,12 +348,12 @@ syn match shComma contained "," " ==== syn match shCaseBar contained skipwhite "\(^\|[^\\]\)\(\\\\\)*\zs|" nextgroup=shCase,shCaseStart,shCaseBar,shComment,shCaseExSingleQuote,shCaseSingleQuote,shCaseDoubleQuote syn match shCaseStart contained skipwhite skipnl "(" nextgroup=shCase,shCaseBar -syn match shCaseLabel contained skipwhite "\%(\\.\|[-a-zA-Z0-9_*.]\)\+" contains=shCharClass +syn match shCaseLabel contained skipwhite "\%(\\.\|[-a-zA-Z0-9_*.]\)\+" contains=shBracketExpr if exists("b:is_bash") ShFoldIfDoFor syn region shCase contained skipwhite skipnl matchgroup=shSnglCase start="\%(\\.\|[^#$()'" \t]\)\{-}\zs)" end=";;" end=";&" end=";;&" end="esac"me=s-1 contains=@shCaseList nextgroup=shCaseStart,shCase,shComment elseif exists("b:is_kornshell") ShFoldIfDoFor syn region shCase contained skipwhite skipnl matchgroup=shSnglCase start="\%(\\.\|[^#$()'" \t]\)\{-}\zs)" end=";;" end=";&" end="esac"me=s-1 contains=@shCaseList nextgroup=shCaseStart,shCase,shComment -else +else ShFoldIfDoFor syn region shCase contained skipwhite skipnl matchgroup=shSnglCase start="\%(\\.\|[^#$()'" \t]\)\{-}\zs)" end=";;" end="esac"me=s-1 contains=@shCaseList nextgroup=shCaseStart,shCase,shComment endif ShFoldIfDoFor syn region shCaseEsac matchgroup=shConditional start="\<case\>" end="\<esac\>" contains=@shCaseEsacList @@ -317,11 +367,9 @@ endif syn region shCaseSingleQuote matchgroup=shQuote start=+'+ end=+'+ contains=shStringSpecial skipwhite skipnl nextgroup=shCaseBar contained syn region shCaseDoubleQuote matchgroup=shQuote start=+"+ skip=+\\\\\|\\.+ end=+"+ contains=@shDblQuoteList,shStringSpecial skipwhite skipnl nextgroup=shCaseBar contained syn region shCaseCommandSub start=+`+ skip=+\\\\\|\\.+ end=+`+ contains=@shCommandSubList skipwhite skipnl nextgroup=shCaseBar contained +call s:GenerateBracketExpressionItems({'itemGroup': 'shCaseRange', 'bracketGroup': 'shBracketExprDelim', 'extraArgs': 'skip=+\\\\+ contained'}) if exists("b:is_bash") - syn region shCaseRange matchgroup=Delimiter start=+\[+ skip=+\\\\+ end=+\]+ contained contains=shCharClass syn match shCharClass '\[:\%(alnum\|alpha\|ascii\|blank\|cntrl\|digit\|graph\|lower\|print\|punct\|space\|upper\|word\|or\|xdigit\):\]' contained -else - syn region shCaseRange matchgroup=Delimiter start=+\[+ skip=+\\\\+ end=+\]+ contained endif " Misc: {{{1 "====== @@ -358,7 +406,7 @@ syn region shCmdParenRegion matchgroup=shCmdSubRegion start="((\@!" skip='\\\\\| if exists("b:is_bash") syn cluster shCommandSubList add=bashSpecialVariables,bashStatement syn cluster shCaseList add=bashAdminStatement,bashStatement - syn keyword bashSpecialVariables contained auto_resume BASH BASH_ALIASES BASH_ALIASES BASH_ARGC BASH_ARGC BASH_ARGV BASH_ARGV BASH_CMDS BASH_CMDS BASH_COMMAND BASH_COMMAND BASH_ENV BASH_EXECUTION_STRING BASH_EXECUTION_STRING BASH_LINENO BASH_LINENO BASHOPTS BASHOPTS BASHPID BASHPID BASH_REMATCH BASH_REMATCH BASH_SOURCE BASH_SOURCE BASH_SUBSHELL BASH_SUBSHELL BASH_VERSINFO BASH_VERSION BASH_XTRACEFD BASH_XTRACEFD CDPATH COLUMNS COLUMNS COMP_CWORD COMP_CWORD COMP_KEY COMP_KEY COMP_LINE COMP_LINE COMP_POINT COMP_POINT COMPREPLY COMPREPLY COMP_TYPE COMP_TYPE COMP_WORDBREAKS COMP_WORDBREAKS COMP_WORDS COMP_WORDS COPROC COPROC DIRSTACK EMACS EMACS ENV ENV EUID FCEDIT FIGNORE FUNCNAME FUNCNAME FUNCNEST FUNCNEST GLOBIGNORE GROUPS histchars HISTCMD HISTCONTROL HISTFILE HISTFILESIZE HISTIGNORE HISTSIZE HISTTIMEFORMAT HISTTIMEFORMAT HOME HOSTFILE HOSTNAME HOSTTYPE IFS IGNOREEOF INPUTRC LANG LC_ALL LC_COLLATE LC_CTYPE LC_CTYPE LC_MESSAGES LC_NUMERIC LC_NUMERIC LINENO LINES LINES MACHTYPE MAIL MAILCHECK MAILPATH MAPFILE MAPFILE OLDPWD OPTARG OPTERR OPTIND OSTYPE PATH PIPESTATUS POSIXLY_CORRECT POSIXLY_CORRECT PPID PROMPT_COMMAND PS1 PS2 PS3 PS4 PWD RANDOM READLINE_LINE READLINE_LINE READLINE_POINT READLINE_POINT REPLY SECONDS SHELL SHELL SHELLOPTS SHLVL TIMEFORMAT TIMEOUT TMPDIR TMPDIR UID + syn keyword bashSpecialVariables contained auto_resume BASH BASH_ALIASES BASH_ARGC BASH_ARGV BASH_CMDS BASH_COMMAND BASH_ENV BASH_EXECUTION_STRING BASH_LINENO BASHOPTS BASHPID BASH_REMATCH BASH_SOURCE BASH_SUBSHELL BASH_VERSINFO BASH_VERSION BASH_XTRACEFD CDPATH COLUMNS COMP_CWORD COMP_KEY COMP_LINE COMP_POINT COMPREPLY COMP_TYPE COMP_WORDBREAKS COMP_WORDS COPROC COPROC_PID DIRSTACK EMACS ENV EUID FCEDIT FIGNORE FUNCNAME FUNCNEST GLOBIGNORE GROUPS histchars HISTCMD HISTCONTROL HISTFILE HISTFILESIZE HISTIGNORE HISTSIZE HISTTIMEFORMAT HOME HOSTFILE HOSTNAME HOSTTYPE IFS IGNOREEOF INPUTRC LANG LC_ALL LC_COLLATE LC_CTYPE LC_MESSAGES LC_NUMERIC LINENO LINES MACHTYPE MAIL MAILCHECK MAILPATH MAPFILE OLDPWD OPTARG OPTERR OPTIND OSTYPE PATH PIPESTATUS POSIXLY_CORRECT PPID PROMPT_COMMAND PS0 PS1 PS2 PS3 PS4 PWD RANDOM READLINE_LINE READLINE_POINT REPLY SECONDS SHELL SHELLOPTS SHLVL TIMEFORMAT TIMEOUT TMPDIR UID syn keyword bashStatement chmod clear complete du egrep expr fgrep find gnufind gnugrep grep head less ls mkdir mv rm rmdir rpm sed sleep sort strip tail syn keyword bashAdminStatement daemon killall killproc nice reload restart start status stop syn keyword bashStatement command compgen @@ -453,7 +501,18 @@ endif "============= syn match shSetOption "\s\zs[-+][a-zA-Z0-9]\+\>" contained syn match shVariable "\<\h\w*\ze=" nextgroup=shVarAssign -syn match shVarAssign "=" contained nextgroup=shCmdParenRegion,shPattern,shDeref,shDerefSimple,shDoubleQuote,shExDoubleQuote,shSingleQuote,shExSingleQuote,shVar +if exists("b:is_bash") + " The subscript form for array values, e.g. "foo=([2]=10 [4]=100)". + syn region shArrayValue contained start="\[\%(..\{-}\]=\)\@=" end="\]=\@=" contains=@shArrayValueList nextgroup=shVarAssign + syn cluster shArrayValueList contains=shArithmetic,shArithParen,shCommandSub,shDeref,shDerefSimple,shExpr,shNumber,shExSingleQuote,shExDoubleQuote,shSingleQuote,shDoubleQuote,shSpecial,shParen,bashSpecialVariables,shParenError +endif +if exists("b:is_bash") || exists("b:is_kornshell") + syn match shVariable "\<\h\w*\%(\[..\{-}\]\)\=\ze\%([|^&*/%+-]\|[<^]<\|[>^]>\)\==" contains=shDerefVarArray nextgroup=shVarAssign + syn match shVarAssign contained "\%([|^&*/%+-]\|[<^]<\|[>^]>\)\==" nextgroup=shArrayRegion,shPattern,shDeref,shDerefSimple,shDoubleQuote,shExDoubleQuote,shSingleQuote,shExSingleQuote,shVar + syn region shArrayRegion contained matchgroup=shShellVariables start="(" skip='\\\\\|\\.' end=")" contains=@shArrayValueList,shArrayValue,shComment +else + syn match shVarAssign contained "=" nextgroup=shPattern,shDeref,shDerefSimple,shDoubleQuote,shExDoubleQuote,shSingleQuote,shExSingleQuote,shVar +endif syn match shVar contained "\h\w*" syn region shAtExpr contained start="@(" end=")" contains=@shIdList if exists("b:is_bash") @@ -482,6 +541,7 @@ if !exists("b:is_posix") endif if exists("b:is_bash") + syn keyword shFunctionKey coproc ShFoldFunctions syn region shFunctionOne matchgroup=shFunction start="^\s*[A-Za-z_0-9:][-a-zA-Z_0-9:]*\s*()\_s*{" end="}" contains=@shFunctionList skipwhite skipnl nextgroup=shFunctionStart,shQuickComment ShFoldFunctions syn region shFunctionTwo matchgroup=shFunction start="\%(do\)\@!\&\<[A-Za-z_0-9:][-a-zA-Z_0-9:]*\>\s*\%(()\)\=\_s*{" end="}" contains=shFunctionKey,@shFunctionList contained skipwhite skipnl nextgroup=shFunctionStart,shQuickComment ShFoldFunctions syn region shFunctionThree matchgroup=shFunction start="^\s*[A-Za-z_0-9:][-a-zA-Z_0-9:]*\s*()\_s*(" end=")" contains=@shFunctionList skipwhite skipnl nextgroup=shFunctionStart,shQuickComment @@ -571,8 +631,11 @@ syn match shDerefOp contained ":\=+" nextgroup=@shDerefPatternList if exists("b:is_bash") || exists("b:is_kornshell") || exists("b:is_posix") syn match shDerefOp contained "#\{1,2}" nextgroup=@shDerefPatternList syn match shDerefOp contained "%\{1,2}" nextgroup=@shDerefPatternList - syn match shDerefPattern contained "[^{}]\+" contains=shDeref,shDerefSimple,shDerefPattern,shDerefString,shCommandSub,shDerefEscape nextgroup=shDerefPattern + syn match shDerefPattern contained "[^{}]\+" contains=shDeref,shDerefSimple,shDerefPattern,shDerefString,shCommandSub,shDerefEscape nextgroup=shDerefPattern skipnl syn region shDerefPattern contained start="{" end="}" contains=shDeref,shDerefSimple,shDerefString,shCommandSub nextgroup=shDerefPattern + " Match parametric bracket expressions with a leading whitespace character. + syn region shDerefPattern contained matchgroup=shBracketExprDelim start="\[" end="\]" contains=@shBracketExprList,shDoubleQuote nextgroup=shDerefPattern + call s:GenerateBracketExpressionItems({'itemGroup': 'shDerefPattern', 'bracketGroup': 'shBracketExprDelim', 'extraArgs': 'contained nextgroup=shDerefPattern'}) syn match shDerefEscape contained '\%(\\\\\)*\\.' endif if exists("b:is_bash") @@ -592,15 +655,16 @@ if exists("b:is_bash") || exists("b:is_kornshell") || exists("b:is_posix") endif if exists("b:is_bash") + " bash : ${parameter/pattern/string} " bash : ${parameter//pattern/string} - " bash : ${parameter//pattern} syn match shDerefPPS contained '/\{1,2}' nextgroup=shDerefPPSleft syn region shDerefPPSleft contained start='.' skip=@\%(\\\\\)*\\/@ matchgroup=shDerefOp end='/' end='\ze}' end='"' nextgroup=shDerefPPSright contains=@shPPSLeftList syn region shDerefPPSright contained start='.' skip=@\%(\\\\\)\+@ end='\ze}' contains=@shPPSRightList - " bash : ${parameter/#substring/replacement} - syn match shDerefPSR contained '/#' nextgroup=shDerefPSRleft,shDoubleQuote,shSingleQuote - syn region shDerefPSRleft contained start='[^"']' skip=@\%(\\\\\)*\\/@ matchgroup=shDerefOp end='/' end='\ze}' nextgroup=shDerefPSRright + " bash : ${parameter/#pattern/string} + " bash : ${parameter/%pattern/string} + syn match shDerefPSR contained '/[#%]' nextgroup=shDerefPSRleft,shDoubleQuote,shSingleQuote + syn region shDerefPSRleft contained start='[^"']' skip=@\%(\\\\\)*\\/@ matchgroup=shDerefOp end='/' end='\ze}' nextgroup=shDerefPSRright contains=shBracketExpr syn region shDerefPSRright contained start='.' skip=@\%(\\\\\)\+@ end='\ze}' endif @@ -631,10 +695,10 @@ if exists("b:is_kornshell") || exists("b:is_posix") " Additional bash Keywords: {{{1 " ===================== elseif exists("b:is_bash") - syn keyword shStatement bg builtin disown export false fg getopts jobs let printf sleep true unalias + syn keyword shStatement bg builtin disown export false fg getopts jobs let printf true unalias syn keyword shStatement typeset nextgroup=shSetOption syn keyword shStatement fc hash history source suspend times type - syn keyword shStatement bind builtin caller compopt declare dirs disown enable export help logout local mapfile popd pushd readarray shopt source typeset + syn keyword shStatement bind caller compopt declare dirs enable help logout local mapfile popd pushd readarray shopt typeset else syn keyword shStatement login newgrp endif @@ -670,6 +734,7 @@ syn sync match shWhileSync grouphere shRepeat "\<while\>" " ===================== if !exists("skip_sh_syntax_inits") hi def link shArithRegion shShellVariables + hi def link shArrayValue shDeref hi def link shAstQuote shDoubleQuote hi def link shAtExpr shSetList hi def link shBkslshSnglQuote shSingleQuote @@ -764,7 +829,10 @@ if !exists("skip_sh_syntax_inits") endif hi def link shArithmetic Special + hi def link shBracketExprDelim Delimiter hi def link shCharClass Identifier + hi def link shCollSymb shCharClass + hi def link shEqClass shCharClass hi def link shSnglCase Statement hi def link shCommandSub Special hi def link shCommandSubBQ shCommandSub @@ -814,6 +882,10 @@ delc ShFoldFunctions delc ShFoldHereDoc delc ShFoldIfDoFor +" Delete the bracket expression function {{{1 +" ====================================== +delfun s:GenerateBracketExpressionItems + " Set Current Syntax: {{{1 " =================== if exists("b:is_bash") diff --git a/runtime/syntax/shaderslang.vim b/runtime/syntax/shaderslang.vim new file mode 100644 index 0000000000..1cae202b04 --- /dev/null +++ b/runtime/syntax/shaderslang.vim @@ -0,0 +1,360 @@ +" Vim syntax file +" Language: Slang +" Maintainer: Austin Shijo <epestr@proton.me> +" Last Change: 2024 Jan 05 + +if exists("b:current_syntax") + finish +endif + +" Read the C syntax to start with +runtime! syntax/c.vim +unlet b:current_syntax + +" Annotations +syn match shaderslangAnnotation /<.*;>/ + +" Attributes +syn match shaderslangAttribute /^\s*\[maxvertexcount(\s*\w\+\s*)\]/ +syn match shaderslangAttribute /^\s*\[domain(\s*"\(tri\|quad\|isoline\)"\s*)\]/ +syn match shaderslangAttribute /^\s*\[earlydepthstencil\]/ +syn match shaderslangAttribute /^\s*\[instance(\s*\w\+\s*)\]/ +syn match shaderslangAttribute /^\s*\[maxtessfactor(\s*\w\+\s*)\]/ +syn match shaderslangAttribute /^\s*\[numthreads(\s*\w\+\s*,\s*\w\+\s*,\s*\w\+\s*)\]/ +syn match shaderslangAttribute /^\s*\[outputcontrolpoints(\s*\w\+\s*)\]/ +syn match shaderslangAttribute /^\s*\[outputtopology(\s*"\(point\|line\|triangle_cw\|triangle_ccw\|triangle\)"\s*)\]/ +syn match shaderslangAttribute /^\s*\[partitioning(\s*"\(integer\|fractional_even\|fractional_odd\|pow2\)"\s*)\]/ +syn match shaderslangAttribute /^\s*\[patchconstantfunc(\s*"\(\d\|\w\|_\)\+"\s*)\]/ +syn match shaderslangAttribute /^\s*\[WaveSize(\s*\w\+\(\s*,\s*\w\+\(\s*,\s*\w\+\)\?\)\?\s*)\]/ +syn match shaderslangAttribute /^\s*\[shader(\s*"\(anyhit\|callable\|closesthit\|intersection\|miss\|raygeneration\)"\s*)\]/ + +syn match shaderslangAttribute /^\s*\[fastopt\]/ +syn match shaderslangAttribute /^\s*\[loop\]/ +syn match shaderslangAttribute /^\s*\[unroll\]/ +syn match shaderslangAttribute /^\s*\[allow_uav_condition\]/ +syn match shaderslangAttribute /^\s*\[branch\]/ +syn match shaderslangAttribute /^\s*\[flatten\]/ +syn match shaderslangAttribute /^\s*\[forcecase\]/ +syn match shaderslangAttribute /^\s*\[call\]/ +syn match shaderslangAttribute /^\s*\[WaveOpsIncludeHelperLanes\]/ + +syn match shaderslangAttribute /\[raypayload\]/ + +" Work graph shader target attributes +syn match shaderslangAttribute /^\s*\[Shader(\s*"\(\d\|\w\|_\)\+"\s*)\]/ + +" Work graph shader function attributes +syn match shaderslangAttribute /^\s*\[NodeLaunch(\s*"\(broadcasting\|coalescing\|thread\)"\s*)\]/ +syn match shaderslangAttribute /^\s*\[NodeIsProgramEntry\]/ +syn match shaderslangAttribute /^\s*\[NodeLocalRootArgumentsTableIndex(\s*\w\+\s*)\]/ +syn match shaderslangAttribute /^\s*\[NumThreads(\s*\w\+\s*,\s*\w\+\s*,\s*\w\+\s*)\]/ +syn match shaderslangAttribute /^\s*\[NodeShareInputOf(\s*"\w\+"\(\s*,\s*\w\+\)\?\s*)\]/ +syn match shaderslangAttribute /^\s*\[NodeDispatchGrid(\s*\w\+\s*,\s*\w\+\s*,\s*\w\+\s*)\]/ +syn match shaderslangAttribute /^\s*\[NodeMaxDispatchGrid(\s*\w\+\s*,\s*\w\+\s*,\s*\w\+\s*)\]/ +syn match shaderslangAttribute /^\s*\[NodeMaxRecursionDepth(\s*\w\+\s*)\]/ +syn match shaderslangAttribute /^\s*\[NodeMaxInputRecordsPerGraphEntryRecord(\s*\w\+\s*,\s*\(true\|false\)\s*)\]/ + +" Work graph record attributes +syn match shaderslangAttribute /\[NodeTrackRWInputSharing\]/ +syn match shaderslangAttribute /\[MaxRecords(\s*\w\+\s*)\]/ +syn match shaderslangAttribute /\[NodeID(\s*"\w\+"\(\s*,\s*\w\+\)\?\s*)\]/ +syn match shaderslangAttribute /\[MaxRecordsSharedWith(\s*\w\+\s*)\]/ +syn match shaderslangAttribute /\[AllowSparseNodes\]/ +syn match shaderslangAttribute /\[NodeArraySize(\s*\w\+\s*)\]/ +syn match shaderslangAttribute /\[UnboundedSparseNodes\]/ + +" Intrinsic functions +syn keyword shaderslangFunc abs acos acosh asin asinh atan atanh cos cosh exp exp2 floor log log10 log2 round rsqrt sin sincos sinh sqrt tan tanh trunc +syn keyword shaderslangFunc AllMemoryBarrier AllMemoryBarrierWithGroupSync DeviceMemoryBarrier DeviceMemoryBarrierWithGroupSync GroupMemoryBarrier GroupMemoryBarrierWithGroupSync +syn keyword shaderslangFunc abort clip errorf printf +syn keyword shaderslangFunc all any countbits faceforward firstbithigh firstbitlow isfinite isinf isnan max min noise pow reversebits sign +syn keyword shaderslangFunc asdouble asfloat asint asuint D3DCOLORtoUBYTE4 f16tof32 f32tof16 +syn keyword shaderslangFunc ceil clamp degrees fma fmod frac frexp ldexp lerp mad modf radiants saturate smoothstep step +syn keyword shaderslangFunc cross determinant distance dot dst length lit msad4 mul normalize rcp reflect refract transpose +syn keyword shaderslangFunc ddx ddx_coarse ddx_fine ddy ddy_coarse ddy_fine fwidth +syn keyword shaderslangFunc EvaluateAttributeAtCentroid EvaluateAttributeAtSample EvaluateAttributeSnapped +syn keyword shaderslangFunc GetRenderTargetSampleCount GetRenderTargetSamplePosition +syn keyword shaderslangFunc InterlockedAdd InterlockedAnd InterlockedCompareExchange InterlockedCompareStore InterlockedExchange InterlockedMax InterlockedMin InterlockedOr InterlockedXor +syn keyword shaderslangFunc InterlockedCompareStoreFloatBitwise InterlockedCompareExchangeFloatBitwise +syn keyword shaderslangFunc Process2DQuadTessFactorsAvg Process2DQuadTessFactorsMax Process2DQuadTessFactorsMin ProcessIsolineTessFactors +syn keyword shaderslangFunc ProcessQuadTessFactorsAvg ProcessQuadTessFactorsMax ProcessQuadTessFactorsMin ProcessTriTessFactorsAvg ProcessTriTessFactorsMax ProcessTriTessFactorsMin +syn keyword shaderslangFunc tex1D tex1Dbias tex1Dgrad tex1Dlod tex1Dproj +syn keyword shaderslangFunc tex2D tex2Dbias tex2Dgrad tex2Dlod tex2Dproj +syn keyword shaderslangFunc tex3D tex3Dbias tex3Dgrad tex3Dlod tex3Dproj +syn keyword shaderslangFunc texCUBE texCUBEbias texCUBEgrad texCUBElod texCUBEproj +syn keyword shaderslangFunc WaveIsFirstLane WaveGetLaneCount WaveGetLaneIndex +syn keyword shaderslangFunc IsHelperLane +syn keyword shaderslangFunc WaveActiveAnyTrue WaveActiveAllTrue WaveActiveBallot +syn keyword shaderslangFunc WaveReadLaneFirst WaveReadLaneAt +syn keyword shaderslangFunc WaveActiveAllEqual WaveActiveAllEqualBool WaveActiveCountBits +syn keyword shaderslangFunc WaveActiveSum WaveActiveProduct WaveActiveBitAnd WaveActiveBitOr WaveActiveBitXor WaveActiveMin WaveActiveMax +syn keyword shaderslangFunc WavePrefixCountBits WavePrefixProduct WavePrefixSum +syn keyword shaderslangFunc QuadReadAcrossX QuadReadAcrossY QuadReadAcrossDiagonal QuadReadLaneAt +syn keyword shaderslangFunc QuadAny QuadAll +syn keyword shaderslangFunc WaveMatch WaveMultiPrefixSum WaveMultiPrefixProduct WaveMultiPrefixCountBits WaveMultiPrefixAnd WaveMultiPrefixOr WaveMultiPrefixXor +syn keyword shaderslangFunc NonUniformResourceIndex +syn keyword shaderslangFunc DispatchMesh SetMeshOutputCounts +syn keyword shaderslangFunc dot4add_u8packed dot4add_i8packed dot2add + +syn keyword shaderslangFunc RestartStrip +syn keyword shaderslangFunc CalculateLevelOfDetail CalculateLevelOfDetailUnclamped Gather GetDimensions GetSamplePosition Load Sample SampleBias SampleCmp SampleCmpLevelZero SampleGrad SampleLevel GatherRaw SampleCmpLevel +syn keyword shaderslangFunc SampleCmpBias SampleCmpGrad +syn keyword shaderslangFunc WriteSamplerFeedback WriteSamplerFeedbackBias WriteSamplerFeedbackGrad WriteSamplerFeedbackLevel +syn keyword shaderslangFunc Append Consume DecrementCounter IncrementCounter +syn keyword shaderslangFunc Load2 Load3 Load4 Store Store2 Store3 Store4 +syn keyword shaderslangFunc GatherRed GatherGreen GatherBlue GatherAlpha GatherCmp GatherCmpRed GatherCmpGreen GatherCmpBlue GatherCmpAlpha +syn match shaderslangFunc /\.mips\[\d\+\]\[\d\+\]/ +syn match shaderslangFunc /\.sample\[\d\+\]\[\d\+\]/ + +" Ray intrinsics +syn keyword shaderslangFunc AcceptHitAndEndSearch CallShader IgnoreHit ReportHit TraceRay +syn keyword shaderslangFunc DispatchRaysIndex DispatchRaysDimensions +syn keyword shaderslangFunc WorldRayOrigin WorldRayDirection RayTMin RayTCurrent RayFlags +syn keyword shaderslangFunc InstanceIndex InstanceID GeometryIndex PrimitiveIndex ObjectRayOrigin ObjectRayDirection ObjectToWorld3x4 ObjectToWorld4x3 WorldToObject3x4 WorldToObject4x3 +syn keyword shaderslangFunc HitKind + +" RayQuery intrinsics +syn keyword shaderslangFunc TraceRayInline Proceed Abort CommittedStatus +syn keyword shaderslangFunc CandidateType CandidateProceduralPrimitiveNonOpaque CandidateTriangleRayT CandidateInstanceIndex CandidateInstanceID CandidateInstanceContributionToHitGroupIndex CandidateGeometryIndex +syn keyword shaderslangFunc CandidatePrimitiveIndex CandidateObjectRayOrigin CandidateObjectRayDirection CandidateObjectToWorld3x4 CandidateObjectToWorld4x3 CandidateWorldToObject3x4 CandidateWorldToObject4x3 +syn keyword shaderslangFunc CommitNonOpaqueTriangleHit CommitProceduralPrimitiveHit CommittedStatus CommittedRayT CommittedInstanceIndex CommittedInstanceID CommittedInstanceContributionToHitGroupIndex +syn keyword shaderslangFunc CommittedGeometryIndex CommittedPrimitiveIndex CommittedObjectRayOrigin CommittedObjectRayDirection CommittedObjectToWorld3x4 CommittedObjectToWorld4x3 CommittedWorldToObject3x4 +syn keyword shaderslangFunc CommittedWorldToObject4x3 CandidateTriangleBarycentrics CandidateTriangleFrontFace CommittedTriangleBarycentrics CommittedTriangleFrontFace + +" Pack/unpack math intrinsics +syn keyword shaderslangFunc unpack_s8s16 unpack_u8u16 unpack_s8s32 unpack_u8u32 +syn keyword shaderslangFunc pack_u8 pack_s8 pack_clamp_u8 pack_clamp_s8 + +" Work graph object methods +syn keyword shaderslangFunc Get FinishedCrossGroupSharing Count GetThreadNodeOutputRecords GetGroupNodeOutputRecords IsValid GroupIncrementOutputCount ThreadIncrementOutputCount OutputComplete + +" Work graph free intrinsics +syn keyword shaderslangFunc GetRemainingRecursionLevels Barrier + +" Layout Qualifiers +syn keyword shaderslangLayoutQual const row_major column_major +syn keyword shaderslangLayoutQual point line triangle lineadj triangleadj +syn keyword shaderslangLayoutQual InputPatch OutputPatch +syn match shaderslangLayoutQual /PointStream<\s*\w\+\s*>/ +syn match shaderslangLayoutQual /LineStream<\s*\w\+\s*>/ +syn match shaderslangLayoutQual /TriangleStream<\s*\w\+\s*>/ + +" User defined Semantics +syn match shaderslangSemantic /:\s*[A-Z]\w*/ +syn match shaderslangSemantic /:\s*packoffset(\s*c\d\+\(\.[xyzw]\)\?\s*)/ " packoffset +syn match shaderslangSemantic /:\s*register(\s*\(r\|x\|v\|t\|s\|cb\|icb\|b\|c\|u\)\d\+\s*)/ " register +syn match shaderslangSemantic /:\s*read(\s*\(\(anyhit\|closesthit\|miss\|caller\)\s*,\s*\)*\(anyhit\|closesthit\|miss\|caller\)\?\s*)/ " read +syn match shaderslangSemantic /:\s*write(\s*\(\(anyhit\|closesthit\|miss\|caller\)\s*,\s*\)*\(anyhit\|closesthit\|miss\|caller\)\?\s*)/ " write + +" System-Value Semantics +" Vertex Shader +syn match shaderslangSemantic /SV_ClipDistance\d\+/ +syn match shaderslangSemantic /SV_CullDistance\d\+/ +syn keyword shaderslangSemantic SV_Position SV_InstanceID SV_PrimitiveID SV_VertexID +syn keyword shaderslangSemantic SV_StartVertexLocation SV_StartInstanceLocation +" Tessellation pipeline +syn keyword shaderslangSemantic SV_DomainLocation SV_InsideTessFactor SV_OutputControlPointID SV_TessFactor +" Geometry Shader +syn keyword shaderslangSemantic SV_GSInstanceID SV_RenderTargetArrayIndex +" Pixel Shader - MSAA +syn keyword shaderslangSemantic SV_Coverage SV_Depth SV_IsFrontFace SV_SampleIndex +syn match shaderslangSemantic /SV_Target[0-7]/ +syn keyword shaderslangSemantic SV_ShadingRate SV_ViewID +syn match shaderslangSemantic /SV_Barycentrics[0-1]/ +" Compute Shader +syn keyword shaderslangSemantic SV_DispatchThreadID SV_GroupID SV_GroupIndex SV_GroupThreadID +" Mesh shading pipeline +syn keyword shaderslangSemantic SV_CullPrimitive +" Work graph record system values +syn keyword shaderslangSemantic SV_DispatchGrid + +" slang structures +syn keyword shaderslangStructure cbuffer + +" Shader profiles +" Cg profiles +syn keyword shaderslangProfile arbfp1 arbvp1 fp20 vp20 fp30 vp30 ps_1_1 ps_1_2 ps_1_3 +" Shader Model 1 +syn keyword shaderslangProfile vs_1_1 +" Shader Model 2 +syn keyword shaderslangProfile ps_2_0 ps_2_x vs_2_0 vs_2_x +" Shader Model 3 +syn keyword shaderslangProfile ps_3_0 vs_3_0 +" Shader Model 4 +syn keyword shaderslangProfile gs_4_0 ps_4_0 vs_4_0 gs_4_1 ps_4_1 vs_4_1 +" Shader Model 5 +syn keyword shaderslangProfile cs_4_0 cs_4_1 cs_5_0 ds_5_0 gs_5_0 hs_5_0 ps_5_0 vs_5_0 +" Shader Model 6 +syn keyword shaderslangProfile cs_6_0 ds_6_0 gs_6_0 hs_6_0 ps_6_0 vs_6_0 lib_6_0 + +" Swizzling +syn match shaderslangSwizzle /\.[xyzw]\{1,4\}\>/ +syn match shaderslangSwizzle /\.[rgba]\{1,4\}\>/ +syn match shaderslangSwizzle /\.\(_m[0-3]\{2}\)\{1,4\}/ +syn match shaderslangSwizzle /\.\(_[1-4]\{2}\)\{1,4\}/ + +" Other Statements +syn keyword shaderslangStatement discard + +" Storage class +syn match shaderslangStorageClass /\<in\(\s+pipeline\)\?\>/ +syn match shaderslangStorageClass /\<out\(\s\+indices\|\s\+vertices\|\s\+primitives\)\?\>/ +syn keyword shaderslangStorageClass inout +syn keyword shaderslangStorageClass extern nointerpolation precise shared groupshared static uniform volatile +syn keyword shaderslangStorageClass snorm unorm +syn keyword shaderslangStorageClass linear centroid nointerpolation noperspective sample +syn keyword shaderslangStorageClass globallycoherent + +" Types +" Buffer types +syn keyword shaderslangType ConstantBuffer Buffer ByteAddressBuffer ConsumeStructuredBuffer StructuredBuffer +syn keyword shaderslangType AppendStructuredBuffer RWBuffer RWByteAddressBuffer RWStructuredBuffer +syn keyword shaderslangType RasterizerOrderedBuffer RasterizerOrderedByteAddressBuffer RasterizerOrderedStructuredBuffer + +" Scalar types +syn keyword shaderslangType bool int uint dword half float double +syn keyword shaderslangType min16float min10float min16int min12int min16uint +syn keyword shaderslangType float16_t float32_t float64_t + +" Vector types +syn match shaderslangType /vector<\s*\w\+,\s*[1-4]\s*>/ +syn keyword shaderslangType bool1 bool2 bool3 bool4 +syn keyword shaderslangType int1 int2 int3 int4 +syn keyword shaderslangType uint1 uint2 uint3 uint4 +syn keyword shaderslangType dword1 dword2 dword3 dword4 +syn keyword shaderslangType half1 half2 half3 half4 +syn keyword shaderslangType float1 float2 float3 float4 +syn keyword shaderslangType double1 double2 double3 double4 +syn keyword shaderslangType min16float1 min16float2 min16float3 min16float4 +syn keyword shaderslangType min10float1 min10float2 min10float3 min10float4 +syn keyword shaderslangType min16int1 min16int2 min16int3 min16int4 +syn keyword shaderslangType min12int1 min12int2 min12int3 min12int4 +syn keyword shaderslangType min16uint1 min16uint2 min16uint3 min16uint4 +syn keyword shaderslangType float16_t1 float16_t2 float16_t3 float16_t4 +syn keyword shaderslangType float32_t1 float32_t2 float32_t3 float32_t4 +syn keyword shaderslangType float64_t1 float64_t2 float64_t3 float64_t4 +syn keyword shaderslangType int16_t1 int16_t2 int16_t3 int16_t4 +syn keyword shaderslangType int32_t1 int32_t2 int32_t3 int32_t4 +syn keyword shaderslangType int64_t1 int64_t2 int64_t3 int64_t4 +syn keyword shaderslangType uint16_t1 uint16_t2 uint16_t3 uint16_t4 +syn keyword shaderslangType uint32_t1 uint32_t2 uint32_t3 uint32_t4 +syn keyword shaderslangType uint64_t1 uint64_t2 uint64_t3 uint64_t4 + +" Packed types +syn keyword shaderslangType uint8_t4_packed int8_t4_packed + +" Matrix types +syn match shaderslangType /matrix<\s*\w\+\s*,\s*[1-4]\s*,\s*[1-4]\s*>/ +syn keyword shaderslangType bool1x1 bool2x1 bool3x1 bool4x1 bool1x2 bool2x2 bool3x2 bool4x2 bool1x3 bool2x3 bool3x3 bool4x3 bool1x4 bool2x4 bool3x4 bool4x4 +syn keyword shaderslangType int1x1 int2x1 int3x1 int4x1 int1x2 int2x2 int3x2 int4x2 int1x3 int2x3 int3x3 int4x3 int1x4 int2x4 int3x4 int4x4 +syn keyword shaderslangType uint1x1 uint2x1 uint3x1 uint4x1 uint1x2 uint2x2 uint3x2 uint4x2 uint1x3 uint2x3 uint3x3 uint4x3 uint1x4 uint2x4 uint3x4 uint4x4 +syn keyword shaderslangType dword1x1 dword2x1 dword3x1 dword4x1 dword1x2 dword2x2 dword3x2 dword4x2 dword1x3 dword2x3 dword3x3 dword4x3 dword1x4 dword2x4 dword3x4 dword4x4 +syn keyword shaderslangType half1x1 half2x1 half3x1 half4x1 half1x2 half2x2 half3x2 half4x2 half1x3 half2x3 half3x3 half4x3 half1x4 half2x4 half3x4 half4x4 +syn keyword shaderslangType float1x1 float2x1 float3x1 float4x1 float1x2 float2x2 float3x2 float4x2 float1x3 float2x3 float3x3 float4x3 float1x4 float2x4 float3x4 float4x4 +syn keyword shaderslangType double1x1 double2x1 double3x1 double4x1 double1x2 double2x2 double3x2 double4x2 double1x3 double2x3 double3x3 double4x3 double1x4 double2x4 double3x4 double4x4 +syn keyword shaderslangType min16float1x1 min16float2x1 min16float3x1 min16float4x1 min16float1x2 min16float2x2 min16float3x2 min16float4x2 min16float1x3 min16float2x3 min16float3x3 min16float4x3 min16float1x4 min16float2x4 min16float3x4 min16float4x4 +syn keyword shaderslangType min10float1x1 min10float2x1 min10float3x1 min10float4x1 min10float1x2 min10float2x2 min10float3x2 min10float4x2 min10float1x3 min10float2x3 min10float3x3 min10float4x3 min10float1x4 min10float2x4 min10float3x4 min10float4x4 +syn keyword shaderslangType min16int1x1 min16int2x1 min16int3x1 min16int4x1 min16int1x2 min16int2x2 min16int3x2 min16int4x2 min16int1x3 min16int2x3 min16int3x3 min16int4x3 min16int1x4 min16int2x4 min16int3x4 min16int4x4 +syn keyword shaderslangType min12int1x1 min12int2x1 min12int3x1 min12int4x1 min12int1x2 min12int2x2 min12int3x2 min12int4x2 min12int1x3 min12int2x3 min12int3x3 min12int4x3 min12int1x4 min12int2x4 min12int3x4 min12int4x4 +syn keyword shaderslangType min16uint1x1 min16uint2x1 min16uint3x1 min16uint4x1 min16uint1x2 min16uint2x2 min16uint3x2 min16uint4x2 min16uint1x3 min16uint2x3 min16uint3x3 min16uint4x3 min16uint1x4 min16uint2x4 min16uint3x4 min16uint4x4 +syn keyword shaderslangType float16_t1x1 float16_t2x1 float16_t3x1 float16_t4x1 float16_t1x2 float16_t2x2 float16_t3x2 float16_t4x2 float16_t1x3 float16_t2x3 float16_t3x3 float16_t4x3 float16_t1x4 float16_t2x4 float16_t3x4 float16_t4x4 +syn keyword shaderslangType float32_t1x1 float32_t2x1 float32_t3x1 float32_t4x1 float32_t1x2 float32_t2x2 float32_t3x2 float32_t4x2 float32_t1x3 float32_t2x3 float32_t3x3 float32_t4x3 float32_t1x4 float32_t2x4 float32_t3x4 float32_t4x4 +syn keyword shaderslangType float64_t1x1 float64_t2x1 float64_t3x1 float64_t4x1 float64_t1x2 float64_t2x2 float64_t3x2 float64_t4x2 float64_t1x3 float64_t2x3 float64_t3x3 float64_t4x3 float64_t1x4 float64_t2x4 float64_t3x4 float64_t4x4 +syn keyword shaderslangType int16_t1x1 int16_t2x1 int16_t3x1 int16_t4x1 int16_t1x2 int16_t2x2 int16_t3x2 int16_t4x2 int16_t1x3 int16_t2x3 int16_t3x3 int16_t4x3 int16_t1x4 int16_t2x4 int16_t3x4 int16_t4x4 +syn keyword shaderslangType int32_t1x1 int32_t2x1 int32_t3x1 int32_t4x1 int32_t1x2 int32_t2x2 int32_t3x2 int32_t4x2 int32_t1x3 int32_t2x3 int32_t3x3 int32_t4x3 int32_t1x4 int32_t2x4 int32_t3x4 int32_t4x4 +syn keyword shaderslangType int64_t1x1 int64_t2x1 int64_t3x1 int64_t4x1 int64_t1x2 int64_t2x2 int64_t3x2 int64_t4x2 int64_t1x3 int64_t2x3 int64_t3x3 int64_t4x3 int64_t1x4 int64_t2x4 int64_t3x4 int64_t4x4 +syn keyword shaderslangType uint16_t1x1 uint16_t2x1 uint16_t3x1 uint16_t4x1 uint16_t1x2 uint16_t2x2 uint16_t3x2 uint16_t4x2 uint16_t1x3 uint16_t2x3 uint16_t3x3 uint16_t4x3 uint16_t1x4 uint16_t2x4 uint16_t3x4 uint16_t4x4 +syn keyword shaderslangType uint32_t1x1 uint32_t2x1 uint32_t3x1 uint32_t4x1 uint32_t1x2 uint32_t2x2 uint32_t3x2 uint32_t4x2 uint32_t1x3 uint32_t2x3 uint32_t3x3 uint32_t4x3 uint32_t1x4 uint32_t2x4 uint32_t3x4 uint32_t4x4 +syn keyword shaderslangType uint64_t1x1 uint64_t2x1 uint64_t3x1 uint64_t4x1 uint64_t1x2 uint64_t2x2 uint64_t3x2 uint64_t4x2 uint64_t1x3 uint64_t2x3 uint64_t3x3 uint64_t4x3 uint64_t1x4 uint64_t2x4 uint64_t3x4 uint64_t4x4 + +" Sampler types +syn keyword shaderslangType SamplerState SamplerComparisonState +syn keyword shaderslangType sampler sampler1D sampler2D sampler3D samplerCUBE sampler_state + +" Texture types +syn keyword shaderslangType Texture1D Texture1DArray Texture2D Texture2DArray Texture2DMS Texture2DMSArray Texture3D TextureCube TextureCubeArray +syn keyword shaderslangType RWTexture1D RWTexture2D RWTexture2DArray RWTexture3D RWTextureCubeArray RWTexture2DMS RWTexture2DMSArray +syn keyword shaderslangType FeedbackTexture2D FeedbackTexture2DArray +syn keyword shaderslangType RasterizerOrderedTexture1D RasterizerOrderedTexture1DArray RasterizerOrderedTexture2D RasterizerOrderedTexture2DArray RasterizerOrderedTexture3D +syn keyword shaderslangTypeDeprec texture texture1D texture2D texture3D + +" Raytracing types +syn keyword shaderslangType RaytracingAccelerationStructure RayDesc RayQuery BuiltInTriangleIntersectionAttributes + +" Work graph input record objects +syn keyword shaderslangType DispatchNodeInputRecord RWDispatchNodeInputRecord GroupNodeInputRecords RWGroupNodeInputRecords ThreadNodeInputRecord RWThreadNodeInputRecord EmptyNodeInput + +" Work graph output node objects +syn keyword shaderslangType NodeOutput NodeOutputArray EmptyNodeOutput EmptyNodeOutputArray + +" Work graph output record objects +syn keyword shaderslangType ThreadNodeOutputRecords GroupNodeOutputRecords + +" State Groups args +syn case ignore " This section case insensitive + +" Blend state group +syn keyword shaderslangStateGroupArg AlphaToCoverageEnable BlendEnable SrcBlend DestBlend BlendOp SrcBlendAlpha DestBlendAlpha BlendOpAlpha RenderTargetWriteMask +syn keyword shaderslangStateGroupVal ZERO ONE SRC_COLOR INV_SRC_COLOR SRC_ALPHA INV_SRC_ALPHA DEST_ALPHA INV_DEST_ALPHA DEST_COLOR INV_DEST_COLOR SRC_ALPHA_SAT BLEND_FACTOR INV_BLEND_FACTOR SRC1_COLOR INV_SRC1_COLOR SRC1_ALPHA INV_SRC1_ALPHA +syn keyword shaderslangStateGroupVal ADD SUBSTRACT REV_SUBSTRACT MIN MAX + +" Rasterizer state group +syn keyword shaderslangStateGroupArg FillMode CullMode FrontCounterClockwise DepthBias DepthBiasClamp SlopeScaledDepthBias ZClipEnable DepthClipEnable ScissorEnable MultisampleEnable AntialiasedLineEnable +syn keyword shaderslangStateGroupVal SOLID WIREFRAME +syn keyword shaderslangStateGroupVal NONE FRONT BACK + +" Sampler state group +syn keyword shaderslangStateGroupArg Filter AddressU AddressV AddressW MipLODBias MaxAnisotropy ComparisonFunc BorderColor MinLOD MaxLOD ComparisonFilter +syn keyword shaderslangStateGroupVal MIN_MAG_MIP_POINT MIN_MAG_POINT_MIP_LINEAR MIN_POINT_MAG_LINEAR_MIP_POINT MIN_POINT_MAG_MIP_LINEAR MIN_LINEAR_MAG_MIP_POINT MIN_LINEAR_MAG_POINT_MIP_LINEAR MIN_MAG_LINEAR_MIP_POINT MIN_MAG_MIP_LINEAR ANISOTROPIC +syn keyword shaderslangStateGroupVal COMPARISON_MIN_MAG_MIP_POINT COMPARISON_MIN_MAG_POINT_MIP_LINEAR COMPARISON_MIN_POINT_MAG_LINEAR_MIP_POINT COMPARISON_MIN_POINT_MAG_MIP_LINEAR COMPARISON_MIN_LINEAR_MAG_MIP_POINT +syn keyword shaderslangStateGroupVal COMPARISON_MIN_LINEAR_MAG_POINT_MIP_LINEAR COMPARISON_MIN_MAG_LINEAR_MIP_POINT COMPARISON_MIN_MAG_MIP_LINEAR COMPARISON_ANISOTROPIC +syn keyword shaderslangStateGroupVal COMPARISON_NEVER COMPARISON_LESS COMPARISON_EQUAL COMPARISON_LESS_EQUAL COMPARISON_GREATER COMPARISON_NOT_EQUAL COMPARISON_GREATER_EQUAL COMPARISON_ALWAYS +syn keyword shaderslangStateGroupVal WRAP MIRROR CLAMP BORDER MIRROR_ONCE +syn keyword shaderslangStateGroupVal SAMPLER_FEEDBACK_MIN_MIP SAMPLER_FEEDBACK_MIP_REGION_USED + +" Ray flags +syn keyword shaderslangStateGroupVal RAY_FLAG_NONE RAY_FLAG_FORCE_OPAQUE RAY_FLAG_FORCE_NON_OPAQUE RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH RAY_FLAG_SKIP_CLOSEST_HIT_SHADER +syn keyword shaderslangStateGroupVal RAY_FLAG_CULL_BACK_FACING_TRIANGLES RAY_FLAG_CULL_FRONT_FACING_TRIANGLES RAY_FLAG_CULL_OPAQUE RAY_FLAG_CULL_NON_OPAQUE +syn keyword shaderslangStateGroupVal RAY_FLAG_SKIP_TRIANGLES RAY_FLAG_SKIP_PROCEDURAL_PRIMITIVES + +" HitKind enum +syn keyword shaderslangStateGroupVal HIT_KIND_TRIANGLE_FRONT_FACE HIT_KIND_TRIANGLE_BACK_FACE + +" RayQuery enums +syn keyword shaderslangStateGroupVal COMMITTED_NOTHING COMMITTED_TRIANGLE_HIT COMMITTED_PROCEDURAL_PRIMITIVE_HIT +syn keyword shaderslangStateGroupVal CANDIDATE_NON_OPAQUE_TRIANGLE CANDIDATE_PROCEDURAL_PRIMITIVE + +" Heap objects +syn keyword shaderslangStateGroupVal ResourceDescriptorHeap SamplerDescriptorHeap + +" Work graph constants +syn keyword shaderslangStateGroupVal UAV_MEMORY GROUP_SHARED_MEMORY NODE_INPUT_MEMORY NODE_OUTPUT_MEMORY ALL_MEMORY GROUP_SYNC GROUP_SCOPE DEVICE_SCOPE + +syn case match " Case sensitive from now on + +" Effect files declarations and functions +" Effect groups, techniques passes +syn keyword shaderslangEffectGroup fxgroup technique11 pass +" Effect functions +syn keyword shaderslangEffectFunc SetBlendState SetDepthStencilState SetRasterizerState SetVertexShader SetHullShader SetDomainShader SetGeometryShader SetPixelShader SetComputeShader CompileShader ConstructGSWithSO SetRenderTargets + +" Default highlighting +hi def link shaderslangProfile shaderslangStatement +hi def link shaderslangStateGroupArg shaderslangStatement +hi def link shaderslangStateGroupVal Number +hi def link shaderslangStatement Statement +hi def link shaderslangType Type +hi def link shaderslangTypeDeprec WarningMsg +hi def link shaderslangStorageClass StorageClass +hi def link shaderslangSemantic PreProc +hi def link shaderslangFunc shaderslangStatement +hi def link shaderslangLayoutQual shaderslangFunc +hi def link shaderslangAnnotation PreProc +hi def link shaderslangStructure Structure +hi def link shaderslangSwizzle SpecialChar +hi def link shaderslangAttribute Statement + +hi def link shaderslangEffectGroup Type +hi def link shaderslangEffectFunc Statement + +let b:current_syntax = "shaderslang" diff --git a/runtime/syntax/tex.vim b/runtime/syntax/tex.vim index 77a40e11d3..4f35bba939 100644 --- a/runtime/syntax/tex.vim +++ b/runtime/syntax/tex.vim @@ -3,7 +3,8 @@ " Maintainer: This runtime file is looking for a new maintainer. " Former Maintainer: Charles E. Campbell " Last Change: Apr 22, 2022 -" 2024 Feb 19 by Vim Project (announce adoption) +" 2024 Feb 19 by Vim Project: announce adoption +" 2025 Jan 18 by Vim Project: add texEmphStyle to texMatchGroup, #16228 " Version: 121 " Former URL: http://www.drchip.org/astronaut/vim/index.html#SYNTAX_TEX " @@ -176,11 +177,11 @@ if !s:tex_excludematcher endif if !s:tex_nospell if !s:tex_no_error - syn cluster texMatchGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texError,texInput,texLength,texLigature,texMatcher,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texZone,texInputFile,texOption,@Spell + syn cluster texMatchGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texError,texInput,texLength,texLigature,texMatcher,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texEmphStyle,texZone,texInputFile,texOption,@Spell syn cluster texMatchNMGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texError,texInput,texLength,texLigature,texMatcherNM,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texZone,texInputFile,texOption,@Spell syn cluster texStyleGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texError,texInput,texLength,texLigature,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texZone,texInputFile,texOption,texStyleStatement,texStyleMatcher,@Spell else - syn cluster texMatchGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMatcher,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texZone,texInputFile,texOption,@Spell + syn cluster texMatchGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMatcher,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texEmphStyle,texZone,texInputFile,texOption,@Spell syn cluster texMatchNMGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texMatcherNM,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texZone,texInputFile,texOption,@Spell syn cluster texStyleGroup contains=texAccent,texBadMath,texComment,texDefCmd,texDelimiter,texDocType,texInput,texLength,texLigature,texNewCmd,texNewEnv,texOnlyMath,texParen,texRefZone,texSection,texSpecialChar,texStatement,texString,texTypeSize,texTypeStyle,texBoldStyle,texBoldItalStyle,texItalStyle,texItalBoldStyle,texZone,texInputFile,texOption,texStyleStatement,texStyleMatcher,@Spell endif diff --git a/runtime/syntax/tiasm.vim b/runtime/syntax/tiasm.vim new file mode 100644 index 0000000000..c79596bdfe --- /dev/null +++ b/runtime/syntax/tiasm.vim @@ -0,0 +1,102 @@ +" Vim syntax file +" Language: TI linear assembly language +" Document: https://downloads.ti.com/docs/esd/SPRUI03B/#SPRUI03B_HTML/assembler-description.html +" Maintainer: Wu, Zhenyu <wuzhenyu@ustc.edu> +" Last Change: 2025 Jan 08 + +if exists("b:current_syntax") + finish +endif + +syn case ignore + +" storage types +syn match tiasmType "\.bits" +syn match tiasmType "\.byte" +syn match tiasmType "\.char" +syn match tiasmType "\.cstring" +syn match tiasmType "\.double" +syn match tiasmType "\.field" +syn match tiasmType "\.float" +syn match tiasmType "\.half" +syn match tiasmType "\.int" +syn match tiasmType "\.long" +syn match tiasmType "\.short" +syn match tiasmType "\.string" +syn match tiasmType "\.ubyte" +syn match tiasmType "\.uchar" +syn match tiasmType "\.uhalf" +syn match tiasmType "\.uint" +syn match tiasmType "\.ulong" +syn match tiasmType "\.ushort" +syn match tiasmType "\.uword" +syn match tiasmType "\.word" + +syn match tiasmIdentifier "[a-z_][a-z0-9_]*" + +syn match tiasmDecimal "\<[1-9]\d*\>" display +syn match tiasmOctal "\<0[0-7][0-7]\+\>\|\<[0-7]\+[oO]\>" display +syn match tiasmHexadecimal "\<0[xX][0-9a-fA-F]\+\>\|\<[0-9][0-9a-fA-F]*[hH]\>" display +syn match tiasmBinary "\<0[bB][0-1]\+\>\|\<[01]\+[bB]\>" display + +syn match tiasmFloat "\<\d\+\.\d*\%(e[+-]\=\d\+\)\=\>" display +syn match tiasmFloat "\<\d\%(e[+-]\=\d\+\)\>" display + +syn match tiasmCharacter "'.'\|''\|'[^']'" + +syn region tiasmString start="\"" end="\"" skip="\"\"" + +syn match tiasmFunction "\$[a-zA-Z_][a-zA-Z_0-9]*\ze(" + +syn keyword tiasmTodo contained TODO FIXME XXX NOTE +syn region tiasmComment start=";" end="$" keepend contains=tiasmTodo,@Spell +syn match tiasmComment "^[*!].*" contains=tiasmTodo,@Spell +syn match tiasmLabel "^[^ *!;][^ :]*" + +syn match tiasmInclude "\.include" +syn match tiasmCond "\.if" +syn match tiasmCond "\.else" +syn match tiasmCond "\.endif" +syn match tiasmMacro "\.macro" +syn match tiasmMacro "\.endm" + +syn match tiasmDirective "\.[A-Za-z][0-9A-Za-z-_]*" + +syn case match + +hi def link tiasmLabel Label +hi def link tiasmComment Comment +hi def link tiasmTodo Todo +hi def link tiasmDirective Statement + +hi def link tiasmInclude Include +hi def link tiasmCond PreCondit +hi def link tiasmMacro Macro + +if exists('g:tiasm_legacy_syntax_groups') + hi def link hexNumber Number + hi def link decNumber Number + hi def link octNumber Number + hi def link binNumber Number + hi def link tiasmHexadecimal hexNumber + hi def link tiasmDecimal decNumber + hi def link tiasmOctal octNumber + hi def link tiasmBinary binNumber +else + hi def link tiasmHexadecimal Number + hi def link tiasmDecimal Number + hi def link tiasmOctal Number + hi def link tiasmBinary Number +endif +hi def link tiasmFloat Float + +hi def link tiasmString String +hi def link tiasmStringEscape Special +hi def link tiasmCharacter Character +hi def link tiasmCharacterEscape Special + +hi def link tiasmIdentifier Identifier +hi def link tiasmType Type +hi def link tiasmFunction Function + +let b:current_syntax = "tiasm" diff --git a/runtime/syntax/typst.vim b/runtime/syntax/typst.vim index dae1424780..b1f05ec853 100644 --- a/runtime/syntax/typst.vim +++ b/runtime/syntax/typst.vim @@ -1,7 +1,8 @@ " Vim syntax file " Language: Typst -" Maintainer: Gregory Anders <greg@gpanders.com> -" Last Change: 2024 Nov 02 +" Previous Maintainer: Gregory Anders +" Maintainer: Luca Saccarola <github.e41mv@aleeas.com> +" Last Change: 2024 Dec 09 " Based on: https://github.com/kaarmu/typst.vim if exists('b:current_syntax') diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim index 3ad04e2957..c4e231d145 100644 --- a/runtime/syntax/vim.vim +++ b/runtime/syntax/vim.vim @@ -60,10 +60,10 @@ syn case ignore syn keyword vimGroup contained Comment Constant String Character Number Boolean Float Identifier Function Statement Conditional Repeat Label Operator Keyword Exception PreProc Include Define Macro PreCondit Type StorageClass Structure Typedef Special SpecialChar Tag Delimiter SpecialComment Debug Underlined Ignore Error Todo " Default highlighting groups {{{2 -syn keyword vimHLGroup contained ErrorMsg IncSearch ModeMsg NonText StatusLine StatusLineNC EndOfBuffer VertSplit DiffText PmenuSbar TabLineSel TabLineFill Cursor lCursor QuickFixLine CursorLineSign CursorLineFold CurSearch PmenuKind PmenuKindSel PmenuMatch PmenuMatchSel PmenuExtra PmenuExtraSel Normal Directory LineNr CursorLineNr MoreMsg Question Search SpellBad SpellCap SpellRare SpellLocal PmenuThumb Pmenu PmenuSel SpecialKey Title WarningMsg WildMenu Folded FoldColumn SignColumn Visual DiffAdd DiffChange DiffDelete TabLine CursorColumn CursorLine ColorColumn MatchParen StatusLineTerm StatusLineTermNC CursorIM LineNrAbove LineNrBelow +syn keyword vimHLGroup contained ErrorMsg IncSearch ModeMsg NonText StatusLine StatusLineNC EndOfBuffer VertSplit DiffText PmenuSbar TabLineSel TabLineFill Cursor lCursor QuickFixLine CursorLineSign CursorLineFold CurSearch PmenuKind PmenuKindSel PmenuMatch PmenuMatchSel PmenuExtra PmenuExtraSel ComplMatchIns Normal Directory LineNr CursorLineNr MoreMsg Question Search SpellBad SpellCap SpellRare SpellLocal PmenuThumb Pmenu PmenuSel SpecialKey Title WarningMsg WildMenu Folded FoldColumn SignColumn Visual DiffAdd DiffChange DiffDelete TabLine CursorColumn CursorLine ColorColumn MatchParen StatusLineTerm StatusLineTermNC CursorIM LineNrAbove LineNrBelow syn match vimHLGroup contained "\<Conceal\>" syn keyword vimOnlyHLGroup contained Menu Scrollbar ToolbarButton ToolbarLine Tooltip VisualNOS -syn keyword nvimHLGroup contained FloatBorder FloatFooter FloatTitle MsgSeparator NormalFloat NormalNC Substitute TermCursor TermCursorNC VisualNC Whitespace WinBar WinBarNC WinSeparator +syn keyword nvimHLGroup contained FloatBorder FloatFooter FloatTitle MsgSeparator NormalFloat NormalNC Substitute TermCursor VisualNC Whitespace WinBar WinBarNC WinSeparator "}}}2 syn case match @@ -185,20 +185,20 @@ Vim9 syn keyword vim9Boolean true false " Numbers {{{2 " ======= syn case ignore -syn match vimNumber '\<\d\+\%(\.\d\+\%(e[+-]\=\d\+\)\=\)\=' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,@vimComment -syn match vimNumber '\<0b[01]\+' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,@vimComment -syn match vimNumber '\<0o\=\o\+' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,@vimComment -syn match vimNumber '\<0x\x\+' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,@vimComment -syn match vimNumber '\<0z\>' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,@vimComment -syn match vimNumber '\<0z\%(\x\x\)\+\%(\.\%(\x\x\)\+\)*' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,@vimComment -syn match vimNumber '\%(^\|\A\)\zs#\x\{6}' skipwhite nextgroup=vimGlobal,vimSubst1,vimCommand,@vimComment +syn match vimNumber '\<\d\+\%(\.\d\+\%(e[+-]\=\d\+\)\=\)\=' skipwhite nextgroup=vimGlobal,vimSubst1,@vimComment +syn match vimNumber '\<0b[01]\+' skipwhite nextgroup=vimGlobal,vimSubst1,@vimComment +syn match vimNumber '\<0o\=\o\+' skipwhite nextgroup=vimGlobal,vimSubst1,@vimComment +syn match vimNumber '\<0x\x\+' skipwhite nextgroup=vimGlobal,vimSubst1,@vimComment +syn match vimNumber '\<0z\>' skipwhite nextgroup=vimGlobal,vimSubst1,@vimComment +syn match vimNumber '\<0z\%(\x\x\)\+\%(\.\%(\x\x\)\+\)*' skipwhite nextgroup=vimGlobal,vimSubst1,@vimComment +syn match vimNumber '\%(^\|\A\)\zs#\x\{6}' skipwhite nextgroup=vimGlobal,vimSubst1,@vimComment syn case match " All vimCommands are contained by vimIsCommand. {{{2 syn cluster vimCmdList contains=vimAbb,vimAddress,vimAutoCmd,vimAugroup,vimBehave,vimCall,vimCatch,vimConst,vimDef,vimDefFold,vimDelcommand,@vimEcho,vimEnddef,vimEndfunction,vimExecute,vimIsCommand,vimExtCmd,vimFor,vimFunction,vimFuncFold,vimGlobal,vimHighlight,vimLet,vimLoadkeymap,vimMap,vimMark,vimMatch,vimNotFunc,vimNormal,vimSet,vimSleep,vimSyntax,vimThrow,vimUnlet,vimUnmap,vimUserCmd,vimMenu,vimMenutranslate,@vim9CmdList -syn cluster vim9CmdList contains=vim9Class,vim9Const,vim9Enum,vim9Export,vim9Final,vim9For,vim9Interface,vim9Type,vim9Var +syn cluster vim9CmdList contains=vim9Abstract,vim9Class,vim9Const,vim9Enum,vim9Export,vim9Final,vim9For,vim9Interface,vim9Type,vim9Var syn match vimCmdSep "[:|]\+" skipwhite nextgroup=@vimCmdList,vimSubst1 -syn match vimIsCommand "\<\%(\h\w*\|[23]mat\%[ch]\)\>" contains=vimCommand +syn match vimIsCommand "\<\%(\h\w*\|[23]mat\%[ch]\)\>" nextgroup=vimBang contains=vimCommand syn match vimBang contained "!" syn match vimVar contained "\<\h[a-zA-Z0-9#_]*\>" syn match vimVar "\<[bwglstav]:\h[a-zA-Z0-9#_]*\>" @@ -207,7 +207,6 @@ syn match vimVar "\s\zs&t_\S[a-zA-Z0-9]\>" syn match vimVar "\s\zs&t_k;" syn match vimFBVar contained "\<[bwglstav]:\h[a-zA-Z0-9#_]*\>" syn keyword vimCommand contained in -syn match vimBang contained "!" syn cluster vimExprList contains=vimEnvvar,vimFunc,vimNumber,vimOper,vimOperParen,vimLetRegister,vimString,vimVar,@vim9ExprList syn cluster vim9ExprList contains=vim9Boolean,vim9Null @@ -275,9 +274,16 @@ syn keyword vimAugroupKey contained aug[roup] skipwhite nextgroup=vimAugroupBan " Operators: {{{2 " ========= syn cluster vimOperGroup contains=vimEnvvar,vimFunc,vimFuncVar,vimOper,vimOperParen,vimNumber,vimString,vimRegister,@vimContinue,vim9Comment,vimVar,vimBoolean,vimNull -syn match vimOper "||\|&&\|[-+*/%.!]" skipwhite nextgroup=vimString,vimSpecFile -syn match vimOper "\%#=1\(==\|!=\|>=\|<=\|=\~\|!\~\|>\|<\|=\|!\~#\)[?#]\{0,2}" skipwhite nextgroup=vimString,vimSpecFile -syn match vimOper "\(\<is\|\<isnot\)[?#]\{0,2}\>" skipwhite nextgroup=vimString,vimSpecFile +syn match vimOper "\a\@<!!" skipwhite nextgroup=vimString,vimSpecFile +syn match vimOper "||\|&&\|[-+*/%.]" skipwhite nextgroup=vimString,vimSpecFile +syn match vimOper "?" skipwhite nextgroup=@vimExprList +" distinguish ternary : from ex-colon +syn match vimOper "\s\@1<=:\ze\s\|\s\@1<=:$" skipwhite nextgroup=@vimExprList +syn match vimOper "??" skipwhite nextgroup=@vimExprList +syn match vimOper "=" skipwhite nextgroup=vimString,vimSpecFile +syn match vimOper "\%#=1\%(==\|!=\|>=\|<=\|=\~\|!\~\|>\|<\)[?#]\=" skipwhite nextgroup=vimString,vimSpecFile +syn match vimOper "\<is\%(not\)\=\>" skipwhite nextgroup=vimString,vimSpecFile +syn match vimOper "\<is\%(not\)\=[?#]" skipwhite nextgroup=vimString,vimSpecFile syn region vimOperParen matchgroup=vimParenSep start="(" end=")" contains=@vimOperGroup syn region vimOperParen matchgroup=vimSep start="#\={" end="}" contains=@vimOperGroup nextgroup=vimVar,vimFuncVar if !exists("g:vimsyn_noerror") && !exists("g:vimsyn_noopererror") @@ -289,9 +295,9 @@ endif syn cluster vimFuncList contains=vimFuncBang,vimFunctionError,vimFuncKey,vimFuncSID,Tag syn cluster vimDefList contains=vimFuncBang,vimFunctionError,vimDefKey,vimFuncSID,Tag -syn cluster vimFuncBodyCommon contains=@vimCmdList,vimCmplxRepeat,vimContinue,vimCtrlChar,vimDef,vimEnvvar,vimFBVar,vimFunc,vimFunction,vimLetHereDoc,vimNotation,vimNotFunc,vimNumber,vimOper,vimOperParen,vimRegister,vimSearch,vimSpecFile,vimString,vimSubst,vimFuncFold,vimDefFold -syn cluster vimFuncBodyList contains=@vimFuncBodyCommon,vimComment,vimLineComment,vimFuncVar,vimInsert,vimConst,vimLet -syn cluster vimDefBodyList contains=@vimFuncBodyCommon,vim9Comment,vim9LineComment,vim9Const,vim9Final,vim9Var,vim9Null,vim9Boolean,vim9For +syn cluster vimFuncBodyCommon contains=@vimCmdList,vimCmplxRepeat,vimContinue,vimCtrlChar,vimDef,vimEnvvar,vimFBVar,vimFunc,vimFunction,vimLetHereDoc,vimNotation,vimNotFunc,vimNumber,vimOper,vimOperParen,vimRegister,vimSpecFile,vimString,vimSubst,vimFuncFold,vimDefFold +syn cluster vimFuncBodyList contains=@vimFuncBodyCommon,vimComment,vimLineComment,vimFuncVar,vimInsert,vimConst,vimLet,vimSearch +syn cluster vimDefBodyList contains=@vimFuncBodyCommon,vim9Comment,vim9LineComment,vim9Const,vim9Final,vim9Var,vim9Null,vim9Boolean,vim9For,vim9Search syn region vimFuncPattern contained matchgroup=vimOper start="/" end="$" contains=@vimSubstList syn match vimFunction "\<fu\%[nction]\>" skipwhite nextgroup=vimCmdSep,vimComment,vimFuncPattern contains=vimFuncKey @@ -309,8 +315,8 @@ syn match vimFuncSID contained "\<[sg]:" syn keyword vimFuncKey contained fu[nction] syn keyword vimDefKey contained def -syn region vimFuncParams contained matchgroup=Delimiter start="(" skip=+\n\s*\\\|\n\s*"\\ + end=")" skipwhite skipempty nextgroup=vimFuncBody,vimFuncComment,vimEndfunction,vimFuncMod,vim9CommentError contains=vimFuncParam,@vimContinue -syn region vimDefParams contained matchgroup=Delimiter start="(" end=")" skipwhite skipempty nextgroup=vimDefBody,vimDefComment,vimEnddef,vimReturnType,vimCommentError contains=vimDefParam,vim9Comment,vimFuncParamEquals +syn region vimFuncParams contained matchgroup=Delimiter start="(" skip=+\n\s*\\\|\n\s*"\\ + end=")" skipwhite skipempty nextgroup=vimFuncBody,vimFuncComment,vimEndfunction,vimFuncMod,vim9CommentError contains=vimFuncParam,vimOperParen,@vimContinue +syn region vimDefParams contained matchgroup=Delimiter start="(" end=")" skipwhite skipempty nextgroup=vimDefBody,vimDefComment,vimEnddef,vimReturnType,vimCommentError contains=vimDefParam,vim9Comment,vimFuncParamEquals,vimOperParen syn match vimFuncParam contained "\<\h\w*\>\|\.\.\." skipwhite nextgroup=vimFuncParamEquals syn match vimDefParam contained "\<\h\w*\>" skipwhite nextgroup=vimParamType,vimFuncParamEquals @@ -553,19 +559,21 @@ syn region vimPatSepZone oneline contained matchgroup=vimPatSepZ start="\\%\ syn region vimPatRegion contained transparent matchgroup=vimPatSepR start="\\[z%]\=(" end="\\)" contains=@vimSubstList oneline syn match vimNotPatSep contained "\\\\" syn cluster vimStringGroup contains=vimEscape,vimEscapeBrace,vimPatSep,vimNotPatSep,vimPatSepErr,vimPatSepZone,@Spell -syn region vimString oneline keepend start=+[^a-zA-Z>!\\@]"+lc=1 skip=+\\\\\|\\"+ matchgroup=vimStringEnd end=+"+ contains=@vimStringGroup extend -syn region vimString oneline keepend start=+[^a-zA-Z>!\\@]'+lc=1 end=+'+ extend +syn region vimString oneline keepend matchgroup=vimString start=+[^a-zA-Z>\\@]"+lc=1 skip=+\\\\\|\\"+ matchgroup=vimStringEnd end=+"+ contains=@vimStringGroup extend +syn region vimString oneline matchgroup=vimString start=+[^a-zA-Z>\\@]'+lc=1 end=+'+ contains=vimQuoteEscape extend "syn region vimString oneline start="\s/\s*\A"lc=1 skip="\\\\\|\\+" end="/" contains=@vimStringGroup " see tst45.vim syn match vimString contained +"[^"]*\\$+ skipnl nextgroup=vimStringCont syn match vimStringCont contained +\(\\\\\|.\)\{-}[^\\]"+ + syn match vimEscape contained "\\." " syn match vimEscape contained +\\[befnrt\"]+ syn match vimEscape contained "\\\o\{1,3}\|\\[xX]\x\{1,2}\|\\u\x\{1,4}\|\\U\x\{1,8}" syn match vimEscape contained "\\<" contains=vimNotation syn match vimEscape contained "\\<\*[^>]*>\=>" +syn match vimQuoteEscape contained "''" -syn region vimString oneline start=+$'+ skip=+''+ end=+'+ contains=@vimStringInterpolation extend -syn region vimString oneline start=+$"+ end=+"+ contains=@vimStringGroup,@vimStringInterpolation extend +syn region vimString oneline matchgroup=vimString start=+$'+ skip=+''+ end=+'+ contains=vimQuoteEscape,@vimStringInterpolation extend +syn region vimString oneline matchgroup=vimString start=+$"+ end=+"+ contains=@vimStringGroup,@vimStringInterpolation extend syn region vimStringInterpolationExpr oneline contained matchgroup=vimSep start=+{+ end=+}+ contains=@vimExprList syn match vimStringInterpolationBrace contained "{{" syn match vimStringInterpolationBrace contained "}}" @@ -678,10 +686,12 @@ syn keyword vimAbb abc[lear] cabc[lear] iabc[lear] skipwhite nextgroup=vimMapMod " Autocmd: {{{2 " ======= -syn match vimAutoEventList contained "\(!\s\+\)\=\(\a\+,\)*\a\+" contains=vimAutoEvent,nvimAutoEvent nextgroup=vimAutoCmdSpace +syn match vimAutoCmdBang contained "\a\@1<=!" skipwhite nextgroup=vimAutoEventList +syn match vimAutoEventList contained "\%(\a\+,\)*\a\+" contains=vimAutoEvent,nvimAutoEvent nextgroup=vimAutoCmdSpace syn match vimAutoCmdSpace contained "\s\+" nextgroup=vimAutoCmdSfxList syn match vimAutoCmdSfxList contained "\S*" skipwhite nextgroup=vimAutoCmdMod,vimAutoCmdBlock -syn keyword vimAutoCmd au[tocmd] do[autocmd] doautoa[ll] skipwhite nextgroup=vimAutoEventList +syn keyword vimAutoCmd au[tocmd] skipwhite nextgroup=vimAutoCmdBang,vimAutoEventList +syn keyword vimAutoCmd do[autocmd] doautoa[ll] skipwhite nextgroup=vimAutoEventList syn match vimAutoCmdMod "\(++\)\=\(once\|nested\)" skipwhite nextgroup=vimAutoCmdBlock syn region vimAutoCmdBlock contained matchgroup=vimSep start="{" end="}" contains=@vimDefBodyList @@ -1013,8 +1023,10 @@ syn match vim9CommentTitleLeader '#\s\+'ms=s+1 contained " Searches And Globals: {{{2 " ==================== -syn match vimSearch '^\s*[/?].*' contains=vimSearchDelim +VimL syn match vimSearch '^\s*[/?].*' contains=vimSearchDelim syn match vimSearchDelim '^\s*\zs[/?]\|[/?]$' contained +Vim9 syn match vim9Search '^\s*:[/?].*' contains=vim9SearchDelim +syn match vim9SearchDelim '^\s*\zs:[/?]\|[/?]$' contained contains=vimCmdSep syn region vimGlobal matchgroup=Statement start='\<g\%[lobal]!\=/' skip='\\.' end='/' skipwhite nextgroup=vimSubst1 syn region vimGlobal matchgroup=Statement start='\<v\%[global]!\=/' skip='\\.' end='/' skipwhite nextgroup=vimSubst1 @@ -1275,6 +1287,7 @@ if !exists("skip_vim_syntax_inits") hi def link vimAugroupError vimError hi def link vimAugroupKey vimCommand hi def link vimAutoCmd vimCommand + hi def link vimAutoCmdBang vimBang hi def link vimAutoEvent Type hi def link vimAutoCmdMod Special hi def link vimBang vimOper @@ -1396,10 +1409,11 @@ if !exists("skip_vim_syntax_inits") hi def link vimPattern Type hi def link vimPlainMark vimMark hi def link vimPlainRegister vimRegister + hi def link vimQuoteEscape vimEscape hi def link vimRegister SpecialChar hi def link vimScriptDelim Comment - hi def link vimSearchDelim Statement hi def link vimSearch vimString + hi def link vimSearchDelim Delimiter hi def link vimSep Delimiter hi def link vimSet vimCommand hi def link vimSetAll vimOption @@ -1492,6 +1506,8 @@ if !exists("skip_vim_syntax_inits") hi def link vim9MethodNameError vimFunctionError hi def link vim9Null Constant hi def link vim9Public vimCommand + hi def link vim9Search vimString + hi def link vim9SearchDelim Delimiter hi def link vim9Static vimCommand hi def link vim9Super Identifier hi def link vim9This Identifier diff --git a/runtime/syntax/xf86conf.vim b/runtime/syntax/xf86conf.vim index e8162f3a35..0f4e5036ff 100644 --- a/runtime/syntax/xf86conf.vim +++ b/runtime/syntax/xf86conf.vim @@ -1,9 +1,9 @@ " Vim syntax file " Language: XF86Config (XFree86 configuration file) +" Maintainer: This runtime file is looking for a new maintainer. +" Last Change: 2025 Jan 06 by Jan-Arvid Harrach (#16397) " Former Maintainer: David Ne\v{c}as (Yeti) <yeti@physics.muni.cz> " Last Change By David: 2010 Nov 01 -" Last Change: 2023 Jan 23 -" Required Vim Version: 6.0 " " Options: let xf86conf_xfree86_version = 3 or 4 " to force XFree86 3.x or 4.x XF86Config syntax @@ -58,7 +58,7 @@ syn match xf86confModeLineValue "\"[^\"]\+\"\(\_s\+[0-9.]\+\)\{9}" nextgroup=xf8 " Sections and subsections if b:xf86conf_xfree86_version >= 4 - syn region xf86confSection matchgroup=xf86confSectionDelim start="^\s*Section\s\+\"\(Files\|Server[_ ]*Flags\|Input[_ ]*Device\|Device\|Video[_ ]*Adaptor\|Server[_ ]*Layout\|DRI\|Extensions\|Vendor\|Keyboard\|Pointer\|InputClass\)\"" end="^\s*EndSection\>" skip="#.*$\|\"[^\"]*\"" contains=xf86confComment,xf86confOption,xf86confKeyword,xf86confSectionError + syn region xf86confSection matchgroup=xf86confSectionDelim start="^\s*Section\s\+\"\(Files\|Server[_ ]*Flags\|Input[_ ]*Device\|Device\|Video[_ ]*Adaptor\|Server[_ ]*Layout\|DRI\|Extensions\|Vendor\|Keyboard\|Pointer\|InputClass\|OutputClass\)\"" end="^\s*EndSection\>" skip="#.*$\|\"[^\"]*\"" contains=xf86confComment,xf86confOption,xf86confKeyword,xf86confSectionError syn region xf86confSectionModule matchgroup=xf86confSectionDelim start="^\s*Section\s\+\"Module\"" end="^\s*EndSection\>" skip="#.*$\|\"[^\"]*\"" contains=xf86confSubsectionAny,xf86confComment,xf86confOption,xf86confKeyword syn region xf86confSectionMonitor matchgroup=xf86confSectionDelim start="^\s*Section\s\+\"Monitor\"" end="^\s*EndSection\>" skip="#.*$\|\"[^\"]*\"" contains=xf86confSubsectionMode,xf86confModeLine,xf86confComment,xf86confOption,xf86confKeyword syn region xf86confSectionModes matchgroup=xf86confSectionDelim start="^\s*Section\s\+\"Modes\"" end="^\s*EndSection\>" skip="#.*$\|\"[^\"]*\"" contains=xf86confSubsectionMode,xf86confModeLine,xf86confComment @@ -162,7 +162,7 @@ syn match xf86confSync "\(\s\+[+-][CHV]_*Sync\)\+" contained " Synchronization if b:xf86conf_xfree86_version >= 4 - syn sync match xf86confSyncSection grouphere xf86confSection "^\s*Section\s\+\"\(Files\|Server[_ ]*Flags\|Input[_ ]*Device\|Device\|Video[_ ]*Adaptor\|Server[_ ]*Layout\|DRI\|Extensions\|Vendor\|Keyboard\|Pointer\|InputClass\)\"" + syn sync match xf86confSyncSection grouphere xf86confSection "^\s*Section\s\+\"\(Files\|Server[_ ]*Flags\|Input[_ ]*Device\|Device\|Video[_ ]*Adaptor\|Server[_ ]*Layout\|DRI\|Extensions\|Vendor\|Keyboard\|Pointer\|InputClass\|OutputClass\)\"" syn sync match xf86confSyncSectionModule grouphere xf86confSectionModule "^\s*Section\s\+\"Module\"" syn sync match xf86confSyncSectionModes groupthere xf86confSectionModes "^\s*Section\s\+\"Modes\"" else diff --git a/runtime/syntax/zsh.vim b/runtime/syntax/zsh.vim index 084f8cdb41..04b39aeac0 100644 --- a/runtime/syntax/zsh.vim +++ b/runtime/syntax/zsh.vim @@ -2,7 +2,7 @@ " Language: Zsh shell script " Maintainer: Christian Brabandt <cb@256bit.org> " Previous Maintainer: Nikolai Weibull <now@bitwi.se> -" Latest Revision: 2022-07-26 +" Latest Revision: 2024 Jan 04 " License: Vim (see :h license) " Repository: https://github.com/chrisbra/vim-zsh @@ -48,8 +48,9 @@ syn match zshPOSIXQuoted '\\u[0-9a-fA-F]\{1,4}' syn match zshPOSIXQuoted '\\U[1-9a-fA-F]\{1,8}' syn region zshString matchgroup=zshStringDelimiter start=+"+ end=+"+ - \ contains=zshQuoted,@zshDerefs,@zshSubstQuoted fold + \ contains=@Spell,zshQuoted,@zshDerefs,@zshSubstQuoted fold syn region zshString matchgroup=zshStringDelimiter start=+'+ end=+'+ fold + \ contains=@Spell syn region zshPOSIXString matchgroup=zshStringDelimiter start=+\$'+ \ skip=+\\[\\']+ end=+'+ contains=zshPOSIXQuoted,zshQuoted syn match zshJobSpec '%\(\d\+\|?\=\w\+\|[%+-]\)' @@ -68,7 +69,7 @@ syn keyword zshConditional if then elif else fi esac select syn keyword zshCase case nextgroup=zshCaseWord skipwhite syn match zshCaseWord /\S\+/ nextgroup=zshCaseIn skipwhite contained transparent -syn keyword zshCaseIn in nextgroup=zshCasePattern skipwhite skipnl contained +syn keyword zshCaseIn in nextgroup=zshComment,zshCasePattern skipwhite skipnl contained syn match zshCasePattern /\S[^)]*)/ contained syn keyword zshRepeat while until repeat @@ -94,22 +95,24 @@ syn match zshRedir '|\@1<!|&\=|\@!' syn region zshHereDoc matchgroup=zshRedir \ start='<\@<!<<\s*\z([^<]\S*\)' - \ end='^\z1\>' - \ contains=@zshSubst,@zshDerefs,zshQuoted,zshPOSIXString + \ end='^\z1$' + \ contains=@Spell,@zshSubst,@zshDerefs,zshQuoted,zshPOSIXString syn region zshHereDoc matchgroup=zshRedir \ start='<\@<!<<\s*\\\z(\S\+\)' - \ end='^\z1\>' - \ contains=@zshSubst,@zshDerefs,zshQuoted,zshPOSIXString + \ end='^\z1$' + \ contains=@Spell syn region zshHereDoc matchgroup=zshRedir \ start='<\@<!<<-\s*\\\=\z(\S\+\)' - \ end='^\s*\z1\>' - \ contains=@zshSubst,@zshDerefs,zshQuoted,zshPOSIXString + \ end='^\t*\z1$' + \ contains=@Spell syn region zshHereDoc matchgroup=zshRedir \ start=+<\@<!<<\s*\(["']\)\z(\S\+\)\1+ - \ end='^\z1\>' + \ end='^\z1$' + \ contains=@Spell syn region zshHereDoc matchgroup=zshRedir \ start=+<\@<!<<-\s*\(["']\)\z(\S\+\)\1+ - \ end='^\s*\z1\>' + \ end='^\t*\z1$' + \ contains=@Spell syn match zshVariable '\<\h\w*' contained |