diff options
| author | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:44:22 +0300 |
|---|---|---|
| committer | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:44:22 +0300 |
| commit | 934382c8a1ce738589dee9ee0f14e1cec812770e (patch) | |
| tree | f13715762efd06518f8aec3a2bf39ac8a615987f /uvim/runtime/pack | |
| parent | 0b24459ac12b6cf9fd5a401d647796ca254a8fa8 (diff) | |
| parent | 7088926316d8d4a7572a242d0765e99adfc8b083 (diff) | |
| download | Project-Tick-934382c8a1ce738589dee9ee0f14e1cec812770e.tar.gz Project-Tick-934382c8a1ce738589dee9ee0f14e1cec812770e.zip | |
Add 'uvim/' from commit '7088926316d8d4a7572a242d0765e99adfc8b083'
git-subtree-dir: uvim
git-subtree-mainline: 0b24459ac12b6cf9fd5a401d647796ca254a8fa8
git-subtree-split: 7088926316d8d4a7572a242d0765e99adfc8b083
Diffstat (limited to 'uvim/runtime/pack')
56 files changed, 23975 insertions, 0 deletions
diff --git a/uvim/runtime/pack/dist/opt/cfilter/plugin/cfilter.vim b/uvim/runtime/pack/dist/opt/cfilter/plugin/cfilter.vim new file mode 100644 index 0000000000..7a71de4764 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/cfilter/plugin/cfilter.vim @@ -0,0 +1,72 @@ +vim9script + +# cfilter.vim: Plugin to filter entries from a quickfix/location list +# Last Change: August 16, 2023 +# Maintainer: Yegappan Lakshmanan (yegappan AT yahoo DOT com) +# Version: 2.0 +# +# Commands to filter the quickfix list: +# :Cfilter[!] /{pat}/ +# Create a new quickfix list from entries matching {pat} in the current +# quickfix list. Both the file name and the text of the entries are +# matched against {pat}. If ! is supplied, then entries not matching +# {pat} are used. The pattern can be optionally enclosed using one of +# the following characters: ', ", /. If the pattern is empty, then the +# last used search pattern is used. +# :Lfilter[!] /{pat}/ +# Same as :Cfilter but operates on the current location list. +# + +def Qf_filter(qf: bool, searchpat: string, bang: string) + var Xgetlist: func + var Xsetlist: func + var cmd: string + var firstchar: string + var lastchar: string + var pat: string + var title: string + var Cond: func + var items: list<any> + + if qf + Xgetlist = function('getqflist') + Xsetlist = function('setqflist') + cmd = $':Cfilter{bang}' + else + Xgetlist = function('getloclist', [0]) + Xsetlist = function('setloclist', [0]) + cmd = $':Lfilter{bang}' + endif + + firstchar = searchpat[0] + lastchar = searchpat[-1 :] + if firstchar == lastchar && + (firstchar == '/' || firstchar == '"' || firstchar == "'") + pat = searchpat[1 : -2] + if pat == '' + # Use the last search pattern + pat = @/ + endif + else + pat = searchpat + endif + + if pat == '' + return + endif + + if bang == '!' + Cond = (_, val) => val.text !~# pat && bufname(val.bufnr) !~# pat + else + Cond = (_, val) => val.text =~# pat || bufname(val.bufnr) =~# pat + endif + + items = filter(Xgetlist(), Cond) + title = $'{cmd} /{pat}/' + Xsetlist([], ' ', {title: title, items: items}) +enddef + +command! -nargs=+ -bang Cfilter Qf_filter(true, <q-args>, <q-bang>) +command! -nargs=+ -bang Lfilter Qf_filter(false, <q-args>, <q-bang>) + +# vim: shiftwidth=2 sts=2 expandtab diff --git a/uvim/runtime/pack/dist/opt/comment/autoload/comment.vim b/uvim/runtime/pack/dist/opt/comment/autoload/comment.vim new file mode 100644 index 0000000000..74e091c358 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/comment/autoload/comment.vim @@ -0,0 +1,166 @@ +vim9script + +# Maintainer: Maxim Kim <habamax@gmail.com> +# Last Update: 2025-06-13 +# +# Toggle comments +# Usage: +# Add following mappings to vimrc: +# import autoload 'dist/comment.vim' +# nnoremap <silent> <expr> gc comment.Toggle() +# xnoremap <silent> <expr> gc comment.Toggle() +# nnoremap <silent> <expr> gcc comment.Toggle() .. '_' +# nnoremap <silent> <expr> gC comment.Toggle() .. '$' +export def Toggle(...args: list<string>): string + if len(args) == 0 + &opfunc = matchstr(expand('<stack>'), '[^. ]*\ze[') + return 'g@' + endif + if empty(&cms) || !&ma | return '' | endif + var cms = substitute(substitute(&cms, '\S\zs%s\s*', ' %s', ''), '%s\ze\S', '%s ', '') + var [lnum1, lnum2] = [line("'["), line("']")] + var cms_l = split(escape(cms, '*.\^$['), '\s*%s\s*') + + var first_col = indent(lnum1) + var start_col = getpos("'[")[2] - 1 + if len(cms_l) == 1 && lnum1 == lnum2 && first_col < start_col + var line_start = getline(lnum1)[0 : start_col - 1] + var line_end = getline(lnum1)[start_col : -1] + line_end = line_end =~ $'\c^\s*{cms_l[0]}' ? + \ substitute(line_end, $'\c^\s*\zs{cms_l[0]}\s\ze\s*', line_end =~ '^\s' ? ' ' : '', '') : + \ printf(substitute(cms, '%s\@!', '%%', ''), line_end) + setline(lnum1, line_start .. line_end) + return '' + endif + + if len(cms_l) == 0 | return '' | endif + if len(cms_l) == 1 | call add(cms_l, '') | endif + var comment = false + var indent_spaces = false + var indent_tabs = false + var indent_min = indent(lnum1) + var indent_start = matchstr(getline(lnum1), '^\s*') + for lnum in range(lnum1, lnum2) + if getline(lnum) =~ '^\s*$' | continue | endif + var indent_str = matchstr(getline(lnum), '^\s*') + if indent_min > indent(lnum) + indent_min = indent(lnum) + indent_start = indent_str + endif + indent_spaces = indent_spaces || (stridx(indent_str, ' ') != -1) + indent_tabs = indent_tabs || (stridx(indent_str, "\t") != -1) + if getline(lnum) !~ $'\c^\s*{cms_l[0]}.*{cms_l[1]}$' + comment = true + endif + endfor + var mixed_indent = indent_spaces && indent_tabs + var lines = [] + var line = '' + for lnum in range(lnum1, lnum2) + if getline(lnum) =~ '^\s*$' + line = getline(lnum) + elseif comment + if exists("g:comment_first_col") || exists("b:comment_first_col") + line = printf(substitute(cms, '%s\@!', '%%', 'g'), getline(lnum)) + else + # consider different whitespace indenting + var indent_current = mixed_indent ? matchstr(getline(lnum), '^\s*') : indent_start + line = printf(indent_current .. substitute(cms, '%s\@!', '%%', 'g'), + strpart(getline(lnum), strlen(indent_current))) + endif + else + line = substitute(getline(lnum), $'\c^\s*\zs{cms_l[0]} \?\| \?{cms_l[1]}$', '', 'g') + endif + add(lines, line) + endfor + noautocmd keepjumps setline(lnum1, lines) + return '' +enddef + + +# Comment text object +# Usage: +# import autoload 'dist/comment.vim' +# onoremap <silent>ic <scriptcmd>comment.ObjComment(v:true)<CR> +# onoremap <silent>ac <scriptcmd>comment.ObjComment(v:false)<CR> +# xnoremap <silent>ic <esc><scriptcmd>comment.ObjComment(v:true)<CR> +# xnoremap <silent>ac <esc><scriptcmd>comment.ObjComment(v:false)<CR> +export def ObjComment(inner: bool) + def IsComment(): bool + var stx = map(synstack(line('.'), col('.')), 'synIDattr(v:val, "name")')->join() + return stx =~? 'Comment' + enddef + + # requires syntax support + if !exists("g:syntax_on") + return + endif + + var pos_init = getcurpos() + + # If not in comment, search next one, + if !IsComment() + if search('\v\k+', 'W', line(".") + 100, 100, () => !IsComment()) <= 0 + return + endif + endif + + # Search for the beginning of the comment block + if IsComment() + if search('\v%(\S+)|%(^\s*$)', 'bW', 0, 200, IsComment) > 0 + search('\v%(\S)|%(^\s*$)', 'W', 0, 200, () => !IsComment()) + else + cursor(1, 1) + search('\v\S+', 'cW', 0, 200) + endif + endif + + var pos_start = getcurpos() + + if !inner + var col = pos_start[2] + var prefix = getline(pos_start[1])[ : col - 2] + while col > 0 && prefix[col - 2] =~ '\s' + col -= 1 + endwhile + pos_start[2] = col + endif + + # Search for the comment end. + if pos_init[1] > pos_start[1] + cursor(pos_init[1], pos_init[2]) + endif + if search('\v%(\S+)|%(^\s*$)', 'W', 0, 200, IsComment) > 0 + search('\S', 'beW', 0, 200, () => !IsComment()) + else + if search('\%$', 'W', 0, 200) > 0 + search('\ze\S', 'beW', line('.'), 200, () => !IsComment()) + endif + endif + + var pos_end = getcurpos() + + if !inner + var spaces = matchstr(getline(pos_end[1]), '\%>.c\s*') + pos_end[2] += spaces->len() + if getline(pos_end[1])[pos_end[2] : ] =~ '^\s*$' + && (pos_start[2] <= 1 || getline(pos_start[1])[ : pos_start[2]] =~ '^\s*$') + if search('\v\s*\_$(\s*\n)+', 'eW', 0, 200) > 0 + pos_end = getcurpos() + endif + endif + endif + + if (pos_end[2] == (getline(pos_end[1])->len() ?? 1)) && pos_start[2] <= 1 + cursor(pos_end[1], 1) + normal! V + cursor(pos_start[1], 1) + else + cursor(pos_end[1], pos_end[2]) + normal! v + if &selection == 'exclusive' + normal! lo + endif + cursor(pos_start[1], pos_start[2]) + endif +enddef diff --git a/uvim/runtime/pack/dist/opt/comment/doc/comment.txt b/uvim/runtime/pack/dist/opt/comment/doc/comment.txt new file mode 100644 index 0000000000..2286f07851 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/comment/doc/comment.txt @@ -0,0 +1,131 @@ +*comment.txt* For Vim version 9.1. Last change: 2025 Jun 22 + + + VIM REFERENCE MANUAL + +Commenting and un-commenting text. + +============================================================================== + +See |comment-install| on how to activate this package. + +The comment.vim package, allows to toggle comments for a single line, a range +of lines or a selected text object. It defines the following mappings: + + *o_gc* +gc{motion} to toggle comments for the selected motion + *v_gc* +{Visual}gc to comment/uncomment the highlighted lines. + +Since gc operates on a motion, it can be used with any motion, for example _ +to comment the current line, or ip to comment the current paragraph. +Default mappings are defined for `gc_` and `gc$`: + *gcc* +gcc to comment/uncomment current line (same as `gc_`) + + *gC* +gC to comment/uncomment to end of current line (same as `gc$`) + +Commenting to the end of a line using `gC` works whenever the filetype plugin +supports it (that is, whenever the comment marker precedes the code) and falls +back to `gcc` otherwise. + +Note: using `gC` may not always result in valid comment markers depending on +the language used. + +Additionally, the plugin defines a comment text-object which requires syntax +highlighting to be enabled. + *v_ac* *ac* +ac "a comment", select current or next comment. + Leading and trailing white space is included. + Trailing newlines are included too. + *v_ic* *ic* +ic "inner comment", select current or next comment. + +This plugin uses the buffer-local 'commentstring' option value to add or remove +comment markers to the selected lines. Whether it will comment or un-comment +depends on the range of lines to act upon. When all of the lines in range +have comment markers, all lines will be un-commented, if it doesn't, the lines +will be commented out. Blank and empty lines are ignored. + +The value of 'commentstring' is the same for the entire buffer and determined +by its filetype (|filetypes|). To adapt it within the buffer for embedded +languages, you can use a plug-in such as +https://github.com/suy/vim-context-commentstring. + +The comment marker will always be padded with blanks whether or not the +'commentstring' value contains whitespace around "%s". + +If the mapping does not seem to work (or uses wrong comment markers), it might +be because of several reasons: +- the filetype is not detected by Vim, see |new-filetype|, +- filetype plugins are not enabled, see |:filetype-plugin-on| or +- the filetype plugin does not set the (correct) 'commentstring' option. + +You can simply configure this using the following autocommand (e.g. for legacy +Vim script): > + + autocmd Filetype vim :setlocal commentstring="%s + +This example sets the " as start of a comment for legacy Vim script. For Vim9 +script, you would instead use the "#" char: > + + autocmd Filetype vim :setlocal commentstring=#\ %s + +============================================================================== +Options: + +*g:comment_first_col* +*b:comment_first_col* + By default comment chars are added in front of the line, i.e. if the line + was indented, commented line would stay indented as well. + + However some filetypes require a comment char on the first column, use this option + to change default behaviour. + + Use g:comment_first_col to change it globally or b:comment_first_col to + target specific filetype(s). + +*g:comment_mappings* + Set to false to disable the default keyboard mappings, e.g. in your vimrc +> + let g:comment_mappings = v:false +< + This option must be set before the package is activated using |packadd|. + +============================================================================== +Mappings: + +The following |<Plug>| mappings are included, which you can use to customise the +keyboard mappings. + +*<Plug>(comment-toggle)* + Normal and visual modes, mapped to gc by default + +*<Plug>(comment-toggle-line)* + Normal mode only, mapped to gcc by default + +*<Plug>(comment-toggle-end)* + Normal mode only, mapped to gC by default + +*<Plug>(comment-text-object-inner)* + Operator pending and visual modes, mapped to ic by default + +*<Plug>(comment-text-object-outer)* + Operator pending and visual modes, mapped to ac by default + +The default keyboard mappings are shown below, you can copy these if you wish +to customise them in your vimrc: +> + nmap gc <Plug>(comment-toggle) + xmap gc <Plug>(comment-toggle) + nmap gcc <Plug>(comment-toggle-line) + nmap gC <Plug>(comment-toggle-end) + + omap ic <Plug>(comment-text-object-inner) + omap ac <Plug>(comment-text-object-outer) + xmap ic <Plug>(comment-text-object-inner) + xmap ac <Plug>(comment-text-object-outer) +< +============================================================================== +vim:tw=78:ts=8:fo=tcq2:ft=help: diff --git a/uvim/runtime/pack/dist/opt/comment/doc/tags b/uvim/runtime/pack/dist/opt/comment/doc/tags new file mode 100644 index 0000000000..6096d114ca --- /dev/null +++ b/uvim/runtime/pack/dist/opt/comment/doc/tags @@ -0,0 +1,17 @@ +<Plug>(comment-text-object-inner) comment.txt /*<Plug>(comment-text-object-inner)* +<Plug>(comment-text-object-outer) comment.txt /*<Plug>(comment-text-object-outer)* +<Plug>(comment-toggle) comment.txt /*<Plug>(comment-toggle)* +<Plug>(comment-toggle-end) comment.txt /*<Plug>(comment-toggle-end)* +<Plug>(comment-toggle-line) comment.txt /*<Plug>(comment-toggle-line)* +ac comment.txt /*ac* +b:comment_first_col comment.txt /*b:comment_first_col* +comment.txt comment.txt /*comment.txt* +g:comment_first_col comment.txt /*g:comment_first_col* +g:comment_mappings comment.txt /*g:comment_mappings* +gC comment.txt /*gC* +gcc comment.txt /*gcc* +ic comment.txt /*ic* +o_gc comment.txt /*o_gc* +v_ac comment.txt /*v_ac* +v_gc comment.txt /*v_gc* +v_ic comment.txt /*v_ic* diff --git a/uvim/runtime/pack/dist/opt/comment/plugin/comment.vim b/uvim/runtime/pack/dist/opt/comment/plugin/comment.vim new file mode 100644 index 0000000000..62817ddfe0 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/comment/plugin/comment.vim @@ -0,0 +1,29 @@ +vim9script + +# Maintainer: Maxim Kim <habamax@gmail.com> +# Last Update: 2025 Mar 21 +# 2025 Jun 22 by Vim Project: add <Plug> mappings #17563 + +import autoload 'comment.vim' + +nnoremap <silent> <expr> <Plug>(comment-toggle) comment.Toggle() +xnoremap <silent> <expr> <Plug>(comment-toggle) comment.Toggle() +nnoremap <silent> <expr> <Plug>(comment-toggle-line) comment.Toggle() .. '_' +nnoremap <silent> <expr> <Plug>(comment-toggle-end) comment.Toggle() .. '$' + +onoremap <silent> <Plug>(comment-text-object-inner) <scriptcmd>comment.ObjComment(v:true)<CR> +onoremap <silent> <Plug>(comment-text-object-outer) <scriptcmd>comment.ObjComment(v:false)<CR> +xnoremap <silent> <Plug>(comment-text-object-inner) <esc><scriptcmd>comment.ObjComment(v:true)<CR> +xnoremap <silent> <Plug>(comment-text-object-outer) <esc><scriptcmd>comment.ObjComment(v:false)<CR> + +if get(g:, 'comment_mappings', true) + nmap gc <Plug>(comment-toggle) + xmap gc <Plug>(comment-toggle) + nmap gcc <Plug>(comment-toggle-line) + nmap gC <Plug>(comment-toggle-end) + + omap ic <Plug>(comment-text-object-inner) + omap ac <Plug>(comment-text-object-outer) + xmap ic <Plug>(comment-text-object-inner) + xmap ac <Plug>(comment-text-object-outer) +endif diff --git a/uvim/runtime/pack/dist/opt/dvorak/dvorak/disable.vim b/uvim/runtime/pack/dist/opt/dvorak/dvorak/disable.vim new file mode 100644 index 0000000000..1e9b0702ff --- /dev/null +++ b/uvim/runtime/pack/dist/opt/dvorak/dvorak/disable.vim @@ -0,0 +1,72 @@ +" Back to Qwerty keyboard after using Dvorak. + +iunmap a +iunmap b +iunmap c +iunmap d +iunmap e +iunmap f +iunmap g +iunmap h +iunmap i +iunmap j +iunmap k +iunmap l +iunmap m +iunmap n +iunmap o +iunmap p +iunmap q +iunmap r +iunmap s +iunmap t +iunmap u +iunmap v +iunmap w +iunmap x +iunmap y +iunmap z +iunmap ; +iunmap ' +iunmap " +iunmap , +iunmap . +iunmap / +iunmap A +iunmap B +iunmap C +iunmap D +iunmap E +iunmap F +iunmap G +iunmap H +iunmap I +iunmap J +iunmap K +iunmap L +iunmap M +iunmap N +iunmap O +iunmap P +iunmap Q +iunmap R +iunmap S +iunmap T +iunmap U +iunmap V +iunmap W +iunmap X +iunmap Y +iunmap Z +iunmap < +iunmap > +iunmap ? +iunmap : +iunmap [ +iunmap ] +iunmap { +iunmap } +iunmap - +iunmap _ +iunmap = +iunmap + diff --git a/uvim/runtime/pack/dist/opt/dvorak/dvorak/enable.vim b/uvim/runtime/pack/dist/opt/dvorak/dvorak/enable.vim new file mode 100644 index 0000000000..8ff363fe97 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/dvorak/dvorak/enable.vim @@ -0,0 +1,77 @@ +" Dvorak keyboard, only in Insert mode. +" +" Change "inoremap" to "map!" to also use in Ex mode. +" Also change disable.vim then: "iunmap" to "unmap!". +" +" You may want to add a list of map's too. + +inoremap a a +inoremap b x +inoremap c j +inoremap d e +inoremap e . +inoremap f u +inoremap g i +inoremap h d +inoremap i c +inoremap j h +inoremap k t +inoremap l n +inoremap m m +inoremap n b +inoremap o r +inoremap p l +inoremap q ' +inoremap r p +inoremap s o +inoremap t y +inoremap u g +inoremap v k +inoremap w , +inoremap x q +inoremap y f +inoremap z ; +inoremap ; s +inoremap ' - +inoremap " _ +inoremap , w +inoremap . v +inoremap / z +inoremap A A +inoremap B X +inoremap C J +inoremap D E +inoremap E > +inoremap F U +inoremap G I +inoremap H D +inoremap I C +inoremap J H +inoremap K T +inoremap L N +inoremap M M +inoremap N B +inoremap O R +inoremap P L +inoremap Q " +inoremap R P +inoremap S O +inoremap T Y +inoremap U G +inoremap V K +inoremap W < +inoremap X Q +inoremap Y F +inoremap Z : +inoremap < W +inoremap > V +inoremap ? Z +inoremap : S +inoremap [ / +inoremap ] = +inoremap { ? +inoremap } + +inoremap - [ +inoremap _ { +inoremap = ] +inoremap + } diff --git a/uvim/runtime/pack/dist/opt/dvorak/plugin/dvorak.vim b/uvim/runtime/pack/dist/opt/dvorak/plugin/dvorak.vim new file mode 100644 index 0000000000..c8d5d5c79f --- /dev/null +++ b/uvim/runtime/pack/dist/opt/dvorak/plugin/dvorak.vim @@ -0,0 +1,16 @@ +" When using a dvorak keyboard this file may be of help to you. +" These mappings have been made by Lawrence Kesteloot <kesteloo@cs.unc.edu>. +" What they do is that the most often used keys, like hjkl, are put in a more +" easy to use position. +" It may take some time to learn using this. + +if exists("g:loaded_dvorak_plugin") + finish +endif +let g:loaded_dvorak_plugin = 1 + +" Key to go into dvorak mode: +map ,d :runtime dvorak/enable.vim<CR> + +" Key to get out of dvorak mode: +map ,q :runtime dvorak/disable.vim<CR> diff --git a/uvim/runtime/pack/dist/opt/editexisting/plugin/editexisting.vim b/uvim/runtime/pack/dist/opt/editexisting/plugin/editexisting.vim new file mode 100644 index 0000000000..52e80c142e --- /dev/null +++ b/uvim/runtime/pack/dist/opt/editexisting/plugin/editexisting.vim @@ -0,0 +1,118 @@ +" Vim Plugin: Edit the file with an existing Vim if possible +" Maintainer: The Vim Project <https://github.com/vim/vim> +" Last Change: 2023 Aug 13 + +" To use add ":packadd! editexisting" in your vimrc file. + +" This plugin serves two purposes: +" 1. On startup, if we were invoked with one file name argument and the file +" is not modified then try to find another Vim instance that is editing +" this file. If there is one then bring it to the foreground and exit. +" 2. When a file is edited and a swap file exists for it, try finding that +" other Vim and bring it to the foreground. Requires Vim 7, because it +" uses the SwapExists autocommand event. + +" Function that finds the Vim instance that is editing "filename" and brings +" it to the foreground. +func s:EditElsewhere(filename) + let fname_esc = substitute(a:filename, "'", "''", "g") + + let servers = serverlist() + while servers != '' + " Get next server name in "servername"; remove it from "servers". + let i = match(servers, "\n") + if i == -1 + let servername = servers + let servers = '' + else + let servername = strpart(servers, 0, i) + let servers = strpart(servers, i + 1) + endif + + " Skip ourselves. + if servername ==? v:servername + continue + endif + + " Check if this server is editing our file. + try + if remote_expr(servername, "bufloaded('" . fname_esc . "')") + " Yes, bring it to the foreground. + if has("win32") + call remote_foreground(servername) + endif + call remote_expr(servername, "foreground()") + + if remote_expr(servername, "exists('*EditExisting')") + " Make sure the file is visible in a window (not hidden). + " If v:swapcommand exists and is set, send it to the server. + if exists("v:swapcommand") + let c = substitute(v:swapcommand, "'", "''", "g") + call remote_expr(servername, "EditExisting('" . fname_esc . "', '" . c . "')") + else + call remote_expr(servername, "EditExisting('" . fname_esc . "', '')") + endif + endif + + if !(has('vim_starting') && has('gui_running') && has('gui_win32')) + " Tell the user what is happening. Not when the GUI is starting + " though, it would result in a message box. + echomsg "File is being edited by " . servername + sleep 2 + endif + return 'q' + endif + catch /^Vim\%((\a\+)\)\=:E241:/ + " Unable to send to this server, ignore it. + endtry + endwhile + return '' +endfunc + +" When the plugin is loaded and there is one file name argument: Find another +" Vim server that is editing this file right now. +if argc() == 1 && !&modified + if s:EditElsewhere(expand("%:p")) == 'q' + quit + endif +endif + +" Setup for handling the situation that an existing swap file is found. +try + au! SwapExists * let v:swapchoice = s:EditElsewhere(expand("<afile>:p")) +catch + " Without SwapExists we don't do anything for ":edit" commands +endtry + +" Function used on the server to make the file visible and possibly execute a +" command. +func! EditExisting(fname, command) + " Get the window number of the file in the current tab page. + let winnr = bufwinnr(a:fname) + if winnr <= 0 + " Not found, look in other tab pages. + let bufnr = bufnr(a:fname) + for i in range(tabpagenr('$')) + if index(tabpagebuflist(i + 1), bufnr) >= 0 + " Make this tab page the current one and find the window number. + exe 'tabnext ' . (i + 1) + let winnr = bufwinnr(a:fname) + break + endif + endfor + endif + + if winnr > 0 + exe winnr . "wincmd w" + elseif exists('*fnameescape') + exe "split " . fnameescape(a:fname) + else + exe "split " . escape(a:fname, " \t\n*?[{`$\\%#'\"|!<") + endif + + if a:command != '' + exe "normal! " . a:command + endif + + redraw +endfunc diff --git a/uvim/runtime/pack/dist/opt/editorconfig/.editorconfig b/uvim/runtime/pack/dist/opt/editorconfig/.editorconfig new file mode 100644 index 0000000000..7eed9e111d --- /dev/null +++ b/uvim/runtime/pack/dist/opt/editorconfig/.editorconfig @@ -0,0 +1,27 @@ +root = true + +[*] +end_of_line = lf +charset = utf-8 +max_line_length = 80 + +[*.{vim,sh}] +indent_style = space +indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = true +max_line_length = 80 + +[*.rb] +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true +max_line_length = 120 + +[*.yml] +indent_style = space +indent_size = 2 + +[*.{bat,vbs,ps1}] +end_of_line = CRLF diff --git a/uvim/runtime/pack/dist/opt/editorconfig/CONTRIBUTORS b/uvim/runtime/pack/dist/opt/editorconfig/CONTRIBUTORS new file mode 100644 index 0000000000..b799668c87 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/editorconfig/CONTRIBUTORS @@ -0,0 +1,6 @@ +Contributors to the EditorConfig Vim Plugin: + +Hong Xu +Trey Hunner +Kent Frazier +Chris White diff --git a/uvim/runtime/pack/dist/opt/editorconfig/LICENSE b/uvim/runtime/pack/dist/opt/editorconfig/LICENSE new file mode 100644 index 0000000000..ed9286e0aa --- /dev/null +++ b/uvim/runtime/pack/dist/opt/editorconfig/LICENSE @@ -0,0 +1,26 @@ +Unless otherwise stated, all files are distributed under the Simplified BSD +license included below. + +Copyright (c) 2011-2019 EditorConfig Team +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/uvim/runtime/pack/dist/opt/editorconfig/LICENSE.PSF b/uvim/runtime/pack/dist/opt/editorconfig/LICENSE.PSF new file mode 100644 index 0000000000..36eb8e0d3b --- /dev/null +++ b/uvim/runtime/pack/dist/opt/editorconfig/LICENSE.PSF @@ -0,0 +1,53 @@ +Some code in editorconfig-vim is derived from code licensed under the +PSF license. The following is the text of that license, retrieved 2019-05-05 +from https://docs.python.org/2.6/license.html#terms-and-conditions-for-accessing-or-otherwise-using-python + +PSF LICENSE AGREEMENT FOR PYTHON 2.6.9 + +1. This LICENSE AGREEMENT is between the Python Software Foundation +(``PSF''), and the Individual or Organization (``Licensee'') accessing and +otherwise using Python 2.6.9 software in source or binary form and its +associated documentation. + +2. Subject to the terms and conditions of this License Agreement, PSF +hereby grants Licensee a nonexclusive, royalty-free, world-wide +license to reproduce, analyze, test, perform and/or display publicly, +prepare derivative works, distribute, and otherwise use Python 2.6.9 +alone or in any derivative version, provided, however, that PSF's +License Agreement and PSF's notice of copyright, i.e., ``Copyright (c) +2001-2010 Python Software Foundation; All Rights Reserved'' are +retained in Python 2.6.9 alone or in any derivative version prepared +by Licensee. + +3. In the event Licensee prepares a derivative work that is based on +or incorporates Python 2.6.9 or any part thereof, and wants to make +the derivative work available to others as provided herein, then +Licensee hereby agrees to include in any such work a brief summary of +the changes made to Python 2.6.9. + +4. PSF is making Python 2.6.9 available to Licensee on an ``AS IS'' +basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. +BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY +REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY +PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 2.6.9 WILL NOT INFRINGE +ANY THIRD PARTY RIGHTS. + +5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON +2.6.9 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS +A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 2.6.9, +OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF. + +6. This License Agreement will automatically terminate upon a material +breach of its terms and conditions. + +7. Nothing in this License Agreement shall be deemed to create any +relationship of agency, partnership, or joint venture between PSF and +Licensee. This License Agreement does not grant permission to use PSF +trademarks or trade name in a trademark sense to endorse or promote +products or services of Licensee, or any third party. + +8. By copying, installing or otherwise using Python 2.6.9, Licensee +agrees to be bound by the terms and conditions of this License +Agreement. + +# vi: set ft=: diff --git a/uvim/runtime/pack/dist/opt/editorconfig/README.md b/uvim/runtime/pack/dist/opt/editorconfig/README.md new file mode 100644 index 0000000000..961c9ae2d2 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/editorconfig/README.md @@ -0,0 +1,148 @@ +# EditorConfig Vim Plugin + +[](https://travis-ci.org/editorconfig/editorconfig-vim) +[](https://ci.appveyor.com/project/cxw42/editorconfig-vim) + +This is an [EditorConfig][] plugin for Vim. This plugin can be found on both +[GitHub][] and [Vim online][]. + +## Installation + +To install this plugin, you can use one of the following ways: + +### Install with the archive + +Download the [archive][] and extract it into your Vim runtime directory +(`~/.vim` on UNIX/Linux and `$VIM_INSTALLATION_FOLDER\vimfiles` on windows). +You should have 4 sub-directories in this runtime directory now: "autoload", +"doc", "ftdetect" and "plugin". + +### Install as Vim8 plugin + +Install as a Vim 8 plugin. Note `local` can be any name, but some path +element must be present. On Windows, instead of `~/.vim` use +`$VIM_INSTALLATION_FOLDER\vimfiles`. +```shell +mkdir -p ~/.vim/pack/local/start +cd ~/.vim/pack/local/start +git clone https://github.com/editorconfig/editorconfig-vim.git +``` + +### Install with [pathogen][] + +Use pathogen (the git repository of this plugin is +https://github.com/editorconfig/editorconfig-vim.git) + +### Install with [Vundle][] + +Use Vundle by adding to your `.vimrc` Vundle plugins section: + +```viml +Plugin 'editorconfig/editorconfig-vim' +``` + +Then call `:PluginInstall`. + +### Install with [vim-plug][] + +Use vim-plug by adding to your `.vimrc` in your plugin section: + +```viml +Plug 'editorconfig/editorconfig-vim' +``` + +Source your `.vimrc` by calling `:source $MYVIMRC`. + +Then call `:PlugInstall`. + +### No external editorconfig core library is required + +Previous versions of this plugin also required a Python "core". +The core included the code to parse `.editorconfig` files. +This plugin **includes** the core, so you don't need to download the +core separately. + +## Supported properties + +The EditorConfig Vim plugin supports the following EditorConfig [properties][]: + +* `indent_style` +* `indent_size` +* `tab_width` +* `end_of_line` +* `charset` +* `insert_final_newline` (Feature `+fixendofline`, available on Vim 7.4.785+, + or [PreserveNoEOL][] is required for this property) +* `trim_trailing_whitespace` +* `max_line_length` +* `root` (only used by EditorConfig core) + +## Selected Options + +The supported options are documented in [editorconfig.txt][] +and can be viewed by executing the following: `:help editorconfig`. You may +need to execute `:helptags ALL` so that Vim is aware of editorconfig.txt. + +### Excluded patterns + +To ensure that this plugin works well with [Tim Pope's fugitive][], use the +following patterns array: + +```viml +let g:EditorConfig_exclude_patterns = ['fugitive://.*'] +``` + +If you wanted to avoid loading EditorConfig for any remote files over ssh: + +```viml +let g:EditorConfig_exclude_patterns = ['scp://.*'] +``` + +Of course these two items could be combined into the following: + +```viml +let g:EditorConfig_exclude_patterns = ['fugitive://.*', 'scp://.*'] +``` + +### Disable for a specific filetype + +You can disable this plugin for a specific buffer by setting +`b:EditorConfig_disable`. Therefore, you can disable the +plugin for all buffers of a specific filetype. For example, to disable +EditorConfig for all git commit messages (filetype `gitcommit`): + +```viml +au FileType gitcommit let b:EditorConfig_disable = 1 +``` + +### Disable rules + +In very rare cases, +you might need to override some project-specific EditorConfig rules in global +or local vimrc in some cases, e.g., to resolve conflicts of trailing whitespace +trimming and buffer autosaving. This is not recommended, but you can: + +```viml +let g:EditorConfig_disable_rules = ['trim_trailing_whitespace'] +``` + +You are able to disable any supported EditorConfig properties. + +## Bugs and Feature Requests + +Feel free to submit bugs, feature requests, and other issues to the +[issue tracker][]. Be sure you have read the [contribution guidelines][]! + +[EditorConfig]: http://editorconfig.org +[GitHub]: https://github.com/editorconfig/editorconfig-vim +[PreserveNoEOL]: http://www.vim.org/scripts/script.php?script_id=4550 +[Tim Pope's fugitive]: https://github.com/tpope/vim-fugitive +[Vim online]: http://www.vim.org/scripts/script.php?script_id=3934 +[Vundle]: https://github.com/gmarik/Vundle.vim +[archive]: https://github.com/editorconfig/editorconfig-vim/archive/master.zip +[contribution guidelines]: https://github.com/editorconfig/editorconfig/blob/master/CONTRIBUTING.md#submitting-an-issue +[issue tracker]: https://github.com/editorconfig/editorconfig-vim/issues +[pathogen]: https://github.com/tpope/vim-pathogen +[properties]: http://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties +[editorconfig.txt]: https://github.com/editorconfig/editorconfig-vim/blob/master/doc/editorconfig.txt +[vim-plug]: https://github.com/junegunn/vim-plug diff --git a/uvim/runtime/pack/dist/opt/editorconfig/autoload/editorconfig.vim b/uvim/runtime/pack/dist/opt/editorconfig/autoload/editorconfig.vim new file mode 100644 index 0000000000..fd97f69bd2 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/editorconfig/autoload/editorconfig.vim @@ -0,0 +1,60 @@ +" autoload/editorconfig.vim: EditorConfig native Vim script plugin +" Copyright (c) 2011-2019 EditorConfig Team +" All rights reserved. +" +" Redistribution and use in source and binary forms, with or without +" modification, are permitted provided that the following conditions are met: +" +" 1. Redistributions of source code must retain the above copyright notice, +" this list of conditions and the following disclaimer. +" 2. Redistributions in binary form must reproduce the above copyright notice, +" this list of conditions and the following disclaimer in the documentation +" and/or other materials provided with the distribution. +" +" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +" POSSIBILITY OF SUCH DAMAGE. +" + +if v:version < 700 + finish +endif + +let s:saved_cpo = &cpo +set cpo&vim + +" {{{1 variables +let s:hook_list = [] + +function! editorconfig#AddNewHook(func) " {{{1 + " Add a new hook + + call add(s:hook_list, a:func) +endfunction + +function! editorconfig#ApplyHooks(config) abort " {{{1 + " apply hooks + + for Hook in s:hook_list + let l:hook_ret = Hook(a:config) + + if type(l:hook_ret) != type(0) && l:hook_ret != 0 + " TODO print some debug info here + endif + endfor +endfunction + +" }}} + +let &cpo = s:saved_cpo +unlet! s:saved_cpo + +" vim: fdm=marker fdc=3 diff --git a/uvim/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core.vim b/uvim/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core.vim new file mode 100644 index 0000000000..6885e17cf0 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core.vim @@ -0,0 +1,147 @@ +" autoload/editorconfig_core.vim: top-level functions for +" editorconfig-core-vimscript and editorconfig-vim. + +" Copyright (c) 2018-2020 EditorConfig Team, including Chris White {{{1 +" All rights reserved. +" +" Redistribution and use in source and binary forms, with or without +" modification, are permitted provided that the following conditions are met: +" +" 1. Redistributions of source code must retain the above copyright notice, +" this list of conditions and the following disclaimer. +" 2. Redistributions in binary form must reproduce the above copyright notice, +" this list of conditions and the following disclaimer in the documentation +" and/or other materials provided with the distribution. +" +" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +" POSSIBILITY OF SUCH DAMAGE. }}}1 + +let s:saved_cpo = &cpo +set cpo&vim + +" Variables {{{1 + +" Note: we create this variable in every script that accesses it. Normally, I +" would put this in plugin/editorconfig.vim. However, in some of my tests, +" the command-line testing environment did not load plugin/* in the normal +" way. Therefore, I do the check everywhere so I don't have to special-case +" the command line. + +if !exists('g:editorconfig_core_vimscript_debug') + let g:editorconfig_core_vimscript_debug = 0 +endif +" }}}1 + +" The latest version of the specification that we support. +" See discussion at https://github.com/editorconfig/editorconfig/issues/395 +function! editorconfig_core#version() + return [0,13,0] +endfunction + +" === CLI =============================================================== {{{1 + +" For use from the command line. Output settings for in_name to +" the buffer named out_name. If an optional argument is provided, it is the +" name of the config file to use (default '.editorconfig'). +" TODO support multiple files +" +" filename (if any) +" @param names {Dictionary} The names of the files to use for this run +" - output [required] Where the editorconfig settings should be written +" - target [required] A string or list of strings to process. Each +" must be a full path. +" - dump [optional] If present, write debug info to this file +" @param job {Dictionary} What to do - same format as the input of +" editorconfig_core#handler#get_configurations(), +" except without the target member. + +function! editorconfig_core#currbuf_cli(names, job) " out_name, in_name, ... + let l:output = [] + + " Preprocess the job + let l:job = deepcopy(a:job) + + if has_key(l:job, 'version') " string to list + let l:ver = split(editorconfig_core#util#strip(l:job.version), '\v\.') + for l:idx in range(len(l:ver)) + let l:ver[l:idx] = str2nr(l:ver[l:idx]) + endfor + + let l:job.version = l:ver + endif + + " TODO provide version output from here instead of the shell script +" if string(a:names) ==? 'version' +" return +" endif +" + if type(a:names) != type({}) || type(a:job) != type({}) + throw 'Need two Dictionary arguments' + endif + + if has_key(a:names, 'dump') + execute 'redir! > ' . fnameescape(a:names.dump) + echom 'Names: ' . string(a:names) + echom 'Job: ' . string(l:job) + let g:editorconfig_core_vimscript_debug = 1 + endif + + if type(a:names['target']) == type([]) + let l:targets = a:names.target + else + let l:targets = [a:names.target] + endif + + for l:target in l:targets + + " Pre-process quoting weirdness so we are more flexible in the face + " of CMake+CTest+BAT+Powershell quoting. + + " Permit wrapping in double-quotes + let l:target = substitute(l:target, '\v^"(.*)"$', '\1', '') + + " Permit empty ('') entries in l:targets + if strlen(l:target)<1 + continue + endif + + if has_key(a:names, 'dump') + echom 'Trying: ' . string(l:target) + endif + + let l:job.target = l:target + let l:options = editorconfig_core#handler#get_configurations(l:job) + + if has_key(a:names, 'dump') + echom 'editorconfig_core#currbuf_cli result: ' . string(l:options) + endif + + if len(l:targets) > 1 + let l:output += [ '[' . l:target . ']' ] + endif + + for [ l:key, l:value ] in items(l:options) + let l:output += [ l:key . '=' . l:value ] + endfor + + endfor "foreach target + + " Write the output file + call writefile(l:output, a:names.output) +endfunction "editorconfig_core#currbuf_cli + +" }}}1 + +let &cpo = s:saved_cpo +unlet! s:saved_cpo + +" vi: set fdm=marker fo-=ro: diff --git a/uvim/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core/fnmatch.vim b/uvim/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core/fnmatch.vim new file mode 100644 index 0000000000..ef9ced9fdc --- /dev/null +++ b/uvim/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core/fnmatch.vim @@ -0,0 +1,467 @@ +" autoload/editorconfig_core/fnmatch.vim: Globbing for +" editorconfig-vim. Ported from the Python core's fnmatch.py. + +" Copyright (c) 2012-2019 EditorConfig Team {{{1 +" All rights reserved. +" +" Redistribution and use in source and binary forms, with or without +" modification, are permitted provided that the following conditions are met: +" +" 1. Redistributions of source code must retain the above copyright notice, +" this list of conditions and the following disclaimer. +" 2. Redistributions in binary form must reproduce the above copyright notice, +" this list of conditions and the following disclaimer in the documentation +" and/or other materials provided with the distribution. +" +" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +" POSSIBILITY OF SUCH DAMAGE. }}}1 + +"Filename matching with shell patterns. +" +"fnmatch(FILENAME, PATH, PATTERN) matches according to the local convention. +"fnmatchcase(FILENAME, PATH, PATTERN) always takes case in account. +" +"The functions operate by translating the pattern into a regular +"expression. They cache the compiled regular expressions for speed. +" +"The function translate(PATTERN) returns a regular expression +"corresponding to PATTERN. (It does not compile it.) + +let s:saved_cpo = &cpo +set cpo&vim + +" variables {{{1 +if !exists('g:editorconfig_core_vimscript_debug') + let g:editorconfig_core_vimscript_debug = 0 +endif +" }}}1 +" === Regexes =========================================================== {{{1 +let s:LEFT_BRACE = '\v[\\]@8<!\{' +" 8 is an arbitrary byte-count limit to the lookbehind (micro-optimization) +"LEFT_BRACE = re.compile( +" r""" +" +" (?<! \\ ) # Not preceded by "\" +" +" \{ # "{" +" +" """, re.VERBOSE +") + +let s:RIGHT_BRACE = '\v[\\]@8<!\}' +" 8 is an arbitrary byte-count limit to the lookbehind (micro-optimization) +"RIGHT_BRACE = re.compile( +" r""" +" +" (?<! \\ ) # Not preceded by "\" +" +" \} # "}" +" +" """, re.VERBOSE +") + +let s:NUMERIC_RANGE = '\v([+-]?\d+)' . '\.\.' . '([+-]?\d+)' +"NUMERIC_RANGE = re.compile( +" r""" +" ( # Capture a number +" [+-] ? # Zero or one "+" or "-" characters +" \d + # One or more digits +" ) +" +" \.\. # ".." +" +" ( # Capture a number +" [+-] ? # Zero or one "+" or "-" characters +" \d + # One or more digits +" ) +" """, re.VERBOSE +") + +" }}}1 +" === Internal functions ================================================ {{{1 + +" Dump the bytes of a:text. For debugging use. +function! s:dump_bytes(text) + let l:idx=0 + while l:idx < strlen(a:text) + let l:byte_val = char2nr(a:text[l:idx]) + echom printf('%10s%-5d%02x %s', '', l:idx, l:byte_val, + \ a:text[l:idx]) + let l:idx+=1 + endwhile +endfunction "s:dump_bytes + +" Dump the characters of a:text and their codepoints. For debugging use. +function! s:dump_chars(text) + let l:chars = split(a:text, '\zs') + let l:idx = 0 + let l:out1 = '' + let l:out2 = '' + while l:idx < len(l:chars) + let l:char = l:chars[l:idx] + let l:out1 .= printf('%5s', l:char) + let l:out2 .= printf('%5x', char2nr(l:char)) + let l:idx+=1 + endwhile + + echom l:out1 + echom l:out2 +endfunction "s:dump_chars + +" }}}1 +" === Translating globs to patterns ===================================== {{{1 + +" Used by s:re_escape: backslash-escape any character below U+0080; +" replace all others with a %U escape. +" See https://vi.stackexchange.com/a/19617/1430 by yours truly +" (https://vi.stackexchange.com/users/1430/cxw). +unlockvar s:replacement_expr +let s:replacement_expr = + \ '\=' . + \ '((char2nr(submatch(1)) >= 128) ? ' . + \ 'printf("%%U%08x", char2nr(submatch(1))) : ' . + \ '("\\" . submatch(1))' . + \ ')' +lockvar s:replacement_expr + +" Escaper for very-magic regexes +function! s:re_escape(text) + return substitute(a:text, '\v([^0-9a-zA-Z_])', s:replacement_expr, 'g') +endfunction + +"def translate(pat, nested=0): +" Translate a shell PATTERN to a regular expression. +" There is no way to quote meta-characters. +function! editorconfig_core#fnmatch#translate(pat, ...) + let l:nested = 0 + if a:0 + let l:nested = a:1 + endif + + if g:editorconfig_core_vimscript_debug + echom '- fnmatch#translate: pattern ' . a:pat + echom printf( + \ '- %d chars', strlen(substitute(a:pat, ".", "x", "g"))) + call s:dump_chars(a:pat) + endif + + let l:pat = a:pat " TODO remove if we wind up not needing this + + " Note: the Python sets MULTILINE and DOTALL, but Vim has \_. + " instead of DOTALL, and \_^ / \_$ instead of MULTILINE. + + let l:is_escaped = 0 + + " Find out whether the pattern has balanced braces. + let l:left_braces=[] + let l:right_braces=[] + call substitute(l:pat, s:LEFT_BRACE, '\=add(l:left_braces, 1)', 'g') + call substitute(l:pat, s:RIGHT_BRACE, '\=add(l:right_braces, 1)', 'g') + " Thanks to http://jeromebelleman.gitlab.io/posts/productivity/vimsub/ + let l:matching_braces = (len(l:left_braces) == len(l:right_braces)) + + " Unicode support (#2). Indexing l:pat[l:index] returns bytes, per + " https://github.com/neovim/neovim/issues/68#issue-28114985 . + " Instead, use split() per vimdoc to break the input string into an + " array of *characters*, and process that. + let l:characters = split(l:pat, '\zs') + + let l:index = 0 " character index + let l:length = len(l:characters) + let l:brace_level = 0 + let l:in_brackets = 0 + + let l:result = '' + let l:numeric_groups = [] + while l:index < l:length + let l:current_char = l:characters[l:index] + let l:index += 1 + +" if g:editorconfig_core_vimscript_debug +" echom ' - fnmatch#translate: ' . l:current_char . '@' . +" \ (l:index-1) . '; result ' . l:result +" endif + + if l:current_char ==# '*' + let l:pos = l:index + if l:pos < l:length && l:characters[l:pos] ==# '*' + let l:result .= '\_.*' + let l:index += 1 " skip the second star + else + let l:result .= '[^/]*' + endif + + elseif l:current_char ==# '?' + let l:result .= '\_[^/]' + + elseif l:current_char ==# '[' + if l:in_brackets + let l:result .= '\[' + else + let l:pos = l:index + let l:has_slash = 0 + while l:pos < l:length && l:characters[l:pos] != ']' + if l:characters[l:pos] ==# '/' && l:characters[l:pos-1] !=# '\' + let has_slash = 1 + break + endif + let l:pos += 1 + endwhile + if l:has_slash + " POSIX IEEE 1003.1-2017 sec. 2.13.3: '/' cannot occur + " in a bracket expression, so [/] matches a literal + " three-character string '[' . '/' . ']'. + let l:result .= '\[' + \ . s:re_escape(join(l:characters[l:index : l:pos-1], '')) + \ . '\/' + " escape the slash + let l:index = l:pos + 1 + " resume after the slash + else + if l:index < l:length && l:characters[l:index] =~# '\v%(\^|\!)' + let l:index += 1 + let l:result .= '[^' + else + let l:result .= '[' + endif + let l:in_brackets = 1 + endif + endif + + elseif l:current_char ==# '-' + if l:in_brackets + let l:result .= l:current_char + else + let l:result .= '\' . l:current_char + endif + + elseif l:current_char ==# ']' + if l:in_brackets && !l:is_escaped + let l:result .= ']' + let l:in_brackets = 0 + elseif l:is_escaped + let l:result .= '\]' + let l:is_escaped = 0 + else + let l:result .= '\]' + endif + + elseif l:current_char ==# '{' + let l:pos = l:index + let l:has_comma = 0 + while l:pos < l:length && (l:characters[l:pos] !=# '}' || l:is_escaped) + if l:characters[l:pos] ==# ',' && ! l:is_escaped + let l:has_comma = 1 + break + endif + let l:is_escaped = l:characters[l:pos] ==# '\' && ! l:is_escaped + let l:pos += 1 + endwhile + if ! l:has_comma && l:pos < l:length + let l:num_range = + \ matchlist(join(l:characters[l:index : l:pos-1], ''), + \ s:NUMERIC_RANGE) + if len(l:num_range) > 0 " Remember the ranges + call add(l:numeric_groups, [ 0+l:num_range[1], 0+l:num_range[2] ]) + let l:result .= '([+-]?\d+)' + else + let l:inner_xlat = editorconfig_core#fnmatch#translate( + \ join(l:characters[l:index : l:pos-1], ''), 1) + let l:inner_result = l:inner_xlat[0] + let l:inner_groups = l:inner_xlat[1] + let l:result .= '\{' . l:inner_result . '\}' + let l:numeric_groups += l:inner_groups + endif + let l:index = l:pos + 1 + elseif l:matching_braces + let l:result .= '%(' + let l:brace_level += 1 + else + let l:result .= '\{' + endif + + elseif l:current_char ==# ',' + if l:brace_level > 0 && ! l:is_escaped + let l:result .= '|' + else + let l:result .= '\,' + endif + + elseif l:current_char ==# '}' + if l:brace_level > 0 && ! l:is_escaped + let l:result .= ')' + let l:brace_level -= 1 + else + let l:result .= '\}' + endif + + elseif l:current_char ==# '/' + if join(l:characters[l:index : (l:index + 2)], '') ==# '**/' + let l:result .= '%(/|/\_.*/)' + let l:index += 3 + else + let l:result .= '\/' + endif + + elseif l:current_char != '\' + let l:result .= s:re_escape(l:current_char) + endif + + if l:current_char ==# '\' + if l:is_escaped + let l:result .= s:re_escape(l:current_char) + endif + let l:is_escaped = ! l:is_escaped + else + let l:is_escaped = 0 + endif + + endwhile + + if ! l:nested + let l:result .= '\_$' + endif + + return [l:result, l:numeric_groups] +endfunction " #editorconfig_core#fnmatch#translate + +let s:_cache = {} +function! s:cached_translate(pat) + if ! has_key(s:_cache, a:pat) + "regex = re.compile(res) + let s:_cache[a:pat] = + \ editorconfig_core#fnmatch#translate(a:pat) + " we don't compile the regex + endif + return s:_cache[a:pat] +endfunction " cached_translate + +" }}}1 +" === Matching functions ================================================ {{{1 + +function! editorconfig_core#fnmatch#fnmatch(name, path, pattern) +"def fnmatch(name, pat): +" """Test whether FILENAME matches PATH/PATTERN. +" +" Patterns are Unix shell style: +" +" - ``*`` matches everything except path separator +" - ``**`` matches everything +" - ``?`` matches any single character +" - ``[seq]`` matches any character in seq +" - ``[!seq]`` matches any char not in seq +" - ``{s1,s2,s3}`` matches any of the strings given (separated by commas) +" +" An initial period in FILENAME is not special. +" Both FILENAME and PATTERN are first case-normalized +" if the operating system requires it. +" If you don't want this, use fnmatchcase(FILENAME, PATTERN). +" """ +" + " Note: This throws away the backslash in '\.txt' on Cygwin, but that + " makes sense since it's Windows under the hood. + " We don't care about shellslash since we're going to change backslashes + " to slashes in just a moment anyway. + let l:localname = fnamemodify(a:name, ':p') + + if editorconfig_core#util#is_win() " normalize + let l:localname = substitute(tolower(l:localname), '\v\\', '/', 'g') + let l:path = substitute(tolower(a:path), '\v\\', '/', 'g') + let l:pattern = tolower(a:pattern) + else + let l:localname = l:localname + let l:path = a:path + let l:pattern = a:pattern + endif + + if g:editorconfig_core_vimscript_debug + echom '- fnmatch#fnmatch testing <' . l:localname . '> against <' . + \ l:pattern . '> wrt <' . l:path . '>' + endif + + return editorconfig_core#fnmatch#fnmatchcase(l:localname, l:path, l:pattern) +endfunction " fnmatch + +function! editorconfig_core#fnmatch#fnmatchcase(name, path, pattern) +"def fnmatchcase(name, pat): +" """Test whether FILENAME matches PATH/PATTERN, including case. +" +" This is a version of fnmatch() which doesn't case-normalize +" its arguments. +" """ +" + let [regex, num_groups] = s:cached_translate(a:pattern) + + let l:escaped_path = s:re_escape(a:path) + let l:regex = '\v' . l:escaped_path . l:regex + + if g:editorconfig_core_vimscript_debug + echom '- fnmatch#fnmatchcase: regex ' . l:regex + call s:dump_chars(l:regex) + echom '- fnmatch#fnmatchcase: checking ' . a:name + call s:dump_chars(a:name) + endif + + let l:match_groups = matchlist(a:name, l:regex)[1:] " [0] = full match + + if g:editorconfig_core_vimscript_debug + echom printf(' Got %d matches', len(l:match_groups)) + endif + + if len(l:match_groups) == 0 + return 0 + endif + + " Check numeric ranges + let pattern_matched = 1 + for l:idx in range(0,len(l:match_groups)) + let l:num = l:match_groups[l:idx] + if l:num ==# '' + break + endif + + let [min_num, max_num] = num_groups[l:idx] + if (min_num > (0+l:num)) || ((0+l:num) > max_num) + let pattern_matched = 0 + break + endif + + " Reject leading zeros without sign. This is very odd --- + " see editorconfig/editorconfig#371. + if match(l:num, '\v^0') != -1 + let pattern_matched = 0 + break + endif + endfor + + if g:editorconfig_core_vimscript_debug + echom '- fnmatch#fnmatchcase: ' . (pattern_matched ? 'matched' : 'did not match') + endif + + return pattern_matched +endfunction " fnmatchcase + +" }}}1 +" === Copyright notices ================================================= {{{1 +" Based on code from fnmatch.py file distributed with Python 2.6. +" Portions Copyright (c) 2001-2010 Python Software Foundation; +" All Rights Reserved. Licensed under PSF License (see LICENSE.PSF file). +" +" Changes to original fnmatch: +" +" - translate function supports ``*`` and ``**`` similarly to fnmatch C library +" }}}1 + +let &cpo = s:saved_cpo +unlet! s:saved_cpo + +" vi: set fdm=marker: diff --git a/uvim/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core/handler.vim b/uvim/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core/handler.vim new file mode 100644 index 0000000000..c9a66e1694 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core/handler.vim @@ -0,0 +1,183 @@ +" autoload/editorconfig_core/handler.vim: Main worker for +" editorconfig-core-vimscript and editorconfig-vim. +" Modified from the Python core's handler.py. + +" Copyright (c) 2012-2019 EditorConfig Team {{{1 +" All rights reserved. +" +" Redistribution and use in source and binary forms, with or without +" modification, are permitted provided that the following conditions are met: +" +" 1. Redistributions of source code must retain the above copyright notice, +" this list of conditions and the following disclaimer. +" 2. Redistributions in binary form must reproduce the above copyright notice, +" this list of conditions and the following disclaimer in the documentation +" and/or other materials provided with the distribution. +" +" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +" POSSIBILITY OF SUCH DAMAGE. }}}1 + +let s:saved_cpo = &cpo +set cpo&vim + +" Return full filepath for filename in each directory in and above path. {{{1 +" Input path must be an absolute path. +" TODO shellslash/shellescape? +function! s:get_filenames(path, config_filename) + let l:path = a:path + let l:path_list = [] + while 1 + call add(l:path_list, editorconfig_core#util#path_join(l:path, a:config_filename)) + let l:newpath = fnamemodify(l:path, ':h') + if l:path ==? l:newpath || !strlen(l:path) + break + endif + let l:path = l:newpath + endwhile + return l:path_list +endfunction " get_filenames + +" }}}1 +" === Main ============================================================== {{{1 + +" Find EditorConfig files and return all options matching target_filename. +" Throws on failure. +" @param job {Dictionary} required 'target'; optional 'config' and 'version' +function! editorconfig_core#handler#get_configurations(job) + " TODO? support VERSION checks? + +" Special exceptions that may be raised by this function include: +" - ``VersionError``: self.version is invalid EditorConfig version +" - ``PathError``: self.filepath is not a valid absolute filepath +" - ``ParsingError``: improperly formatted EditorConfig file found + + let l:job = deepcopy(a:job) + if has_key(l:job, 'config') + let l:config_filename = l:job.config + else + let l:config_filename = '.editorconfig' + let l:job.config = l:config_filename + endif + + if has_key(l:job, 'version') + let l:version = l:job.version + else + let l:version = editorconfig_core#version() + let l:job.version = l:version + endif + + let l:target_filename = l:job.target + + "echom 'Beginning job ' . string(l:job) + if !s:check_assertions(l:job) + throw "Assertions failed" + endif + + let l:fullpath = fnamemodify(l:target_filename,':p') + let l:path = fnamemodify(l:fullpath, ':h') + let l:conf_files = s:get_filenames(l:path, l:config_filename) + + " echom 'fullpath ' . l:fullpath + " echom 'path ' . l:path + + let l:retval = {} + + " Attempt to find and parse every EditorConfig file in filetree + for l:conf_fn in l:conf_files + "echom 'Trying ' . l:conf_fn + let l:parsed = editorconfig_core#ini#read_ini_file(l:conf_fn, l:target_filename) + if !has_key(l:parsed, 'options') + continue + endif + " echom ' Has options' + + " Merge new EditorConfig file's options into current options + let l:old_options = l:retval + let l:retval = l:parsed.options + " echom 'Old options ' . string(l:old_options) + " echom 'New options ' . string(l:retval) + call extend(l:retval, l:old_options, 'force') + + " Stop parsing if parsed file has a ``root = true`` option + if l:parsed.root + break + endif + endfor + + call s:preprocess_values(l:job, l:retval) + return l:retval +endfunction " get_configurations + +function! s:check_assertions(job) +" TODO +" """Raise error if filepath or version have invalid values""" + +" # Raise ``PathError`` if filepath isn't an absolute path +" if not os.path.isabs(self.filepath): +" raise PathError("Input file must be a full path name.") + + " Throw if version specified is greater than current + let l:v = a:job.version + let l:us = editorconfig_core#version() + " echom 'Comparing requested version ' . string(l:v) . + " \ ' to our version ' . string(l:us) + if l:v[0] > l:us[0] || l:v[1] > l:us[1] || l:v[2] > l:us[2] + throw 'Required version ' . string(l:v) . + \ ' is greater than the current version ' . string(l:us) + endif + + return 1 " All OK if we got here +endfunction " check_assertions + +" }}}1 + +" Preprocess option values for consumption by plugins. {{{1 +" Modifies its argument in place. +function! s:preprocess_values(job, opts) + + " Lowercase option value for certain options + for l:name in ['end_of_line', 'indent_style', 'indent_size', + \ 'insert_final_newline', 'trim_trailing_whitespace', + \ 'charset'] + if has_key(a:opts, l:name) + let a:opts[l:name] = tolower(a:opts[l:name]) + endif + endfor + + " Set indent_size to "tab" if indent_size is unspecified and + " indent_style is set to "tab", provided we are at least v0.10.0. + if get(a:opts, 'indent_style', '') ==? "tab" && + \ !has_key(a:opts, 'indent_size') && + \ ( a:job.version[0]>0 || a:job.version[1] >=10 ) + let a:opts['indent_size'] = 'tab' + endif + + " Set tab_width to indent_size if indent_size is specified and + " tab_width is unspecified + if has_key(a:opts, 'indent_size') && !has_key(a:opts, 'tab_width') && + \ get(a:opts, 'indent_size', '') !=? "tab" + let a:opts['tab_width'] = a:opts['indent_size'] + endif + + " Set indent_size to tab_width if indent_size is "tab" + if has_key(a:opts, 'indent_size') && has_key(a:opts, 'tab_width') && + \ get(a:opts, 'indent_size', '') ==? "tab" + let a:opts['indent_size'] = a:opts['tab_width'] + endif +endfunction " preprocess_values + +" }}}1 + +let &cpo = s:saved_cpo +unlet! s:saved_cpo + +" vi: set fdm=marker fdl=1: diff --git a/uvim/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core/ini.vim b/uvim/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core/ini.vim new file mode 100644 index 0000000000..73716969e7 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core/ini.vim @@ -0,0 +1,264 @@ +" autoload/editorconfig_core/ini.vim: Config-file parser for +" editorconfig-core-vimscript and editorconfig-vim. +" Modified from the Python core's ini.py. + +" Copyright (c) 2012-2019 EditorConfig Team {{{2 +" All rights reserved. +" +" Redistribution and use in source and binary forms, with or without +" modification, are permitted provided that the following conditions are met: +" +" 1. Redistributions of source code must retain the above copyright notice, +" this list of conditions and the following disclaimer. +" 2. Redistributions in binary form must reproduce the above copyright notice, +" this list of conditions and the following disclaimer in the documentation +" and/or other materials provided with the distribution. +" +" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +" POSSIBILITY OF SUCH DAMAGE. }}}2 + +let s:saved_cpo = &cpo +set cpo&vim + +" variables {{{2 +if !exists('g:editorconfig_core_vimscript_debug') + let g:editorconfig_core_vimscript_debug = 0 +endif +" }}}2 +" === Constants, including regexes ====================================== {{{2 +" Regular expressions for parsing section headers and options. +" Allow ``]`` and escaped ``;`` and ``#`` characters in section headers. +" In fact, allow \ to escape any single character - it needs to cover at +" least \ * ? [ ! ] { }. +unlockvar s:SECTCRE s:OPTCRE s:MAX_SECTION_NAME s:MAX_PROPERTY_NAME s:MAX_PROPERTY_VALUE +let s:SECTCRE = '\v^\s*\[(%([^\\#;]|\\.)+)\]' + +" Regular expression for parsing option name/values. +" Allow any amount of whitespaces, followed by separator +" (either ``:`` or ``=``), followed by any amount of whitespace and then +" any characters to eol +let s:OPTCRE = '\v\s*([^:=[:space:]][^:=]*)\s*([:=])\s*(.*)$' + +let s:MAX_SECTION_NAME = 4096 +let s:MAX_PROPERTY_NAME = 1024 +let s:MAX_PROPERTY_VALUE = 4096 + +lockvar s:SECTCRE s:OPTCRE s:MAX_SECTION_NAME s:MAX_PROPERTY_NAME s:MAX_PROPERTY_VALUE + +" }}}2 +" === Main ============================================================== {{{1 + +" Read \p config_filename and return the options applicable to +" \p target_filename. This is the main entry point in this file. +function! editorconfig_core#ini#read_ini_file(config_filename, target_filename) + if !filereadable(a:config_filename) + return {} + endif + + try + let l:lines = readfile(a:config_filename) + if &encoding !=? 'utf-8' + " strip BOM + if len(l:lines) > 0 && l:lines[0][:2] ==# "\xEF\xBB\xBF" + let l:lines[0] = l:lines[0][3:] + endif + " convert from UTF-8 to 'encoding' + call map(l:lines, 'iconv(v:val, "utf-8", &encoding)') + endif + let result = s:parse(a:config_filename, a:target_filename, l:lines) + catch + " rethrow, but with a prefix since throw 'Vim...' fails. + throw 'Could not read editorconfig file at ' . v:throwpoint . ': ' . string(v:exception) + endtry + + return result +endfunction + +function! s:parse(config_filename, target_filename, lines) +" Parse a sectioned setup file. +" The sections in setup file contains a title line at the top, +" indicated by a name in square brackets (`[]'), plus key/value +" options lines, indicated by `name: value' format lines. +" Continuations are represented by an embedded newline then +" leading whitespace. Blank lines, lines beginning with a '#', +" and just about everything else are ignored. + + let l:in_section = 0 + let l:matching_section = 0 + let l:optname = '' + let l:lineno = 0 + let l:e = [] " Errors, if any + + let l:options = {} " Options applicable to this file + let l:is_root = 0 " Whether a:config_filename declares root=true + + while 1 + if l:lineno == len(a:lines) + break + endif + + let l:line = a:lines[l:lineno] + let l:lineno = l:lineno + 1 + + " comment or blank line? + if editorconfig_core#util#strip(l:line) ==# '' + continue + endif + if l:line =~# '\v^[#;]' + continue + endif + + " is it a section header? + if g:editorconfig_core_vimscript_debug + echom "Header? <" . l:line . ">" + endif + + let l:mo = matchlist(l:line, s:SECTCRE) + if len(l:mo) + let l:sectname = l:mo[1] + let l:in_section = 1 + if strlen(l:sectname) > s:MAX_SECTION_NAME + " Section name too long => ignore the section + let l:matching_section = 0 + else + let l:matching_section = s:matches_filename( + \ a:config_filename, a:target_filename, l:sectname) + endif + + if g:editorconfig_core_vimscript_debug + echom 'In section ' . l:sectname . ', which ' . + \ (l:matching_section ? 'matches' : 'does not match') + \ ' file ' . a:target_filename . ' (config ' . + \ a:config_filename . ')' + endif + + " So sections can't start with a continuation line + let l:optname = '' + + " Is it an option line? + else + let l:mo = matchlist(l:line, s:OPTCRE) + if len(l:mo) + let l:optname = mo[1] + let l:optval = mo[3] + + if g:editorconfig_core_vimscript_debug + echom printf('Saw raw opt <%s>=<%s>', l:optname, l:optval) + endif + + let l:optval = editorconfig_core#util#strip(l:optval) + " allow empty values + if l:optval ==? '""' + let l:optval = '' + endif + let l:optname = s:optionxform(l:optname) + if !l:in_section && optname ==? 'root' + let l:is_root = (optval ==? 'true') + endif + if g:editorconfig_core_vimscript_debug + echom printf('Saw opt <%s>=<%s>', l:optname, l:optval) + endif + + if l:matching_section && + \ strlen(l:optname) <= s:MAX_PROPERTY_NAME && + \ strlen(l:optval) <= s:MAX_PROPERTY_VALUE + let l:options[l:optname] = l:optval + endif + else + " a non-fatal parsing error occurred. set up the + " exception but keep going. the exception will be + " raised at the end of the file and will contain a + " list of all bogus lines + call add(e, "Parse error in '" . a:config_filename . "' at line " . + \ l:lineno . ": '" . l:line . "'") + endif + endif + endwhile + + " if any parsing errors occurred, raise an exception + if len(l:e) + throw string(l:e) + endif + + return {'root': l:is_root, 'options': l:options} +endfunction! + +" }}}1 +" === Helpers =========================================================== {{{1 + +" Preprocess option names +function! s:optionxform(optionstr) + let l:result = substitute(a:optionstr, '\v\s+$', '', 'g') " rstrip + return tolower(l:result) +endfunction + +" Return true if \p glob matches \p target_filename +function! s:matches_filename(config_filename, target_filename, glob) +" config_dirname = normpath(dirname(config_filename)).replace(sep, '/') + let l:config_dirname = fnamemodify(a:config_filename, ':p:h') . '/' + + if editorconfig_core#util#is_win() + " Regardless of whether shellslash is set, make everything slashes + let l:config_dirname = + \ tolower(substitute(l:config_dirname, '\v\\', '/', 'g')) + endif + + let l:glob = substitute(a:glob, '\v\\([#;])', '\1', 'g') + + " Take account of the path to the editorconfig file. + " editorconfig-core-c/src/lib/editorconfig.c says: + " "Pattern would be: /dir/of/editorconfig/file[double_star]/[section] if + " section does not contain '/', or /dir/of/editorconfig/file[section] + " if section starts with a '/', or /dir/of/editorconfig/file/[section] if + " section contains '/' but does not start with '/'." + + if stridx(l:glob, '/') != -1 " contains a slash + if l:glob[0] ==# '/' + let l:glob = l:glob[1:] " trim leading slash + endif +" This will be done by fnmatch +" let l:glob = l:config_dirname . l:glob + else " does not contain a slash + let l:config_dirname = l:config_dirname[:-2] + " Trim trailing slash + let l:glob = '**/' . l:glob + endif + + if g:editorconfig_core_vimscript_debug + echom '- ini#matches_filename: checking <' . a:target_filename . + \ '> against <' . l:glob . '> with respect to config file <' . + \ a:config_filename . '>' + echom '- ini#matches_filename: config_dirname is ' . l:config_dirname + endif + + return editorconfig_core#fnmatch#fnmatch(a:target_filename, + \ l:config_dirname, l:glob) +endfunction " matches_filename + +" }}}1 +" === Copyright notices ================================================= {{{2 +" Based on code from ConfigParser.py file distributed with Python 2.6. +" Portions Copyright (c) 2001-2010 Python Software Foundation; +" All Rights Reserved. Licensed under PSF License (see LICENSE.PSF file). +" +" Changes to original ConfigParser: +" +" - Special characters can be used in section names +" - Octothorpe can be used for comments (not just at beginning of line) +" - Only track INI options in sections that match target filename +" - Stop parsing files with when ``root = true`` is found +" }}}2 + +let &cpo = s:saved_cpo +unlet! s:saved_cpo + +" vi: set fdm=marker fdl=1: diff --git a/uvim/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core/util.vim b/uvim/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core/util.vim new file mode 100644 index 0000000000..c4df04af17 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/editorconfig/autoload/editorconfig_core/util.vim @@ -0,0 +1,84 @@ +" util.vim: part of editorconfig-core-vimscript and editorconfig-vim. +" Copyright (c) 2018-2019 EditorConfig Team, including Chris White {{{1 +" All rights reserved. +" +" Redistribution and use in source and binary forms, with or without +" modification, are permitted provided that the following conditions are met: +" +" 1. Redistributions of source code must retain the above copyright notice, +" this list of conditions and the following disclaimer. +" 2. Redistributions in binary form must reproduce the above copyright notice, +" this list of conditions and the following disclaimer in the documentation +" and/or other materials provided with the distribution. +" +" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +" POSSIBILITY OF SUCH DAMAGE. }}}1 + +let s:saved_cpo = &cpo +set cpo&vim + +" A verbatim copy of ingo#fs#path#Separator() {{{1 +" from https://github.com/vim-scripts/ingo-library/blob/558132e2221db3af26dc2f2c6756d092d48a459f/autoload/ingo/fs/path.vim +" distributed under the Vim license. +function! editorconfig_core#util#Separator() + return (exists('+shellslash') && ! &shellslash ? '\' : '/') +endfunction " }}}1 + +" path_join(): ('a','b')->'a/b'; ('a/','b')->'a/b'. {{{1 +function! editorconfig_core#util#path_join(a, b) + " TODO shellescape/shellslash? + "echom 'Joining <' . a:a . '> and <' . a:b . '>' + "echom 'Length is ' . strlen(a:a) + "echom 'Last char is ' . char2nr(a:a[-1]) + if a:a !~# '\v%(\/|\\)$' + return a:a . editorconfig_core#util#Separator() . a:b + else + return a:a . a:b + endif +endfunction " }}}1 + +" is_win() by xolox {{{1 +" The following function is modified from +" https://github.com/xolox/vim-misc/blob/master/autoload/xolox/misc/os.vim +" Copyright (c) 2015 Peter Odding <peter@peterodding.com> +" +" Permission is hereby granted, free of charge, to any person obtaining a copy +" of this software and associated documentation files (the "Software"), to deal +" in the Software without restriction, including without limitation the rights +" to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +" copies of the Software, and to permit persons to whom the Software is +" furnished to do so, subject to the following conditions: +" +" The above copyright notice and this permission notice shall be included in all +" copies or substantial portions of the Software. +" +" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +" OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +" SOFTWARE. +function! editorconfig_core#util#is_win() + " Returns 1 (true) when on Microsoft Windows, 0 (false) otherwise. + return has('win16') || has('win32') || has('win64') +endfunction " }}}1 + +" strip() {{{1 +function! editorconfig_core#util#strip(s) + return substitute(a:s, '\v^\s+|\s+$','','g') +endfunction " }}}1 + +let &cpo = s:saved_cpo +unlet! s:saved_cpo + +" vi: set fdm=marker: diff --git a/uvim/runtime/pack/dist/opt/editorconfig/doc/editorconfig.txt b/uvim/runtime/pack/dist/opt/editorconfig/doc/editorconfig.txt new file mode 100644 index 0000000000..87355c008e --- /dev/null +++ b/uvim/runtime/pack/dist/opt/editorconfig/doc/editorconfig.txt @@ -0,0 +1,238 @@ +*editorconfig.txt* EditorConfig plugin for vim. + +File: editorconfig.txt +Version: 1.1.1 +Maintainer: EditorConfig Team <http://editorconfig.org> +Description: EditorConfig vim plugin + +CONTENTS~ + *editorconfig-contents* +---------------------------------------------------------------------------- +1. Overview |editorconfig-overview| +2. Installation |editorconfig-installation| +3. Commands |editorconfig-commands| +4. Settings |editorconfig-settings| +5. Advanced |editorconfig-advanced| +6. License |editorconfig-license| + + +OVERVIEW~ + *editorconfig-overview* +---------------------------------------------------------------------------- +This is the EditorConfig plugin for vim. + + +INSTALLATION~ + *editorconfig-installation* +---------------------------------------------------------------------------- +Follow the instructions in the README.md file to install this plugin. + +COMMANDS~ + *editorconfig-commands* +---------------------------------------------------------------------------- + + *:EditorConfigReload* +Command: + :EditorConfigReload + +Reload the EditorConfig conf files. When `.editorconfig` files are modified, +this command could prevent you to reload the current edited file to load the +new configuration. + +SETTINGS~ + *editorconfig-settings* +---------------------------------------------------------------------------- + *g:EditorConfig_core_mode* +Specify the mode of EditorConfig core. Generally it is OK to leave this option +empty. Currently, the supported modes are "vim_core" (default) and +"external_command". + + vim_core: Use the included Vim script EditorConfig Core. + external_command: Run external EditorConfig Core. + +If "g:EditorConfig_core_mode" is not specified, this plugin will automatically +choose "vim_core". + +If you choose "external_command" mode, you must also set +|g:EditorConfig_exec_path|. + +Changes to "g:EditorConfig_core_mode" will not take effect until Vim +is restarted. + + *b:EditorConfig_disable* +This is a buffer-local variable that disables the EditorConfig plugin for a +single buffer. + +Example: Disable EditorConfig for the current buffer: +> + let b:EditorConfig_disable = 1 +< +Example: Disable EditorConfig for all git commit messages: +> + au FileType gitcommit let b:EditorConfig_disable = 1 +< + + *g:EditorConfig_exclude_patterns* +This is a list contains file path patterns which will be ignored by +EditorConfig plugin. When the path of the opened buffer (i.e. +"expand('%:p')") matches any of the patterns in the list, EditorConfig will +not load for this file. The default is an empty list. + +Example: Avoid loading EditorConfig for any remote files over ssh +> + let g:EditorConfig_exclude_patterns = ['scp://.*'] +< + + *g:EditorConfig_exec_path* +The file path to the EditorConfig core executable. You can set this value in +your |vimrc| like this: +> + let g:EditorConfig_exec_path = 'Path to your EditorConfig Core executable' +< +The default value is empty. + +If "g:EditorConfig_exec_path" is not set, the plugin will use the "vim_core" +mode regardless of the setting of |g:EditorConfig_core_mode|. + +Changes to "g:EditorConfig_exec_path" will not take effect until Vim +is restarted. + + *g:EditorConfig_max_line_indicator* +The way to show the line where the maximal length is reached. Accepted values +are "line", "fill", "exceeding" and "fillexceeding", otherwise there will be +no max line indicator. + + "line": the right column of the max line length column will be + highlighted on all lines, by adding +1 to 'colorcolumn'. + + "fill": all the columns to the right of the max line length + column will be highlighted on all lines, by setting + 'colorcolumn' to a list starting from "max_line_length + + 1" to the number of columns on the screen. + + "exceeding": the right column of the max line length column will be + highlighted on lines that exceed the max line length, by + adding a match for the ColorColumn group. + + "fillexceeding": all the columns to the right of the max line length + column will be highlighted on lines that exceed the max + line length, by adding a match for the ColorColumn group. + + "none": no max line length indicator will be shown. Recommended + when you do not want any indicator to be shown, but any + value other than those listed above also work as "none". + +To set this option, add any of the following lines to your |vimrc| file: +> + let g:EditorConfig_max_line_indicator = "line" + let g:EditorConfig_max_line_indicator = "fill" + let g:EditorConfig_max_line_indicator = "exceeding" + let g:EditorConfig_max_line_indicator = "fillexceeding" + let g:EditorConfig_max_line_indicator = "none" +< +The default value is "line". + + *g:EditorConfig_enable_for_new_buf* +Set this to 1 if you want EditorConfig plugin to set options +for new empty buffers too. +Path to .editorconfig will be determined based on CWD (see |getcwd()|) +> + let g:EditorConfig_enable_for_new_buf = 1 +< +This option defaults to 0. + + *g:EditorConfig_preserve_formatoptions* +Set this to 1 if you don't want your formatoptions modified when +max_line_length is set: +> + let g:EditorConfig_preserve_formatoptions = 1 +< +This option defaults to 0. + + *g:EditorConfig_softtabstop_space* +When spaces are used for indent, Vim's 'softtabstop' feature will make the +backspace key delete one indent level. If you turn off that feature (by +setting the option to 0), only a single space will be deleted. +This option defaults to 1, which enables 'softtabstop' and uses the +'shiftwidth' value for it. You can also set this to -1 to automatically follow +the current 'shiftwidth' value (since Vim 7.3.693). Or set this to [] if +EditorConfig should not touch 'softtabstop' at all. + + *g:EditorConfig_softtabstop_tab* +When tabs are used for indent, Vim's 'softtabstop' feature only applies to +backspacing over existing runs of spaces. +This option defaults to 1, so backspace will delete one indent level worth of +spaces; -1 does the same but automatically follows the current 'shiftwidth' +value. Set this to 0 to have backspace delete just a single space character. +Or set this to [] if EditorConfig should not touch 'softtabstop' at all. + + *g:EditorConfig_verbose* +Set this to 1 if you want debug info printed: +> + let g:EditorConfig_verbose = 1 +< + +ADVANCED~ + *editorconfig-advanced* +---------------------------------------------------------------------------- + *editorconfig-hook* + *EditorConfig#AddNewHook()* +While this plugin offers several builtin supported properties (as mentioned +here: https://github.com/editorconfig/editorconfig-vim#supported-properties), +we are also able to add our own hooks to support additional EditorConfig +properties, including those not in the EditorConfig standard. For example, we +are working on an Objective-C project, and all our "*.m" files should be +Objective-C source files. However, vim sometimes detect "*.m" files as MATLAB +source files, which causes incorrect syntax highlighting, code indentation, +etc. To solve the case, we could write the following code into the |vimrc| +file: +> + function! FiletypeHook(config) + if has_key(a:config, 'vim_filetype') + let &filetype = a:config['vim_filetype'] + endif + + return 0 " Return 0 to show no error happened + endfunction + + call editorconfig#AddNewHook(function('FiletypeHook')) +< +And add the following code to your .editorconfig file: +> + [*.m] + vim_filetype = objc +< +Then try to open an Objective-C file, you will find the |filetype| is set to +"objc". + +License~ + *editorconfig-license* +---------------------------------------------------------------------------- + +License: + Copyright (c) 2011-2019 EditorConfig Team + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + + +vim:ft=help:tw=78:cc= diff --git a/uvim/runtime/pack/dist/opt/editorconfig/doc/tags b/uvim/runtime/pack/dist/opt/editorconfig/doc/tags new file mode 100644 index 0000000000..8c8276551d --- /dev/null +++ b/uvim/runtime/pack/dist/opt/editorconfig/doc/tags @@ -0,0 +1,21 @@ +:EditorConfigReload editorconfig.txt /*:EditorConfigReload* +EditorConfig#AddNewHook() editorconfig.txt /*EditorConfig#AddNewHook()* +b:EditorConfig_disable editorconfig.txt /*b:EditorConfig_disable* +editorconfig-advanced editorconfig.txt /*editorconfig-advanced* +editorconfig-commands editorconfig.txt /*editorconfig-commands* +editorconfig-contents editorconfig.txt /*editorconfig-contents* +editorconfig-hook editorconfig.txt /*editorconfig-hook* +editorconfig-installation editorconfig.txt /*editorconfig-installation* +editorconfig-license editorconfig.txt /*editorconfig-license* +editorconfig-overview editorconfig.txt /*editorconfig-overview* +editorconfig-settings editorconfig.txt /*editorconfig-settings* +editorconfig.txt editorconfig.txt /*editorconfig.txt* +g:EditorConfig_core_mode editorconfig.txt /*g:EditorConfig_core_mode* +g:EditorConfig_enable_for_new_buf editorconfig.txt /*g:EditorConfig_enable_for_new_buf* +g:EditorConfig_exclude_patterns editorconfig.txt /*g:EditorConfig_exclude_patterns* +g:EditorConfig_exec_path editorconfig.txt /*g:EditorConfig_exec_path* +g:EditorConfig_max_line_indicator editorconfig.txt /*g:EditorConfig_max_line_indicator* +g:EditorConfig_preserve_formatoptions editorconfig.txt /*g:EditorConfig_preserve_formatoptions* +g:EditorConfig_softtabstop_space editorconfig.txt /*g:EditorConfig_softtabstop_space* +g:EditorConfig_softtabstop_tab editorconfig.txt /*g:EditorConfig_softtabstop_tab* +g:EditorConfig_verbose editorconfig.txt /*g:EditorConfig_verbose* diff --git a/uvim/runtime/pack/dist/opt/editorconfig/ftdetect/editorconfig.vim b/uvim/runtime/pack/dist/opt/editorconfig/ftdetect/editorconfig.vim new file mode 100644 index 0000000000..d1f8e00a58 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/editorconfig/ftdetect/editorconfig.vim @@ -0,0 +1 @@ +autocmd BufNewFile,BufRead .editorconfig setfiletype dosini diff --git a/uvim/runtime/pack/dist/opt/editorconfig/plugin/editorconfig.vim b/uvim/runtime/pack/dist/opt/editorconfig/plugin/editorconfig.vim new file mode 100644 index 0000000000..914e7788ff --- /dev/null +++ b/uvim/runtime/pack/dist/opt/editorconfig/plugin/editorconfig.vim @@ -0,0 +1,614 @@ +" plugin/editorconfig.vim: EditorConfig native Vim script plugin file +" Copyright (c) 2011-2019 EditorConfig Team +" All rights reserved. +" +" Redistribution and use in source and binary forms, with or without +" modification, are permitted provided that the following conditions are met: +" +" 1. Redistributions of source code must retain the above copyright notice, +" this list of conditions and the following disclaimer. +" 2. Redistributions in binary form must reproduce the above copyright notice, +" this list of conditions and the following disclaimer in the documentation +" and/or other materials provided with the distribution. +" +" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +" ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +" LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +" POSSIBILITY OF SUCH DAMAGE. +" + +" check for Vim versions and duplicate script loading. +if v:version < 700 || exists("g:loaded_EditorConfig") + finish +endif +let g:loaded_EditorConfig = 1 + +let s:saved_cpo = &cpo +set cpo&vim + +" variables {{{1 + +" Make sure the globals all exist +if !exists('g:EditorConfig_exec_path') + let g:EditorConfig_exec_path = '' +endif + +if !exists('g:EditorConfig_verbose') + let g:EditorConfig_verbose = 0 +endif + +if !exists('g:EditorConfig_preserve_formatoptions') + let g:EditorConfig_preserve_formatoptions = 0 +endif + +if !exists('g:EditorConfig_max_line_indicator') + let g:EditorConfig_max_line_indicator = 'line' +endif + +if !exists('g:EditorConfig_exclude_patterns') + let g:EditorConfig_exclude_patterns = [] +endif + +if !exists('g:EditorConfig_disable_rules') + let g:EditorConfig_disable_rules = [] +endif + +if !exists('g:EditorConfig_enable_for_new_buf') + let g:EditorConfig_enable_for_new_buf = 0 +endif + +if !exists('g:EditorConfig_softtabstop_space') + let g:EditorConfig_softtabstop_space = 1 +endif + +if !exists('g:EditorConfig_softtabstop_tab') + let g:EditorConfig_softtabstop_tab = 1 +endif + +" Copy some of the globals into script variables --- changes to these +" globals won't affect the plugin until the plugin is reloaded. +if exists('g:EditorConfig_core_mode') && !empty(g:EditorConfig_core_mode) + let s:editorconfig_core_mode = g:EditorConfig_core_mode +else + let s:editorconfig_core_mode = '' +endif + +if exists('g:EditorConfig_exec_path') && !empty(g:EditorConfig_exec_path) + let s:editorconfig_exec_path = g:EditorConfig_exec_path +else + let s:editorconfig_exec_path = '' +endif + +let s:initialized = 0 + +" }}}1 + +" shellslash handling {{{1 +function! s:DisableShellSlash(bufnr) " {{{2 + " disable shellslash for proper escaping of Windows paths + + " In Windows, 'shellslash' also changes the behavior of 'shellescape'. + " It makes 'shellescape' behave like in UNIX environment. So ':setl + " noshellslash' before evaluating 'shellescape' and restore the + " settings afterwards when 'shell' does not contain 'sh' somewhere. + let l:shell = getbufvar(a:bufnr, '&shell') + if has('win32') && empty(matchstr(l:shell, 'sh')) + let s:old_shellslash = getbufvar(a:bufnr, '&shellslash') + setbufvar(a:bufnr, '&shellslash', 0) + endif +endfunction " }}}2 + +function! s:ResetShellSlash(bufnr) " {{{2 + " reset shellslash to the user-set value, if any + if exists('s:old_shellslash') + setbufvar(a:bufnr, '&shellslash', s:old_shellslash) + unlet! s:old_shellslash + endif +endfunction " }}}2 +" }}}1 + +" Mode initialization functions {{{1 + +function! s:InitializeVimCore() +" Initialize vim core. Returns 1 on failure; 0 on success +" At the moment, all we need to do is to check that it is installed. + try + let l:vim_core_ver = editorconfig_core#version() + catch + return 1 + endtry + return 0 +endfunction + +function! s:InitializeExternalCommand() +" Initialize external_command mode + + if empty(s:editorconfig_exec_path) + echo 'Please specify a g:EditorConfig_exec_path' + return 1 + endif + + if g:EditorConfig_verbose + echo 'Checking for external command ' . s:editorconfig_exec_path . ' ...' + endif + + if !executable(s:editorconfig_exec_path) + echo 'File ' . s:editorconfig_exec_path . ' is not executable.' + return 1 + endif + + return 0 +endfunction +" }}}1 + +function! s:Initialize() " Initialize the plugin. {{{1 + " Returns truthy on error, falsy on success. + + if empty(s:editorconfig_core_mode) + let s:editorconfig_core_mode = 'vim_core' " Default core choice + endif + + if s:editorconfig_core_mode ==? 'external_command' + if s:InitializeExternalCommand() + echohl WarningMsg + echo 'EditorConfig: Failed to initialize external_command mode. ' . + \ 'Falling back to vim_core mode.' + echohl None + let s:editorconfig_core_mode = 'vim_core' + endif + endif + + if s:editorconfig_core_mode ==? 'vim_core' + if s:InitializeVimCore() + echohl ErrorMsg + echo 'EditorConfig: Failed to initialize vim_core mode. ' . + \ 'The plugin will not function.' + echohl None + return 1 + endif + + elseif s:editorconfig_core_mode ==? 'external_command' + " Nothing to do here, but this elseif is required to avoid + " external_command falling into the else clause. + + else " neither external_command nor vim_core + echohl ErrorMsg + echo "EditorConfig: I don't know how to use mode " . s:editorconfig_core_mode + echohl None + return 1 + endif + + let s:initialized = 1 + return 0 +endfunction " }}}1 + +function! s:GetFilenames(path, filename) " {{{1 +" Yield full filepath for filename in each directory in and above path + + let l:path_list = [] + let l:path = a:path + while 1 + let l:path_list += [l:path . '/' . a:filename] + let l:newpath = fnamemodify(l:path, ':h') + if l:path == l:newpath + break + endif + let l:path = l:newpath + endwhile + return l:path_list +endfunction " }}}1 + +function! s:UseConfigFiles(from_autocmd) abort " Apply config to the current buffer {{{1 + " from_autocmd is truthy if called from an autocmd, falsy otherwise. + + " Get the properties of the buffer we are working on + if a:from_autocmd + let l:bufnr = str2nr(expand('<abuf>')) + let l:buffer_name = expand('<afile>:p') + let l:buffer_path = expand('<afile>:p:h') + else + let l:bufnr = bufnr('%') + let l:buffer_name = expand('%:p') + let l:buffer_path = expand('%:p:h') + endif + call setbufvar(l:bufnr, 'editorconfig_tried', 1) + + " Only process normal buffers (do not treat help files as '.txt' files) + " When starting Vim with a directory, the buftype might not yet be set: + " Therefore, also check if buffer_name is a directory. + if index(['', 'acwrite'], &buftype) == -1 || isdirectory(l:buffer_name) + return + endif + + if empty(l:buffer_name) + if g:EditorConfig_enable_for_new_buf + let l:buffer_name = getcwd() . "/." + else + if g:EditorConfig_verbose + echo 'Skipping EditorConfig for unnamed buffer' + endif + return + endif + endif + + if getbufvar(l:bufnr, 'EditorConfig_disable', 0) + if g:EditorConfig_verbose + echo 'EditorConfig disabled --- skipping buffer "' . l:buffer_name . '"' + endif + return + endif + + " Ignore specific patterns + for pattern in g:EditorConfig_exclude_patterns + if l:buffer_name =~ pattern + if g:EditorConfig_verbose + echo 'Skipping EditorConfig for buffer "' . l:buffer_name . + \ '" based on pattern "' . pattern . '"' + endif + return + endif + endfor + + " Check if any .editorconfig does exist + let l:conf_files = s:GetFilenames(l:buffer_path, '.editorconfig') + let l:conf_found = 0 + for conf_file in conf_files + if filereadable(conf_file) + let l:conf_found = 1 + break + endif + endfor + if !l:conf_found + return + endif + + if !s:initialized + if s:Initialize() + return + endif + endif + + if g:EditorConfig_verbose + echo 'Applying EditorConfig ' . s:editorconfig_core_mode . + \ ' on file "' . l:buffer_name . '"' + endif + + if s:editorconfig_core_mode ==? 'vim_core' + if s:UseConfigFiles_VimCore(l:bufnr, l:buffer_name) == 0 + call setbufvar(l:bufnr, 'editorconfig_applied', 1) + endif + elseif s:editorconfig_core_mode ==? 'external_command' + call s:UseConfigFiles_ExternalCommand(l:bufnr, l:buffer_name) + call setbufvar(l:bufnr, 'editorconfig_applied', 1) + else + echohl Error | + \ echo "Unknown EditorConfig Core: " . + \ s:editorconfig_core_mode | + \ echohl None + endif +endfunction " }}}1 + +" Custom commands, and autoloading {{{1 + +" Autocommands, and function to enable/disable the plugin {{{2 +function! s:EditorConfigEnable(should_enable) + augroup editorconfig + autocmd! + if a:should_enable + autocmd BufNewFile,BufReadPost,BufFilePost * call s:UseConfigFiles(1) + autocmd VimEnter,BufNew * call s:UseConfigFiles(1) + endif + augroup END +endfunction + +" }}}2 + +" Commands {{{2 +command! EditorConfigEnable call s:EditorConfigEnable(1) +command! EditorConfigDisable call s:EditorConfigEnable(0) + +command! EditorConfigReload call s:UseConfigFiles(0) " Reload EditorConfig files +" }}}2 + +" On startup, enable the autocommands +call s:EditorConfigEnable(1) + +" }}}1 + +" UseConfigFiles function for different modes {{{1 + +function! s:UseConfigFiles_VimCore(bufnr, target) +" Use the Vim script EditorConfig core + try + let l:config = editorconfig_core#handler#get_configurations( + \ { 'target': a:target } ) + call s:ApplyConfig(a:bufnr, l:config) + return 0 " success + catch + return 1 " failure + endtry +endfunction + +function! s:UseConfigFiles_ExternalCommand(bufnr, target) +" Use external EditorConfig core (e.g., the C core) + + call s:DisableShellSlash(a:bufnr) + let l:exec_path = shellescape(s:editorconfig_exec_path) + call s:ResetShellSlash(a:bufnr) + + call s:SpawnExternalParser(a:bufnr, l:exec_path, a:target) +endfunction + +function! s:SpawnExternalParser(bufnr, cmd, target) " {{{2 +" Spawn external EditorConfig. Used by s:UseConfigFiles_ExternalCommand() + + let l:cmd = a:cmd + + if empty(l:cmd) + throw 'No cmd provided' + endif + + let l:config = {} + + call s:DisableShellSlash(a:bufnr) + let l:cmd = l:cmd . ' ' . shellescape(a:target) + call s:ResetShellSlash(a:bufnr) + + let l:parsing_result = split(system(l:cmd), '\v[\r\n]+') + + " if editorconfig core's exit code is not zero, give out an error + " message + if v:shell_error != 0 + echohl ErrorMsg + echo 'Failed to execute "' . l:cmd . '". Exit code: ' . + \ v:shell_error + echo '' + echo 'Message:' + echo l:parsing_result + echohl None + return + endif + + if g:EditorConfig_verbose + echo 'Output from EditorConfig core executable:' + echo l:parsing_result + endif + + for one_line in l:parsing_result + let l:eq_pos = stridx(one_line, '=') + + if l:eq_pos == -1 " = is not found. Skip this line + continue + endif + + let l:eq_left = strpart(one_line, 0, l:eq_pos) + if l:eq_pos + 1 < strlen(one_line) + let l:eq_right = strpart(one_line, l:eq_pos + 1) + else + let l:eq_right = '' + endif + + let l:config[l:eq_left] = l:eq_right + endfor + + call s:ApplyConfig(a:bufnr, l:config) +endfunction " }}}2 + +" }}}1 + +" Set the buffer options {{{1 +function! s:SetCharset(bufnr, charset) abort " apply config['charset'] + + " Remember the buffer's state so we can set `nomodifed` at the end + " if appropriate. + let l:orig_fenc = getbufvar(a:bufnr, "&fileencoding") + let l:orig_enc = getbufvar(a:bufnr, "&encoding") + let l:orig_modified = getbufvar(a:bufnr, "&modified") + + if a:charset == "utf-8" + call setbufvar(a:bufnr, '&fileencoding', 'utf-8') + call setbufvar(a:bufnr, '&bomb', 0) + elseif a:charset == "utf-8-bom" + call setbufvar(a:bufnr, '&fileencoding', 'utf-8') + call setbufvar(a:bufnr, '&bomb', 1) + elseif a:charset == "latin1" + call setbufvar(a:bufnr, '&fileencoding', 'latin1') + call setbufvar(a:bufnr, '&bomb', 0) + elseif a:charset == "utf-16be" + call setbufvar(a:bufnr, '&fileencoding', 'utf-16be') + call setbufvar(a:bufnr, '&bomb', 1) + elseif a:charset == "utf-16le" + call setbufvar(a:bufnr, '&fileencoding', 'utf-16le') + call setbufvar(a:bufnr, '&bomb', 1) + endif + + let l:new_fenc = getbufvar(a:bufnr, "&fileencoding") + + " If all we did was change the fileencoding from the default to a copy + " of the default, we didn't actually modify the file. + if !l:orig_modified && (l:orig_fenc ==# '') && (l:new_fenc ==# l:orig_enc) + if g:EditorConfig_verbose + echo 'Setting nomodified on buffer ' . a:bufnr + endif + call setbufvar(a:bufnr, '&modified', 0) + endif +endfunction + +function! s:ApplyConfig(bufnr, config) abort + if g:EditorConfig_verbose + echo 'Options: ' . string(a:config) + endif + + if s:IsRuleActive('indent_style', a:config) + if a:config["indent_style"] == "tab" + call setbufvar(a:bufnr, '&expandtab', 0) + elseif a:config["indent_style"] == "space" + call setbufvar(a:bufnr, '&expandtab', 1) + endif + endif + + if s:IsRuleActive('tab_width', a:config) + let l:tabstop = str2nr(a:config["tab_width"]) + call setbufvar(a:bufnr, '&tabstop', l:tabstop) + else + " Grab the current ts so we can use it below + let l:tabstop = getbufvar(a:bufnr, '&tabstop') + endif + + if s:IsRuleActive('indent_size', a:config) + " if indent_size is 'tab', set shiftwidth to tabstop; + " if indent_size is a positive integer, set shiftwidth to the integer + " value + if a:config["indent_size"] == "tab" + call setbufvar(a:bufnr, '&shiftwidth', l:tabstop) + if type(g:EditorConfig_softtabstop_tab) != type([]) + call setbufvar(a:bufnr, '&softtabstop', + \ g:EditorConfig_softtabstop_tab > 0 ? + \ l:tabstop : g:EditorConfig_softtabstop_tab) + endif + else + let l:indent_size = str2nr(a:config["indent_size"]) + if l:indent_size > 0 + call setbufvar(a:bufnr, '&shiftwidth', l:indent_size) + if type(g:EditorConfig_softtabstop_space) != type([]) + call setbufvar(a:bufnr, '&softtabstop', + \ g:EditorConfig_softtabstop_space > 0 ? + \ l:indent_size : g:EditorConfig_softtabstop_space) + endif + endif + endif + + endif + + if s:IsRuleActive('end_of_line', a:config) && + \ getbufvar(a:bufnr, '&modifiable') + if a:config["end_of_line"] == "lf" + call setbufvar(a:bufnr, '&fileformat', 'unix') + elseif a:config["end_of_line"] == "crlf" + call setbufvar(a:bufnr, '&fileformat', 'dos') + elseif a:config["end_of_line"] == "cr" + call setbufvar(a:bufnr, '&fileformat', 'mac') + endif + endif + + if s:IsRuleActive('charset', a:config) && + \ getbufvar(a:bufnr, '&modifiable') + call s:SetCharset(a:bufnr, a:config["charset"]) + endif + + augroup editorconfig_trim_trailing_whitespace + autocmd! BufWritePre <buffer> + if s:IsRuleActive('trim_trailing_whitespace', a:config) && + \ get(a:config, 'trim_trailing_whitespace', 'false') ==# 'true' + execute 'autocmd BufWritePre <buffer=' . a:bufnr . '> call s:TrimTrailingWhitespace()' + endif + augroup END + + if s:IsRuleActive('insert_final_newline', a:config) + if exists('+fixendofline') + if a:config["insert_final_newline"] == "false" + call setbufvar(a:bufnr, '&fixendofline', 0) + else + call setbufvar(a:bufnr, '&fixendofline', 1) + endif + elseif exists(':SetNoEOL') == 2 + if a:config["insert_final_newline"] == "false" + silent! SetNoEOL " Use the PreserveNoEOL plugin to accomplish it + endif + endif + endif + + " highlight the columns following max_line_length + if s:IsRuleActive('max_line_length', a:config) && + \ a:config['max_line_length'] != 'off' + let l:max_line_length = str2nr(a:config['max_line_length']) + + if l:max_line_length >= 0 + call setbufvar(a:bufnr, '&textwidth', l:max_line_length) + if g:EditorConfig_preserve_formatoptions == 0 + " setlocal formatoptions+=tc + let l:fo = getbufvar(a:bufnr, '&formatoptions') + if l:fo !~# 't' + let l:fo .= 't' + endif + if l:fo !~# 'c' + let l:fo .= 'c' + endif + call setbufvar(a:bufnr, '&formatoptions', l:fo) + endif + endif + + if exists('+colorcolumn') + if l:max_line_length > 0 + if g:EditorConfig_max_line_indicator == 'line' + " setlocal colorcolumn+=+1 + let l:cocol = getbufvar(a:bufnr, '&colorcolumn') + if !empty(l:cocol) + let l:cocol .= ',' + endif + let l:cocol .= '+1' + call setbufvar(a:bufnr, '&colorcolumn', l:cocol) + elseif g:EditorConfig_max_line_indicator == 'fill' && + \ l:max_line_length < getbufvar(a:bufnr, '&columns') + " Fill only if the columns of screen is large enough + call setbufvar(a:bufnr, '&colorcolumn', + \ join(range(l:max_line_length+1, + \ getbufvar(a:bufnr, '&columns')), + \ ',')) + elseif g:EditorConfig_max_line_indicator == 'exceeding' + call setbufvar(a:bufnr, '&colorcolumn', '') + for l:match in getmatches() + if get(l:match, 'group', '') == 'ColorColumn' + call matchdelete(get(l:match, 'id')) + endif + endfor + call matchadd('ColorColumn', + \ '\%' . (l:max_line_length + 1) . 'v.', 100) + elseif g:EditorConfig_max_line_indicator == 'fillexceeding' + let &l:colorcolumn = '' + for l:match in getmatches() + if get(l:match, 'group', '') == 'ColorColumn' + call matchdelete(get(l:match, 'id')) + endif + endfor + call matchadd('ColorColumn', + \ '\%'. (l:max_line_length + 1) . 'v.\+', -1) + endif + endif + endif + endif + + call editorconfig#ApplyHooks(a:config) +endfunction + +" }}}1 + +function! s:TrimTrailingWhitespace() " {{{1 + " Called from within a buffer-specific autocmd, so we can use '%' + if getbufvar('%', '&modifiable') + " don't lose user position when trimming trailing whitespace + let s:view = winsaveview() + try + silent! keeppatterns keepjumps %s/\s\+$//e + finally + call winrestview(s:view) + endtry + endif +endfunction " }}}1 + +function! s:IsRuleActive(name, config) " {{{1 + return index(g:EditorConfig_disable_rules, a:name) < 0 && + \ has_key(a:config, a:name) +endfunction "}}}1 + +let &cpo = s:saved_cpo +unlet! s:saved_cpo + +" vim: fdm=marker fdc=3 diff --git a/uvim/runtime/pack/dist/opt/helpcurwin/autoload/helpcurwin.vim b/uvim/runtime/pack/dist/opt/helpcurwin/autoload/helpcurwin.vim new file mode 100644 index 0000000000..52a2d4f7cf --- /dev/null +++ b/uvim/runtime/pack/dist/opt/helpcurwin/autoload/helpcurwin.vim @@ -0,0 +1,42 @@ +vim9script + +# Open Vim help on {subject} in the current window (rather than a new split) +# +# Maintainer: The Vim Project <https://github.com/vim/vim> +# Last change: 2025 Dec 02 + +export def Open(subject: string): void + + const HELPCURWIN: func = (): string => { + if !getcompletion(subject, 'help')->empty() || subject->empty() + if &buftype != 'help' + execute 'silent noautocmd keepalt enew' + setlocal buftype=help noswapfile + endif + endif + return $'help {subject}' + } + + var contmod: bool = true + if &modified + echohl MoreMsg + echo $'Buffer {bufname()} is modified - continue? (y/n)' + echohl None + contmod = (getcharstr() == 'y') + endif + if contmod + try + execute HELPCURWIN() + catch + echohl Error + # {subject} invalid - Echo 'helpcurwin: E149:' (omit 'Vim(help):') + echo $'helpcurwin: {v:exception->substitute("^[^:]\+:", "", "")}' + echohl None + endtry + else + echo $'Aborted opening in current window, :help {subject}' + endif + +enddef + +# vim: ts=8 sts=2 sw=2 et diff --git a/uvim/runtime/pack/dist/opt/helpcurwin/doc/helpcurwin.txt b/uvim/runtime/pack/dist/opt/helpcurwin/doc/helpcurwin.txt new file mode 100644 index 0000000000..2bca8d0341 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/helpcurwin/doc/helpcurwin.txt @@ -0,0 +1,59 @@ +*helpcurwin.txt* For Vim version 9.1. Last change: 2025 Dec 02 + +The helpcurwin optional package enables opening help in the current window. + +1. :HelpCurwin |helpcurwin-command| +2. helpcurwin#Open() |helpcurwin-function| +3. <Plug>HelpCurwin; |helpcurwin-mapping| + + +============================================================================== +1. :HelpCurwin *:HelpCurwin* *helpcurwin-command* + +:HelpCurwin Use the current window to display the help file, + |help.txt| in read-only mode. It leaves any other + windows as-is (including when there is another + help window(s)). + +:HelpCurwin {subject} Like ":HelpCurwin" but, additionally open the + applicable help file at the tag {subject}. + For example: > + + :HelpCurwin version9.2 +< + It should otherwise behave like :help {subject}. + +You may also want to save typing with a command line abbreviation, +for example: >vi + + cnoreabbrev <expr> hc getcmdtype() == ":" && + \ getcmdline() == 'hc' ? 'HelpCurwin' : 'hc' +< + +============================================================================== +2. helpcurwin#Open() *helpcurwin-function* + +The underlying `:def` function may also be used, for example: >vim + + :vim9cmd helpcurwin#Open('version9.2') +< +This may be useful from other scripts where you want to bring up help on +{subject} in the current window. + + +============================================================================== +3. <Plug>HelpCurwin; *helpcurwin-mapping* + +There may be times when you want to get the help for a WORD, such as when it +is in a Vim9 script. If you want to open it in the same window, you can map +<Plug>HelpCurwin; to keys of your choosing to enable that. For example: >vim9 + + nnoremap <Leader>hc <Plug>HelpCurwin; + +Once sourced (in this instance, when <Leader>hc is typed), the applicable +help file will be opened in the current window at the tag for <cWORD> (that +is, the |WORD| under the cursor), if it exists. + + +============================================================================== + vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/uvim/runtime/pack/dist/opt/helpcurwin/doc/tags b/uvim/runtime/pack/dist/opt/helpcurwin/doc/tags new file mode 100644 index 0000000000..c552ad5fac --- /dev/null +++ b/uvim/runtime/pack/dist/opt/helpcurwin/doc/tags @@ -0,0 +1,5 @@ +:HelpCurwin helpcurwin.txt /*:HelpCurwin* +helpcurwin-command helpcurwin.txt /*helpcurwin-command* +helpcurwin-function helpcurwin.txt /*helpcurwin-function* +helpcurwin-mapping helpcurwin.txt /*helpcurwin-mapping* +helpcurwin.txt helpcurwin.txt /*helpcurwin.txt* diff --git a/uvim/runtime/pack/dist/opt/helpcurwin/plugin/helpcurwin.vim b/uvim/runtime/pack/dist/opt/helpcurwin/plugin/helpcurwin.vim new file mode 100644 index 0000000000..e9960954a3 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/helpcurwin/plugin/helpcurwin.vim @@ -0,0 +1,14 @@ +vim9script + +# Open Vim help on {subject} in the current window (rather than a new split) +# +# Maintainer: The Vim Project <https://github.com/vim/vim> +# Last change: 2026 Jan 29 + +import autoload '../autoload/helpcurwin.vim' + +command -bar -nargs=? -complete=help HelpCurwin helpcurwin.Open(<q-args>) + +nnoremap <Plug>HelpCurwin; <ScriptCmd>helpcurwin.Open(expand('<cWORD>'))<CR> + +# vim: ts=8 sts=2 sw=2 et diff --git a/uvim/runtime/pack/dist/opt/helptoc/autoload/helptoc.vim b/uvim/runtime/pack/dist/opt/helptoc/autoload/helptoc.vim new file mode 100644 index 0000000000..f3ce9febf9 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/helptoc/autoload/helptoc.vim @@ -0,0 +1,1341 @@ +vim9script noclear + +# the Vim HelpTOC plugin, creates a table of contents in a popup +# Maintainer: Vim project +# Original Author: @lacygoill +# Latest Change: 2025 Oct 17 +# +# Config {{{1 +# g:helptoc {{{2 +# Create the g:helptoc dict (used to specify the shell_prompt and other +# options) when it does not exist +g:helptoc = exists('g:helptoc') ? g:helptoc : {} + +# Set the initial shell_prompt pattern matching a default bash prompt +g:helptoc.shell_prompt = get(g:helptoc, 'shell_prompt', '^\w\+@\w\+:\f\+\$\s') + +# Track the prior prompt (used to reset b:toc if 'shell_prompt' changes) +g:helptoc.prior_shell_prompt = g:helptoc.shell_prompt + +def UpdateUserSettings() #{{{2 + + if g:helptoc.shell_prompt != g:helptoc.prior_shell_prompt + # invalidate cache: user config has changed + unlet! b:toc + # reset the prior prompt to the new prompt + g:helptoc.prior_shell_prompt = g:helptoc.shell_prompt + endif + + # helptoc popup presentation options{{{ + # Enable users to choose whether, in toc and help text popups, to have: + # - border (default [], which is a border, so is usually wanted) + # - borderchars (default single box drawing; use [] for Vim's defaults) + # - borderhighlight (default [], but a user may prefer something else) + # - close (default 'none'; mouse users may prefer 'button') + # - drag (default true, which is a popup_menu's default) + # - scrollbar (default false; for long tocs/HELP_TEXT true may be better) + # For example, in a Vim9 script .vimrc, these settings will produce tocs + # with borders that have the same highlight group as the inactive + # statusline, a scrollbar, and an 'X' close button: + # g:helptoc.popup_borderchars = get(g:helptoc, 'popup_borderchars', [' ']) + # g:helptoc.popup_borderhighlight = get(g:helptoc, + # 'popup_borderhighlight', ['StatusLineNC']) + # g:helptoc.popup_close = get(g:helptoc, 'popup_close', 'button') + # g:helptoc.popup_scrollbar = get(g:helptoc, 'popup_scrollbar', true) + # }}} + g:helptoc.popup_border = get(g:helptoc, 'popup_border', []) + g:helptoc.popup_borderchars = get(g:helptoc, 'popup_borderchars', + ['─', '│', '─', '│', '┌', '┐', '┘', '└']) + g:helptoc.popup_borderhighlight = get(g:helptoc, 'popup_borderhighlight', + []) + g:helptoc.popup_drag = get(g:helptoc, 'popup_drag', true) + g:helptoc.popup_close = get(g:helptoc, 'popup_close', 'none') + g:helptoc.popup_scrollbar = get(g:helptoc, 'popup_scrollbar', false) + # For sanitized tocs, allow the user to specify the level indicator + g:helptoc.level_indicator = get(g:helptoc, 'level_indicator', '| ') +enddef + +UpdateUserSettings() + +# Syntax {{{1 + +# Used by sanitized tocs (asciidoc, html, markdown, tex, vim, and xhtml) +def SanitizedTocSyntax(): void + silent execute "syntax match helptocLevel _^\\(" .. + g:helptoc.level_indicator .. "\\)*_ contained" + silent execute "syntax region helptocText start=_^\\(" .. + g:helptoc.level_indicator .. "\\)*_ end=_$_ contains=helptocLevel" + highlight link helptocText Normal + highlight link helptocLevel NonText +enddef + +# Init {{{1 +# Constants {{{2 +# HELP_TEXT {{{3 +const HELP_TEXT: list<string> =<< trim END + normal commands in help window + ────────────────────────────── + ? hide this help window + <C-J> scroll down one line + <C-K> scroll up one line + + normal commands in TOC menu + ─────────────────────────── + j select next entry + k select previous entry + J same as j, and jump to corresponding line in main buffer + K same as k, and jump to corresponding line in main buffer + c select nearest entry from cursor position in main buffer + g select first entry + G select last entry + H collapse one level + L expand one level + p print selected entry on command-line + + P same as p but automatically, whenever selection changes + press multiple times to toggle feature on/off + + q quit menu + z redraw menu with selected entry at center + + increase width of popup menu + - decrease width of popup menu + / look for given text with fuzzy algorithm + ? show help window + + <C-D> scroll down half a page + <C-U> scroll up half a page + s split window, and jump to selected entry + <PageUp> scroll down a whole page + <PageDown> scroll up a whole page + <Home> select first entry + <End> select last entry + + title meaning + ───────────── + example: 12/34 (5/6) + broken down: + + 12 index of selected entry + 34 index of last entry + 5 index of deepest level currently visible + 6 index of maximum possible level + + tip + ─── + after inserting a pattern to look for with the / command, + if you press <Esc> instead of <CR>, you can then get + more context for each remaining entry by pressing J or K +END + +# UPTOINC_H {{{3 +const UPTOINC_H: string = '\v\c^%(%([<][^h][^>]*[>])|\s)*[<]h' + +# MATCH_ENTRY {{{3 +const MATCH_ENTRY: dict<dict<func: bool>> = { + + help: {}, + + # This lets the user get a TOC when piping `info(1)` to Vim:{{{ + # + # $ info coreutils | vim - + #}}} + # But it assumes that they have some heuristics to set the `info` filetype.{{{ + # + # Possibly by inspecting the first line from `scripts.vim`: + # + # if getline(1) =~ '^File: .*\.info, Node: .*, \%(Next\|Prev\): .*, Up: \|This is the top of the INFO tree.' + # setfiletype info + # endif + #}}} + info: { + 1: (l: string, nextline): bool => l =~ '^\d\+\%(\.\d\+\)\+ ' && nextline =~ '^=\+$', + 2: (l: string, nextline): bool => l =~ '^\d\+\%(\.\d\+\)\+ ' && nextline =~ '^-\+$', + }, + + # For asciidoc, these patterns should match: + # https://docs.asciidoctor.org/asciidoc/latest/sections/titles-and-levels/ + asciidoc: { + 1: (l: string, _): bool => l =~ '\v^%(\=|#)\s', + 2: (l: string, _): bool => l =~ '\v^%(\={2}|#{2})\s', + 3: (l: string, _): bool => l =~ '\v^%(\={3}|#{3})\s', + 4: (l: string, _): bool => l =~ '\v^%(\={4}|#{4})\s', + 5: (l: string, _): bool => l =~ '\v^%(\={5}|#{5})\s', + 6: (l: string, _): bool => l =~ '\v^%(\={6}|#{6})\s', + }, + + html: { + 1: (l: string, _): bool => l =~ $"{UPTOINC_H}1", + 2: (l: string, _): bool => l =~ $"{UPTOINC_H}2", + 3: (l: string, _): bool => l =~ $"{UPTOINC_H}3", + 4: (l: string, _): bool => l =~ $"{UPTOINC_H}4", + 5: (l: string, _): bool => l =~ $"{UPTOINC_H}5", + 6: (l: string, _): bool => l =~ $"{UPTOINC_H}6", + }, + + man: { + 1: (l: string, _): bool => l =~ '^\S', + 2: (l: string, _): bool => l =~ '\v^%( {3})=\S', + 3: (l: string, _): bool => l =~ '\v^\s+%(%(\+|-)\S+,\s+)*(\+|-)\S+' + }, + + # For markdown, these patterns should match: + # https://spec.commonmark.org/0.31.2/#atx-headings and + # https://spec.commonmark.org/0.31.2/#setext-headings + markdown: { + 1: (l: string, nextline: string): bool => + (l =~ '\v^ {0,3}#%(\s|$)' || nextline =~ '\v^ {0,3}\=+$') && + l =~ '\S', + 2: (l: string, nextline: string): bool => + (l =~ '\v^ {0,3}##%(\s|$)' || nextline =~ '\v^ {0,3}-+$') && + l =~ '\S', + 3: (l: string, _): bool => l =~ '\v {0,3}#{3}%(\s|$)', + 4: (l: string, _): bool => l =~ '\v {0,3}#{4}%(\s|$)', + 5: (l: string, _): bool => l =~ '\v {0,3}#{5}%(\s|$)', + 6: (l: string, _): bool => l =~ '\v {0,3}#{6}%(\s|$)', + }, + + terminal: { + 1: (l: string, _): bool => l =~ g:helptoc.shell_prompt + }, + + # For LaTeX, this should meet + # https://mirrors.rit.edu/CTAN/info/latex2e-help-texinfo/latex2e.pdf + # including: + # para 6.3: + # \section{Heading} + # \section[Alternative ToC Heading]{Heading} + # para 25.1.2: + # \section*{Not for the TOC heading} + # \addcontentsline{toc}{section}{Alternative ToC Heading} + tex: { + 1: (l: string, _): bool => l =~ '^[\\]\(\%(part\|chapter\)' .. + '\%([\u005B{]\)\|addcontentsline{toc}{\%(part\|chapter\)\)', + 2: (l: string, _): bool => l =~ '^[\\]\%(section' .. + '\%([\u005B{]\)\|addcontentsline{toc}{section}\)', + 3: (l: string, _): bool => l =~ '^[\\]\%(subsection' .. + '\%([\u005B{]\)\|addcontentsline{toc}{subsection}\)', + 4: (l: string, _): bool => l =~ '^[\\]\%(subsubsection' .. + '\%([\u005B{]\)\|addcontentsline{toc}{subsubsection}\)', + }, + + vim: { + 1: (l: string, _): bool => l =~ '\v\{{3}1', + 2: (l: string, _): bool => l =~ '\v\{{3}2', + 3: (l: string, _): bool => l =~ '\v\{{3}3', + 4: (l: string, _): bool => l =~ '\v\{{3}4', + 5: (l: string, _): bool => l =~ '\v\{{3}5', + 6: (l: string, _): bool => l =~ '\v\{{3}6', + }, + + xhtml: { + 1: (l: string, _): bool => l =~ $"{UPTOINC_H}1", + 2: (l: string, _): bool => l =~ $"{UPTOINC_H}2", + 3: (l: string, _): bool => l =~ $"{UPTOINC_H}3", + 4: (l: string, _): bool => l =~ $"{UPTOINC_H}4", + 5: (l: string, _): bool => l =~ $"{UPTOINC_H}5", + 6: (l: string, _): bool => l =~ $"{UPTOINC_H}6", + } +} + +# HELP_RULERS {{{3 +const HELP_RULERS: dict<string> = { + '=': '^=\{40,}$', + '-': '^-\{40,}', +} +const HELP_RULER: string = HELP_RULERS->values()->join('\|') + +# HELP_TAG {{{3 +# The regex is copied from the help syntax plugin +const HELP_TAG: string = '\*[#-)!+-~]\+\*\%(\s\|$\)\@=' + +# Adapted from `$VIMRUNTIME/syntax/help.vim`.{{{ +# +# The original regex is: +# +# ^[-A-Z .][-A-Z0-9 .()_]*\ze\(\s\+\*\|$\) +# +# Allowing a space or a hyphen at the start can give false positives, and is +# useless, so we don't allow them. +#}}} + +# HELP_HEADLINE {{{3 +const HELP_HEADLINE: string = '^\C[A-Z.][-A-Z0-9 .()_]*\%(\s\+\*+\@!\|$\)' +# ^--^ +# To prevent some false positives under `:help feature-list`. +# Others {{{2 +var lvls: dict<number> +def InitHelpLvls() + lvls = { + '*01.1*': 0, + '1.': 0, + '1.2': 0, + '1.2.3': 0, + 'header ~': 0, + HEADLINE: 0, + tag: 0, + } +enddef + +var fuzzy_entries: list<dict<any>> +var help_winid: number +var print_entry: bool +var selected_entry_match: number + +# Interface {{{1 +export def Open() #{{{2 + g:helptoc.type = GetType() + if !MATCH_ENTRY->has_key(g:helptoc.type) + return + endif + if g:helptoc.type == 'terminal' && win_gettype() == 'popup' + # trying to deal with a popup menu on top of a popup terminal seems + # too tricky for now + echomsg 'does not work in a popup window; only in a regular window' + return + endif + + UpdateUserSettings() + + # invalidate the cache if the buffer's contents has changed + if exists('b:toc') && &filetype != 'man' + if b:toc.changedtick != b:changedtick + # in a terminal buffer, `b:changedtick` does not change + || g:helptoc.type == 'terminal' && line('$') > b:toc.linecount + unlet! b:toc + endif + endif + + if !exists('b:toc') + SetToc() + endif + + var winpos: list<number> = winnr()->win_screenpos() + var height: number = winheight(0) - 2 + var width: number = winwidth(0) + b:toc.width = b:toc.width ?? width / 3 + # the popup needs enough space to display the help message in its title + if b:toc.width < 30 + b:toc.width = 30 + endif + # Is `popup_menu()` OK with a list of dictionaries?{{{ + # + # Yes, see `:help popup_create-arguments`. + # Although, it expects dictionaries with the keys `text` and `props`. + # But we use dictionaries with the keys `text` and `lnum`. + # IOW, we abuse the feature which lets us use text properties in a popup. + #}}} + var winid: number = GetTocEntries() + ->popup_menu({ + line: winpos[0], + col: winpos[1] + width - 1, + pos: 'topright', + highlight: g:helptoc.type == 'terminal' ? 'Terminal' : 'Normal', + minheight: height, + maxheight: height, + minwidth: b:toc.width, + maxwidth: b:toc.width, + filter: Filter, + callback: Callback, + border: g:helptoc.popup_border, + borderchars: g:helptoc.popup_borderchars, + borderhighlight: g:helptoc.popup_borderhighlight, + close: g:helptoc.popup_close, + drag: g:helptoc.popup_drag, + scrollbar: g:helptoc.popup_scrollbar, + }) + # Specify filetypes using sanitized toc syntax{{{ + # Those filetypes have a normalized toc structure. The top level is + # unprefixed and levels 2 to 6 are prefixed, by default, with a vertical + # line and space for each level below 1: + # Level 1 + # | Level 2 + # ... + # | | | | | Level 6 }}} + final SanitizedTocSyntaxTypes: list<string> = + ['asciidoc', 'html', 'markdown', 'tex', 'vim', 'xhtml'] + if index(SanitizedTocSyntaxTypes, g:helptoc.type) != -1 + # Specified types' toc popups use a common syntax + Win_execute(winid, 'SanitizedTocSyntax()') + else + # Other types' toc popups use the same syntax as the buffer itself + Win_execute(winid, [$'ownsyntax {&filetype}', '&l:conceallevel = 3']) + endif + # In a help file, we might reduce some noisy tags to a trailing asterisk. + # Hide those. + if g:helptoc.type == 'help' + matchadd('Conceal', '\*$', 0, -1, {window: winid}) + endif + SelectNearestEntryFromCursor(winid) + + # Can't set the title before jumping to the relevant line, otherwise the + # indicator in the title might be wrong + SetTitle(winid) +enddef + +# Core {{{1 +def SetToc() #{{{2 + # Lambdas: + # CHARACTER_REFERENCES_TO_CHARACTERS {{{3 + # These are used for AsciiDoc, Markdown, and [X]HTML, all of which allow + # for decimal, hexadecimal, and XML predefined entities. + # Decimal character references: e.g., § to § + # Hexadecimal character references: e.g., § to § + # XML predefined entities to chars: e.g., < to < + # All HTML5 named character references could be handled, though is that + # warranted for the few that may appear in a toc entry, especially when + # they are often mnemonic? Future: A common Vim dict/enum could be useful? + const CHARACTER_REFERENCES_TO_CHARACTERS = (text: string): string => + text->substitute('\v\�*([1-9]\d{0,6});', + '\=nr2char(str2nr(submatch(1), 10), 1)', 'g') + ->substitute('\c\v\�*([1-9a-f][[:xdigit:]]{1,5});', + '\=nr2char(str2nr(submatch(1), 16), 1)', 'g') + ->substitute('\C&', '\="\u0026"', 'g') + ->substitute('\C'', "\u0027", 'g') + ->substitute('\C>', "\u003E", 'g') + ->substitute('\C<', "\u003C", 'g') + ->substitute('\C"', "\u0022", 'g') + + # SANITIZE_ASCIIDOC {{{3 + # 1 - Substitute the = or # heading markup with the level indicator + # 2 - Substitute XML predefined, dec, and hex char refs in the entry + # AsciiDoc recommends only using named char refs defined in XML: + # https://docs.asciidoctor.org/asciidoc/latest/subs/replacements/ + const SANITIZE_ASCIIDOC = (text: string): string => + text->substitute('\v^(\={1,6}|#{1,6})\s+', + '\=repeat(g:helptoc.level_indicator, len(submatch(1)) - 1)', '') + ->CHARACTER_REFERENCES_TO_CHARACTERS() + + # SANITIZE_HTML {{{3 + # 1 - Remove any leading spaces or tabs + # 2 - Remove any <!--HTML comments--> + # 3 - Remove any <?processing_instructions?> + # 4 - Remove any leading tags (and any blanks) other than <h1 to <h6 + # 5 - Remove any persisting leading blanks + # 6 - Handle empty XHTML headings, e.g., <h6 /> + # 7 - Remove trailing content following the </h[1-6]> + # 8 - Remove the <h1 + # 9 - Substitute the h2 to h6 heading tags with level indicator/level + # 10 - Remove intra-heading tags like <em>, </em>, <strong>, etc. + # 11 - Substitute XML predefined, dec and hex character references + const SANITIZE_HTML = (text: string): string => + text->substitute('^\s*', '', '') + ->substitute('[<]!--.\{-}--[>]', '', 'g') + ->substitute('[<]?[^?]\+?[>]', '', 'g') + ->substitute('\v%([<][^Hh][^1-6]?[^>][>])*\s*', '', '') + ->substitute('^\s\+', '', '') + ->substitute('\v[<][Hh]([1-6])\s*[/][>].*', + '\=repeat(g:helptoc.level_indicator, ' .. + 'str2nr(submatch(1)) - 1) ' .. + '.. "[Empty heading " .. submatch(1) .. "]"', '') + ->substitute('[<][/][Hh][1-6][>].*$', '', '') + ->substitute('[<][Hh]1[^>]*[>]', '', '') + ->substitute('\v[<][Hh]([2-6])[^>]*[>]', + '\=repeat(g:helptoc.level_indicator, ' .. + 'str2nr(submatch(1)) - 1)', '') + ->substitute('[<][/]\?[[:alpha:]][^>]*[>]', '', 'g') + ->CHARACTER_REFERENCES_TO_CHARACTERS() + + # SANITIZE_MARKDOWN #{{{3 + # 1 - Hyperlink incl image, e.g. [](\uri), to Vim... + # 2 - Hyperlink [text](/uri) to text + # 3 - Substitute the # ATX heading markup with the level indicator/level + # The omitted markup reflects CommonMark Spec: + # https://spec.commonmark.org/0.31.2/#atx-headings + # 4 - Substitute decimal, hexadecimal, and XML predefined char refs + const SANITIZE_MARKDOWN = (text: string): string => + text->substitute('\v[\u005B][\u005D]' + .. '[(][^)]+[)][\u005D][(][^)]+[)]', '\1', '') + ->substitute('\v[\u005B]([^\u005D]+)[\u005D][(][^)]+[)]', + '\1', '') + ->substitute('\v^ {0,3}(#{1,6})\s*', + '\=repeat(g:helptoc.level_indicator, len(submatch(1)) - 1)', + '') + ->CHARACTER_REFERENCES_TO_CHARACTERS() + + # SANITIZE_TERMINAL {{{3 + # Omit the prompt, which may be very long and otherwise just adds clutter + const SANITIZE_TERMINAL = (text: string): string => + text->substitute('^' .. g:helptoc.shell_prompt, '', '') + + # SANITIZE_TEX #{{{3 + # 1 - Use any [toc-title] overrides to move its content into the + # {heading} instead of the (non-ToC) heading's text + # 2 - Replace \part{ or \addcontentsline{toc}{part} with '[PART] ' + # 3 - Omit \chapter{ or \addcontentsline{toc}{chapter} + # 4 - Omit \section{ or \addcontentsline{toc}{section} + # 5 - Omit \subsection{ or \addcontentsline{toc}{subsection} + # 6 - Omit \subsubsection{ or \addcontentsline{toc}{subsubsection} + # 7 - Omit the trailing } + # 8 - Unescape common escaped characters &%$_#{}~^\ + const SANITIZE_TEX = (text: string): string => + text->substitute('\v^[\\](part|chapter|%(sub){0,2}section)' .. + '[\u005B]([^\u005D]+).*', '\\\1{\2}', '') + ->substitute('^[\\]\(part\|addcontentsline{toc}{part}\){', + '[PART] ', '') + ->substitute('^[\\]\(chapter\|addcontentsline{toc}{chapter}\){', + '', '') + ->substitute('^[\\]\(section\|addcontentsline{toc}{section}\){', + '\=g:helptoc.level_indicator', '') + ->substitute('^[\\]\(subsection\|' .. + 'addcontentsline{toc}{subsection}\){', + '\=repeat(g:helptoc.level_indicator, 2)', '') + ->substitute('^[\\]\(subsubsection\|' .. + 'addcontentsline{toc}{subsubsection}\){', + '\=repeat(g:helptoc.level_indicator, 3)', '') + ->substitute('}[^}]*$', '', '') + ->substitute('\\\([&%$_#{}~\\^]\)', '\1', 'g') + + # SANITIZE_VIM {{{3 + # #1 - Omit leading Vim9 script # or Vim script " markers and blanks + # #2 - Omit numbered 3x { markers + const SANITIZE_VIM = (text: string): string => + text->substitute('\v^[#[:blank:]"]*(.+)\ze[{]{3}([1-6])', + '\=submatch(2) == "1" ? submatch(1) : ' .. + 'repeat(g:helptoc.level_indicator, str2nr(submatch(2)) - 1)' .. + ' .. submatch(1)', 'g') + ->substitute('[#[:blank:]"]*{\{3}[1-6]', '', '') + #}}}3 + + final toc: dict<any> = {entries: []} + toc.changedtick = b:changedtick + if !toc->has_key('width') + toc.width = 0 + endif + # We cache the toc in `b:toc` to get better performance.{{{ + # + # Without caching, when we press `H`, `L`, `H`, `L`, ... quickly for a few + # seconds, there is some lag if we then try to move with `j` and `k`. + # This can only be perceived in big man pages like with `:Man ffmpeg-all`. + #}}} + b:toc = toc + + if g:helptoc.type == 'help' + SetTocHelp() + return + endif + + if g:helptoc.type == 'terminal' + b:toc.linecount = line('$') + endif + + var curline: string = getline(1) + var nextline: string + var lvl_and_test: list<list<any>> = MATCH_ENTRY + ->get(g:helptoc.type, {}) + ->items() + ->sort((l: list<any>, ll: list<any>): number => + l[0]->str2nr() - ll[0]->str2nr()) + + var skip_next: bool = false + var skip_fence: bool = false + + # Non-help headings processing + for lnum: number in range(1, line('$')) + if skip_next + skip_next = false + curline = nextline + continue + endif + + nextline = getline(lnum + 1) + + # Special handling for markdown filetype using setext headings + if g:helptoc.type == 'markdown' + # ignore fenced codeblock lines + if curline =~ '^```.' + skip_fence = true + elseif curline =~ '^```$' + skip_fence = !skip_fence + endif + if skip_fence + curline = nextline + continue + endif + # Check for setext formatted headings (= or - underlined) + if nextline =~ '^\s\{0,3}=\+$' && curline =~ '\S' + # Level 1 heading (one or more =, up to three spaces preceding) + b:toc.entries->add({ + lnum: lnum, + lvl: 1, + text: SANITIZE_MARKDOWN('# ' .. trim(curline)), + }) + skip_next = true + curline = nextline + continue + elseif nextline =~ '^\s\{0,3}-\+$' && curline =~ '\S' + # Level 2 heading (one or more -, up to three spaces preceding) + b:toc.entries->add({ + lnum: lnum, + lvl: 2, + text: SANITIZE_MARKDOWN('## ' .. trim(curline)), + }) + skip_next = true + curline = nextline + continue + endif + endif + + # Regular processing for markdown ATX-style headings + other filetypes + for [lvl: string, IsEntry: func: bool] in lvl_and_test + if IsEntry(curline, nextline) + if g:helptoc.type == 'asciidoc' + curline = curline->SANITIZE_ASCIIDOC() + elseif g:helptoc.type == 'html' || g:helptoc.type == 'xhtml' + curline = curline->SANITIZE_HTML() + elseif g:helptoc.type == 'markdown' + curline = curline->SANITIZE_MARKDOWN() + elseif g:helptoc.type == 'terminal' + curline = curline->SANITIZE_TERMINAL() + elseif g:helptoc.type == 'tex' + curline = curline->SANITIZE_TEX() + elseif g:helptoc.type == 'vim' + curline = curline->SANITIZE_VIM() + endif + b:toc.entries->add({ + lnum: lnum, + lvl: lvl->str2nr(), + text: curline, + }) + break + endif + endfor + curline = nextline + endfor + + InitMaxAndCurLvl() +enddef + +def SetTocHelp() #{{{2 + var main_ruler: string + for line: string in getline(1, '$') + if line =~ HELP_RULER + main_ruler = line =~ '=' ? HELP_RULERS['='] : HELP_RULERS['-'] + break + endif + endfor + + var prevline: string + var curline: string = getline(1) + var nextline: string + var in_list: bool + var last_numbered_entry: number + InitHelpLvls() + for lnum: number in range(1, line('$')) + nextline = getline(lnum + 1) + + if main_ruler != '' && curline =~ main_ruler + last_numbered_entry = 0 + # The information gathered in `lvls` might not be applicable to + # all the main sections of a help file. Let's reset it whenever + # we find a ruler. + InitHelpLvls() + endif + + # Do not assume that a list ends on an empty line. + # See the list at `:help gdb` for a counter-example. + if in_list + && curline !~ '^\d\+.\s' + && curline !~ '^\s*$' + && curline !~ '^[<[:blank:]]' + in_list = false + endif + + if prevline =~ '^\d\+\.\s' + && curline !~ '^\s*$' + && curline !~ $'^\s*{HELP_TAG}' + in_list = true + endif + + # 1. + if prevline =~ '^\d\+\.\s' + # Let's assume that the start of a main entry is always followed by an + # empty line, or a line starting with a tag + && (curline =~ '^>\=\s*$' || curline =~ $'^\s*{HELP_TAG}') + # ignore a numbered line in a list + && !in_list + var current_numbered_entry: number = prevline + ->matchstr('^\d\+\ze\.\s') + ->str2nr() + if current_numbered_entry > last_numbered_entry + AddEntryInTocHelp('1.', lnum - 1, prevline) + last_numbered_entry = prevline + ->matchstr('^\d\+\ze\.\s') + ->str2nr() + endif + endif + + # 1.2 + if curline =~ '^\d\+\.\d\+\s' + if curline =~ $'\%({HELP_TAG}\s*\|\~\)$' + || (prevline =~ $'^\s*{HELP_TAG}' || nextline =~ $'^\s*{HELP_TAG}') + || (prevline =~ HELP_RULER || nextline =~ HELP_RULER) + || (prevline =~ '^\s*$' && nextline =~ '^\s*$') + AddEntryInTocHelp('1.2', lnum, curline) + endif + # 1.2.3 + elseif curline =~ '^\s\=\d\+\.\d\+\.\d\+\s' + AddEntryInTocHelp('1.2.3', lnum, curline) + endif + + # HEADLINE + if curline =~ HELP_HEADLINE + && curline !~ '^CTRL-' + && prevline->IsSpecialHelpLine() + && (nextline ->IsSpecialHelpLine() + || nextline =~ '^\s*(\|^\t\|^N[oO][tT][eE]:') + AddEntryInTocHelp('HEADLINE', lnum, curline) + endif + + # header ~ + if curline =~ '\~$' + && curline =~ '\w' + && curline !~ '^[[:blank:]<]\|\t\|---+---\|^NOTE:' + && curline !~ '^\d\+\.\%(\d\+\%(\.\d\+\)\=\)\=\s' + && prevline !~ $'^\s*{HELP_TAG}' + && prevline !~ '\~$' + && nextline !~ '\~$' + AddEntryInTocHelp('header ~', lnum, curline) + endif + + # *some_tag* + if curline =~ HELP_TAG + AddEntryInTocHelp('tag', lnum, curline) + endif + + # In the Vim user manual, a main section is a special case.{{{ + # + # It's not a simple numbered section: + # + # 01.1 + # + # It's used as a tag: + # + # *01.1* Two manuals + # ^ ^ + #}}} + if prevline =~ main_ruler && curline =~ '^\*\d\+\.\d\+\*' + AddEntryInTocHelp('*01.1*', lnum, curline) + endif + + [prevline, curline] = [curline, nextline] + endfor + + # let's ignore the tag on the first line (not really interesting) + if b:toc.entries->get(0, {})->get('lnum') == 1 + b:toc.entries->remove(0) + endif + + # let's also ignore anything before the first `1.` line + var i: number = b:toc.entries + ->copy() + ->map((_, entry: dict<any>) => entry.text) + ->match('^\s*1\.\s') + if i > 0 + b:toc.entries->remove(0, i - 1) + endif + + InitMaxAndCurLvl() + + # set level of tag entries to the deepest level + var has_tag: bool = b:toc.entries + ->copy() + ->map((_, entry: dict<any>) => entry.text) + ->match(HELP_TAG) >= 0 + if has_tag + ++b:toc.maxlvl + endif + b:toc.entries + ->map((_, entry: dict<any>) => entry.lvl == 0 + ? entry->extend({lvl: b:toc.maxlvl}) + : entry) + + # fix indentation + var min_lvl: number = b:toc.entries + ->copy() + ->map((_, entry: dict<any>) => entry.lvl) + ->min() + for entry: dict<any> in b:toc.entries + entry.text = entry.text + ->substitute('^\s*', () => + repeat(' ', (entry.lvl - min_lvl) * 3), '') + endfor +enddef + +def AddEntryInTocHelp(type: string, lnum: number, line: string) #{{{2 + # don't add a duplicate entry + if lnum == b:toc.entries->get(-1, {})->get('lnum') + # For a numbered line containing a tag, *do* add an entry. + # But only for its numbered prefix, not for its tag. + # The former is the line's most meaningful representation. + if b:toc.entries->get(-1, {})->get('type') == 'tag' + b:toc.entries->remove(-1) + else + return + endif + endif + + var text: string = line + if type == 'tag' + var tags: list<string> + text->substitute(HELP_TAG, () => !!tags->add(submatch(0)), 'g') + text = tags + # we ignore errors and warnings because those are meaningless in + # a TOC where no context is available + ->filter((_, tag: string): bool => tag !~ '\*[EW]\d\+\*') + ->join() + if text !~ HELP_TAG + return + endif + endif + + var maxlvl: number = lvls->values()->max() + if type == 'tag' + lvls[type] = 0 + elseif type == '1.2' + lvls[type] = lvls[type] ?? lvls->get('1.', maxlvl) + 1 + elseif type == '1.2.3' + lvls[type] = lvls[type] ?? lvls->get('1.2', maxlvl) + 1 + else + lvls[type] = lvls[type] ?? maxlvl + 1 + endif + + # Ignore noisy tags.{{{ + # + # 14. Linking groups *:hi-link* *:highlight-link* *E412* *E413* + # ^----------------------------------------^ + # ^\s*\d\+\.\%(\d\+\.\=\)*\s\+.\{-}\zs\*.* + # --- + # + # We don't use conceal because then, `matchfuzzypos()` could match + # concealed characters, which would be confusing. + #}}} + # MAKING YOUR OWN SYNTAX FILES *mysyntaxfile* + # ^------------^ + # ^\s*[A-Z].\{-}\*\zs.* + # + var after_HEADLINE: string = '^\s*[A-Z].\{-}\*\zs.*' + # 14. Linking groups *:hi-link* *:highlight-link* *E412* *E413* + # ^----------------------------------------^ + # ^\s*\d\+\.\%(\d\+\.\=\)*\s\+.\{-}\*\zs.* + var after_numbered: string = '^\s*\d\+\.\%(\d\+\.\=\)*\s\+.\{-}\*\zs.*' + # 01.3 Using the Vim tutor *tutor* *vimtutor* + # ^----------------^ + var after_numbered_tutor: string = '^\*\d\+\.\%(\d\+\.\=\)*.\{-}\t\*\zs.*' + var noisy_tags: string = + $'{after_HEADLINE}\|{after_numbered}\|{after_numbered_tutor}' + text = text->substitute(noisy_tags, '', '') + # We don't remove the trailing asterisk, because the help syntax plugin + # might need it to highlight some headlines. + + b:toc.entries->add({ + lnum: lnum, + lvl: lvls[type], + text: text, + type: type, + }) +enddef + +def InitMaxAndCurLvl() #{{{2 + b:toc.maxlvl = b:toc.entries + ->copy() + ->map((_, entry: dict<any>) => entry.lvl) + ->max() + b:toc.curlvl = b:toc.maxlvl +enddef + +def Popup_settext(winid: number, entries: list<dict<any>>) #{{{2 + var text: list<any> + # When we fuzzy search the toc, the dictionaries in `entries` contain a + # `props` key, to highlight each matched character individually. + # We don't want to process those dictionaries further. + # The processing should already have been done by the caller. + if entries->get(0, {})->has_key('props') + text = entries + else + text = entries + ->copy() + ->map((_, entry: dict<any>): string => entry.text) + endif + popup_settext(winid, text) + SetTitle(winid) + redraw +enddef + +def SetTitle(winid: number) #{{{2 + var curlnum: number + var lastlnum: number = line('$', winid) + var is_empty: bool = lastlnum == 1 + && winid->winbufnr()->getbufoneline(1) == '' + if is_empty + [curlnum, lastlnum] = [0, 0] + else + curlnum = line('.', winid) + endif + var newtitle: string = printf(' %*d/%d (%d/%d)', + len(lastlnum), curlnum, + lastlnum, + b:toc.curlvl, + b:toc.maxlvl, + ) + + var width: number = winid->popup_getoptions().minwidth + newtitle = printf('%s%*s', + newtitle, + width - newtitle->strlen(), + 'press ? for help ') + + popup_setoptions(winid, {title: newtitle}) +enddef + +def SelectNearestEntryFromCursor(winid: number) #{{{2 + var lnum: number = line('.') + if lnum == 1 + Win_execute(winid, 'normal! 1G') + return + endif + + if lnum == line('$') + Win_execute(winid, 'normal! G') + return + endif + + var collapsed_entries: list<dict<any>> = b:toc.entries + ->deepcopy() + ->filter((_, entry: dict<any>): bool => entry.lvl <= b:toc.curlvl) + var firstline: number = collapsed_entries + ->reverse() + ->indexof((_, entry: dict<any>): bool => entry.lnum <= lnum) + firstline = len(collapsed_entries) - firstline + + if firstline <= 0 + return + endif + Win_execute(winid, $'normal! {firstline}Gzz') +enddef + +def Filter(winid: number, key: string): bool #{{{2 + # support various normal commands for moving/scrolling + if [ + 'j', 'J', 'k', 'K', "\<Down>", "\<Up>", "\<C-N>", "\<C-P>", + "\<C-D>", "\<C-U>", + "\<PageUp>", "\<PageDown>", + 'g', 'G', "\<Home>", "\<End>", + 'z' + ]->index(key) >= 0 + var scroll_cmd: string = { + J: 'j', + K: 'k', + g: '1G', + "\<Home>": '1G', + "\<End>": 'G', + z: 'zz' + }->get(key, key) + + var old_lnum: number = line('.', winid) + Win_execute(winid, $'normal! {scroll_cmd}') + var new_lnum: number = line('.', winid) + + if print_entry + PrintEntry(winid) + endif + + # wrap around the edges + if new_lnum == old_lnum + scroll_cmd = { + j: '1G', + J: '1G', + k: 'G', + K: 'G', + "\<Down>": '1G', + "\<Up>": 'G', + "\<C-N>": '1G', + "\<C-P>": 'G', + }->get(key, '') + if !scroll_cmd->empty() + Win_execute(winid, $'normal! {scroll_cmd}') + endif + endif + + # move the cursor to the corresponding line in the main buffer + if key == 'J' || key == 'K' + var lnum: number = GetBufLnum(winid) + execute $'normal! 0{lnum}zt' + # Install a match in the regular buffer to highlight the position + # of the entry in the latter + MatchDelete() + selected_entry_match = matchaddpos('IncSearch', [lnum], 0, -1) + endif + SetTitle(winid) + + return true + endif + + if key == 'c' + SelectNearestEntryFromCursor(winid) + return true + endif + + # when we press `p`, print the selected line (useful when it's truncated) + if key == 'p' + PrintEntry(winid) + return true + endif + + # same thing, but automatically + if key == 'P' + print_entry = !print_entry + if print_entry + PrintEntry(winid) + else + echo '' + endif + return true + endif + + if key == 'q' + popup_close(winid, -1) + return true + endif + + if key == '?' + ToggleHelp(winid) + return true + endif + + # scroll help window + if key == "\<C-J>" || key == "\<C-K>" + var scroll_cmd: string = {"\<C-J>": 'j', "\<C-K>": 'k'}->get(key, key) + if scroll_cmd == 'j' && line('.', help_winid) == line('$', help_winid) + scroll_cmd = '1G' + elseif scroll_cmd == 'k' && line('.', help_winid) == 1 + scroll_cmd = 'G' + endif + Win_execute(help_winid, $'normal! {scroll_cmd}') + return true + endif + + # split main window + if key == 's' + split + return popup_filter_menu(winid, "\<CR>") + endif + + # increase/decrease the popup's width + if key == '+' || key == '-' + var width: number = winid->popup_getoptions().minwidth + if key == '-' && width == 1 + || key == '+' && winid->popup_getpos().col == 1 + return true + endif + width = width + (key == '+' ? 1 : -1) + # remember the last width if we close and re-open the TOC later + b:toc.width = width + popup_setoptions(winid, {minwidth: width, maxwidth: width}) + return true + endif + + if key == 'H' && b:toc.curlvl > 1 + || key == 'L' && b:toc.curlvl < b:toc.maxlvl + CollapseOrExpand(winid, key) + return true + endif + + if key == '/' + # This is probably what the user expects if they've started a first + # fuzzy search, press Escape, then start a new one. + DisplayNonFuzzyToc(winid) + + [{ + group: 'HelpToc', + event: 'CmdlineChanged', + pattern: '@', + cmd: $'FuzzySearch({winid})', + replace: true, + }, + { + group: 'HelpToc', + event: 'CmdlineLeave', + pattern: '@', + cmd: 'TearDown()', + replace: true, + }]->autocmd_add() + + # Need to evaluate `winid` right now{{{ + # with an `eval`'ed and `execute()`'ed heredoc because: + # + # - the mappings can only access the script-local namespace + # - `winid` is in the function namespace; not in the script-local one + #}}} + var input_mappings: list<string> =<< trim eval END + cnoremap <buffer><nowait> <Down> <ScriptCmd>Filter({winid}, 'j')<CR> + cnoremap <buffer><nowait> <Up> <ScriptCmd>Filter({winid}, 'k')<CR> + cnoremap <buffer><nowait> <C-N> <ScriptCmd>Filter({winid}, 'j')<CR> + cnoremap <buffer><nowait> <C-P> <ScriptCmd>Filter({winid}, 'k')<CR> + END + input_mappings->execute() + + var look_for: string + try + popup_setoptions(winid, {mapping: true}) + look_for = input('look for: ', '', $'custom,{Complete->string()}') + | redraw + | echo '' + catch /Vim:Interrupt/ + TearDown() + finally + popup_setoptions(winid, {mapping: false}) + endtry + return look_for == '' ? true : popup_filter_menu(winid, "\<CR>") + endif + + return popup_filter_menu(winid, key) +enddef + +def FuzzySearch(winid: number) #{{{2 + var look_for: string = getcmdline() + if look_for == '' + DisplayNonFuzzyToc(winid) + return + endif + + # We match against *all* entries; not just the currently visible ones. + # Rationale: If we use a (fuzzy) search, we're probably lost. We don't + # know where the info is. + var matches: list<list<any>> = b:toc.entries + ->copy() + ->matchfuzzypos(look_for, {key: 'text'}) + + fuzzy_entries = matches->get(0, [])->copy() + var pos: list<list<number>> = matches->get(1, []) + + var text: list<dict<any>> + if !has('textprop') + text = matches->get(0, []) + else + var buf: number = winid->winbufnr() + if prop_type_get('help-fuzzy-toc', {bufnr: buf}) == {} + prop_type_add('help-fuzzy-toc', { + bufnr: buf, + combine: false, + highlight: 'IncSearch', + }) + endif + text = matches + ->get(0, []) + ->map((i: number, match: dict<any>) => ({ + text: match.text, + props: pos[i]->copy()->map((_, col: number) => ({ + col: col + 1, + length: 1, + type: 'help-fuzzy-toc', + }))})) + endif + Win_execute(winid, 'normal! 1Gzt') + Popup_settext(winid, text) +enddef + +def DisplayNonFuzzyToc(winid: number) #{{{2 + fuzzy_entries = null_list + Popup_settext(winid, GetTocEntries()) +enddef + +def PrintEntry(winid: number) #{{{2 + echo GetTocEntries()[line('.', winid) - 1]['text'] +enddef + +def CollapseOrExpand(winid: number, key: string) #{{{2 + # Must be saved before we reset the popup contents, so we can + # automatically select the least unexpected entry in the updated popup. + var buf_lnum: number = GetBufLnum(winid) + + # find the nearest lower level for which the contents of the TOC changes + if key == 'H' + while b:toc.curlvl > 1 + var old: list<dict<any>> = GetTocEntries() + --b:toc.curlvl + var new: list<dict<any>> = GetTocEntries() + # In `:help`, there are only entries in levels 3. + # We don't want to collapse to level 2, nor 1. + # It would clear the TOC which is confusing. + if new->empty() + ++b:toc.curlvl + break + endif + var did_change: bool = new != old + if did_change || b:toc.curlvl == 1 + break + endif + endwhile + # find the nearest upper level for which the contents of the TOC changes + else + while b:toc.curlvl < b:toc.maxlvl + var old: list<dict<any>> = GetTocEntries() + ++b:toc.curlvl + var did_change: bool = GetTocEntries() != old + if did_change || b:toc.curlvl == b:toc.maxlvl + break + endif + endwhile + endif + + # Update the popup contents + var toc_entries: list<dict<any>> = GetTocEntries() + Popup_settext(winid, toc_entries) + + # Try to select the same entry; if it's no longer visible, select its + # direct parent. + var toc_lnum: number = 0 + for entry: dict<any> in toc_entries + if entry.lnum > buf_lnum + break + endif + ++toc_lnum + endfor + Win_execute(winid, $'normal! {toc_lnum ?? 1}Gzz') +enddef + +def MatchDelete() #{{{2 + if selected_entry_match == 0 + return + endif + + selected_entry_match->matchdelete() + selected_entry_match = 0 +enddef + +def Callback(winid: number, choice: number) #{{{2 + MatchDelete() + + if help_winid != 0 + help_winid->popup_close() + help_winid = 0 + endif + + if choice == -1 + fuzzy_entries = null_list + return + endif + if choice == -2 # Button X is clicked (when close: 'button') + return + endif + + var lnum: number = GetTocEntries() + ->get(choice - 1, {}) + ->get('lnum') + + fuzzy_entries = null_list + + if lnum == 0 + return + endif + + # Moving the cursor with `normal! 123G` instead of `cursor()` adds an + # entry in the jumplist (which is useful if you want to come back where + # you were). + execute $'normal! {lnum}Gzvzt' +enddef + +def ToggleHelp(menu_winid: number) #{{{2 + # Show/hide HELP_TEXT in a second popup when '?' is typed{{{ + # (when a helptoc popup is open). A scrollbar on this popup makes sense + # because it is very long and, even if it's not used for scrolling, works + # well as an indicator of how far through the HELP_TEXT popup you are. }}} + if help_winid == 0 + var height: number = [HELP_TEXT->len(), winheight(0) * 2 / 3]->min() + var longest_line: number = HELP_TEXT + ->copy() + ->map((_, line: string) => line->strcharlen()) + ->max() + var width: number = [longest_line, winwidth(0) - 4]->min() + var zindex: number = popup_getoptions(menu_winid).zindex + ++zindex + help_winid = HELP_TEXT->popup_create({ + pos: 'center', + minheight: height, + maxheight: height, + minwidth: width, + maxwidth: width, + highlight: &buftype == 'terminal' ? 'Terminal' : 'Normal', + zindex: zindex, + border: g:helptoc.popup_border, + borderchars: g:helptoc.popup_borderchars, + borderhighlight: g:helptoc.popup_borderhighlight, + close: g:helptoc.popup_close, + scrollbar: true, + }) + + setwinvar(help_winid, '&cursorline', true) + setwinvar(help_winid, '&linebreak', true) + matchadd('Special', '^<\S\+\|^\S\{,2} \@=', 0, -1, + {window: help_winid}) + matchadd('Number', '\d\+', 0, -1, {window: help_winid}) + for lnum: number in HELP_TEXT->len()->range() + if HELP_TEXT[lnum] =~ '^─\+$' + matchaddpos('Title', [lnum], 0, -1, {window: help_winid}) + endif + endfor + + else + if IsVisible(help_winid) + popup_hide(help_winid) + else + popup_show(help_winid) + endif + endif +enddef + +def Win_execute(winid: number, cmd: any) #{{{2 + # wrapper around `win_execute()` to enforce a redraw, which might be necessary + # whenever we change the cursor position + win_execute(winid, cmd) + redraw +enddef + +def TearDown() #{{{2 + autocmd_delete([{group: 'HelpToc'}]) + cunmap <buffer> <Down> + cunmap <buffer> <Up> + cunmap <buffer> <C-N> + cunmap <buffer> <C-P> +enddef +# Util {{{1 +def GetType(): string #{{{2 + return &buftype == 'terminal' ? 'terminal' : &filetype +enddef + +def GetTocEntries(): list<dict<any>> #{{{2 + return fuzzy_entries ?? b:toc.entries + ->copy() + ->filter((_, entry: dict<any>): bool => entry.lvl <= b:toc.curlvl) +enddef + +def GetBufLnum(winid: number): number #{{{2 + var toc_lnum: number = line('.', winid) + return GetTocEntries() + ->get(toc_lnum - 1, {}) + ->get('lnum') +enddef + +def IsVisible(win: number): bool #{{{2 + return win->popup_getpos()->get('visible') +enddef + +def IsSpecialHelpLine(line: string): bool #{{{2 + return line =~ '^[<>]\=\s*$' + || line =~ '^\s*\*' + || line =~ HELP_RULER + || line =~ HELP_HEADLINE +enddef + +def Complete(..._): string #{{{2 + return b:toc.entries + ->copy() + ->map((_, entry: dict<any>) => + entry.text->trim(' ~')->substitute('*', '', 'g')) + ->filter((_, text: string): bool => text =~ '^[-a-zA-Z0-9_() ]\+$') + ->sort() + ->uniq() + ->join("\n") +enddef #}}}2 +#}}}1 +# vim:et:ft=vim:fdm=marker: diff --git a/uvim/runtime/pack/dist/opt/helptoc/doc/helptoc.txt b/uvim/runtime/pack/dist/opt/helptoc/doc/helptoc.txt new file mode 100644 index 0000000000..667dadd2b1 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/helptoc/doc/helptoc.txt @@ -0,0 +1,351 @@ +*helptoc.txt* For Vim version 9.1. Last change: 2025 Aug 10 + + + VIM REFERENCE MANUAL + +Interactive table of contents for help buffers and several other filetypes +============================================================================== + + +1. OVERVIEW *HelpToc-overview* + +The helptoc.vim plugin provides one command, :HelpToc, which generates a +hierarchical table of contents in a popup window, which is based on the +structure of a Vim buffer. See |Helptoc-mappings| for a list of supported key +mappings in the popup window. + +It was designed initially for help buffers, but it also works with buffers of +the following types: + - asciidoc + - html + - man + - markdown + - terminal + - tex + - vim Note: only with numbered fold markers, e.g. {{{1 + - xhtml + +1.1 The :HelpToc command *HelpToc-:HelpToc* + +The :HelpToc command takes no arguments and it cannot be executed from an +unsupported filetype. Also, it cannot be used to generate a table of contents +for an inactive buffer. + +For most buffers of the supported types, :HelpToc may be entered directly +in Vim's |Command-line-mode|. How to use it from Vim's |Terminal-Job| mode is +explained in |HelpToc-terminal-buftype|. + +You may choose to map :HelpToc to keys making it easier to use. These are +examples of what could be used: > + + nnoremap <Leader>ht <Cmd>HelpToc<CR> + tnoremap <C-t><C-t> <Cmd>HelpToc<CR> +< + +2. DETAILS *HelpToc-details* + +When the :HelpToc command is executed from an active buffer of a supported +type, a popup window is produced. The window contains a hierarchical and +interactive table of contents. The entries are based on the "headings" in +the buffer. + +Jumping to an entry's position in the buffer can be achieved by pressing +enter on the applicable entry. Navigation, and other commands applicable to +the popup window, such as expanding and contracting levels, fuzzy searching, +and jumping to the previous or next entry (leaving the table of contents +itself displayed, using J and K), is provided at |help-TOC|, so that is not +reproduced in this help file. + + +3. TYPES *HelpToc-types* + +Some filetypes have more predictable structures than others. For example, +markdown and asciidoc make the identification of headings (aside from edge +cases, such as when in quotes) straightforward. Some filetypes do not have +such obvious or reliable headings/levels (particularly help buffers). +Further, in some instances, how to enter the :HelpToc command is not +necessarily obvious, e.g., when in a Vim |terminal-window|. So, the following +headings address specific details regarding the in-scope types. + +3.1 asciidoc *HelpToc-asciidoc-filetype* + +The heading levels in asciidoc are typically identified by lines starting +with a "=" (up to six, one per level), one or more blank characters, and the +heading text. :HelpToc will generate a table of contents based on either +that form of heading type or, because asciidoc also allows for ATX heading +level syntax (i.e., using the "#" character), headings using that format too. + +As asciidoc is very structured, its table of contents entries are presented +in a standardized form - see |HelpToc-standardized-toc|. So, the initial +"=" or "#" characters and the following space from the related heading are +not included in the table of contents' entries. + +3.2 html *HelpToc-html-filetype* + +HTML provides for six levels of headings, <h1> to <h6>, which may be either +upper or lower case and preceded by all sorts of content like <!--comments-->. +:HelpToc will produce a table of contents based on the six heading levels. + +As HTML is very structured, its table of contents entries are presented +in a standardized form - see |HelpToc-standardized-toc|. So, the <h1> to <h6> +tags, any preceding content, and any trailing content after the heading text, +is not included in the table of contents' entries. + +3.3 man pages *HelpToc-man-filetype* + +Retrieving man pages is typically performed in the terminal. To use :HelpToc +to generate a table of contents, the |man.vim| filetype plugin is a +prerequisite. It is provided with Vim, and may be sourced with: > + + :runtime ftplugin/man.vim +< +Once sourced, the |:Man| command will open the applicable man page in a new +buffer (of "man" filetype). For example: > + + :Man pwd +< +Once in the man buffer, entering :HelpToc opens the table of contents popup, +with level 1 containing section names like NAME, SYNOPSIS, etc. Levels +below 1 include subsections and options, with the level depending on the +number of spaces preceding the content. + +The table of contents for a man buffer's popup window has the same syntax +highlighting as man pages. This reflects that its content is reproduced +as-is, i.e., no preceding tags, level-indicating data, etc., need be omitted +for optimal presentation. + +3.4 markdown *HelpToc-markdown-filetype* + +The headings and levels in markdown typically are ATX formatted headings +(lines starting with up to three spaces, one to six "#", then (optionally) +one or blank characters and the heading text). The alternate form, +setext, uses up to three spaces, the heading 1 text, followed by a +line of one or more "=". The setext heading 2 is similar, but for one or +more "-" instead of "=". There is no heading 3+ in setext. ATX and setext +headings are supported. + +As markdown is very structured, its table of contents entries are presented +in a standardized form - see |HelpToc-standardized-toc|. So, they do not +include any leading spaces, any initial "#" characters and the following blank +character(s) in the table of contents' entries. + +3.5 terminal *HelpToc-terminal-buftype* + +There are no explicit "headings" for a terminal buffer. However, :HelpToc +displays the history of executed shell commands. Those may be specified +by changing the pattern used to match the Vim terminal's prompt. +See |HelpToc-configuration| for examples. + +To access the terminal's table of contents, from the Vim's |Terminal-Job| mode +enter CTRL-W N to go to |Terminal-Normal| mode. From there, enter :HelpToc to +generate the table of contents. If you use the terminal's table of contents +a lot, an appropriate mapping may make it easier than using CTRL-W N - e.g.: > + + tnoremap <C-t><C-t> <Cmd>HelpToc<CR> +< +As the terminal has only "level 1", the table of contents is presented in a +standardized form - see |HelpToc-standardized-toc| - including only the history +list of commands. The prompt itself is also omitted since it adds no value +repeating it for every entry. + +3.6 tex *HelpToc-tex-filetype* + +In LaTeX, a document may be structured hierarchically using part, chapter, +and sectioning commands. Document structure levels are: + \part{} + \chapter{} + \section{} + \subsection{} + \subsubsection{} + +To keep things simpler, \part{} is supported, though treated as being at +the same level as chapter. Consequently, there are four levels displayed +for a tex filetype's table of contents, regardless of the \documentclass{}, +i.e., part and chapter (at level 1), section (level 2), subsection (level 3), +and subsubsection (level 4). + +Also supported are: + - The "*" used to produce unnumbered headings, which are not intended + for reproduction in a table of contents: > + \section*{Unnumbered section heading not produced in the TOC} +< - Manual toc entries using \addcontentsline, for example: > + \addcontentsline{toc}{section}{entry in the TOC only!} +< +The table of contents for a tex filetype is in a standardized form - +see |HelpToc-standardized-toc|. Omitted are: the "\", the part, chapter, +*section, or addcontentsline, and the left and right curly +brackets preceding and following each heading's text. + +3.7 vim *HelpToc-vim-filetype* + +Vim script and Vim9 script do not have headings or levels inherently like +markup languages. However, Vim provides for |folds| defined by markers (|{{{|), +which themselves may be succeeded by a number explicitly indicating the fold +level. This is the structure recognized and supported by helptoc.vim. +So, for example, the following would produce three table of contents entries: > + + vim9script + # Variables {{{1 + var b: bool = true + var s: string = $"Fold markers are great? {b}!" + # Functions {{{1 + def MyFunction(): void #{{{2 + echo s + enddef + MyFunction() +< +The table of contents for that script would appear like this: + Variables + Functions + | MyFunction(): void + +Note: The numbered |{{{| marker structure is the only one supported by + helptoc.vim for the vim filetype. + +As the {{{1 to {{{6 markers make the "headings" explicit, the table of +contents is in a standardized form - see |HelpToc-standardized-toc|. +It does not include any leading comment markers (i.e., either # or ") and +omits the markers themselves. + +3.8 xhtml *HelpToc-xhtml-filetype* + +Although XHTML, being XML, is more strictly structured than HTML/HTML5, +there is no practical difference in treatment required for the xhtml filetype +because, at the heading level, the tags that matter are very similar. +See |HelpToc-html-filetype| for how an xhtml filetype's table of contents is +supported. + + +4. STANDARDIZED TOC *HelpToc-standardized-toc* + +The table of contents for a help buffer, terminal, or man page, make sense +being presented in the form they appear, minus trailing content (such as tags). + +The table of contents for a markdown, asciidoc, [x]html, terminal, or tex +buffer have superfluous content if the entire line was to be returned. +For example: +- Markdown has "#" characters before headings when using ATX heading notation. +- Asciidoc will have either those or, more often, "=" characters before its + headings. +- HTML, aside from the "<h" headings, may have additional tags, comments, + and whitespace before its headings. +- The Vim terminal has the shell prompt, which adds nothing if repeated for + every heading (and may be very long). +- LaTeX has "\" level indicators like "\section{" and a trailing "}". +Consequently, standardising these filetypes' tables of contents, removing +the "noise", and indicating the contents level of each entry, makes sense. + +HelpToc standardizes the markdown, asciidoc, [x]html, terminal and tex tables +of contents by removing extraneous characters, markup indicators, and tags. +It also applies simple, unobtrusive syntax highlighting to the text and level +indicators. By default, it will appear like the following example (though +any level indicators will be less prominent, using |NonText| highlight group). + + Level 1 + | Level 2 + | | Level 3 + | | | Level 4 + | | | | Level 5 + | | | | | Level 6 + +Note: The "| " level indicator may be changed - see |HelpToc-configuration|. + + +5. CONFIGURATION *HelpToc-configuration* + +All configuration is achieved utilizing the g:helptoc dictionary. Any of the +following may be adjusted to meet your needs or preferences: + +g:helptoc key what it controls +------------- ---------------- +shell_prompt The terminal prompt, used for creating a table of contents + for the terminal (history list). The default is, + '^\w\+@\w\+:\f\+\$\s', which should match many users' bash + prompt. To change it, either interactively or in your .vimrc, + use (for example for a bare Bourne shell "$ " prompt): > + vim9 g:helptoc.shell_prompt = '^\$\s' + +<level_indicator This key's value controls the level indicator used in + standardized tables of contents. The default is '| ' + (i.e., a vertical bar and a space), but may be changed to + whatever you want. For example, for a broken bar and space: > + vim9 g:helptoc.level_indicator = '¦ ' +< +popup_border By default, the table of contents border will appear above, + right, below, and left of the popup window. If you prefer + not to have the border on the right and left (for example + only), you can achieve that with: > + vim9 g:helptoc.popup_border = [1, 0, 1, 0] +<popup_borderchars + The default border characters for the table of contents popup + window is the list ['─', '│', '─', '│', '┌', '┐', '┘', '└']. + There's nothing wrong with those box drawing characters, + though, for example, if you wanted a border that only uses + ASCII characters, you could make the border spaces only: > + vim9 g:helptoc.popup_borderchars = [' '] +<popup_borderhighlight + The default border highlight group is Normal. You can change + that, perhaps in combination with popup_borderchars, above, + to create a very clearly prominent border. For example, if + the popup_borderchars are made [' '], like above, the border + could be made a solid colour different to the background + with: > + vim9 g:helptoc.popup_borderhighlight = ['Cursor'] + +< Note: Choosing a highlight group that persists when + colorschemes change may be a good idea if you + do choose to customize this. + +popup_drag By default, table of contents popup windows may be dragged + with a mouse. If you want to prevent that from happening, + for whatever reason, you may deactivate it with: > + vim9 g:helptoc.popup_drag = false +< +popup_close Table of contents popups have "none" as the default setting + for this option. If you use a mouse, you may want either + to have the option to close popup windows by clicking on them + or to have a clickable "X" in the top right corner. For the + former, use "click", and for the latter, use "button", e.g.: > + vim9 g:helptoc.popup_close = "button" +<popup_scrollbar + No scrollbar is provided on helptoc popups by default. If you + do want scrollbars (which may be useful as an indicator of how + far through the table of contents you are, not just for using + with a mouse) you may choose to have them with: > + vim9 g:helptoc.popup_scrollbar = true +< +NOTE: Information about the "popup_*" options, above, relate to popup options, +which are explained at the 'second argument' part of |popup_create-arguments|. + + +6. LIMITATIONS *HelpToc-limitations* + +- The help filetype may have edge case formatting patterns. Those may result + in some "headings" not being identified and/or may impact the heading levels + of entries in the table of contents itself. +- Terminal window table of contents may not be active (insofar as jumping to + entries going to the Vim terminal's related command line). For example, if + Vim's terminal is set to Windows PowerShell Core, the table of contents will + display successfully, though the entries go nowhere when Enter, J, or K are + entered on them. +- The tex filetype may have variable sectioning commands depending on the + document class. Consequently, some compromises are made, though they should + have minimal impact. Specifically: + * In instances where \part{} and \chapter{} appear in the same buffer, they + will both present at the top level in the table of contents. This should + be a minor matter because, in many instances, chapters will be in a + separate document using \include{}. + * An article or beamer \documentclass without a \part{} (or any document + with neither any \part{} nor any \chapter{} command) will have no content + at level 1. Consequently, its table of contents entries will all appear + preceded by at least one "| " (by default) because its headings start at + level 2 (presuming \section{} is present). +- The vim filetype is only supported where numbered fold markers are applied. + This is intentional (including not handling unnumbered markers, which, when + used in combination with numbered ones, may be used for folding comments). + helptoc.vim itself provides an exemplar of how to use numbered fold markers, + not only for folds, but to support generating a useful table of contents + using :HelpToc. + +============================================================================== +vim:tw=78:ts=8:fo=tcq2:ft=help: diff --git a/uvim/runtime/pack/dist/opt/helptoc/doc/tags b/uvim/runtime/pack/dist/opt/helptoc/doc/tags new file mode 100644 index 0000000000..cd62dec896 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/helptoc/doc/tags @@ -0,0 +1,16 @@ +HelpToc-:HelpToc helptoc.txt /*HelpToc-:HelpToc* +HelpToc-asciidoc-filetype helptoc.txt /*HelpToc-asciidoc-filetype* +HelpToc-configuration helptoc.txt /*HelpToc-configuration* +HelpToc-details helptoc.txt /*HelpToc-details* +HelpToc-html-filetype helptoc.txt /*HelpToc-html-filetype* +HelpToc-limitations helptoc.txt /*HelpToc-limitations* +HelpToc-man-filetype helptoc.txt /*HelpToc-man-filetype* +HelpToc-markdown-filetype helptoc.txt /*HelpToc-markdown-filetype* +HelpToc-overview helptoc.txt /*HelpToc-overview* +HelpToc-standardized-toc helptoc.txt /*HelpToc-standardized-toc* +HelpToc-terminal-buftype helptoc.txt /*HelpToc-terminal-buftype* +HelpToc-tex-filetype helptoc.txt /*HelpToc-tex-filetype* +HelpToc-types helptoc.txt /*HelpToc-types* +HelpToc-vim-filetype helptoc.txt /*HelpToc-vim-filetype* +HelpToc-xhtml-filetype helptoc.txt /*HelpToc-xhtml-filetype* +helptoc.txt helptoc.txt /*helptoc.txt* diff --git a/uvim/runtime/pack/dist/opt/helptoc/plugin/helptoc.vim b/uvim/runtime/pack/dist/opt/helptoc/plugin/helptoc.vim new file mode 100644 index 0000000000..4fb3bc393d --- /dev/null +++ b/uvim/runtime/pack/dist/opt/helptoc/plugin/helptoc.vim @@ -0,0 +1,5 @@ +vim9script noclear + +import autoload '../autoload/helptoc.vim' + +command -bar HelpToc helptoc.Open() diff --git a/uvim/runtime/pack/dist/opt/hlyank/plugin/hlyank.vim b/uvim/runtime/pack/dist/opt/hlyank/plugin/hlyank.vim new file mode 100644 index 0000000000..4b568fae24 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/hlyank/plugin/hlyank.vim @@ -0,0 +1,39 @@ +vim9script + +# Highlight Yank plugin +# Last Change: 2025 Mar 22 + +def HighlightedYank() + + var hlgroup = get(g:, "hlyank_hlgroup", "IncSearch") + var duration = min([get(g:, "hlyank_duration", 300), 3000]) + var in_visual = get(g:, "hlyank_invisual", true) + + if v:event.operator ==? 'y' + if !in_visual && visualmode() != null_string + visualmode(1) + return + endif + # if clipboard has autoselect (default on linux) exiting from Visual with + # ESC generates bogus event and this highlights previous yank + if &clipboard =~ 'autoselect' && v:event.regname == "*" && v:event.visual + return + endif + var [beg, end] = [getpos("'["), getpos("']")] + var type = v:event.regtype ?? 'v' + var pos = getregionpos(beg, end, {type: type, exclusive: false}) + var m = matchaddpos(hlgroup, pos->mapnew((_, v) => { + var col_beg = v[0][2] + v[0][3] + var col_end = v[1][2] + v[1][3] + 1 + return [v[0][1], col_beg, col_end - col_beg] + })) + var winid = win_getid() + timer_start(duration, (_) => m->matchdelete(winid)) + endif +enddef + +augroup hlyank + autocmd! + autocmd TextYankPost * HighlightedYank() +augroup END +# vim:sts=2:sw=2:et: diff --git a/uvim/runtime/pack/dist/opt/justify/plugin/justify.vim b/uvim/runtime/pack/dist/opt/justify/plugin/justify.vim new file mode 100644 index 0000000000..57be790423 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/justify/plugin/justify.vim @@ -0,0 +1,316 @@ +" Function to left and right align text. +" +" Written by: Preben "Peppe" Guldberg <c928400@student.dtu.dk> +" Created: 980806 14:13 (or around that time anyway) +" Revised: 001103 00:36 (See "Revisions" below) + + +" function Justify( [ textwidth [, maxspaces [, indent] ] ] ) +" +" Justify() will left and right align a line by filling in an +" appropriate amount of spaces. Extra spaces are added to existing +" spaces starting from the right side of the line. As an example, the +" following documentation has been justified. +" +" The function takes the following arguments: + +" textwidth argument +" ------------------ +" If not specified, the value of the 'textwidth' option is used. If +" 'textwidth' is zero a value of 80 is used. +" +" Additionally the arguments 'tw' and '' are accepted. The value of +" 'textwidth' will be used. These are handy, if you just want to specify +" the maxspaces argument. + +" maxspaces argument +" ------------------ +" If specified, alignment will only be done, if the longest space run +" after alignment is no longer than maxspaces. +" +" An argument of '' is accepted, should the user like to specify all +" arguments. +" +" To aid user defined commands, negative values are accepted aswell. +" Using a negative value specifies the default behaviour: any length of +" space runs will be used to justify the text. + +" indent argument +" --------------- +" This argument specifies how a line should be indented. The default is +" to keep the current indentation. +" +" Negative values: Keep current amount of leading whitespace. +" Positive values: Indent all lines with leading whitespace using this +" amount of whitespace. +" +" Note that the value 0, needs to be quoted as a string. This value +" leads to a left flushed text. +" +" Additionally units of 'shiftwidth'/'sw' and 'tabstop'/'ts' may be +" added. In this case, if the value of indent is positive, the amount of +" whitespace to be added will be multiplied by the value of the +" 'shiftwidth' and 'tabstop' settings. If these units are used, the +" argument must be given as a string, eg. Justify('','','2sw'). +" +" If the values of 'sw' or 'tw' are negative, they are treated as if +" they were 0, which means that the text is flushed left. There is no +" check if a negative number prefix is used to change the sign of a +" negative 'sw' or 'ts' value. +" +" As with the other arguments, '' may be used to get the default +" behaviour. + + +" Notes: +" +" If the line, adjusted for space runs and leading/trailing whitespace, +" is wider than the used textwidth, the line will be left untouched (no +" whitespace removed). This should be equivalent to the behaviour of +" :left, :right and :center. +" +" If the resulting line is shorter than the used textwidth it is left +" untouched. +" +" All space runs in the line are truncated before the alignment is +" carried out. +" +" If you have set 'noexpandtab', :retab! is used to replace space runs +" with whitespace using the value of 'tabstop'. This should be +" conformant with :left, :right and :center. +" +" If joinspaces is set, an extra space is added after '.', '?' and '!'. +" If 'cpoptions' include 'j', extra space is only added after '.'. +" (This may on occasion conflict with maxspaces.) + + +" Related mappings: +" +" Mappings that will align text using the current text width, using at +" most four spaces in a space run and keeping current indentation. +nmap _j :%call Justify('tw',4)<CR> +vmap _j :call Justify('tw',4)<CR> +" +" Mappings that will remove space runs and format lines (might be useful +" prior to aligning the text). +nmap ,gq :%s/\s\+/ /g<CR>gq1G +vmap ,gq :s/\s\+/ /g<CR>gvgq + + +" User defined command: +" +" The following is an ex command that works as a shortcut to the Justify +" function. Arguments to Justify() can be added after the command. +com! -range -nargs=* Justify <line1>,<line2>call Justify(<f-args>) +" +" The following commands are all equivalent: +" +" 1. Simplest use of Justify(): +" :call Justify() +" :Justify +" +" 2. The _j mapping above via the ex command: +" :%Justify tw 4 +" +" 3. Justify visualised text at 72nd column while indenting all +" previously indented text two shiftwidths +" :'<,'>call Justify(72,'','2sw') +" :'<,'>Justify 72 -1 2sw +" +" This documentation has been justified using the following command: +":se et|kz|1;/^" function Justify(/+,'z-g/^" /s/^" //|call Justify(70,3)|s/^/" / + +" Revisions: +" 001103: If 'joinspaces' was set, calculations could be wrong. +" Tabs at start of line could also lead to errors. +" Use setline() instead of "exec 's/foo/bar/' - safer. +" Cleaned up the code a bit. +" +" Todo: Convert maps to the new script specific form + +" Error function +function! Justify_error(message) + echohl Error + echo "Justify([tw, [maxspaces [, indent]]]): " . a:message + echohl None +endfunction + + +" Now for the real thing +function! Justify(...) range + + if a:0 > 3 + call Justify_error("Too many arguments (max 3)") + return 1 + endif + + " Set textwidth (accept 'tw' and '' as arguments) + if a:0 >= 1 + if a:1 =~ '^\(tw\)\=$' + let tw = &tw + elseif a:1 =~ '^\d\+$' + let tw = a:1 + else + call Justify_error("tw must be a number (>0), '' or 'tw'") + return 2 + endif + else + let tw = &tw + endif + if tw == 0 + let tw = 80 + endif + + " Set maximum number of spaces between WORDs + if a:0 >= 2 + if a:2 == '' + let maxspaces = tw + elseif a:2 =~ '^-\d\+$' + let maxspaces = tw + elseif a:2 =~ '^\d\+$' + let maxspaces = a:2 + else + call Justify_error("maxspaces must be a number or ''") + return 3 + endif + else + let maxspaces = tw + endif + if maxspaces <= 1 + call Justify_error("maxspaces should be larger than 1") + return 4 + endif + + " Set the indentation style (accept sw and ts units) + let indent_fix = '' + if a:0 >= 3 + if (a:3 == '') || a:3 =~ '^-[1-9]\d*\(shiftwidth\|sw\|tabstop\|ts\)\=$' + let indent = -1 + elseif a:3 =~ '^-\=0\(shiftwidth\|sw\|tabstop\|ts\)\=$' + let indent = 0 + elseif a:3 =~ '^\d\+\(shiftwidth\|sw\|tabstop\|ts\)\=$' + let indent = substitute(a:3, '\D', '', 'g') + elseif a:3 =~ '^\(shiftwidth\|sw\|tabstop\|ts\)$' + let indent = 1 + else + call Justify_error("indent: a number with 'sw'/'ts' unit") + return 5 + endif + if indent >= 0 + while indent > 0 + let indent_fix = indent_fix . ' ' + let indent = indent - 1 + endwhile + let indent_sw = 0 + if a:3 =~ '\(shiftwidth\|sw\)' + let indent_sw = &sw + elseif a:3 =~ '\(tabstop\|ts\)' + let indent_sw = &ts + endif + let indent_fix2 = '' + while indent_sw > 0 + let indent_fix2 = indent_fix2 . indent_fix + let indent_sw = indent_sw - 1 + endwhile + let indent_fix = indent_fix2 + endif + else + let indent = -1 + endif + + " Avoid substitution reports + let save_report = &report + set report=1000000 + + " Check 'joinspaces' and 'cpo' + if &js == 1 + if &cpo =~ 'j' + let join_str = '\(\. \)' + else + let join_str = '\([.!?!] \)' + endif + endif + + let cur = a:firstline + while cur <= a:lastline + + let str_orig = getline(cur) + let save_et = &et + set et + exec cur . "retab" + let &et = save_et + let str = getline(cur) + + let indent_str = indent_fix + let indent_n = strlen(indent_str) + " Shall we remember the current indentation + if indent < 0 + let indent_orig = matchstr(str_orig, '^\s*') + if strlen(indent_orig) > 0 + let indent_str = indent_orig + let indent_n = strlen(matchstr(str, '^\s*')) + endif + endif + + " Trim trailing, leading and running whitespace + let str = substitute(str, '\s\+$', '', '') + let str = substitute(str, '^\s\+', '', '') + let str = substitute(str, '\s\+', ' ', 'g') + let str_n = strdisplaywidth(str) + + " Possible addition of space after punctuation + if exists("join_str") + let str = substitute(str, join_str, '\1 ', 'g') + endif + let join_n = strdisplaywidth(str) - str_n + + " Can extraspaces be added? + " Note that str_n may be less than strlen(str) [joinspaces above] + if strdisplaywidth(str) <= tw - indent_n && str_n > 0 + " How many spaces should be added + let s_add = tw - str_n - indent_n - join_n + let s_nr = strlen(substitute(str, '\S', '', 'g') ) - join_n + let s_dup = s_add / s_nr + let s_mod = s_add % s_nr + + " Test if the changed line fits with tw + if 0 <= (str_n + (maxspaces - 1)*s_nr + indent_n) - tw + + " Duplicate spaces + while s_dup > 0 + let str = substitute(str, '\( \+\)', ' \1', 'g') + let s_dup = s_dup - 1 + endwhile + + " Add extra spaces from the end + while s_mod > 0 + let str = substitute(str, '\(\(\s\+\S\+\)\{' . s_mod . '}\)$', ' \1', '') + let s_mod = s_mod - 1 + endwhile + + " Indent the line + if indent_n > 0 + let str = substitute(str, '^', indent_str, '' ) + endif + + " Replace the line + call setline(cur, str) + + " Convert to whitespace + if &et == 0 + exec cur . 'retab!' + endif + + endif " Change of line + endif " Possible change + + let cur = cur + 1 + endwhile + + norm ^ + + let &report = save_report + +endfunction + +" EOF vim: tw=78 ts=8 sw=4 sts=4 noet ai diff --git a/uvim/runtime/pack/dist/opt/matchit/autoload/matchit.vim b/uvim/runtime/pack/dist/opt/matchit/autoload/matchit.vim new file mode 100644 index 0000000000..1e660ffd3e --- /dev/null +++ b/uvim/runtime/pack/dist/opt/matchit/autoload/matchit.vim @@ -0,0 +1,817 @@ +" matchit.vim: (global plugin) Extended "%" matching +" autload script of matchit plugin, see ../plugin/matchit.vim +" Last Change: Jan 09, 2026 + +" Neovim does not support scriptversion +if has("vimscript-4") + scriptversion 4 +endif + +let s:last_mps = "" +let s:last_words = ":" +let s:patBR = "" + +let s:save_cpo = &cpo +set cpo&vim + +" Auto-complete mappings: (not yet "ready for prime time") +" TODO Read :help write-plugin for the "right" way to let the user +" specify a key binding. +" let g:match_auto = '<C-]>' +" let g:match_autoCR = '<C-CR>' +" if exists("g:match_auto") +" execute "inoremap " . g:match_auto . ' x<Esc>"=<SID>Autocomplete()<CR>Pls' +" endif +" if exists("g:match_autoCR") +" execute "inoremap " . g:match_autoCR . ' <CR><C-R>=<SID>Autocomplete()<CR>' +" endif +" if exists("g:match_gthhoh") +" execute "inoremap " . g:match_gthhoh . ' <C-O>:call <SID>Gthhoh()<CR>' +" endif " gthhoh = "Get the heck out of here!" + +let s:notslash = '\\\@1<!\%(\\\\\)*' + +function s:RestoreOptions() + " In s:CleanUp(), :execute "set" restore_options . + let restore_options = "" + if get(b:, 'match_ignorecase', &ic) != &ic + let restore_options ..= (&ic ? " " : " no") .. "ignorecase" + let &ignorecase = b:match_ignorecase + endif + if &ve != '' + let restore_options = " ve=" .. &ve .. restore_options + set ve= + endif + if &smartcase + let restore_options = " smartcase " .. restore_options + set nosmartcase + endif + return restore_options +endfunction + +function matchit#Match_wrapper(word, forward, mode) range + let restore_options = s:RestoreOptions() + " In s:CleanUp(), we may need to check whether the cursor moved forward. + let startpos = [line("."), col(".")] + " if a count has been applied, use the default [count]% mode (see :h N%) + if v:count + exe "normal! " .. v:count .. "%" + return s:CleanUp(restore_options, a:mode, startpos) + end + if a:mode =~# "v" && mode(1) =~# 'ni' + exe "norm! gv" + elseif a:mode == "o" && mode(1) !~# '[vV]' + exe "norm! v" + " If this function was called from Visual mode, make sure that the cursor + " is at the correct end of the Visual range: + elseif a:mode == "v" + execute "normal! gv\<Esc>" + let startpos = [line("."), col(".")] + endif + + " Check for custom match function hook + if exists("b:match_function") + try + let result = call(b:match_function, [a:forward]) + if !empty(result) + call cursor(result) + return s:CleanUp(restore_options, a:mode, startpos) + endif + catch /.*/ + if exists("b:match_debug") + echohl WarningMsg + echom 'matchit: b:match_function error: ' .. v:exception + echohl NONE + endif + return s:CleanUp(restore_options, a:mode, startpos) + endtry + " Empty result: fall through to regular matching + endif + + " First step: if not already done, set the script variables + " s:do_BR flag for whether there are backrefs + " s:pat parsed version of b:match_words + " s:all regexp based on s:pat and the default groups + if !exists("b:match_words") || b:match_words == "" + let match_words = "" + elseif b:match_words =~ ":" + let match_words = b:match_words + else + " Allow b:match_words = "GetVimMatchWords()" . + execute "let match_words =" b:match_words + endif +" Thanks to Preben "Peppe" Guldberg and Bram Moolenaar for this suggestion! + if (match_words != s:last_words) || (&mps != s:last_mps) + \ || exists("b:match_debug") + let s:last_mps = &mps + " quote the special chars in 'matchpairs', replace [,:] with \| and then + " append the builtin pairs (/*, */, #if, #ifdef, #ifndef, #else, #elif, + " #elifdef, #elifndef, #endif) + let default = escape(&mps, '[$^.*~\\/?]') .. (strlen(&mps) ? "," : "") .. + \ '\/\*:\*\/,#\s*if\%(n\=def\)\=:#\s*else\>:#\s*elif\%(n\=def\)\=\>:#\s*endif\>' + " s:all = pattern with all the keywords + let match_words = s:Append(match_words, default) + let s:last_words = match_words + if match_words !~ s:notslash .. '\\\d' + let s:do_BR = 0 + let s:pat = match_words + else + let s:do_BR = 1 + let s:pat = s:ParseWords(match_words) + endif + let s:all = substitute(s:pat, s:notslash .. '\zs[,:]\+', '\\|', 'g') + " un-escape \, and \: to , and : + let s:all = substitute(s:all, s:notslash .. '\zs\\\(:\|,\)', '\1', 'g') + " Just in case there are too many '\(...)' groups inside the pattern, make + " sure to use \%(...) groups, so that error E872 can be avoided + let s:all = substitute(s:all, '\\(', '\\%(', 'g') + let s:all = '\%(' .. s:all .. '\)' + if exists("b:match_debug") + let b:match_pat = s:pat + endif + " Reconstruct the version with unresolved backrefs. + let s:patBR = substitute(match_words .. ',', + \ s:notslash .. '\zs[,:]*,[,:]*', ',', 'g') + let s:patBR = substitute(s:patBR, s:notslash .. '\zs:\{2,}', ':', 'g') + " un-escape \, to , + let s:patBR = substitute(s:patBR, '\\,', ',', 'g') + endif + + " Second step: set the following local variables: + " matchline = line on which the cursor started + " curcol = number of characters before match + " prefix = regexp for start of line to start of match + " suffix = regexp for end of match to end of line + " Require match to end on or after the cursor and prefer it to + " start on or before the cursor. + let matchline = getline(startpos[0]) + if a:word != '' + " word given + if a:word !~ s:all + echohl WarningMsg|echo 'Missing rule for word:"'.a:word.'"'|echohl NONE + return s:CleanUp(restore_options, a:mode, startpos) + endif + let matchline = a:word + let curcol = 0 + let prefix = '^\%(' + let suffix = '\)$' + " Now the case when "word" is not given + else " Find the match that ends on or after the cursor and set curcol. + let regexp = s:Wholematch(matchline, s:all, startpos[1]-1) + let curcol = match(matchline, regexp) + " If there is no match, give up. + if curcol == -1 + return s:CleanUp(restore_options, a:mode, startpos) + endif + let endcol = matchend(matchline, regexp) + let suf = strlen(matchline) - endcol + let prefix = (curcol ? '^.*\%' .. (curcol + 1) .. 'c\%(' : '^\%(') + let suffix = (suf ? '\)\%' .. (endcol + 1) .. 'c.*$' : '\)$') + endif + if exists("b:match_debug") + let b:match_match = matchstr(matchline, regexp) + let b:match_col = curcol+1 + endif + + " Third step: Find the group and single word that match, and the original + " (backref) versions of these. Then, resolve the backrefs. + " Set the following local variable: + " group = colon-separated list of patterns, one of which matches + " = ini:mid:fin or ini:fin + " + " Now, set group and groupBR to the matching group: 'if:endif' or + " 'while:endwhile' or whatever. A bit of a kluge: s:Choose() returns + " group . "," . groupBR, and we pick it apart. + let group = s:Choose(s:pat, matchline, ",", ":", prefix, suffix, s:patBR) + let i = matchend(group, s:notslash .. ",") + let groupBR = strpart(group, i) + let group = strpart(group, 0, i-1) + " Now, matchline =~ prefix . substitute(group,':','\|','g') . suffix + if s:do_BR " Do the hard part: resolve those backrefs! + let group = s:InsertRefs(groupBR, prefix, group, suffix, matchline) + endif + if exists("b:match_debug") + let b:match_wholeBR = groupBR + let i = matchend(groupBR, s:notslash .. ":") + let b:match_iniBR = strpart(groupBR, 0, i-1) + endif + + " Fourth step: Set the arguments for searchpair(). + let i = matchend(group, s:notslash .. ":") + let j = matchend(group, '.*' .. s:notslash .. ":") + let ini = strpart(group, 0, i-1) + let mid = substitute(strpart(group, i,j-i-1), s:notslash .. '\zs:', '\\|', 'g') + let fin = strpart(group, j) + "Un-escape the remaining , and : characters. + let ini = substitute(ini, s:notslash .. '\zs\\\(:\|,\)', '\1', 'g') + let mid = substitute(mid, s:notslash .. '\zs\\\(:\|,\)', '\1', 'g') + let fin = substitute(fin, s:notslash .. '\zs\\\(:\|,\)', '\1', 'g') + " searchpair() requires that these patterns avoid \(\) groups. + let ini = substitute(ini, s:notslash .. '\zs\\(', '\\%(', 'g') + let mid = substitute(mid, s:notslash .. '\zs\\(', '\\%(', 'g') + let fin = substitute(fin, s:notslash .. '\zs\\(', '\\%(', 'g') + " Set mid. This is optimized for readability, not micro-efficiency! + if a:forward && matchline =~ prefix .. fin .. suffix + \ || !a:forward && matchline =~ prefix .. ini .. suffix + let mid = "" + endif + " Set flag. This is optimized for readability, not micro-efficiency! + if a:forward && matchline =~ prefix .. fin .. suffix + \ || !a:forward && matchline !~ prefix .. ini .. suffix + let flag = "bW" + else + let flag = "W" + endif + " Set skip. + if exists("b:match_skip") + let skip = b:match_skip + elseif exists("b:match_comment") " backwards compatibility and testing! + let skip = "r:" .. b:match_comment + else + let skip = 's:comment\|string' + endif + let skip = s:ParseSkip(skip) + if exists("b:match_debug") + let b:match_ini = ini + let b:match_tail = (strlen(mid) ? mid .. '\|' : '') .. fin + endif + + " Fifth step: actually start moving the cursor and call searchpair(). + " Later, :execute restore_cursor to get to the original screen. + let view = winsaveview() + call cursor(0, curcol + 1) + if skip =~ 'synID' && !(has("syntax") && exists("g:syntax_on")) + let skip = "0" + else + execute "if " .. skip .. "| let skip = '0' | endif" + endif + let sp_return = searchpair(ini, mid, fin, flag, skip) + if &selection isnot# 'inclusive' && a:mode == 'v' + " move cursor one pos to the right, because selection is not inclusive + " add virtualedit=onemore, to make it work even when the match ends the + " line + if !(col('.') < col('$')-1) + let eolmark=1 " flag to set a mark on eol (since we cannot move there) + endif + norm! l + endif + let final_position = "call cursor(" .. line(".") .. "," .. col(".") .. ")" + " Restore cursor position and original screen. + call winrestview(view) + normal! m' + if sp_return > 0 + execute final_position + endif + if exists('eolmark') && eolmark + call setpos("''", [0, line('.'), col('$'), 0]) " set mark on the eol + endif + return s:CleanUp(restore_options, a:mode, startpos, mid .. '\|' .. fin) +endfun + +" Restore options and do some special handling for Operator-pending mode. +" The optional argument is the tail of the matching group. +fun! s:CleanUp(options, mode, startpos, ...) + if strlen(a:options) + execute "set" a:options + endif + " Open folds, if appropriate. + if a:mode != "o" + if &foldopen =~ "percent" + normal! zv + endif + " In Operator-pending mode, we want to include the whole match + " (for example, d%). + " This is only a problem if we end up moving in the forward direction. + elseif (a:startpos[0] < line(".")) || + \ (a:startpos[0] == line(".") && a:startpos[1] < col(".")) + if a:0 + " Check whether the match is a single character. If not, move to the + " end of the match. + let matchline = getline(".") + let currcol = col(".") + let regexp = s:Wholematch(matchline, a:1, currcol-1) + let endcol = matchend(matchline, regexp) + if endcol > currcol " This is NOT off by one! + call cursor(0, endcol) + endif + endif " a:0 + endif " a:mode != "o" && etc. + return 0 +endfun + +" Example (simplified HTML patterns): if +" a:groupBR = '<\(\k\+\)>:</\1>' +" a:prefix = '^.\{3}\(' +" a:group = '<\(\k\+\)>:</\(\k\+\)>' +" a:suffix = '\).\{2}$' +" a:matchline = "123<tag>12" or "123</tag>12" +" then extract "tag" from a:matchline and return "<tag>:</tag>" . +fun! s:InsertRefs(groupBR, prefix, group, suffix, matchline) + if a:matchline !~ a:prefix .. + \ substitute(a:group, s:notslash .. '\zs:', '\\|', 'g') .. a:suffix + return a:group + endif + let i = matchend(a:groupBR, s:notslash .. ':') + let ini = strpart(a:groupBR, 0, i-1) + let tailBR = strpart(a:groupBR, i) + let word = s:Choose(a:group, a:matchline, ":", "", a:prefix, a:suffix, + \ a:groupBR) + let i = matchend(word, s:notslash .. ":") + let wordBR = strpart(word, i) + let word = strpart(word, 0, i-1) + " Now, a:matchline =~ a:prefix . word . a:suffix + if wordBR != ini + let table = s:Resolve(ini, wordBR, "table") + else + let table = "" + let d = 0 + while d < 10 + if tailBR =~ s:notslash .. '\\' .. d + let table = table .. d + else + let table = table .. "-" + endif + let d = d + 1 + endwhile + endif + let d = 9 + while d + if table[d] != "-" + let backref = substitute(a:matchline, a:prefix .. word .. a:suffix, + \ '\' .. table[d], "") + " Are there any other characters that should be escaped? + let backref = escape(backref, '*,:') + execute s:Ref(ini, d, "start", "len") + let ini = strpart(ini, 0, start) .. backref .. strpart(ini, start+len) + let tailBR = substitute(tailBR, s:notslash .. '\zs\\' .. d, + \ escape(backref, '\\&'), 'g') + endif + let d = d-1 + endwhile + if exists("b:match_debug") + if s:do_BR + let b:match_table = table + let b:match_word = word + else + let b:match_table = "" + let b:match_word = "" + endif + endif + return ini .. ":" .. tailBR +endfun + +" String append item2 to item and add ',' in between items +fun! s:Append(item, item2) + if a:item == '' + return a:item2 + endif + " there is already a trailing comma, don't add another one + if a:item[-1:] == ',' + return a:item .. a:item2 + endif + return a:item .. ',' .. a:item2 +endfun + +" Input a comma-separated list of groups with backrefs, such as +" a:groups = '\(foo\):end\1,\(bar\):end\1' +" and return a comma-separated list of groups with backrefs replaced: +" return '\(foo\):end\(foo\),\(bar\):end\(bar\)' +fun! s:ParseWords(groups) + let groups = substitute(a:groups .. ",", s:notslash .. '\zs[,:]*,[,:]*', ',', 'g') + let groups = substitute(groups, s:notslash .. '\zs:\{2,}', ':', 'g') + let parsed = "" + while groups =~ '[^,:]' + let i = matchend(groups, s:notslash .. ':') + let j = matchend(groups, s:notslash .. ',') + let ini = strpart(groups, 0, i-1) + let tail = strpart(groups, i, j-i-1) .. ":" + let groups = strpart(groups, j) + let parsed = parsed .. ini + let i = matchend(tail, s:notslash .. ':') + while i != -1 + " In 'if:else:endif', ini='if' and word='else' and then word='endif'. + let word = strpart(tail, 0, i-1) + let tail = strpart(tail, i) + let i = matchend(tail, s:notslash .. ':') + let parsed = parsed .. ":" .. s:Resolve(ini, word, "word") + endwhile " Now, tail has been used up. + let parsed = parsed .. "," + endwhile " groups =~ '[^,:]' + let parsed = substitute(parsed, ',$', '', '') + return parsed +endfun + +" TODO I think this can be simplified and/or made more efficient. +" TODO What should I do if a:start is out of range? +" Return a regexp that matches all of a:string, such that +" matchstr(a:string, regexp) represents the match for a:pat that starts +" as close to a:start as possible, before being preferred to after, and +" ends after a:start . +" Usage: +" let regexp = s:Wholematch(getline("."), 'foo\|bar', col(".")-1) +" let i = match(getline("."), regexp) +" let j = matchend(getline("."), regexp) +" let match = matchstr(getline("."), regexp) +fun! s:Wholematch(string, pat, start) + let group = '\%(' .. a:pat .. '\)' + let prefix = (a:start ? '\(^.*\%<' .. (a:start + 2) .. 'c\)\zs' : '^') + let len = strlen(a:string) + let suffix = (a:start+1 < len ? '\(\%>' .. (a:start+1) .. 'c.*$\)\@=' : '$') + if a:string !~ prefix .. group .. suffix + let prefix = '' + endif + return prefix .. group .. suffix +endfun + +" No extra arguments: s:Ref(string, d) will +" find the d'th occurrence of '\(' and return it, along with everything up +" to and including the matching '\)'. +" One argument: s:Ref(string, d, "start") returns the index of the start +" of the d'th '\(' and any other argument returns the length of the group. +" Two arguments: s:Ref(string, d, "foo", "bar") returns a string to be +" executed, having the effect of +" :let foo = s:Ref(string, d, "start") +" :let bar = s:Ref(string, d, "len") +fun! s:Ref(string, d, ...) + let len = strlen(a:string) + if a:d == 0 + let start = 0 + else + let cnt = a:d + let match = a:string + while cnt + let cnt = cnt - 1 + let index = matchend(match, s:notslash .. '\\(') + if index == -1 + return "" + endif + let match = strpart(match, index) + endwhile + let start = len - strlen(match) + if a:0 == 1 && a:1 == "start" + return start - 2 + endif + let cnt = 1 + while cnt + let index = matchend(match, s:notslash .. '\\(\|\\)') - 1 + if index == -2 + return "" + endif + " Increment if an open, decrement if a ')': + let cnt = cnt + (match[index]=="(" ? 1 : -1) " ')' + let match = strpart(match, index+1) + endwhile + let start = start - 2 + let len = len - start - strlen(match) + endif + if a:0 == 1 + return len + elseif a:0 == 2 + return "let " .. a:1 .. "=" .. start .. "| let " .. a:2 .. "=" .. len + else + return strpart(a:string, start, len) + endif +endfun + +" Count the number of disjoint copies of pattern in string. +" If the pattern is a literal string and contains no '0' or '1' characters +" then s:Count(string, pattern, '0', '1') should be faster than +" s:Count(string, pattern). +fun! s:Count(string, pattern, ...) + let pat = escape(a:pattern, '\\') + if a:0 > 1 + let foo = substitute(a:string, '[^' .. a:pattern .. ']', "a:1", "g") + let foo = substitute(a:string, pat, a:2, "g") + let foo = substitute(foo, '[^' .. a:2 .. ']', "", "g") + return strlen(foo) + endif + let result = 0 + let foo = a:string + let index = matchend(foo, pat) + while index != -1 + let result = result + 1 + let foo = strpart(foo, index) + let index = matchend(foo, pat) + endwhile + return result +endfun + +" s:Resolve('\(a\)\(b\)', '\(c\)\2\1\1\2') should return table.word, where +" word = '\(c\)\(b\)\(a\)\3\2' and table = '-32-------'. That is, the first +" '\1' in target is replaced by '\(a\)' in word, table[1] = 3, and this +" indicates that all other instances of '\1' in target are to be replaced +" by '\3'. The hard part is dealing with nesting... +" Note that ":" is an illegal character for source and target, +" unless it is preceded by "\". +fun! s:Resolve(source, target, output) + let word = a:target + let i = matchend(word, s:notslash .. '\\\d') - 1 + let table = "----------" + while i != -2 " There are back references to be replaced. + let d = word[i] + let backref = s:Ref(a:source, d) + " The idea is to replace '\d' with backref. Before we do this, + " replace any \(\) groups in backref with :1, :2, ... if they + " correspond to the first, second, ... group already inserted + " into backref. Later, replace :1 with \1 and so on. The group + " number w+b within backref corresponds to the group number + " s within a:source. + " w = number of '\(' in word before the current one + let w = s:Count( + \ substitute(strpart(word, 0, i-1), '\\\\', '', 'g'), '\(', '1') + let b = 1 " number of the current '\(' in backref + let s = d " number of the current '\(' in a:source + while b <= s:Count(substitute(backref, '\\\\', '', 'g'), '\(', '1') + \ && s < 10 + if table[s] == "-" + if w + b < 10 + " let table[s] = w + b + let table = strpart(table, 0, s) .. (w+b) .. strpart(table, s+1) + endif + let b = b + 1 + let s = s + 1 + else + execute s:Ref(backref, b, "start", "len") + let ref = strpart(backref, start, len) + let backref = strpart(backref, 0, start) .. ":" .. table[s] + \ .. strpart(backref, start+len) + let s = s + s:Count(substitute(ref, '\\\\', '', 'g'), '\(', '1') + endif + endwhile + let word = strpart(word, 0, i-1) .. backref .. strpart(word, i+1) + let i = matchend(word, s:notslash .. '\\\d') - 1 + endwhile + let word = substitute(word, s:notslash .. '\zs:', '\\', 'g') + if a:output == "table" + return table + elseif a:output == "word" + return word + else + return table .. word + endif +endfun + +" Assume a:comma = ",". Then the format for a:patterns and a:1 is +" a:patterns = "<pat1>,<pat2>,..." +" a:1 = "<alt1>,<alt2>,..." +" If <patn> is the first pattern that matches a:string then return <patn> +" if no optional arguments are given; return <patn>,<altn> if a:1 is given. +fun! s:Choose(patterns, string, comma, branch, prefix, suffix, ...) + let tail = (a:patterns =~ a:comma .. "$" ? a:patterns : a:patterns .. a:comma) + let i = matchend(tail, s:notslash .. a:comma) + if a:0 + let alttail = (a:1 =~ a:comma .. "$" ? a:1 : a:1 .. a:comma) + let j = matchend(alttail, s:notslash .. a:comma) + endif + let current = strpart(tail, 0, i-1) + if a:branch == "" + let currpat = current + else + let currpat = substitute(current, s:notslash .. a:branch, '\\|', 'g') + endif + " un-escape \, and \: to , and : + let currpat = substitute(currpat, s:notslash .. '\zs\\\(:\|,\)', '\1', 'g') + while a:string !~ a:prefix .. currpat .. a:suffix + let tail = strpart(tail, i) + let i = matchend(tail, s:notslash .. a:comma) + if i == -1 + return -1 + endif + let current = strpart(tail, 0, i-1) + if a:branch == "" + let currpat = current + else + let currpat = substitute(current, s:notslash .. a:branch, '\\|', 'g') + endif + " un-escape \, and \: to , and : + let currpat = substitute(currpat, s:notslash .. '\zs\\\(:\|,\)', '\1', 'g') + if a:0 + let alttail = strpart(alttail, j) + let j = matchend(alttail, s:notslash .. a:comma) + endif + endwhile + if a:0 + let current = current .. a:comma .. strpart(alttail, 0, j-1) + endif + return current +endfun + +fun! matchit#Match_debug() + let b:match_debug = 1 " Save debugging information. + " pat = all of b:match_words with backrefs parsed + amenu &Matchit.&pat :echo b:match_pat<CR> + " match = bit of text that is recognized as a match + amenu &Matchit.&match :echo b:match_match<CR> + " curcol = cursor column of the start of the matching text + amenu &Matchit.&curcol :echo b:match_col<CR> + " wholeBR = matching group, original version + amenu &Matchit.wh&oleBR :echo b:match_wholeBR<CR> + " iniBR = 'if' piece, original version + amenu &Matchit.ini&BR :echo b:match_iniBR<CR> + " ini = 'if' piece, with all backrefs resolved from match + amenu &Matchit.&ini :echo b:match_ini<CR> + " tail = 'else\|endif' piece, with all backrefs resolved from match + amenu &Matchit.&tail :echo b:match_tail<CR> + " fin = 'endif' piece, with all backrefs resolved from match + amenu &Matchit.&word :echo b:match_word<CR> + " '\'.d in ini refers to the same thing as '\'.table[d] in word. + amenu &Matchit.t&able :echo '0:' .. b:match_table .. ':9'<CR> +endfun + +" Jump to the nearest unmatched "(" or "if" or "<tag>" if a:spflag == "bW" +" or the nearest unmatched "</tag>" or "endif" or ")" if a:spflag == "W". +" Return a "mark" for the original position, so that +" let m = MultiMatch("bW", "n") ... call winrestview(m) +" will return to the original position. If there is a problem, do not +" move the cursor and return {}, unless a count is given, in which case +" go up or down as many levels as possible and again return {}. +" TODO This relies on the same patterns as % matching. It might be a good +" idea to give it its own matching patterns. +fun! matchit#MultiMatch(spflag, mode) + let restore_options = s:RestoreOptions() + let startpos = [line("."), col(".")] + " save v:count1 variable, might be reset from the restore_cursor command + let level = v:count1 + if a:mode == "o" && mode(1) !~# '[vV]' + exe "norm! v" + endif + + " First step: if not already done, set the script variables + " s:do_BR flag for whether there are backrefs + " s:pat parsed version of b:match_words + " s:all regexp based on s:pat and the default groups + " This part is copied and slightly modified from matchit#Match_wrapper(). + if !exists("b:match_words") || b:match_words == "" + let match_words = "" + " Allow b:match_words = "GetVimMatchWords()" . + elseif b:match_words =~ ":" + let match_words = b:match_words + else + execute "let match_words =" b:match_words + endif + if (match_words != s:last_words) || (&mps != s:last_mps) || + \ exists("b:match_debug") + let default = escape(&mps, '[$^.*~\\/?]') .. (strlen(&mps) ? "," : "") .. + \ '\/\*:\*\/,#\s*if\%(n\=def\)\=:#\s*else\>:#\s*elif\>:#\s*endif\>' + let s:last_mps = &mps + let match_words = s:Append(match_words, default) + let s:last_words = match_words + if match_words !~ s:notslash .. '\\\d' + let s:do_BR = 0 + let s:pat = match_words + else + let s:do_BR = 1 + let s:pat = s:ParseWords(match_words) + endif + let s:all = '\%(' .. substitute(s:pat, '[,:]\+', '\\|', 'g') .. '\)' + if exists("b:match_debug") + let b:match_pat = s:pat + endif + " Reconstruct the version with unresolved backrefs. + let s:patBR = substitute(match_words .. ',', + \ s:notslash .. '\zs[,:]*,[,:]*', ',', 'g') + let s:patBR = substitute(s:patBR, s:notslash .. '\zs:\{2,}', ':', 'g') + endif + + " Second step: figure out the patterns for searchpair() + " and save the screen, cursor position, and 'ignorecase'. + " - TODO: A lot of this is copied from matchit#Match_wrapper(). + " - maybe even more functionality should be split off + " - into separate functions! + let openlist = split(s:pat .. ',', s:notslash .. '\zs:.\{-}' .. s:notslash .. ',') + let midclolist = split(',' .. s:pat, s:notslash .. '\zs,.\{-}' .. s:notslash .. ':') + call map(midclolist, {-> split(v:val, s:notslash .. ':')}) + let closelist = [] + let middlelist = [] + call map(midclolist, {i,v -> [extend(closelist, v[-1 : -1]), + \ extend(middlelist, v[0 : -2])]}) + call map(openlist, {i,v -> v =~# s:notslash .. '\\|' ? '\%(' .. v .. '\)' : v}) + call map(middlelist, {i,v -> v =~# s:notslash .. '\\|' ? '\%(' .. v .. '\)' : v}) + call map(closelist, {i,v -> v =~# s:notslash .. '\\|' ? '\%(' .. v .. '\)' : v}) + let open = join(openlist, ',') + let middle = join(middlelist, ',') + let close = join(closelist, ',') + if exists("b:match_skip") + let skip = b:match_skip + elseif exists("b:match_comment") " backwards compatibility and testing! + let skip = "r:" .. b:match_comment + else + let skip = 's:comment\|string' + endif + let skip = s:ParseSkip(skip) + let view = winsaveview() + + " Third step: call searchpair(). + " Replace '\('--but not '\\('--with '\%(' and ',' with '\|'. + let openpat = substitute(open, '\%(' .. s:notslash .. '\)\@<=\\(', '\\%(', 'g') + let openpat = substitute(openpat, ',', '\\|', 'g') + let closepat = substitute(close, '\%(' .. s:notslash .. '\)\@<=\\(', '\\%(', 'g') + let closepat = substitute(closepat, ',', '\\|', 'g') + let middlepat = substitute(middle, '\%(' .. s:notslash .. '\)\@<=\\(', '\\%(', 'g') + let middlepat = substitute(middlepat, ',', '\\|', 'g') + + if skip =~ 'synID' && !(has("syntax") && exists("g:syntax_on")) + let skip = '0' + else + try + execute "if " .. skip .. "| let skip = '0' | endif" + catch /^Vim\%((\a\+)\)\=:E363/ + " We won't find anything, so skip searching, should keep Vim responsive. + return {} + endtry + endif + mark ' + while level + if searchpair(openpat, middlepat, closepat, a:spflag, skip) < 1 + call s:CleanUp(restore_options, a:mode, startpos) + return {} + endif + let level = level - 1 + endwhile + + " Restore options and return a string to restore the original position. + call s:CleanUp(restore_options, a:mode, startpos) + return view +endfun + +" Search backwards for "if" or "while" or "<tag>" or ... +" and return "endif" or "endwhile" or "</tag>" or ... . +" For now, this uses b:match_words and the same script variables +" as matchit#Match_wrapper() . Later, it may get its own patterns, +" either from a buffer variable or passed as arguments. +" fun! s:Autocomplete() +" echo "autocomplete not yet implemented :-(" +" if !exists("b:match_words") || b:match_words == "" +" return "" +" end +" let startpos = matchit#MultiMatch("bW") +" +" if startpos == "" +" return "" +" endif +" " - TODO: figure out whether 'if' or '<tag>' matched, and construct +" " - the appropriate closing. +" let matchline = getline(".") +" let curcol = col(".") - 1 +" " - TODO: Change the s:all argument if there is a new set of match pats. +" let regexp = s:Wholematch(matchline, s:all, curcol) +" let suf = strlen(matchline) - matchend(matchline, regexp) +" let prefix = (curcol ? '^.\{' . curcol . '}\%(' : '^\%(') +" let suffix = (suf ? '\).\{' . suf . '}$' : '\)$') +" " Reconstruct the version with unresolved backrefs. +" let patBR = substitute(b:match_words.',', '[,:]*,[,:]*', ',', 'g') +" let patBR = substitute(patBR, ':\{2,}', ':', "g") +" " Now, set group and groupBR to the matching group: 'if:endif' or +" " 'while:endwhile' or whatever. +" let group = s:Choose(s:pat, matchline, ",", ":", prefix, suffix, patBR) +" let i = matchend(group, s:notslash . ",") +" let groupBR = strpart(group, i) +" let group = strpart(group, 0, i-1) +" " Now, matchline =~ prefix . substitute(group,':','\|','g') . suffix +" if s:do_BR +" let group = s:InsertRefs(groupBR, prefix, group, suffix, matchline) +" endif +" " let g:group = group +" +" " - TODO: Construct the closing from group. +" let fake = "end" . expand("<cword>") +" execute startpos +" return fake +" endfun + +" Close all open structures. "Get the heck out of here!" +" fun! s:Gthhoh() +" let close = s:Autocomplete() +" while strlen(close) +" put=close +" let close = s:Autocomplete() +" endwhile +" endfun + +" Parse special strings as typical skip arguments for searchpair(): +" s:foo becomes (current syntax item) =~ foo +" S:foo becomes (current syntax item) !~ foo +" r:foo becomes (line before cursor) =~ foo +" R:foo becomes (line before cursor) !~ foo +fun! s:ParseSkip(str) + let skip = a:str + if skip[1] == ":" + if skip[0] ==# "s" + let skip = "synIDattr(synID(line('.'),col('.'),1),'name') =~? '" .. + \ strpart(skip,2) .. "'" + elseif skip[0] ==# "S" + let skip = "synIDattr(synID(line('.'),col('.'),1),'name') !~? '" .. + \ strpart(skip,2) .. "'" + elseif skip[0] ==# "r" + let skip = "strpart(getline('.'),0,col('.'))=~'" .. strpart(skip,2) .. "'" + elseif skip[0] ==# "R" + let skip = "strpart(getline('.'),0,col('.'))!~'" .. strpart(skip,2) .. "'" + endif + endif + return skip +endfun + +let &cpo = s:save_cpo +unlet s:save_cpo + +" vim:sts=2:sw=2:et: diff --git a/uvim/runtime/pack/dist/opt/matchit/doc/matchit.txt b/uvim/runtime/pack/dist/opt/matchit/doc/matchit.txt new file mode 100644 index 0000000000..e82bacdeb0 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/matchit/doc/matchit.txt @@ -0,0 +1,445 @@ +*matchit.txt* Extended "%" matching Last change: 2026 Jan 06 + + VIM REFERENCE MANUAL by Benji Fisher et al + + +*matchit* *matchit.vim* + +1. Extended matching with "%" |matchit-intro| +2. Activation |matchit-activate| +3. Configuration |matchit-configure| +4. Supporting a New Language |matchit-newlang| +5. Known Bugs and Limitations |matchit-bugs| + +The functionality mentioned here is a plugin, see |add-plugin|. +This plugin is only available if 'compatible' is not set. + +============================================================================== +1. Extended matching with "%" *matchit-intro* + + *matchit-%* +% Cycle forward through matching groups, such as "if", "else", "endif", + as specified by |b:match_words|. + + *g%* *v_g%* *o_g%* +g% Cycle backwards through matching groups, as specified by + |b:match_words|. For example, go from "if" to "endif" to "else". + + *[%* *v_[%* *o_[%* +[% Go to [count] previous unmatched group, as specified by + |b:match_words|. Similar to |[{|. + + *]%* *v_]%* *o_]%* +]% Go to [count] next unmatched group, as specified by + |b:match_words|. Similar to |]}|. + + *v_a%* +a% In Visual mode, select the matching group, as specified by + |b:match_words|, containing the cursor. Similar to |v_a[|. + A [count] is ignored, and only the first character of the closing + pattern is selected. + +In Vim, as in plain vi, the percent key, |%|, jumps the cursor from a brace, +bracket, or paren to its match. This can be configured with the 'matchpairs' +option. The matchit plugin extends this in several ways: + + You can match whole words, such as "if" and "endif", not just + single characters. You can also specify a |regular-expression|. + You can define groups with more than two words, such as "if", + "else", "endif". Banging on the "%" key will cycle from the "if" to + the first "else", the next "else", ..., the closing "endif", and back + to the opening "if". Nested structures are skipped. Using |g%| goes + in the reverse direction. + By default, words inside comments and strings are ignored, unless + the cursor is inside a comment or string when you type "%". If the + only thing you want to do is modify the behavior of "%" so that it + behaves this way, you do not have to define |b:match_words|, since the + script uses the 'matchpairs' option as well as this variable. + +See |matchit-details| for details on what the script does, and |b:match_words| +for how to specify matching patterns. + +MODES: *matchit-modes* *matchit-v_%* *matchit-o_%* + +Mostly, % and related motions (|g%| and |[%| and |]%|) should just work like built-in +|motion| commands in |Operator-pending| and |Visual| modes (as of 8.1.648) + +LANGUAGES: *matchit-languages* + +Currently, the following languages are supported: Ada, ASP with VBS, Csh, +DTD, Entity, Essbase, Fortran, HTML, JSP (same as HTML), LaTeX, Lua, Pascal, +SGML, Shell, Tcsh, Vim, XML. Other languages may already have support via +the default |filetype-plugin|s in the standard vim distribution. + +To support a new language, see |matchit-newlang| below. + +DETAILS: *matchit-details* *matchit-parse* + +Here is an outline of what matchit.vim does each time you hit the "%" key. If +there are |backref|s in |b:match_words| then the first step is to produce a +version in which these back references have been eliminated; if there are no +|backref|s then this step is skipped. This step is called parsing. For +example, "\(foo\|bar\):end\1" is parsed to yield +"\(foo\|bar\):end\(foo\|bar\)". This can get tricky, especially if there are +nested groups. If debugging is turned on, the parsed version is saved as +|b:match_pat|. + + *matchit-choose* +Next, the script looks for a word on the current line that matches the pattern +just constructed. It includes the patterns from the 'matchpairs' option. +The goal is to do what you expect, which turns out to be a little complicated. +The script follows these rules: + + Insist on a match that ends on or after the cursor. + Prefer a match that includes the cursor position (that is, one that + starts on or before the cursor). + Prefer a match that starts as close to the cursor as possible. + If more than one pattern in |b:match_words| matches, choose the one + that is listed first. + +Examples: + + Suppose you > + :let b:match_words = '<:>,<tag>:</tag>' +< and hit "%" with the cursor on or before the "<" in "a <tag> is born". + The pattern '<' comes first, so it is preferred over '<tag>', which + also matches. If the cursor is on the "t", however, then '<tag>' is + preferred, because this matches a bit of text containing the cursor. + If the two groups of patterns were reversed then '<' would never be + preferred. + + Suppose you > + :let b:match_words = 'if:end if' +< (Note the space!) and hit "%" with the cursor at the end of "end if". + Then "if" matches, which is probably not what you want, but if the + cursor starts on the "end " then "end if" is chosen. (You can avoid + this problem by using a more complicated pattern.) + +If there is no match, the cursor does not move. (Before version 1.13 of the +script, it would fall back on the usual behavior of |%|). If debugging is +turned on, the matched bit of text is saved as |b:match_match| and the cursor +column of the start of the match is saved as |b:match_col|. + +Next, the script looks through |b:match_words| (original and parsed versions) +for the group and pattern that match. If debugging is turned on, the group is +saved as |b:match_ini| (the first pattern) and |b:match_tail| (the rest). If +there are |backref|s then, in addition, the matching pattern is saved as +|b:match_word| and a table of translations is saved as |b:match_table|. If +there are |backref|s, these are determined from the matching pattern and +|b:match_match| and substituted into each pattern in the matching group. + +The script decides whether to search forwards or backwards and chooses +arguments for the |searchpair()| function. Then, the cursor is moved to the +start of the match, and |searchpair()| is called. By default, matching +structures inside strings and comments are ignored. This can be changed by +setting |b:match_skip|. + +============================================================================== +2. Activation *matchit-activate* + +To use the matchit plugin add this line to your |vimrc|: > + packadd! matchit + +The script should start working the next time you start Vim. + +To use the matchit plugin after Vim has started, execute this command: > + packadd matchit + +(Earlier versions of the script did nothing unless a |buffer-variable| named +|b:match_words| was defined. Even earlier versions contained autocommands +that set this variable for various file types. Now, |b:match_words| is +defined in many of the default |filetype-plugin|s instead.) + +For a new language, you can add autocommands to the script or to your vimrc +file, but the recommended method is to add a line such as > + let b:match_words = '\<foo\>:\<bar\>' +to the |filetype-plugin| for your language. See |b:match_words| below for how +this variable is interpreted. + +TROUBLESHOOTING *matchit-troubleshoot* + +The script should work in most installations of Vim. It may not work if Vim +was compiled with a minimal feature set, for example if the |+syntax| option +was not enabled. If your Vim has support for syntax compiled in, but you do +not have |syntax| highlighting turned on, matchit.vim should work, but it may +fail to skip matching groups in comments and strings. If the |filetype| +mechanism is turned off, the |b:match_words| variable will probably not be +defined automatically. + +2.1 Temporarily disable the matchit plugin *matchit-disable* *:MatchDisable* + +To temporarily disable the matchit plugin, after it has been loaded, +execute this command: > + :MatchDisable + +This will delete all the defined key mappings to the Vim default. +Now the "%" command will work like before loading the plugin |%| + +2.2 Re-enable the matchit plugin *:MatchEnable* + +To re-enable the plugin, after it was disabled, use the following command: > + :MatchEnable + +This will resetup the key mappings. + +============================================================================== +3. Configuration *matchit-configure* + +There are several variables that govern the behavior of matchit.vim. Note +that these are variables local to the buffer, not options, so use |:let| to +define them, not |:set|. Some of these variables have values that matter; for +others, it only matters whether the variable has been defined. All of these +can be defined in the |filetype-plugin| or autocommand that defines +|b:match_words| or "on the fly." + +The main variable is |b:match_words|. It is described in the section below on +supporting a new language. + + *MatchError* *matchit-hl* *matchit-highlight* +MatchError is the highlight group for error messages from the script. By +default, it is linked to WarningMsg. If you do not want to be bothered by +error messages, you can define this to be something invisible. For example, +if you use the GUI version of Vim and your command line is normally white, you +can do > + :hi MatchError guifg=white guibg=white +< + *b:match_ignorecase* +If you > + :let b:match_ignorecase = 1 +then matchit.vim acts as if 'ignorecase' is set: for example, "end" and "END" +are equivalent. If you > + :let b:match_ignorecase = 0 +then matchit.vim treats "end" and "END" differently. (There will be no +b:match_infercase option unless someone requests it.) + + *b:match_debug* +Define b:match_debug if you want debugging information to be saved. See +|matchit-debug|, below. + + *b:match_skip* +If b:match_skip is defined, it is passed as the skip argument to +|searchpair()|. This controls when matching structures are skipped, or +ignored. By default, they are ignored inside comments and strings, as +determined by the |syntax| mechanism. (If syntax highlighting is turned off, +nothing is skipped.) You can set b:match_skip to a string, which evaluates to +a non-zero, numerical value if the match is to be skipped or zero if the match +should not be skipped. In addition, the following special values are +supported by matchit.vim: + s:foo becomes (current syntax item) =~ foo + S:foo becomes (current syntax item) !~ foo + r:foo becomes (line before cursor) =~ foo + R:foo becomes (line before cursor) !~ foo +(The "s" is meant to suggest "syntax", and the "r" is meant to suggest +"regular expression".) + +Examples: + + You can get the default behavior with > + :let b:match_skip = 's:comment\|string' +< + If you want to skip matching structures unless they are at the start + of the line (ignoring whitespace) then you can > + :let b:match_skip = 'R:^\s*' +< Do not do this if strings or comments can span several lines, since + the normal syntax checking will not be done if you set b:match_skip. + + In LaTeX, since "%" is used as the comment character, you can > + :let b:match_skip = 'r:%' +< Unfortunately, this will skip anything after "\%", an escaped "%". To + allow for this, and also "\\%" (an escaped backslash followed by the + comment character) you can > + :let b:match_skip = 'r:\(^\|[^\\]\)\(\\\\\)*%' +< + See the $VIMRUNTIME/ftplugin/vim.vim for an example that uses both + syntax and a regular expression. + + *b:match_function* +If b:match_function is defined, matchit.vim will first call this function to +perform matching. This is useful for languages with an indentation-based block +structure (such as Python) or other complex matching requirements that cannot +be expressed with regular expression patterns. + +The function should accept one argument: + forward - 1 for forward search (% command) + 0 for backward search (g% command) + +The function should return a list with one of these values: + [line, col] - Match found at the specified position + [] - No match found; fall through to regular matching + (|b:match_words|, matchpairs, etc.) + +The cursor position is not changed by the function; matchit handles cursor +movement based on the returned position. + +If the function throws an error, matchit gives up and doesn't continue. +Enable |b:match_debug| to see error messages from custom match functions. + +Python example (simplified): > + let s:keywords = {'if': 'elif\|else', 'elif': 'elif\|else'} + + function! s:PythonMatch(forward) abort + let keyword = matchstr(getline('.'), '^\s*\zs\w\+') + let pattern = get(s:keywords, keyword, '') + if empty(pattern) | return [] | endif + + " Forward-only. Backwards left as an exercise for the reader. + let [lnum, col] = searchpos('^\s*\%(' . pattern . '\)\>', 'nW' 0, 0, + \ 'indent(".") != ' . indent('.')) + return lnum > 0 ? [lnum, col] : [] + endfunction + + let b:match_function = function('s:PythonMatch') +< +See |matchit-newlang| below for more details on supporting new languages. + +============================================================================== +4. Supporting a New Language *matchit-newlang* + *b:match_words* +In order for matchit.vim to support a new language, you must define a suitable +pattern for |b:match_words|. You may also want to set some of the +|matchit-configure| variables, as described above. If your language has a +complicated syntax, or many keywords, you will need to know something about +Vim's |regular-expression|s. + +The format for |b:match_words| is similar to that of the 'matchpairs' option: +it is a comma (,)-separated list of groups; each group is a colon(:)-separated +list of patterns (regular expressions). Commas and colons that are part of a +pattern should be escaped with backslashes ('\:' and '\,'). It is OK to have +only one group; the effect is undefined if a group has only one pattern. +A simple example is > + :let b:match_words = '\<if\>:\<endif\>,' + \ . '\<while\>:\<continue\>:\<break\>:\<endwhile\>' +(In Vim regular expressions, |\<| and |\>| denote word boundaries. Thus "if" +matches the end of "endif" but "\<if\>" does not.) Then banging on the "%" +key will bounce the cursor between "if" and the matching "endif"; and from +"while" to any matching "continue" or "break", then to the matching "endwhile" +and back to the "while". It is almost always easier to use |literal-string|s +(single quotes) as above: '\<if\>' rather than "\\<if\\>" and so on. + +Exception: If the ":" character does not appear in b:match_words, then it is +treated as an expression to be evaluated. For example, > + :let b:match_words = 'GetMatchWords()' +allows you to define a function. This can return a different string depending +on the current syntax, for example. + +Once you have defined the appropriate value of |b:match_words|, you will +probably want to have this set automatically each time you edit the +appropriate file type. The recommended way to do this is by adding the +definition to a |filetype-plugin| file. + +Tips: Be careful that your initial pattern does not match your final pattern. +See the example above for the use of word-boundary expressions. It is usually +better to use ".\{-}" (as many as necessary) instead of ".*" (as many as +possible). See |\{-|. For example, in the string "<tag>label</tag>", "<.*>" +matches the whole string whereas "<.\{-}>" and "<[^>]*>" match "<tag>" and +"</tag>". + + *matchit-spaces* *matchit-s:notend* +If "if" is to be paired with "end if" (Note the space!) then word boundaries +are not enough. Instead, define a regular expression s:notend that will match +anything but "end" and use it as follows: > + :let s:notend = '\%(\<end\s\+\)\@<!' + :let b:match_words = s:notend . '\<if\>:\<end\s\+if\>' +< *matchit-s:sol* +This is a simplified version of what is done for Ada. The s:notend is a +|script-variable|. Similarly, you may want to define a start-of-line regular +expression > + :let s:sol = '\%(^\|;\)\s*' +if keywords are only recognized after the start of a line or after a +semicolon (;), with optional white space. + + *matchit-backref* +In any group, the expressions |\1|, |\2|, ..., |\9| refer to parts of the +INITIAL pattern enclosed in |\(|escaped parentheses|\)|. These are referred +to as back references, or backrefs. For example, > + :let b:match_words = '\<b\(o\+\)\>:\(h\)\1\>' +means that "bo" pairs with "ho" and "boo" pairs with "hoo" and so on. Note +that "\1" does not refer to the "\(h\)" in this example. If you have +"\(nested \(parentheses\)\) then "\d" refers to the d-th "\(" and everything +up to and including the matching "\)": in "\(nested\(parentheses\)\)", "\1" +refers to everything and "\2" refers to "\(parentheses\)". If you use a +variable such as |s:notend| or |s:sol| in the previous paragraph then remember +to count any "\(" patterns in this variable. You do not have to count groups +defined by |\%(\)|. + +It should be possible to resolve back references from any pattern in the +group. For example, > + :let b:match_words = '\(foo\)\(bar\):more\1:and\2:end\1\2' +would not work because "\2" cannot be determined from "morefoo" and "\1" +cannot be determined from "andbar". On the other hand, > + :let b:match_words = '\(\(foo\)\(bar\)\):\3\2:end\1' +should work (and have the same effect as "foobar:barfoo:endfoobar"), although +this has not been thoroughly tested. + +You can use |zero-width| patterns such as |\@<=| and |\zs|. (The latter has +not been thoroughly tested in matchit.vim.) For example, if the keyword "if" +must occur at the start of the line, with optional white space, you might use +the pattern "\(^\s*\)\@<=if" so that the cursor will end on the "i" instead of +at the start of the line. For another example, if HTML had only one tag then +one could > + :let b:match_words = '<:>,<\@<=tag>:<\@<=/tag>' +so that "%" can bounce between matching "<" and ">" pairs or (starting on +"tag" or "/tag") between matching tags. Without the |\@<=|, the script would +bounce from "tag" to the "<" in "</tag>", and another "%" would not take you +back to where you started. + +DEBUGGING *matchit-debug* *:MatchDebug* + +If you are having trouble figuring out the appropriate definition of +|b:match_words| then you can take advantage of the same information I use when +debugging the script. This is especially true if you are not sure whether +your patterns or my script are at fault! To make this more convenient, I have +made the command :MatchDebug, which defines the variable |b:match_debug| and +creates a Matchit menu. This menu makes it convenient to check the values of +the variables described below. You will probably also want to read +|matchit-details| above. + +Defining the variable |b:match_debug| causes the script to set the following +variables, each time you hit the "%" key. Several of these are only defined +if |b:match_words| includes |backref|s. + + *b:match_pat* +The b:match_pat variable is set to |b:match_words| with |backref|s parsed. + *b:match_match* +The b:match_match variable is set to the bit of text that is recognized as a +match. + *b:match_col* +The b:match_col variable is set to the cursor column of the start of the +matching text. + *b:match_wholeBR* +The b:match_wholeBR variable is set to the comma-separated group of patterns +that matches, with |backref|s unparsed. + *b:match_iniBR* +The b:match_iniBR variable is set to the first pattern in |b:match_wholeBR|. + *b:match_ini* +The b:match_ini variable is set to the first pattern in |b:match_wholeBR|, +with |backref|s resolved from |b:match_match|. + *b:match_tail* +The b:match_tail variable is set to the remaining patterns in +|b:match_wholeBR|, with |backref|s resolved from |b:match_match|. + *b:match_word* +The b:match_word variable is set to the pattern from |b:match_wholeBR| that +matches |b:match_match|. + *b:match_table* +The back reference '\'.d refers to the same thing as '\'.b:match_table[d] in +|b:match_word|. + +============================================================================== +5. Known Bugs and Limitations *matchit-bugs* + +Repository: https://github.com/chrisbra/matchit/ +Bugs can be reported at the repository and the latest development snapshot can +also be downloaded there. + +Just because I know about a bug does not mean that it is on my todo list. I +try to respond to reports of bugs that cause real problems. If it does not +cause serious problems, or if there is a work-around, a bug may sit there for +a while. Moral: if a bug (known or not) bothers you, let me know. + +It would be nice if "\0" were recognized as the entire pattern. That is, it +would be nice if "foo:\end\0" had the same effect as "\(foo\):\end\1". I may +try to implement this in a future version. (This is not so easy to arrange as +you might think!) + +============================================================================== +vim:tw=78:ts=8:fo=tcq2:ft=help: diff --git a/uvim/runtime/pack/dist/opt/matchit/doc/tags b/uvim/runtime/pack/dist/opt/matchit/doc/tags new file mode 100644 index 0000000000..008c5686d1 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/matchit/doc/tags @@ -0,0 +1,53 @@ +:MatchDebug matchit.txt /*:MatchDebug* +:MatchDisable matchit.txt /*:MatchDisable* +:MatchEnable matchit.txt /*:MatchEnable* +MatchError matchit.txt /*MatchError* +[% matchit.txt /*[%* +]% matchit.txt /*]%* +b:match_col matchit.txt /*b:match_col* +b:match_debug matchit.txt /*b:match_debug* +b:match_function matchit.txt /*b:match_function* +b:match_ignorecase matchit.txt /*b:match_ignorecase* +b:match_ini matchit.txt /*b:match_ini* +b:match_iniBR matchit.txt /*b:match_iniBR* +b:match_match matchit.txt /*b:match_match* +b:match_pat matchit.txt /*b:match_pat* +b:match_skip matchit.txt /*b:match_skip* +b:match_table matchit.txt /*b:match_table* +b:match_tail matchit.txt /*b:match_tail* +b:match_wholeBR matchit.txt /*b:match_wholeBR* +b:match_word matchit.txt /*b:match_word* +b:match_words matchit.txt /*b:match_words* +g% matchit.txt /*g%* +matchit matchit.txt /*matchit* +matchit-% matchit.txt /*matchit-%* +matchit-activate matchit.txt /*matchit-activate* +matchit-backref matchit.txt /*matchit-backref* +matchit-bugs matchit.txt /*matchit-bugs* +matchit-choose matchit.txt /*matchit-choose* +matchit-configure matchit.txt /*matchit-configure* +matchit-debug matchit.txt /*matchit-debug* +matchit-details matchit.txt /*matchit-details* +matchit-disable matchit.txt /*matchit-disable* +matchit-highlight matchit.txt /*matchit-highlight* +matchit-hl matchit.txt /*matchit-hl* +matchit-intro matchit.txt /*matchit-intro* +matchit-languages matchit.txt /*matchit-languages* +matchit-modes matchit.txt /*matchit-modes* +matchit-newlang matchit.txt /*matchit-newlang* +matchit-o_% matchit.txt /*matchit-o_%* +matchit-parse matchit.txt /*matchit-parse* +matchit-s:notend matchit.txt /*matchit-s:notend* +matchit-s:sol matchit.txt /*matchit-s:sol* +matchit-spaces matchit.txt /*matchit-spaces* +matchit-troubleshoot matchit.txt /*matchit-troubleshoot* +matchit-v_% matchit.txt /*matchit-v_%* +matchit.txt matchit.txt /*matchit.txt* +matchit.vim matchit.txt /*matchit.vim* +o_[% matchit.txt /*o_[%* +o_]% matchit.txt /*o_]%* +o_g% matchit.txt /*o_g%* +v_[% matchit.txt /*v_[%* +v_]% matchit.txt /*v_]%* +v_a% matchit.txt /*v_a%* +v_g% matchit.txt /*v_g%* diff --git a/uvim/runtime/pack/dist/opt/matchit/plugin/matchit.vim b/uvim/runtime/pack/dist/opt/matchit/plugin/matchit.vim new file mode 100644 index 0000000000..947f530261 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/matchit/plugin/matchit.vim @@ -0,0 +1,127 @@ +" matchit.vim: (global plugin) Extended "%" matching +" Maintainer: Christian Brabandt +" Version: 1.21 +" Last Change: 2024 May 20 +" Repository: https://github.com/chrisbra/matchit +" Previous URL:http://www.vim.org/script.php?script_id=39 +" Previous Maintainer: Benji Fisher PhD <benji@member.AMS.org> + +" Documentation: +" The documentation is in a separate file: ../doc/matchit.txt + +" Credits: +" Vim editor by Bram Moolenaar (Thanks, Bram!) +" Original script and design by Raul Segura Acevedo +" Support for comments by Douglas Potts +" Support for back references and other improvements by Benji Fisher +" Support for many languages by Johannes Zellner +" Suggestions for improvement, bug reports, and support for additional +" languages by Jordi-Albert Batalla, Neil Bird, Servatius Brandt, Mark +" Collett, Stephen Wall, Dany St-Amant, Yuheng Xie, and Johannes Zellner. + +" Debugging: +" If you'd like to try the built-in debugging commands... +" :MatchDebug to activate debugging for the current buffer +" This saves the values of several key script variables as buffer-local +" variables. See the MatchDebug() function, below, for details. + +" TODO: I should think about multi-line patterns for b:match_words. +" This would require an option: how many lines to scan (default 1). +" This would be useful for Python, maybe also for *ML. +" TODO: Maybe I should add a menu so that people will actually use some of +" the features that I have implemented. +" TODO: Eliminate the MultiMatch function. Add yet another argument to +" Match_wrapper() instead. +" TODO: Allow :let b:match_words = '\(\(foo\)\(bar\)\):\3\2:end\1' +" TODO: Make backrefs safer by using '\V' (very no-magic). +" TODO: Add a level of indirection, so that custom % scripts can use my +" work but extend it. + +" Allow user to prevent loading and prevent duplicate loading. +if exists("g:loaded_matchit") || &cp + finish +endif +let g:loaded_matchit = 1 + +let s:save_cpo = &cpo +set cpo&vim + +fun MatchEnable() + nnoremap <silent> <Plug>(MatchitNormalForward) :<C-U>call matchit#Match_wrapper('',1,'n')<CR> + nnoremap <silent> <Plug>(MatchitNormalBackward) :<C-U>call matchit#Match_wrapper('',0,'n')<CR> + xnoremap <silent> <Plug>(MatchitVisualForward) :<C-U>call matchit#Match_wrapper('',1,'v')<CR> + \:if col("''") != col("$") \| exe ":normal! m'" \| endif<cr>gv`` + xnoremap <silent> <Plug>(MatchitVisualBackward) :<C-U>call matchit#Match_wrapper('',0,'v')<CR>m'gv`` + onoremap <silent> <Plug>(MatchitOperationForward) :<C-U>call matchit#Match_wrapper('',1,'o')<CR> + onoremap <silent> <Plug>(MatchitOperationBackward) :<C-U>call matchit#Match_wrapper('',0,'o')<CR> + + " Analogues of [{ and ]} using matching patterns: + nnoremap <silent> <Plug>(MatchitNormalMultiBackward) :<C-U>call matchit#MultiMatch("bW", "n")<CR> + nnoremap <silent> <Plug>(MatchitNormalMultiForward) :<C-U>call matchit#MultiMatch("W", "n")<CR> + xnoremap <silent> <Plug>(MatchitVisualMultiBackward) :<C-U>call matchit#MultiMatch("bW", "n")<CR>m'gv`` + xnoremap <silent> <Plug>(MatchitVisualMultiForward) :<C-U>call matchit#MultiMatch("W", "n")<CR>m'gv`` + onoremap <silent> <Plug>(MatchitOperationMultiBackward) :<C-U>call matchit#MultiMatch("bW", "o")<CR> + onoremap <silent> <Plug>(MatchitOperationMultiForward) :<C-U>call matchit#MultiMatch("W", "o")<CR> + + " text object: + xmap <silent> <Plug>(MatchitVisualTextObject) <Plug>(MatchitVisualMultiBackward)o<Plug>(MatchitVisualMultiForward) + + if !exists("g:no_plugin_maps") + nmap <silent> % <Plug>(MatchitNormalForward) + nmap <silent> g% <Plug>(MatchitNormalBackward) + xmap <silent> % <Plug>(MatchitVisualForward) + xmap <silent> g% <Plug>(MatchitVisualBackward) + omap <silent> % <Plug>(MatchitOperationForward) + omap <silent> g% <Plug>(MatchitOperationBackward) + + " Analogues of [{ and ]} using matching patterns: + nmap <silent> [% <Plug>(MatchitNormalMultiBackward) + nmap <silent> ]% <Plug>(MatchitNormalMultiForward) + xmap <silent> [% <Plug>(MatchitVisualMultiBackward) + xmap <silent> ]% <Plug>(MatchitVisualMultiForward) + omap <silent> [% <Plug>(MatchitOperationMultiBackward) + omap <silent> ]% <Plug>(MatchitOperationMultiForward) + + " Text object + xmap a% <Plug>(MatchitVisualTextObject) + endif +endfun + +fun MatchDisable() + " remove all the setup keymappings + nunmap % + nunmap g% + xunmap % + xunmap g% + ounmap % + ounmap g% + + nunmap [% + nunmap ]% + xunmap [% + xunmap ]% + ounmap [% + ounmap ]% + + xunmap a% +endfun + +" Call this function to turn on debugging information. Every time the main +" script is run, buffer variables will be saved. These can be used directly +" or viewed using the menu items below. +if !exists(":MatchDebug") + command! -nargs=0 MatchDebug call matchit#Match_debug() +endif +if !exists(":MatchDisable") + command! -nargs=0 MatchDisable :call MatchDisable() +endif +if !exists(":MatchEnable") + command! -nargs=0 MatchEnable :call MatchEnable() +endif + +call MatchEnable() + +let &cpo = s:save_cpo +unlet s:save_cpo + +" vim:sts=2:sw=2:et: diff --git a/uvim/runtime/pack/dist/opt/netrw/LICENSE.txt b/uvim/runtime/pack/dist/opt/netrw/LICENSE.txt new file mode 100644 index 0000000000..702c2386ac --- /dev/null +++ b/uvim/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/uvim/runtime/pack/dist/opt/netrw/README.md b/uvim/runtime/pack/dist/opt/netrw/README.md new file mode 100644 index 0000000000..4b139f6249 --- /dev/null +++ b/uvim/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 information 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 and 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 balloon + 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/uvim/runtime/pack/dist/opt/netrw/autoload/netrw.vim b/uvim/runtime/pack/dist/opt/netrw/autoload/netrw.vim new file mode 100644 index 0000000000..1520ff969e --- /dev/null +++ b/uvim/runtime/pack/dist/opt/netrw/autoload/netrw.vim @@ -0,0 +1,9675 @@ +" Creator: Charles E Campbell +" Previous Maintainer: Luca Saccarola <github.e41mv@aleeas.com> +" Maintainer: This runtime file is looking for a new maintainer. +" Last Change: +" 2025 Aug 07 by Vim Project (use correct "=~#" for netrw_stylesize option #17901) +" 2025 Aug 07 by Vim Project (netrw#BrowseX() distinguishes remote files #17794) +" 2025 Aug 22 by Vim Project netrw#Explore handle terminal correctly #18069 +" 2025 Sep 05 by Vim Project ensure netrw#fs#Dirname() returns trailing slash #18199 +" 2025 Sep 11 by Vim Project only keep cursor position in tree mode #18275 +" 2025 Sep 17 by Vim Project tighten the regex to handle remote compressed archives #18318 +" 2025 Sep 18 by Vim Project 'equalalways' not always respected #18358 +" 2025 Oct 01 by Vim Project fix navigate to parent folder #18464 +" 2025 Oct 26 by Vim Project fix parsing of remote user names #18611 +" 2025 Oct 27 by Vim Project align comment after #18611 +" 2025 Nov 01 by Vim Project fix NetrwChgPerm #18674 +" 2025 Nov 13 by Vim Project don't wipe unnamed buffers #18740 +" 2025 Nov 18 by Vim Project use UNC paths when using scp and Windows paths #18764 +" 2025 Nov 28 by Vim Project fix undefined variable in *NetrwMenu #18829 +" 2025 Dec 26 by Vim Project fix use of g:netrw_cygwin #19015 +" 2026 Jan 19 by Vim Project do not create swapfiles #18854 +" 2026 Feb 15 by Vim Project fix global variable initialization for MS-Windows #19287 +" 2026 Feb 21 by Vim Project better absolute path detection on MS-Windows #19477 +" 2026 Feb 27 by Vim Project Make the hostname validation more strict +" 2026 Mar 01 by Vim Project include portnumber in hostname checking #19533 +" 2026 Apr 01 by Vim Project use fnameescape() with netrw#FileUrlEdit() +" 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 + +let g:loaded_netrw = "v184" + +if !has("patch-9.1.1054") && !has('nvim') + echoerr 'netrw needs Vim v9.1.1054' + finish +endif + +let s:keepcpo= &cpo +setl cpo&vim + +" Netrw Variables: {{{1 + +" s:NetrwInit: initializes variables if they haven't been defined {{{2 + +function s:NetrwInit(name, default) + if !exists(a:name) + let {a:name} = a:default + endif +endfunction + +" 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 + +" Default option values: {{{2 +call s:NetrwInit("g:netrw_localcopycmdopt","") +call s:NetrwInit("g:netrw_localcopydircmdopt","") +call s:NetrwInit("g:netrw_localmkdiropt","") +call s:NetrwInit("g:netrw_localmovecmdopt","") + +" Default values for netrw's global protocol variables {{{2 + +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") +call s:NetrwInit("g:netrw_cursor" , 2) +let s:netrw_usercul = &cursorline +let s:netrw_usercuc = &cursorcolumn +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") || 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 + let g:netrw_list_cmd= "" + endif +endif +call s:NetrwInit("g:netrw_list_hide","") +" Default values - lh-lz ---------- {{{3 +if !exists("g:netrw_localcmdshell") + let g:netrw_localcmdshell= "" +endif + +if !exists("g:netrw_localcopycmd") + let g:netrw_localcopycmd = 'cp' + let g:netrw_localcopycmdopt = '' + + if has("win32") && !g:netrw_cygwin + let g:netrw_localcopycmd = $COMSPEC + let g:netrw_localcopycmdopt = ' /c copy' + endif +endif + +if !exists("g:netrw_localcopydircmd") + let g:netrw_localcopydircmd = 'cp' + let g:netrw_localcopydircmdopt = '-R' + + if has("win32") && !g:netrw_cygwin + let g:netrw_localcopydircmd = "xcopy" + let g:netrw_localcopydircmdopt = " /E /I /H /C /Y" + endif +endif + +if !exists("g:netrw_localmkdir") + if has("win32") + if g:netrw_cygwin + let g:netrw_localmkdir= "mkdir" + else + let g:netrw_localmkdir = $COMSPEC + let g:netrw_localmkdiropt= " /c mkdir" + endif + else + let g:netrw_localmkdir= "mkdir" + endif +endif + +if !exists("g:netrw_localmovecmd") + if has("win32") + if g:netrw_cygwin + let g:netrw_localmovecmd= "mv" + else + let g:netrw_localmovecmd = $COMSPEC + let g:netrw_localmovecmdopt= " /c move" + endif + elseif has("unix") || has("macunix") + let g:netrw_localmovecmd= "mv" + else + let g:netrw_localmovecmd= "" + endif +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") || 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") + let g:netrw_sort_sequence = !empty(&suffixes) + \ ? printf('[\/]$,*,\%(%s\)[*@]\=$', &suffixes->split(',')->map('escape(v:val, ".*$~")')->join('\|')) + \ : '[\/]$,*' +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. + +" Netrw Initialization: {{{1 + +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#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 +function 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) + if !g:netrw_cygwin && has("win32") + let curdir= substitute(curdir,'\','/','g') + endif + let curfiledir = substitute(expand("%:p"),'^\(.*[/\\]\)[^/\\]*$','\1','e') + if &buftype == "terminal" + let curfiledir = curdir + 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("clipboard") && g:netrw_clipboard + sil! let keepregstar = @* + sil! let keepregplus = @+ + endif + sil! let keepregslash= @/ + + " if dosplit + " -or- buffer is not a terminal AND file has been modified AND file not hidden when abandoned + " -or- Texplore used + if a:dosplit || (&buftype != "terminal" && &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") || 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 + call netrw#msg#Notify('WARNING', 'using Nexplore or <s-down> improperly; see help for netrw-starstar') + if 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 + call netrw#msg#Notify('WARNING', 'using Pexplore or <s-up> improperly; see help for netrw-starstar') + if 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/ + call netrw#msg#Notify('WARNING', printf("no match with pattern<%s>", pattern)) + 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/ + call netrw#msg#Notify('WARNING', printf('no files matched pattern<%s>', pattern)) + if &hls | let keepregslash= s:ExplorePatHls(pattern) | endif + if 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] =~ '\*\*\/') + call netrw#msg#Notify('WARNING', 'no files matched') + if 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 + call netrw#msg#Notify('NOTE', 'no more files match Explore pattern') + 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 + call netrw#msg#Notify('WARNING', 'your vim needs the +path_extra feature for Exploring with **!') + if 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("clipboard") && g:netrw_clipboard + if @* != keepregstar | sil! let @* = keepregstar | endif + if @+ != keepregplus | sil! let @+ = keepregplus | endif + endif + sil! let @/ = keepregslash +endfunction + +" 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) +function netrw#Lexplore(count,rightside,...) + 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 + +endfunction + +" netrw#MakeTgt: make a target out of the directory name provided {{{2 +function netrw#MakeTgt(dname) + " simplify the target (eg. /abc/def/../ghi -> /abc/ghi) + let svpos = winsaveview() + let s:netrwmftgt_islocal= (a:dname !~ '^\a\{3,}://') + 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 winrestview(svpos) +endfunction + +" 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()) +function netrw#Obtain(islocal,fname,...) + " 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#msg#Notify('ERROR', 'attempting to use NetrwObtain on something not a filename or a list') + return + endif + if a:0 > 0 + let tgtdir= a:1 + else + let tgtdir= getcwd() + endif + + if exists("b:netrw_islocal") && b:netrw_islocal + " obtain a file from local b:netrw_curdir to (local) tgtdir + if exists("b:netrw_curdir") && getcwd() != b:netrw_curdir + let topath = netrw#fs#ComposePath(tgtdir,"") + if has("win32") && !g:netrw_cygwin + " transfer files one at time + for fname in fnamelist + call system(g:netrw_localcopycmd.g:netrw_localcopycmdopt." ".netrw#os#Escape(fname)." ".netrw#os#Escape(topath)) + if v:shell_error != 0 + call netrw#msg#Notify('WARNING', printf('consider setting g:netrw_localcopycmd<%s> to something that works', g:netrw_localcopycmd)) + return + endif + endfor + else + " transfer files with one command + let filelist= join(map(deepcopy(fnamelist),"netrw#os#Escape(v:val)")) + call system(g:netrw_localcopycmd.g:netrw_localcopycmdopt." ".filelist." ".netrw#os#Escape(topath)) + if v:shell_error != 0 + call netrw#msg#Notify('WARNING', printf('consider setting g:netrw_localcopycmd<%s> to something that works', g:netrw_localcopycmd)) + return + endif + endif + elseif !exists("b:netrw_curdir") + call netrw#msg#Notify('ERROR', "local browsing directory doesn't exist!") + else + call netrw#msg#Notify('WARNING', 'local browsing directory and current directory are identical') + endif + + else + " obtain files from remote b:netrw_curdir to local tgtdir + if type(a:fname) == 1 + call s:SetupNetrwStatusLine('%f %h%m%r%=%9*Obtaining '.a:fname) + endif + call s:NetrwMethod(b:netrw_curdir) + if !s:NetrwValidateHostname(g:netrw_machine) + call netrw#msg#Notify('ERROR', 'Rejecting invalid hostname: <%s>', g:netrw_machine) + return + endif + + if b:netrw_method == 4 + " obtain file using scp + 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(netrw#os#Escape(g:netrw_machine.":".path.v:val,1)," ")')) + call netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_scp_cmd.netrw#os#Escape(useport,1)." ".filelist." ".netrw#os#Escape(tgtdir,1)) + + elseif b:netrw_method == 2 + " obtain file using ftp + .netrc + 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 + endif + + if exists("b:netrw_fname") && b:netrw_fname != "" + call setline(line("$")+1,'cd "'.b:netrw_fname.'"') + endif + + if exists("g:netrw_ftpextracmd") + NetrwKeepj put =g:netrw_ftpextracmd + endif + for fname in fnamelist + call setline(line("$")+1,'get "'.fname.'"') + endfor + if exists("g:netrw_port") && g:netrw_port != "" + call netrw#os#Execute(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".netrw#os#Escape(g:netrw_machine,1)." ".netrw#os#Escape(g:netrw_port,1)) + else + call netrw#os#Execute(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".netrw#os#Escape(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) !~ "^$" && getline(1) !~ '^Trying ' + let debugkeep= &debug + setl debug=msg + call netrw#msg#Notify('ERROR', getline(1)) + let &debug= debugkeep + endif + + elseif b:netrw_method == 3 + " obtain with ftp + machine, id, passwd, and fname (ie. no .netrc) + 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 + else + NetrwKeepj put ='open '.g:netrw_machine + 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 exists("g:netrw_ftpmode") && g:netrw_ftpmode != "" + NetrwKeepj put =g:netrw_ftpmode + endif + + if exists("b:netrw_fname") && b:netrw_fname != "" + NetrwKeepj call setline(line("$")+1,'cd "'.b:netrw_fname.'"') + endif + + if exists("g:netrw_ftpextracmd") + NetrwKeepj put =g:netrw_ftpextracmd + endif + + if exists("g:netrw_ftpextracmd") + NetrwKeepj put =g:netrw_ftpextracmd + endif + for fname in fnamelist + NetrwKeepj call setline(line("$")+1,'get "'.fname.'"') + 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 + " Note: using "_dd to delete to the black hole register; avoids messing up @@ + NetrwKeepj norm! 1G"_dd + call netrw#os#Execute(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 netrw#msg#Notify('ERROR', getline(1)) + endif + + elseif b:netrw_method == 9 + " obtain file using sftp + if a:fname =~ '/' + let localfile= substitute(a:fname,'^.*/','','') + else + let localfile= a:fname + endif + call netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_sftp_cmd." ".netrw#os#Escape(g:netrw_machine.":".b:netrw_fname,1).netrw#os#Escape(localfile)." ".netrw#os#Escape(tgtdir)) + + elseif !exists("b:netrw_method") || b:netrw_method < 0 + " probably a badly formed url; protocol not recognized + return + + else + " protocol recognized but not supported for Obtain (yet?) + call netrw#msg#Notify('ERROR', '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 + +endfunction + +" netrw#Nread: save position, call netrw#NetRead(), and restore position {{{2 +function netrw#Nread(mode,fname) + let svpos= winsaveview() + call netrw#NetRead(a:mode,a:fname) + 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 +endfunction + +" 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:" +function s:NetrwOptionsSave(vt) + + if !exists("{a:vt}netrw_optionsave") + let {a:vt}netrw_optionsave= 1 + else + return + endif + + " Save current settings and current directory + 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 + 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 + if g:netrw_keepdir + let {a:vt}netrw_dirkeep = getcwd() + endif + if has("clipboard") && g:netrw_clipboard + sil! let {a:vt}netrw_starkeep = @* + sil! let {a:vt}netrw_pluskeep = @+ + endif + sil! let {a:vt}netrw_slashkeep= @/ + +endfunction + +" 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 +function s:NetrwOptionsSafe(islocal) + 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 + if &ft == "netrw" + keepalt NetrwKeepj doau FileType netrw + endif + +endfunction + +" s:NetrwOptionsRestore: restore options (based on prior s:NetrwOptionsSave) {{{2 +function 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("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 +endfunction + +" 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") +function s:NetrwSetSafeSetting(setting,safesetting) + + if a:setting =~ '^&' + exe "let settingval= ".a:setting + + if settingval != 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#msg#Notify('ERROR', printf("(s:NetrwRestoreSetting) doesn't know how to restore %s with a safesetting of type#%s", a:setting, type(a:safesetting))) + endif + endif + endif + +endfunction + +" 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 +function s:NetrwRestoreSetting(keepvar,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 + + + if setting != 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#msg#Notify('ERROR', printf("(s:NetrwRestoreSetting) doesn't know how to restore %s with a setting of type#%s", a:keepvar, type(a:setting))) + endif + endif + + exe "unlet ".a:keepvar + endif + +endfunction + +" NetrwStatusLine: {{{2 + +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 + +" 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 +function netrw#NetRead(mode,...) + + " 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 + + " 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 + + " NetRead: get temporary filename {{{3 + let tmpfile= s:GetTempfile("") + if tmpfile == "" + return + endif + + while ichoice <= a:0 + + " attempt to repeat with previous host-file-etc + if exists("b:netrw_lastfile") && a:0 == 0 + let choice = b:netrw_lastfile + let ichoice= ichoice + 1 + + else + exe "let choice= a:" . ichoice + + 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 '"' + 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 + call netrw#msg#Notify('ERROR', printf('Unbalanced string in filename "%s"', wholechoice)) + 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 + + " NetRead: Determine method of read (ftp, rcp, etc) {{{3 + call s:NetrwMethod(choice) + if !exists("b:netrw_method") || b:netrw_method < 0 + return + endif + if !s:NetrwValidateHostname(g:netrw_machine) + call netrw#msg#Notify('ERROR', printf('Rejecting invalid hostname: <%s>', g:netrw_machine)) + return + endif + let tmpfile= s:GetTempfile(b:netrw_fname) " apply correct suffix + + " Check whether or not NetrwBrowse() should be handling this request + if choice =~ "^.*[\/]$" && b:netrw_method != 5 && choice !~ '^https\=://' + NetrwKeepj call s:NetrwBrowse(0,choice) + 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 + " 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 netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_rcp_cmd." ".s:netrw_rcpmode." ".netrw#os#Escape(uid_machine.":".b:netrw_fname,1)." ".netrw#os#Escape(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> + 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 + if exists("g:netrw_ftpextracmd") + NetrwKeepj put =g:netrw_ftpextracmd + endif + call setline(line("$")+1,'get "'.netrw_fname.'" '.tmpfile) + if exists("g:netrw_port") && g:netrw_port != "" + call netrw#os#Execute(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".netrw#os#Escape(g:netrw_machine,1)." ".netrw#os#Escape(g:netrw_port,1)) + else + call netrw#os#Execute(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".netrw#os#Escape(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) !~ "^$" && getline(1) !~ '^Trying ' + let debugkeep = &debug + setl debug=msg + call netrw#msg#Notify('ERROR', getline(1)) + 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 + 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 + else + NetrwKeepj put ='open '.g:netrw_machine + 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") + NetrwKeepj put ='\"'.s:netrw_passwd.'\"' + endif + elseif exists("s:netrw_passwd") + NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"' + endif + endif + + if exists("g:netrw_ftpmode") && g:netrw_ftpmode != "" + NetrwKeepj put =g:netrw_ftpmode + endif + if exists("g:netrw_ftpextracmd") + NetrwKeepj put =g:netrw_ftpextracmd + endif + NetrwKeepj put ='get \"'.netrw_fname.'\" '.tmpfile + + " 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 netrw#os#Execute(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 netrw#msg#Notify('ERROR', getline(1)) + 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 + if exists("g:netrw_port") && g:netrw_port != "" + let useport= " ".g:netrw_scpport." ".g:netrw_port + else + let useport= "" + endif + " Using UNC notation in windows to get a unix like path. + " 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\):[/\\]\(.*\)$', '//' .. $COMPUTERNAME .. '/\1$/\2', '') + else + let tmpfile_get = tmpfile + endif + call netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_scp_cmd.useport." ".escape(netrw#os#Escape(g:netrw_machine.":".b:netrw_fname,1),' ')." ".netrw#os#Escape(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 + if g:netrw_http_cmd == "" + call netrw#msg#Notify('ERROR', 'neither the wget nor the fetch command is available') + 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) + if exists("g:netrw_http_xcmd") + call netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_http_cmd." ".netrw#os#Escape(b:netrw_http."://".g:netrw_machine.b:netrw_fname,1)." ".g:netrw_http_xcmd." ".netrw#os#Escape(tmpfile,1)) + else + call netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_http_cmd." ".netrw#os#Escape(tmpfile,1)." ".netrw#os#Escape(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) + let netrw_html= substitute(b:netrw_fname,"#.*$","","") + let netrw_tag = substitute(b:netrw_fname,"^.*#","","") + call netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_http_cmd." ".netrw#os#Escape(tmpfile,1)." ".netrw#os#Escape(b:netrw_http."://".g:netrw_machine.netrw_html,1)) + let result = s:NetrwGetFile(readcmd, tmpfile, b:netrw_method) + exe 'NetrwKeepj norm! 1G/<\s*a\s*name=\s*"'.netrw_tag.'"/'."\<CR>" + endif + let b:netrw_lastfile = choice + setl ro nomod + + "......................................... + " NetRead: (dav) NetRead Method #6 {{{3 + elseif b:netrw_method == 6 + + if !executable(g:netrw_dav_cmd) + call netrw#msg#Notify('ERROR', printf('%s is not executable', g:netrw_dav_cmd)) + return + endif + if g:netrw_dav_cmd =~ "curl" + call netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_dav_cmd." ".netrw#os#Escape("dav://".g:netrw_machine.b:netrw_fname,1)." ".netrw#os#Escape(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 netrw#os#Execute(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 netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_rsync_cmd." ".netrw#os#Escape(g:netrw_machine.g:netrw_rsync_sep.b:netrw_fname,1)." ".netrw#os#Escape(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 + if g:netrw_fetch_cmd == "" + call netrw#msg#Notify('ERROR', "fetch command not available") + return + endif + if exists("g:netrw_option") && g:netrw_option =~ ":https\=" + let netrw_option= "http" + else + let netrw_option= "ftp" + endif + + if exists("g:netrw_uid") && g:netrw_uid != "" && exists("s:netrw_passwd") && s:netrw_passwd != "" + call netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_fetch_cmd." ".netrw#os#Escape(tmpfile,1)." ".netrw#os#Escape(netrw_option."://".g:netrw_uid.':'.s:netrw_passwd.'@'.g:netrw_machine."/".b:netrw_fname,1)) + else + call netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_fetch_cmd." ".netrw#os#Escape(tmpfile,1)." ".netrw#os#Escape(netrw_option."://".g:netrw_machine."/".b:netrw_fname,1)) + endif + + let result = s:NetrwGetFile(readcmd,tmpfile, b:netrw_method) + let b:netrw_lastfile = choice + setl ro nomod + + "......................................... + " NetRead: (sftp) NetRead Method #9 {{{3 + elseif b:netrw_method == 9 + call netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_sftp_cmd." ".netrw#os#Escape(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 netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_file_cmd." ".netrw#os#Escape(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#msg#Notify('WARNING', printf('unable to comply with your request<%s>', choice)) + endif + endwhile + + " NetRead: cleanup {{{3 + if exists("b:netrw_method") + 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 netrw#fs#Remove(tmpfile) + endif + NetrwKeepj call s:NetrwOptionsRestore("w:") + +endfunction + +" netrw#NetWrite: responsible for writing a file over the net {{{2 +function netrw#NetWrite(...) range + + " 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 == "" + return + endif + + if a:0 == 0 + let ichoice = 0 + else + let ichoice = 1 + endif + + let curbufname= expand("%") + 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. + 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:','') + exe "sil NetrwKeepj ".a:firstline."," . a:lastline . "w! ".fnameescape(v:cmdarg)." ".fnameescape(cygtmpfile) + else + " write (selected portion of) file to temporary + 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 + 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 + call netrw#msg#Notify('ERROR', printf('Unbalanced string in filename "%s"', wholechoice)) + 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 + + " Determine method of write (ftp, rcp, etc) {{{4 + NetrwKeepj call s:NetrwMethod(choice) + if !exists("b:netrw_method") || b:netrw_method < 0 + return + endif + if !s:NetrwValidateHostname(g:netrw_machine) + call netrw#msg#Notify('ERROR', printf('Rejecting invalid hostname: <%s>', g:netrw_machine)) + 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..." + endif + + "......................................... + " NetWrite: (rcp) NetWrite Method #1 {{{3 + if b:netrw_method == 1 + 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 netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_rcp_cmd." ".s:netrw_rcpmode." ".netrw#os#Escape(tmpfile,1)." ".netrw#os#Escape(uid_machine.":".b:netrw_fname,1)) + let b:netrw_lastfile = choice + + "......................................... + " NetWrite: (ftp + <.netrc>) NetWrite Method #2 {{{3 + elseif b:netrw_method == 2 + 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 + + setl ff=unix + NetrwKeepj put =g:netrw_ftpmode + if exists("g:netrw_ftpextracmd") + NetrwKeepj put =g:netrw_ftpextracmd + endif + NetrwKeepj call setline(line("$")+1,'put "'.tmpfile.'" "'.netrw_fname.'"') + if exists("g:netrw_port") && g:netrw_port != "" + call netrw#os#Execute(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".netrw#os#Escape(g:netrw_machine,1)." ".netrw#os#Escape(g:netrw_port,1)) + else + call netrw#os#Execute(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".netrw#os#Escape(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) !~ "^$" + call netrw#msg#Notify('ERROR', getline(1)) + 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 + 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 + else + NetrwKeepj put ='open '.g:netrw_machine + 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") && s:netrw_passwd != "" + NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"' + endif + endif + NetrwKeepj put =g:netrw_ftpmode + if exists("g:netrw_ftpextracmd") + NetrwKeepj put =g:netrw_ftpextracmd + endif + NetrwKeepj put ='put \"'.tmpfile.'\" \"'.netrw_fname.'\"' + " 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 netrw#os#Execute(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 netrw#msg#Notify('ERROR', getline(1)) + 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 + if exists("g:netrw_port") && g:netrw_port != "" + let useport= " ".g:netrw_scpport." ".fnameescape(g:netrw_port) + else + let useport= "" + endif + call netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_scp_cmd.useport." ".netrw#os#Escape(tmpfile,1)." ".netrw#os#Escape(g:netrw_machine.":".b:netrw_fname,1)) + let b:netrw_lastfile = choice + + "......................................... + " NetWrite: (http) NetWrite Method #5 {{{3 + elseif b:netrw_method == 5 + let curl= substitute(g:netrw_http_put_cmd,'\s\+.*$',"","") + if executable(curl) + let url= g:netrw_choice + call netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_http_put_cmd." ".netrw#os#Escape(tmpfile,1)." ".netrw#os#Escape(url,1) ) + else + call netrw#msg#Notify('ERROR', printf("can't write to http using <%s>", g:netrw_http_put_cmd)) + endif + + "......................................... + " NetWrite: (dav) NetWrite Method #6 (cadaver) {{{3 + elseif b:netrw_method == 6 + + " 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 netrw#os#Execute(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 netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_rsync_cmd." ".netrw#os#Escape(tmpfile,1)." ".netrw#os#Escape(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 + 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) + let sftpcmd= substitute(g:netrw_sftp_cmd,"%TEMPFILE%",escape(tmpfile,'\'),"g") + call netrw#os#Execute(s:netrw_silentxfer."%!".sftpcmd.' '.netrw#os#Escape(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#msg#Notify('WARNING', printf('unable to comply with your request<%s>', choice)) + let leavemod= 1 + endif + endwhile + + " NetWrite: Cleanup: {{{3 + if s:FileReadable(tmpfile) + call netrw#fs#Remove(tmpfile) + endif + call s:NetrwOptionsRestore("w:") + + if a:firstline == 1 && a:lastline == line("$") + " restore modifiability; usually equivalent to set nomod + let &l:mod= mod + elseif !exists("leavemod") + " indicate that the buffer has not been modified since last written + setl nomod + endif + +endfunction + +" 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. +function netrw#NetSource(...) + 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}) + if s:FileReadable(s:netrw_tmpfile) + exe "so ".fnameescape(s:netrw_tmpfile) + if delete(s:netrw_tmpfile) + call netrw#msg#Notify('ERROR', 'unable to delete directory <%s>', s:netrw_tmpfile) + endif + unlet s:netrw_tmpfile + else + call netrw#msg#Notify('ERROR', printf('unable to source <%s>!', a:{i})) + endif + let i= i + 1 + endwhile + endif +endfunction + +" netrw#SetTreetop: resets the tree top to the current directory/specified directory {{{2 +" (implements the :Ntree command) +function 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#msg#Notify('ERROR', printf("sorry, %s doesn't seem to be a directory!", a:1)) + 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 + +endfunction + +" 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) +function s:NetrwGetFile(readcmd, tfile, method) + + " readcmd=='t': simply do nothing + if a:readcmd == 't' + return + endif + + " get name of remote filename (ie. url and all) + let rfile= bufname("%") + + if exists("*NetReadFixup") + " for the use of NetReadFixup (not otherwise used internally) + let line2= line("$") + endif + + if a:readcmd[0] == '%' + " get file into buffer + + " 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 zip#Browse(tfile) + elseif rfile =~ '\.tar$' + call tar#Browse(tfile) + elseif rfile =~ '\.tar\.gz$' + call tar#Browse(tfile) + elseif rfile =~ '\.tar\.bz2$' + call tar#Browse(tfile) + elseif rfile =~ '\.tar\.xz$' + call tar#Browse(tfile) + elseif rfile =~ '\.txz$' + call tar#Browse(tfile) + else + 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. + " setl ft= + let iskkeep= &isk + setl isk-=/ + filetype detect + let &l:isk= iskkeep + 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 + call netrw#msg#Notify('WARNING', printf('attempt to read<%s> into a non-modifiable buffer!', a:tfile)) + return + + elseif s:FileReadable(a:tfile) + " read file after current line + let curline = line(".") + let lastline= line("$") + exe "NetrwKeepj ".a:readcmd." ".fnameescape(v:cmdarg)." ".fnameescape(a:tfile) + let line1= curline + 1 + let line2= line("$") - lastline + 1 + + else + " not readable + call netrw#msg#Notify('WARNING', printf('file <%s> not readable', a:tfile)) + return + endif + + " User-provided (ie. optional) fix-it-up command + if exists("*NetReadFixup") + NetrwKeepj call NetReadFixup(a:method, line1, line2) + endif + + if has("gui") && has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu + " update the Buffers menu + NetrwKeepj call s:UpdateBuffersMenu() + endif + + + " make sure file is being displayed + " redraw! + +endfunction + +" 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) +function s:NetrwMethod(choice) + + " sanity check: choice should have at least three slashes in it + if strlen(substitute(a:choice,'[^/]','','g')) < 3 + call netrw#msg#Notify('ERROR', 'not a netrw-style url; netrw uses protocol://[user@]hostname[:port]/[path])') + let b:netrw_method = -1 + 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 + 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\=://\(.*\)$' + + " Determine Method + " Method#1: rcp://user@hostname/...path-to-file {{{3 + if match(a:choice,rcpurm) == 0 + 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 + 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 + 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 + 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 + 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 + 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',"") + if userid != "" + let g:netrw_uid= userid + endif + + if curmachine != g:netrw_machine + if exists("s:netrw_hup[".g:netrw_machine."]") + call netrw#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 netrw#NetUserPass("ftp:".host) + + elseif has("win32") && s:netrw_ftp_cmd =~# '-[sS]:' + if g:netrw_ftp_cmd =~# '-[sS]:\S*MACHINE\>' + let s:netrw_ftp_cmd= substitute(g:netrw_ftp_cmd,'\<MACHINE\>',g:netrw_machine,'') + endif + let b:netrw_method= 2 + elseif s:FileReadable(expand("$HOME/.netrc")) && !g:netrw_ignorenetrc + let b:netrw_method= 2 + else + if !exists("g:netrw_uid") || g:netrw_uid == "" + call netrw#NetUserPass() + elseif !exists("s:netrw_passwd") || s:netrw_passwd == "" + call netrw#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 + 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 + 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 netrw#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 + 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 + 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 + 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',"") + 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") + let b:netrw_method = 10 + let b:netrw_fname = substitute(a:choice,fileurm,'\1',"") + + " Cannot Determine Method {{{3 + else + call netrw#msg#Notify('WARNING', 'cannot determine method (format: protocol://[user@]hostname[:port]/[path])') + 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 + +endfunction + +" s:NetrwValidateHostname: Validate that the hostname is valid {{{2 +" Input: +" hostname, may include an optional username and port number, e.g. +" user@hostname:port +" allow a alphanumeric hostname or an IPv(4/6) address +" Output: +" true if g:netrw_machine is valid according to RFC1123 #Section 2 +function s:NetrwValidateHostname(hostname) + " Username: + let user_pat = '\%([a-zA-Z0-9._-]\+@\)\?' + " Hostname: 1-64 chars, alphanumeric/dots/hyphens. + " No underscores. No leading/trailing dots/hyphens. + let host_pat = '[a-zA-Z0-9]\%([-a-zA-Z0-9.]\{0,62}[a-zA-Z0-9]\)\?' + " Port: 16 bit unsigned integer + let port_pat = '\%(:\d\{1,5\}\)\?$' + + " IPv4: 1-3 digits separated by dots + let ipv4_pat = '\%(\d\{1,3}\.\)\{3\}\d\{1,3\}' + + " IPv6: Hex, colons, and optional brackets + let ipv6_pat = '\[\?\%([a-fA-F0-9:]\{2,}\)\+\]\?' + + return a:hostname =~? '^'.user_pat.host_pat.port_pat || + \ a:hostname =~? '^'.user_pat.ipv4_pat.port_pat || + \ a:hostname =~? '^'.user_pat.ipv6_pat.port_pat +endfunction + +" NetUserPass: set username and password for subsequent ftp transfer {{{2 +" Usage: :call netrw#NetUserPass() -- will prompt for userid and password +" :call netrw#NetUserPass("uid") -- will prompt for password +" :call netrw#NetUserPass("uid","password") -- sets global userid and password +" :call netrw#NetUserPass("ftp:host") -- looks up userid and password using hup dictionary +" :call netrw#NetUserPass("host","uid","password") -- sets hup dictionary with host, userid, password +function netrw#NetUserPass(...) + + + 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 + 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 + 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. + 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 + 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 + + 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 + endif + +endfunction + +" Shared Browsing Support: {{{1 + +" s:ExplorePatHls: converts an Explore pattern into a regular expression search pattern {{{2 +function s:ExplorePatHls(pattern) + let repat= substitute(a:pattern,'^**/\{1,2}','','') + let repat= escape(repat,'][.\') + let repat= '\<'.substitute(repat,'\*','\\(\\S\\+ \\)*\\S\\+','g').'\>' + return repat +endfunction + +" 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 +function s:NetrwBookHistHandler(chg,curdir) + if !exists("g:netrw_dirhistmax") || g:netrw_dirhistmax <= 0 + return + endif + + let ykeep = @@ + let curbufnr = bufnr("%") + + if a:chg == 0 + " bookmark the current directory + 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 + if exists("g:netrw_bookmarklist[v:count-1]") + 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 + if exists("g:netrw_bookmarklist") + let cnt= 1 + for bmd in g:netrw_bookmarklist + 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 ) + if exists("g:netrw_dirhist_{cnt}") + 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) + 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 + endif + + elseif a:chg == 4 + " u: change to the previous directory stored on the history list + 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}") + if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("b:netrw_curdir") + setl ma noro + sil! NetrwKeepj %d _ + setl nomod + endif + 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 + if g:netrw_dirhistmax > 0 + let g:netrw_dirhistcnt= ( g:netrw_dirhistcnt + 1 ) % g:netrw_dirhistmax + if exists("g:netrw_dirhist_{g:netrw_dirhistcnt}") + if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("b:netrw_curdir") + setl ma noro + sil! NetrwKeepj %d _ + setl nomod + endif + 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 + 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 s:MergeBookmarks() + NetrwKeepj call remove(g:netrw_bookmarklist,iremove-1) + echo "removed ".dremove." from g:netrw_bookmarklist" + endif + + try + call s:NetrwBookHistSave() + catch + endtry + endif + call s:NetrwBookmarkMenu() + call s:NetrwTgtMenu() + let @@= ykeep +endfunction + +" 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() +function s:NetrwBookHistRead() + if !exists("g:netrw_dirhistmax") || g:netrw_dirhistmax <= 0 + return + endif + let ykeep= @@ + + " read bookmarks + if !exists("s:netrw_initbookhist") + let home = s:NetrwHome() + let savefile= home."/.netrwbook" + if filereadable(s:NetrwFile(savefile)) + exe "keepalt NetrwKeepj so ".savefile + endif + + " read history + if g:netrw_dirhistmax > 0 + let savefile= home."/.netrwhist" + if filereadable(s:NetrwFile(savefile)) + exe "keepalt NetrwKeepj so ".savefile + endif + let s:netrw_initbookhist= 1 + au VimLeave * call s:NetrwBookHistSave() + endif + endif + + let @@= ykeep +endfunction + +" 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) +function s:NetrwBookHistSave() + if !exists("g:netrw_dirhistmax") || g:netrw_dirhistmax <= 0 + return + endif + + let savefile= s:NetrwHome()."/.netrwhist" + 1split + + " setting up a new buffer which will become .netrwhist + call s:NetrwEnew() + 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 + 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}."'") + 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 + endif + + " save bookmarks + sil NetrwKeepj %d _ + if exists("g:netrw_bookmarklist") && g:netrw_bookmarklist != [] + " 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 + endif + + " cleanup -- remove buffer used to construct history + let bgone= bufnr("%") + q! + exe "keepalt ".bgone."bwipe!" + +endfunction + +" 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() +function 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") + call netrw#msg#Notify('ERROR', "netrw can't run -- your vim is missing shellescape()") + return + endif + if !exists("*fnameescape") + call netrw#msg#Notify('ERROR', "netrw can't run -- your vim is missing fnameescape()") + 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 =~ '\.xz$' + exe "sil NetrwKeepj keepalt doau BufReadPost ".fnameescape(substitute(s:fname,'\.xz$','','')) + 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) && exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST + 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 + call netrw#msg#Notify('ERROR', printf("netrw doesn't understand your dirname<%s>", dirname)) + 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 + + " 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 +endfunction + +" 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 +function s:NetrwFile(fname) + + " clean up any leading treedepthstring + if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST + let fname= substitute(a:fname,'^'.s:treedepthstring.'\+','','') + 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 !g:netrw_cygwin && has("win32") + if isabsolutepath(fname) + " windows, but full path given + let ret= fname + else + " windows, relative path given + let ret= netrw#fs#ComposePath(b:netrw_curdir,fname) + endif + + elseif fname =~ '^/' + " not windows, full path given + let ret= fname + else + " not windows, relative path given + let ret= netrw#fs#ComposePath(b:netrw_curdir,fname) + endif + else + " vim and netrw agree on the current directory + let ret= fname + endif + + return ret +endfunction + +" s:NetrwFileInfo: supports qf (query for file information) {{{2 +function s:NetrwFileInfo(islocal,fname) + 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 + if (has("unix") || has("macunix")) && executable("/bin/ls") + + if getline(".") == "../" + echo system("/bin/ls ".lsopt." ".netrw#os#Escape("..")) + + elseif w:netrw_liststyle == s:TREELIST && getline(".") !~ '^'.s:treedepthstring + echo system("/bin/ls ".lsopt." ".netrw#os#Escape(b:netrw_curdir)) + + elseif exists("b:netrw_curdir") + echo system("/bin/ls ".lsopt." ".netrw#os#Escape(netrw#fs#ComposePath(b:netrw_curdir,a:fname))) + + else + echo system("/bin/ls ".lsopt." ".netrw#os#Escape(s:NetrwFile(a:fname))) + endif + else + " use vim functions to return information about file below cursor + 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))) + endif + else + echo "sorry, \"qf\" not supported yet for remote files" + endif + let @@= ykeep +endfunction + +" 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) +function s:NetrwGetBuffer(islocal,dirname) + let dirname= a:dirname + + " re-use buffer if possible {{{3 + if !exists("s:netrwbuf") + let s:netrwbuf= {} + endif + + if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST + let bufnum = -1 + + if !empty(s:netrwbuf) && has_key(s:netrwbuf,netrw#fs#AbsPath(dirname)) + if has_key(s:netrwbuf,"NetrwTreeListing") + let bufnum= s:netrwbuf["NetrwTreeListing"] + else + let bufnum= s:netrwbuf[netrw#fs#AbsPath(dirname)] + endif + if !bufexists(bufnum) + call remove(s:netrwbuf,"NetrwTreeListing") + let bufnum= -1 + endif + elseif bufnr("NetrwTreeListing") != -1 + let bufnum= bufnr("NetrwTreeListing") + else + let bufnum= -1 + endif + + elseif has_key(s:netrwbuf,netrw#fs#AbsPath(dirname)) + let bufnum= s:netrwbuf[netrw#fs#AbsPath(dirname)] + if !bufexists(bufnum) + call remove(s:netrwbuf,netrw#fs#AbsPath(dirname)) + let bufnum= -1 + endif + + else + let bufnum= -1 + endif + + " 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 + if dirname == curbuf && line("$") == 1 && getline("%") == "" + return 0 + else " DEBUG + 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 s:NetrwEnew(dirname) + " name the buffer + if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST + " Got enew buffer; transform into a NetrwTreeListing + 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> + else + call s:NetrwBufRename(dirname) + " enter the new buffer into the s:netrwbuf dictionary + let s:netrwbuf[netrw#fs#AbsPath(dirname)]= bufnr("%") + endif + + else " Re-use the buffer + " ignore all events + let eikeep= &ei + setl ei=all + + if &ft == "netrw" + exe "sil! NetrwKeepj noswapfile b ".bufnum + else + call s:NetrwEditBuf(bufnum) + endif + 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) + return 0 + + elseif g:netrw_fastbrowse == 0 || (a:islocal && g:netrw_fastbrowse == 1) + NetrwKeepj call s:NetrwListSettings(a:islocal) + sil NetrwKeepj %d _ + return 0 + + elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST + setl ma + sil NetrwKeepj %d _ + NetrwKeepj call s:NetrwListSettings(a:islocal) + return 0 + + else + 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 + let fname= expand("%") + NetrwKeepj call s:NetrwListSettings(a:islocal) + call s:NetrwBufRename(fname) + + " delete all lines from buffer {{{3 + sil! keepalt NetrwKeepj %d _ + + return 0 +endfunction + +" s:NetrwGetWord: it gets the directory/file named under the cursor {{{2 +function s:NetrwGetWord() + 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 + endif + + if exists("w:netrw_bannercnt") && line(".") < w:netrw_bannercnt + " Active Banner support + 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 + NetrwKeepj norm! 0 + let dirname= substitute(getline('.'),'\t -->.*$','','') + + elseif w:netrw_liststyle == s:LONGLIST + NetrwKeepj norm! 0 + let dirname= substitute(getline('.'),'^\(\%(\S\+ \)*\S\+\).\{-}$','\1','e') + + elseif exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST + let dirname= substitute(getline('.'),'^\('.s:treedepthstring.'\)*','','e') + let dirname= substitute(dirname,'\t -->.*$','','') + + else + 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) + endif + + let filestart = (virtcol(".")/b:netrw_cpf)*b:netrw_cpf + 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) + + let dirname= substitute(dirname,'\s\+$','','e') + 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 + + return dirname +endfunction + +" 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() +function s:NetrwListSettings(islocal) + let fname= bufname("%") + " 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 + exe "setl ts=".(g:netrw_maxfilenamelen+1) + setl isk+=.,~,- + if g:netrw_fastbrowse > a:islocal + setl bh=hide + else + setl bh=delete + endif +endfunction + +" s:NetrwListStyle: change list style (thin - long - wide - tree) {{{2 +" islocal=0: remote browsing +" =1: local browsing +function 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 + call netrw#msg#Notify('WARNING', printf('bad value for g:netrw_liststyle (=%s)', w:netrw_liststyle)) + 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 + NetrwKeepj call winrestview(svpos) + let @@= ykeep + +endfunction + +" s:NetrwBannerCtrl: toggles the display of the banner {{{2 +function 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') + if result <= 0 && exists("w:netrw_bannercnt") + exe "NetrwKeepj ".w:netrw_bannercnt + endif + endif + let @@= ykeep +endfunction + +" 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 +function 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 + let mbfiles = glob(fnameescape(a:{i}), 0, 1, 1) + 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() +endfunction + +" s:NetrwBookmarkMenu: Uses menu priorities {{{2 +" .2.[cnt] for bookmarks, and +" .3.[cnt] for history +" (see s:NetrwMenu()) +function s:NetrwBookmarkMenu() + if !exists("s:netrw_menucnt") + return + endif + + " 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") + 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 + 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) + 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 +endfunction + +" 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 +function s:NetrwBrowseChgDir(islocal, newdir, cursor, ...) + let ykeep= @@ + if !exists("b:netrw_curdir") + 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 + let dirname = b:netrw_curdir + + if a:cursor && w:netrw_liststyle == s:TREELIST + " dirname is the path to the word under the cursor + let dirname = s:NetrwTreePath(w:netrw_treetop) + " 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 != "../" + let dirname = fnamemodify(dirname, ":h") + endif + 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 + let dirpat = has("amiga") ? '[\/:]$' : '[\/]$' + + if newdir !~ dirpat && !(a:islocal && isdirectory(s:NetrwFile(netrw#fs#ComposePath(dirname, newdir)))) + " ------------------------------ + " NetrwBrowseChgDir: edit a file {{{3 + " ------------------------------ + + " save position for benefit of Rexplore + let s:rexposn_{bufnr("%")}= winsaveview() + + let dirname = isabsolutepath(newdir) + \ ? netrw#fs#AbsPath(newdir) + \ : netrw#fs#ComposePath(dirname, newdir) + + " 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 _ + else + exe "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 | + else + exe "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 + 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 + " -------------------------------------- + + " The following regexps expect '/' as path separator + let dirname = substitute(netrw#fs#AbsPath(dirname), '\\', '/', 'ge') . '/' + + 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 = netrw#fs#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 +endfunction + +" s:NetrwBrowseUpDir: implements the "-" mappings {{{2 +" for thin, long, and wide: cursor placed just after banner +" for tree, keeps cursor on current filename +function 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 +endfunction + +" netrw#BrowseX: (implements "x") executes a special "viewer" script or program for the {{{2 +" given filename; typically this means given their extension. +function netrw#BrowseX(fname) + " special core dump handler + if a:fname =~ '/core\(\.\d\+\)\=$' && exists("g:Netrw_corehandler") + if type(g:Netrw_corehandler) == v:t_func + " g:Netrw_corehandler is a function reference (see :help Funcref) + call g:Netrw_corehandler(s:NetrwFile(a:fname)) + elseif type(g:Netrw_corehandler) == v:t_list + " g:Netrw_corehandler is a List of function references (see :help Funcref) + for Fncref in g:Netrw_corehandler + if type(Fncref) == v:t_func + call Fncref(a:fname) + endif + endfor + endif + return + endif + + let fname = a:fname + " special ~ handler for local + if fname =~ '^\~' && expand("$HOME") != "" + let fname = substitute(fname, '^\~', expand("$HOME"), '') + endif + + if fname =~ '^[a-z]\+://' + " open a remote file + call netrw#os#Open(fname) + else + call netrw#os#Open(s:NetrwFile(fname)) + endif +endfunction + +" 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. +function s:NetrwBufRename(newname) + let oldbufname= bufname(bufnr("%")) + + if oldbufname != a:newname + let b:junk= 1 + exe 'sil! keepj keepalt file '.fnameescape(a:newname) + let oldbufnr= bufnr(oldbufname) + if oldbufname != "" && oldbufnr != -1 && oldbufnr != bufnr("%") + exe "bwipe! ".oldbufnr + endif + endif + +endfunction + +" netrw#CheckIfRemote: returns 1 if current file looks like an url, 0 else {{{2 +function netrw#CheckIfRemote(...) + if a:0 > 0 + let curfile= a:1 + else + let curfile= expand("%") + endif + if curfile =~ '^\a\{3,}://' + return 1 + else + return 0 + endif +endfunction + +" s:NetrwChgPerm: (implements "gp") change file permission {{{2 +function s:NetrwChgPerm(islocal,curdir) + let ykeep = @@ + call inputsave() + let newperm= input("Enter new permission: ") + call inputrestore() + let fullpath = fnamemodify(netrw#fs#PathJoin(a:curdir, expand("<cfile>")), ':p') + let chgperm= substitute(g:netrw_chgperm,'\<FILENAME\>',netrw#os#Escape(fullpath),'') + let chgperm= substitute(chgperm,'\<PERM\>',netrw#os#Escape(newperm),'') + call system(chgperm) + if v:shell_error != 0 + NetrwKeepj call netrw#msg#Notify('WARNING', printf('changing permission on file<%s> seems to have failed', fullpath)) + endif + if a:islocal + NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) + endif + let @@= ykeep +endfunction + +" s:NetrwClearExplore: clear explore variables (if any) {{{2 +function 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! +endfunction + +" s:NetrwEditBuf: decides whether or not to use keepalt to edit a buffer {{{2 +function s:NetrwEditBuf(bufnum) + if exists("g:netrw_altfile") && g:netrw_altfile && &ft == "netrw" + exe "sil! NetrwKeepj keepalt noswapfile b ".fnameescape(a:bufnum) + else + exe "sil! NetrwKeepj noswapfile b ".fnameescape(a:bufnum) + endif +endfunction + +" s:NetrwEditFile: decides whether or not to use keepalt to edit a file {{{2 +" NetrwKeepj [keepalt] <OPT> <CMD> <FILENAME> +function s:NetrwEditFile(cmd,opt,fname) + if exists("g:netrw_altfile") && g:netrw_altfile && &ft == "netrw" + exe "NetrwKeepj noswapfile keepalt ".a:opt." ".a:cmd." ".fnameescape(a:fname) + else + if a:cmd =~# 'e\%[new]!' && !&hidden && getbufvar(bufname('%'), '&modified', 0) + call setbufvar(bufname('%'), '&bufhidden', 'hide') + endif + exe "NetrwKeepj noswapfile ".a:opt." ".a:cmd." ".fnameescape(a:fname) + endif +endfunction + +" s:NetrwExploreListUniq: {{{2 +function 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 +endfunction + +" s:NetrwForceChgDir: (gd support) Force treatment as a directory {{{2 +function 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 +endfunction + +" s:NetrwForceFile: (gf support) Force treatment as a file {{{2 +function 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 +endfunction + +" 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 +function 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 == "" + call netrw#msg#Notify('WARNING', 'your hiding list is empty!') + let @@= ykeep + return + endif + endif + + NetrwKeepj call s:NetrwRefresh(a:islocal,s:NetrwBrowseChgDir(a:islocal,'./',0)) + NetrwKeepj call winrestview(svpos) + let @@= ykeep +endfunction + +" s:NetrwHideEdit: allows user to edit the file/directory hiding list {{{2 +function 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 +endfunction + +" s:NetrwHidden: invoked by "gh" {{{2 +function 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 strdisplaywidth(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 +endfunction + +" s:NetrwHome: this function determines a "home" for saving bookmarks and history {{{2 +function s:NetrwHome() + if has('nvim') + let home = netrw#fs#PathJoin(stdpath('state'), 'netrw') + elseif exists('g:netrw_home') + let home = expand(g:netrw_home) + elseif exists('$MYVIMDIR') + let home = expand('$MYVIMDIR')->substitute('/$', '', '') + else + " Pick the first redable directory in 'runtimepath' + for path in split(&rtp, ',') + if isdirectory(s:NetrwFile(path)) && filewritable(s:NetrwFile(path)) + let home = path + break + endif + endfor + + if empty(path) + " just pick the first directory + let home = substitute(&rtp, ',.*$', '', '') + endif + endif + + " insure that the home directory exists + if g:netrw_dirhistmax > 0 && !isdirectory(s:NetrwFile(home)) + if exists("g:netrw_mkdir") + call system(g:netrw_mkdir." ".s:ShellEscape(s:NetrwFile(home))) + else + call mkdir(home) + endif + endif + + " Normalize directory if on Windows + if has("win32") + let home = substitute(home, '/', '\\', 'g') + endif + + let g:netrw_home = home + return home +endfunction + +" s:NetrwLeftmouse: handles the <leftmouse> when in a netrw browsing window {{{2 +function 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 +endfunction + +" s:NetrwCLeftmouse: used to select a file/directory for a target {{{2 +function s:NetrwCLeftmouse(islocal) + if &ft != "netrw" + return + endif + call s:NetrwMarkFileTgt(a:islocal) +endfunction + +" 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 +function s:NetrwServerEdit(islocal,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#msg#Notify('ERROR', 'you need a gui-capable vim and client-server to use <ctrl-r>') + endif + +endfunction + +" s:NetrwSLeftmouse: marks the file under the cursor. May be dragged to select additional files {{{2 +function s:NetrwSLeftmouse(islocal) + if &ft != "netrw" + return + endif + + let s:ngw= s:NetrwGetWord() + call s:NetrwMarkFile(a:islocal,s:ngw) + +endfunction + +" s:NetrwSLeftdrag: invoked via a shift-leftmouse and dragging {{{2 +" Used to mark multiple files. +function s:NetrwSLeftdrag(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 +endfunction + +" s:NetrwSLeftrelease: terminates shift-leftmouse dragging {{{2 +function s:NetrwSLeftrelease(islocal) + 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 +endfunction + +" s:NetrwListHide: uses [range]g~...~d to delete files that match {{{2 +" comma-separated patterns given in g:netrw_list_hide +function s:NetrwListHide() + 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. + let listhide= g:netrw_list_hide + let sep = strpart(substitute('~@#$%^&*{};:,<.>?|1234567890','['.escape(listhide,'-]^\').']','','ge'),1,1) + + while listhide != "" + if listhide =~ ',' + let hide = substitute(listhide,',.*$','','e') + let listhide = substitute(listhide,'^.\{-},\(.*\)$','\1','e') + else + let hide = listhide + let listhide = "" + endif + if g:netrw_sort_by =~ '^[ts]' + if hide =~ '^\^' + let hide= substitute(hide,'^\^','^\(\\d\\+/\)','') + elseif hide =~ '^\\(\^' + let hide= substitute(hide,'^\\(\^','\\(^\\(\\d\\+/\\)','') + endif + endif + + " Prune the list by hiding any files which match + if g:netrw_hide == 1 + exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$g'.sep.hide.sep.'d' + elseif g:netrw_hide == 2 + exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$g'.sep.hide.sep.'s@^@ /-KEEP-/ @' + endif + endwhile + + if g:netrw_hide == 2 + exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$v@^ /-KEEP-/ @d' + exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s@^\%( /-KEEP-/ \)\+@@e' + endif + + " remove any blank lines that have somehow remained. + " This seems to happen under Windows. + exe 'sil! NetrwKeepj 1,$g@^\s*$@d' + + let @@= ykeep +endfunction + +" s:NetrwMakeDir: this function makes a directory (both local and remote) {{{2 +" implements the "d" mapping. +function 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)) + call netrw#msg#Notify('WARNING', printf('<%s> is already a directory!', newdirname)) + let @@= ykeep + return + endif + if s:FileReadable(fullnewdir) + call netrw#msg#Notify('WARNING', printf('<%s> is already a file!', newdirname)) + let @@= ykeep + return + endif + + " requested new local directory is neither a pre-existing file or + " directory, so make it! + if has("unix") + call mkdir(fullnewdir,"p",xor(0777, system("umask"))) + else + call mkdir(fullnewdir,"p") + endif + + " on success refresh listing + let svpos= winsaveview() + call s:NetrwRefresh(1,s:NetrwBrowseChgDir(1,'./',0)) + call winrestview(svpos) + + 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 netrw#os#Execute("sil! !".mkdircmd." ".netrw#os#Escape(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) + else + call netrw#msg#Notify('ERROR', printf('unable to make directory<%s>', newdirname)) + 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 +endfunction + +" s:TreeSqueezeDir: allows a shift-cr (gvim only) to squeeze the current tree-listing directory {{{2 +function 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 +endfunction + +" s:NetrwMaps: {{{2 +function s:NetrwMaps(islocal) + + " mouse <Plug> maps: {{{3 + if g:netrw_mousemaps && g:netrw_retmap + 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> 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))"<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> 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> + 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 +endfunction + +" 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. +function s:NetrwCommands(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#msg#Notify('WARNING', "win#".winnr()." not a former netrw window")|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>) + +endfunction + +" s:NetrwMarkFiles: apply s:NetrwMarkFile() to named file(s) {{{2 +" glob()ing only works with local files +function s:NetrwMarkFiles(islocal,...) + let curdir = s:NetrwGetCurdir(a:islocal) + let i = 1 + while i <= a:0 + if a:islocal + let mffiles= glob(a:{i}, 0, 1, 1) + else + let mffiles= [a:{i}] + endif + for mffile in mffiles + call s:NetrwMarkFile(a:islocal,mffile) + endfor + let i= i + 1 + endwhile +endfunction + +" s:NetrwMarkTarget: implements :MT (mark target) {{{2 +function 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) +endfunction + +" 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 +function s:NetrwMarkFile(islocal,fname) + + " sanity check + if empty(a: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 + let b:netrw_islocal= a:islocal + + if index(s:netrwmarkfilelist_{curbufnr},a:fname) == -1 + " append filename to buffer's markfilelist + 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 filter(s:netrwmarkfilelist_{curbufnr},'v:val != a:fname') + if s:netrwmarkfilelist_{curbufnr} == [] + " local markfilelist is empty; remove it entirely + call s:NetrwUnmarkList(curbufnr,curdir) + else + " rebuild match list to display markings correctly + 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 + endif + endif + + else + " initialize new markfilelist + + let s:netrwmarkfilelist_{curbufnr}= [] + call add(s:netrwmarkfilelist_{curbufnr},substitute(a:fname,'[|@]$','','')) + + " 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 + endif + + " handle global markfilelist + if exists("s:netrwmarkfilelist") + let dname= netrw#fs#ComposePath(b:netrw_curdir,a:fname) + if index(s:netrwmarkfilelist,dname) == -1 + " append new filename to global markfilelist + call add(s:netrwmarkfilelist,netrw#fs#ComposePath(b:netrw_curdir,a:fname)) + else + " remove new filename from global markfilelist + call filter(s:netrwmarkfilelist,'v:val != "'.dname.'"') + if s:netrwmarkfilelist == [] + unlet s:netrwmarkfilelist + endif + endif + else + " initialize new global-directory markfilelist + let s:netrwmarkfilelist= [] + call add(s:netrwmarkfilelist,netrw#fs#ComposePath(b:netrw_curdir,a:fname)) + 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} != "" + if exists("g:did_drchip_netrwlist_syntax") + exe "2match netrwMarkFile /".s:netrwmarkfilemtch_{curbufnr}."/" + endif + else + 2match none + endif + endif + let @@= ykeep +endfunction + +" 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 +function 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 +endfunction + +" 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"} +function 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}) + call netrw#msg#Notify('ERROR', 'there are no marked files in this window (:help netrw-mf)') + 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#fs#WinPath(exe) + if a:islocal + if g:netrw_keepdir + let fname= netrw#os#Escape(netrw#fs#ComposePath(curdir,fname)) + endif + call system(exe." ".fname) + if v:shell_error + call netrw#msg#Notify('WARNING', printf('unable to apply<%s> to file<%s>', exe, fname)) + endif + else + let fname= netrw#os#Escape(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#fs#WinPath(g:netrw_compress)." ".netrw#os#Escape(netrw#fs#ComposePath(b:netrw_curdir,fname))) + if v:shell_error + call netrw#msg#Notify('WARNING', printf('consider setting g:netrw_compress<%s> to something that works', g:netrw_compress)) + endif + else + " fname not a compressed file, so compress it + NetrwKeepj call s:RemoteSystem(netrw#fs#WinPath(g:netrw_compress)." ".netrw#os#Escape(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 +endfunction + +" 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 +function s:NetrwMarkFileCopy(islocal,...) + + let curdir = s:NetrwGetCurdir(a:islocal) + let curbufnr = bufnr("%") + if !exists("b:netrw_curdir") + let b:netrw_curdir= curdir + endif + + " sanity check + if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) + call netrw#msg#Notify('ERROR', 'there are no marked files in this window (:help netrw-mf)') + return 0 + endif + + if !exists("s:netrwmftgt") + call netrw#msg#Notify('ERROR', 'your marked file target is empty! (:help netrw-mt)') + return 0 + endif + + if a:islocal && s:netrwmftgt_islocal + " Copy marked files, local directory to local directory + if !executable(g:netrw_localcopycmd) + call netrw#msg#Notify('ERROR', printf('g:netrw_localcopycmd<%s> not executable on your system, aborting', g:netrw_localcopycmd)) + return + endif + + " copy marked files while within the same directory (ie. allow renaming) + if simplify(s:netrwmftgt."/") ==# simplify(b:netrw_curdir."/") + " copy multiple marked files inside the same directory + for oldname in s:netrwmarkfilelist_{curbufnr} + call inputsave() + let newname= input(printf("Copy %s to: ", oldname), oldname, 'file') + call inputrestore() + + if empty(newname) + return 0 + endif + + let tgt = netrw#fs#ComposePath(s:netrwmftgt, newname) + let oldname = netrw#fs#ComposePath(b:netrw_curdir, oldname) + if tgt ==# oldname + continue + endif + + let ret = filecopy(oldname, tgt) + if ret == v:false + call netrw#msg#Notify('ERROR', $'copy failed, unable to filecopy() <{oldname}> to <{tgt}>') + break + endif + endfor + call s:NetrwUnmarkList(curbufnr,curdir) + NetrwKeepj call s:NetrwRefreshDir(a:islocal, b:netrw_curdir) + return ret + else + let args = [] + for arg in s:netrwmarkfilelist_{curbufnr} + call add(args, netrw#fs#ComposePath(b:netrw_curdir, arg)) + endfor + let tgt = s:netrwmftgt + endif + + let copycmd = g:netrw_localcopycmd + let copycmdopt = g:netrw_localcopycmdopt + + " on Windows, no builtin command supports copying multiple files at once + " (powershell's Copy-Item cmdlet does but requires , as file separator) + if len(s:netrwmarkfilelist_{curbufnr}) > 1 && has("win32") && !g:netrw_cygwin + " copy multiple marked files + for file in args + let dest = netrw#fs#ComposePath(tgt, fnamemodify(file, ':t')) + let ret = filecopy(file, dest) + if ret == v:false + call netrw#msg#Notify('ERROR', $'copy failed, unable to filecopy() <{file}> to <{dest}>') + break + endif + endfor + call s:NetrwUnmarkList(curbufnr,curdir) + NetrwKeepj call s:NetrwRefreshDir(a:islocal, b:netrw_curdir) + return ret + endif + + if len(args) == 1 && isdirectory(s:NetrwFile(args[0])) + let copycmd = g:netrw_localcopydircmd + let copycmdopt = g:netrw_localcopydircmdopt + if has('win32') && g:netrw_localcopydircmd == "xcopy" + " 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 = netrw#fs#ComposePath(tgt, fnamemodify(simplify(netrw#fs#PathJoin(args[0],".")),":t")) + endif + endif + + " prepare arguments for shell call + let args = join(map(args,'netrw#os#Escape(v:val)')) + let tgt = netrw#os#Escape(tgt) + + " enforce noshellslash for system calls + if exists('+shellslash') && &shellslash + for var in ['copycmd', 'args', 'tgt'] + let {var} = substitute({var}, '/', '\', 'g') + endfor + endif + + " shell call + let shell_cmd = printf("%s %s %s %s", copycmd, copycmdopt, args, tgt) + call system(shell_cmd) + if v:shell_error != 0 + if exists("b:netrw_curdir") && b:netrw_curdir != getcwd() && g:netrw_keepdir + call netrw#msg#Notify('ERROR', printf("copy failed; perhaps due to vim's current directory<%s> not matching netrw's (%s) (see :help netrw-cd)", getcwd(), b:netrw_curdir)) + else + call netrw#msg#Notify('ERROR', printf("tried using g:netrw_localcopycmd<%s>; it doesn't work!", g:netrw_localcopycmd)) + endif + return 0 + endif + + elseif a:islocal && !s:netrwmftgt_islocal + " Copy marked files, local directory to remote directory + NetrwKeepj call s:NetrwUpload(s:netrwmarkfilelist_{curbufnr},s:netrwmftgt) + + elseif !a:islocal && s:netrwmftgt_islocal + " Copy marked files, remote directory to local directory + NetrwKeepj call netrw#Obtain(a:islocal,s:netrwmarkfilelist_{curbufnr},s:netrwmftgt) + + elseif !a:islocal && !s:netrwmftgt_islocal + " Copy marked files, remote directory to remote directory + let curdir = getcwd() + let tmpdir = s:GetTempfile("") + if tmpdir !~ '/' + let tmpdir= curdir."/".tmpdir + endif + call mkdir(tmpdir) + if isdirectory(s:NetrwFile(tmpdir)) + if s:NetrwLcd(tmpdir) + return + endif + NetrwKeepj call netrw#Obtain(a:islocal,s:netrwmarkfilelist_{curbufnr},tmpdir) + let localfiles= map(deepcopy(s:netrwmarkfilelist_{curbufnr}),'substitute(v:val,"^.*/","","")') + NetrwKeepj call s:NetrwUpload(localfiles,s:netrwmftgt) + if getcwd() == tmpdir + for fname in s:netrwmarkfilelist_{curbufnr} + call netrw#fs#Remove(fname) + endfor + if s:NetrwLcd(curdir) + return + endif + if delete(tmpdir,"d") + call netrw#msg#Notify('ERROR', printf('unable to delete directory <%s>!', tmpdir)) + endif + else + if s:NetrwLcd(curdir) + return + endif + endif + endif + endif + + " ------- + " cleanup + " ------- + " remove markings from local buffer + call s:NetrwUnmarkList(curbufnr,curdir) " remove markings from local buffer + " 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 + " remove markings from local buffer + NetrwKeepj call s:NetrwUnmarkList(curbufnr,curdir) + + " refresh buffers + if s:netrwmftgt_islocal + NetrwKeepj call s:NetrwRefreshDir(s:netrwmftgt_islocal,s:netrwmftgt) + endif + if a:islocal && s:netrwmftgt != curdir + NetrwKeepj call s:NetrwRefreshDir(a:islocal,curdir) + endif + endif + + return 1 +endfunction + +" 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. +function s:NetrwMarkFileDiff(islocal) + let curbufnr= bufnr("%") + + " sanity check + if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) + call netrw#msg#Notify('ERROR', 'there are no marked files in this window (:help netrw-mf)') + return + endif + let curdir= s:NetrwGetCurdir(a:islocal) + + if exists("s:netrwmarkfilelist_{".curbufnr."}") + let cnt = 0 + for fname in s:netrwmarkfilelist + let cnt= cnt + 1 + if cnt == 1 + exe "NetrwKeepj e ".fnameescape(fname) + diffthis + elseif cnt == 2 || cnt == 3 + below vsplit + exe "NetrwKeepj e ".fnameescape(fname) + diffthis + else + break + endif + endfor + call s:NetrwUnmarkList(curbufnr,curdir) + endif + +endfunction + +" s:NetrwMarkFileEdit: (invoked by me) put marked files on arg list and start editing them {{{2 +" Uses global markfilelist +function s:NetrwMarkFileEdit(islocal) + + let curdir = s:NetrwGetCurdir(a:islocal) + let curbufnr = bufnr("%") + + " sanity check + if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) + call netrw#msg#Notify('ERROR', 'there are no marked files in this window (:help netrw-mf)') + return + endif + + 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() + exe "sil args ".flist + endif + echo "(use :bn, :bp to navigate files; :Rex to return)" + +endfunction + +" s:NetrwMarkFileQFEL: convert a quickfix-error or location list into a marked file list {{{2 +function s:NetrwMarkFileQFEL(islocal,qfel) + call s:NetrwUnmarkAll() + let curbufnr= bufnr("%") + + if !empty(a:qfel) + for entry in a:qfel + let bufnmbr= entry["bufnr"] + if !exists("s:netrwmarkfilelist_{curbufnr}") + 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 s:NetrwMarkFile(a:islocal,bufname(bufnmbr)) + else + endif + endfor + echo "(use me to edit marked files)" + else + call netrw#msg#Notify('WARNING', "can't convert quickfix error list; its empty!") + endif + +endfunction + +" 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 +function 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}) + call netrw#msg#Notify('ERROR', 'there are no marked files in this window (:help netrw-mf)') + 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= netrw#os#Escape(netrw#fs#WinPath(netrw#fs#ComposePath(curdir,fname))) + endif + else + let fname= netrw#os#Escape(netrw#fs#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 + call netrw#msg#Notify('ERROR', printf('command<%s> failed, aborting', xcmd)) + 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 + call netrw#msg#Notify('ERROR', 'no files marked!') + 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,'netrw#os#Escape(v:val)'),' '),'g') + else + let cmd= cmd.' '.join(map(s:netrwmarkfilelist,'netrw#os#Escape(v:val)'),' ') + endif + if a:islocal + call system(cmd) + if v:shell_error < 0 + call netrw#msg#Notify('ERROR', printf('command<%s> failed, aborting',xcmd)) + 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 +endfunction + +" 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. +function 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 + call netrw#msg#Notify('ERROR', 'no files marked!') + endif +endfunction + +" s:NetrwMarkFileVimCmd: (invoked by mv) execute arbitrary vim command on marked files, one at a time {{{2 +" Uses the local marked-file list. +function 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}) + call netrw#msg#Notify('ERROR', 'there are no marked files in this window (:help netrw-mf)') + 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 + call netrw#msg#Notify('ERROR', 'no files marked!') + endif +endfunction + +" s:NetrwMarkFileGrep: (invoked by mg) This function applies vimgrep to marked files {{{2 +" Uses the global markfilelist +function s:NetrwMarkFileGrep(islocal) + let svpos = winsaveview() + let curbufnr = bufnr("%") + let curdir = s:NetrwGetCurdir(a:islocal) + + if exists("s:netrwmarkfilelist") + let netrwmarkfilelist= join(map(deepcopy(s:netrwmarkfilelist), "fnameescape(v:val)")) + call s:NetrwUnmarkAll() + else + let netrwmarkfilelist= "*" + endif + + " ask user for pattern + 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 + try + exe "NetrwKeepj noautocmd vimgrep".patbang." ".pat." ".netrwmarkfilelist + catch /^Vim\%((\a\+)\)\=:E480/ + call netrw#msg#Notify('WARNING', printf('no match with pattern<%s>', pat)) + return + endtry + echo "(use :cn, :cp to navigate, :Rex to return)" + + 2match none + NetrwKeepj call winrestview(svpos) + + if exists("nonisi") + " original, user-supplied pattern did not begin with a character from isident + if pat =~# nonisi.'j$\|'.nonisi.'gj$\|'.nonisi.'jg$' + call s:NetrwMarkFileQFEL(a:islocal,getqflist()) + endif + endif + +endfunction + +" 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 +function s:NetrwMarkFileMove(islocal) + let curdir = s:NetrwGetCurdir(a:islocal) + let curbufnr = bufnr("%") + + " sanity check + if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) + call netrw#msg#Notify('ERROR', 'there are no marked files in this window (:help netrw-mf)') + return + endif + + if !exists("s:netrwmftgt") + call netrw#msg#Notify('ERROR', 'your marked file target is empty! (:help netrw-mt)') + return 0 + endif + + if a:islocal && s:netrwmftgt_islocal + " move: local -> local + if !executable(g:netrw_localmovecmd) + call netrw#msg#Notify('ERROR', printf('g:netrw_localmovecmd<%s> not executable on your system, aborting', g:netrw_localmovecmd)) + return + endif + + let tgt = netrw#os#Escape(s:netrwmftgt) + if has("win32") && !g:netrw_cygwin && g:netrw_localmovecmd =~ '\s' && g:netrw_localmovecmdopt == "" + let movecmd = substitute(g:netrw_localmovecmd,'\s.*$','','') + let movecmdargs = substitute(g:netrw_localmovecmd,'^.\{-}\(\s.*\)$','\1','') + else + let movecmd = g:netrw_localmovecmd + let movecmdargs = g:netrw_localmovecmdopt + endif + + " build args list + let args = [] + for fname in s:netrwmarkfilelist_{curbufnr} + if g:netrw_keepdir + " Jul 19, 2022: fixing file move when g:netrw_keepdir is 1 + let fname= netrw#fs#ComposePath(b:netrw_curdir, fname) + endif + call add(args, netrw#os#Escape(fname)) + endfor + + " enforce noshellslash for system calls + if exists('+shellslash') && &shellslash + let tgt = substitute(tgt, '/', '\', 'g') + call map(args, "substitute(v:val, '/', '\\', 'g')") + endif + + for fname in args + let shell_cmd = printf("%s %s %s %s", movecmd, movecmdargs, fname, tgt) + let ret= system(shell_cmd) + if v:shell_error != 0 + if exists("b:netrw_curdir") && b:netrw_curdir != getcwd() && !g:netrw_keepdir + call netrw#msg#Notify('ERROR', printf("move failed; perhaps due to vim's current directory<%s> not matching netrw's (%s) (see :help netrw-cd)", getcwd(), b:netrw_curdir)) + else + call netrw#msg#Notify('ERROR', printf("tried using g:netrw_localmovecmd<%s>; it doesn't work!", g:netrw_localmovecmd)) + endif + break + endif + endfor + + elseif a:islocal && !s:netrwmftgt_islocal + " move: local -> remote + let mflist= s:netrwmarkfilelist_{curbufnr} + NetrwKeepj call s:NetrwMarkFileCopy(a:islocal) + 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 + let mflist= s:netrwmarkfilelist_{curbufnr} + NetrwKeepj call s:NetrwMarkFileCopy(a:islocal) + 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 + let mflist= s:netrwmarkfilelist_{curbufnr} + NetrwKeepj call s:NetrwMarkFileCopy(a:islocal) + for fname in mflist + let barefname = substitute(fname,'^\(.*/\)\(.\{-}\)$','\2','') + let ok = s:NetrwRemoteRmFile(b:netrw_curdir,barefname,1) + endfor + unlet mflist + endif + + " ------- + " cleanup + " ------- + + " remove markings from local buffer + call s:NetrwUnmarkList(curbufnr,curdir) " remove markings from local buffer + + " refresh buffers + if !s:netrwmftgt_islocal + NetrwKeepj call s:NetrwRefreshDir(s:netrwmftgt_islocal,s:netrwmftgt) + endif + if a:islocal + NetrwKeepj call s:NetrwRefreshDir(a:islocal,b:netrw_curdir) + endif + if g:netrw_fastbrowse <= 1 + NetrwKeepj call s:LocalBrowseRefresh() + endif + +endfunction + +" 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). +function s:NetrwMarkFileRegexp(islocal) + + " get the regular expression + call inputsave() + let regexp= input("Enter regexp: ","","file") + call inputrestore() + + if a:islocal + let curdir= s:NetrwGetCurdir(a:islocal) + " get the matching list of files using local glob() + let dirname = escape(b:netrw_curdir,g:netrw_glob_escape) + let filelist= glob(netrw#fs#ComposePath(dirname,regexp),0,1,1) + + " mark the list of files + for fname in filelist + if fname =~ '^'.fnameescape(curdir) + NetrwKeepj call s:NetrwMarkFile(a:islocal,substitute(fname,'^'.fnameescape(curdir).'/','','')) + else + NetrwKeepj call s:NetrwMarkFile(a:islocal,substitute(fname,'^.*/','','')) + endif + endfor + + else + + " convert displayed listing into a filelist + let eikeep = &ei + let areg = @a + sil NetrwKeepj %y a + setl ei=all ma + 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') + 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)" + +endfunction + +" s:NetrwMarkFileSource: (invoked by ms) This function sources marked files {{{2 +" Uses the local marked file list. +function s:NetrwMarkFileSource(islocal) + let curbufnr= bufnr("%") + + " sanity check + if !exists("s:netrwmarkfilelist_{curbufnr}") || empty(s:netrwmarkfilelist_{curbufnr}) + call netrw#msg#Notify('ERROR', 'there are no marked files in this window (:help netrw-mf)') + return + endif + 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= netrw#fs#ComposePath(curdir,fname) + endif + else + let fname= curdir.fname + endif + " the autocmds will handle sourcing both local and remote files + exe "so ".fnameescape(fname) + endfor + 2match none + endif +endfunction + +" s:NetrwMarkFileTag: (invoked by mT) This function applies g:netrw_ctags to marked files {{{2 +" Uses the global markfilelist +function 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}) + call netrw#msg#Notify('ERROR', 'there are no marked files in this window (:help netrw-mf)') + return + endif + + if exists("s:netrwmarkfilelist") + let netrwmarkfilelist= join(map(deepcopy(s:netrwmarkfilelist), "netrw#os#Escape(v:val,".!a:islocal.")")) + call s:NetrwUnmarkAll() + + if a:islocal + + call system(g:netrw_ctags." ".netrwmarkfilelist) + if v:shell_error + call netrw#msg#Notify('ERROR', printf('g:netrw_ctags<%s> is not executable!', g:netrw_ctags)) + 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 +endfunction + +" 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 +function 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 = netrw#fs#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 ".netrw#os#Escape(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 +endfunction + +" s:NetrwGetCurdir: gets current directory and sets up b:netrw_curdir if necessary {{{2 +function s:NetrwGetCurdir(islocal) + + if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST + let b:netrw_curdir = s:NetrwTreePath(w:netrw_treetop) + elseif !exists("b:netrw_curdir") + let b:netrw_curdir= getcwd() + endif + + if b:netrw_curdir !~ '\<\a\{3,}://' + let curdir= b:netrw_curdir + if g:netrw_keepdir == 0 + call s:NetrwLcd(curdir) + endif + endif + + return b:netrw_curdir +endfunction + +" s:NetrwOpenFile: query user for a filename and open it {{{2 +function s:NetrwOpenFile(islocal) + call inputsave() + let fname = input("Enter filename: ") + call inputrestore() + + if empty(fname) + return + endif + + " save position for benefit of Rexplore + let s:rexposn_{bufnr("%")}= winsaveview() + + execute "NetrwKeepj e " . fnameescape(!isabsolutepath(fname) + \ ? netrw#fs#ComposePath(b:netrw_curdir, fname) + \ : fname) +endfunction + +" 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. +function netrw#Shrink() + 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 + else + exe "vert resize ".t:netrw_winwidth + 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 + elseif winwidth(bufwinnr(t:netrw_lexbufnr)) >= 0 + exe "vert resize ".t:netrw_winwidth + else + call netrw#Lexplore(0,0) + endif + + else + call netrw#Lexplore(0,0) + endif + let wiw= wiwkeep + +endfunction + +" s:NetSortSequence: allows user to edit the sorting sequence {{{2 +function 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 +endfunction + +" 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. +function s:NetrwUnmarkList(curbufnr,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 = netrw#fs#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 +endfunction + +" s:NetrwUnmarkAll: remove the global marked file list and all local ones {{{2 +function s:NetrwUnmarkAll() + if exists("s:netrwmarkfilelist") + unlet s:netrwmarkfilelist + endif + sil call s:NetrwUnmarkAll2() + 2match none +endfunction + +" s:NetrwUnmarkAll2: unmark all files from all buffers {{{2 +function 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 +endfunction + +" 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 +function 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") + 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) +endfunction + +" s:NetrwMenu: generates the menu for gvim and netrw {{{2 +function s:NetrwMenu(domenu) + + if !exists("g:NetrwMenuPriority") + let g:NetrwMenuPriority= 80 + endif + + if has("menu") && has("gui_running") && &go =~# 'm' && g:netrw_menu + + if !exists("s:netrw_menu_enabled") && a:domenu + 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 + exe 'sil! unmenu '.g:NetrwTopLvlMenu + sil! unlet s:netrw_menu_enabled + endif + endif + return + endif + +endfunction + +" s:NetrwObtain: obtain file under cursor or from markfile list {{{2 +" Used by the O maps (as <SID>NetrwObtain()) +function s:NetrwObtain(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 + +endfunction + +" 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 +function 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#msg#Notify('ERROR', printf('unable to write <%s>!', (exists("prevbufname") ? prevbufname : 'n/a'))) + 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 +endfunction + +" 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) +function s:NetrwUpload(fname,tgt,...) + + if a:tgt =~ '^\a\{3,}://' + let tgtdir= substitute(a:tgt,'^\a\{3,}://[^/]\+/\(.\{-}\)$','\1','') + else + let tgtdir= substitute(a:tgt,'^\(.*\)/[^/]*$','\1','') + endif + + if a:0 > 0 + let fromdir= a:1 + else + let fromdir= getcwd() + endif + + if type(a:fname) == 1 + " handle uploading a single file using NetWrite + 1split + exe "NetrwKeepj e ".fnameescape(s:NetrwFile(a:fname)) + if a:tgt =~ '/$' + let wfname= substitute(a:fname,'^.*/','','') + exe "w! ".fnameescape(a:tgt.wfname) + else + exe "w ".fnameescape(a:tgt) + endif + q! + + elseif type(a:fname) == 3 + " handle uploading a list of files via scp + let curdir= getcwd() + if a:tgt =~ '^scp:' + if s:NetrwLcd(fromdir) + return + endif + let filelist= deepcopy(s:netrwmarkfilelist_{bufnr('%')}) + let args = join(map(filelist,"netrw#os#Escape(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 netrw#os#Execute(s:netrw_silentxfer."!".g:netrw_scp_cmd.netrw#os#Escape(useport,1)." ".args." ".netrw#os#Escape(machine.":".tgt,1)) + if s:NetrwLcd(curdir) + return + endif + + elseif a:tgt =~ '^ftp:' + call s:NetrwMethod(a:tgt) + if !s:NetrwValidateHostname(g:netrw_machine) + call netrw#msg#Notify('ERROR', printf('Rejecting invalid hostname: <%s>', g:netrw_machine)) + return + endif + + if b:netrw_method == 2 + " handle uploading a list of files via ftp+.netrc + let netrw_fname = b:netrw_fname + sil NetrwKeepj new + + NetrwKeepj put =g:netrw_ftpmode + + if exists("g:netrw_ftpextracmd") + NetrwKeepj put =g:netrw_ftpextracmd + endif + + NetrwKeepj call setline(line("$")+1,'lcd "'.fromdir.'"') + + if tgtdir == "" + let tgtdir= '/' + endif + NetrwKeepj call setline(line("$")+1,'cd "'.tgtdir.'"') + + for fname in a:fname + NetrwKeepj call setline(line("$")+1,'put "'.s:NetrwFile(fname).'"') + endfor + + if exists("g:netrw_port") && g:netrw_port != "" + call netrw#os#Execute(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".netrw#os#Escape(g:netrw_machine,1)." ".netrw#os#Escape(g:netrw_port,1)) + else + call netrw#os#Execute(s:netrw_silentxfer."%!".s:netrw_ftp_cmd." -i ".netrw#os#Escape(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) !~ "^$" && getline(1) !~ '^Trying ' + call netrw#msg#Notify('ERROR', getline(1)) + 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 + else + NetrwKeepj put ='open '.g:netrw_machine + 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") + NetrwKeepj call setline(line("$")+1,'"'.s:netrw_passwd.'"') + endif + elseif exists("s:netrw_passwd") + NetrwKeepj put ='user \"'.g:netrw_uid.'\" \"'.s:netrw_passwd.'\"' + endif + endif + + NetrwKeepj call setline(line("$")+1,'lcd "'.fromdir.'"') + + if exists("b:netrw_fname") && b:netrw_fname != "" + NetrwKeepj call setline(line("$")+1,'cd "'.b:netrw_fname.'"') + endif + + if exists("g:netrw_ftpextracmd") + NetrwKeepj put =g:netrw_ftpextracmd + endif + + for fname in a:fname + NetrwKeepj call setline(line("$")+1,'put "'.fname.'"') + 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 netrw#os#Execute(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) !~ "^$" && getline(1) !~ '^Trying ' + let debugkeep= &debug + setl debug=msg + call netrw#msg#Notify('ERROR', getline(1)) + let &debug = debugkeep + let mod = 1 + else + bw!|q + endif + elseif !exists("b:netrw_method") || b:netrw_method < 0 + return + endif + else + call netrw#msg#Notify('ERROR', printf("can't obtain files with protocol from<%s>", a:tgt)) + endif + endif + +endfunction + +" s:NetrwPreview: supports netrw's "p" map {{{2 +function s:NetrwPreview(path) range + 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") + if !isdirectory(s:NetrwFile(a:path)) + 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 + 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 + 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 + let eikeep = &ei + set ei=BufEnter + exe (g:netrw_alto? "top " : "bot ").(g:netrw_preview? "vert " : "")."pedit ".fnameescape(a:path) + let &ei= eikeep + if exists("pvhkeep") + let &pvh= pvhkeep + endif + else + call netrw#msg#Notify('WARNING', printf('sorry, cannot preview a directory such as <%s>', a:path)) + endif + else + call netrw#msg#Notify('WARNING', 'sorry, to preview your vim needs the quickfix feature compiled in') + endif + NetrwKeepj call s:NetrwOptionsRestore("s:") + let @@= ykeep +endfunction + +" s:NetrwRefresh: {{{2 +function s:NetrwRefresh(islocal,dirname) + " at the current time (Mar 19, 2007) all calls to NetrwRefresh() call NetrwBrowseChgDir() first. + setl ma noro + 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() + + sil! NetrwKeepj %d _ + if a:islocal + NetrwKeepj call netrw#LocalBrowseCheck(a:dirname) + else + NetrwKeepj call s:NetrwBrowse(a:islocal,a:dirname) + endif + + " restore position + 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("%")} != "" + exe "2match netrwMarkFile /".s:netrwmarkfilemtch_{bufnr("%")}."/" + else + 2match none + endif + endif + + " restore + let @@= ykeep +endfunction + +" s:NetrwRefreshDir: refreshes a directory by name {{{2 +" Called by NetrwMarkFileCopy() +" Interfaces to s:NetrwRefresh() and s:LocalBrowseRefresh() +function 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 +endfunction + +" 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. +function s:NetrwSetChgwin(...) + if a:0 > 0 + 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 +endfunction + +" 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. +function s:NetrwSetSort() + 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 + + " sanity check + if w:netrw_bannercnt > line("$") + " apparently no files were left after a Hiding pattern was used + 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 + +endfunction + +" 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. +function s:NetrwSetTgt(islocal,bookhist,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) + +endfunction + +" s:NetrwSortStyle: change sorting style (name - time - size - exten) and refresh display {{{2 +function 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) +endfunction + +" 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 +function 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 + if &ea + exe "keepalt wincmd =" + endif + + 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 + if &ea + exe "keepalt wincmd =" + endif + + else + call netrw#msg#Notify('ERROR', '(NetrwSplit) unsupported mode='.a:mode) + endif + + let @@= ykeep +endfunction + +" s:NetrwTgtMenu: {{{2 +function s:NetrwTgtMenu() + if !exists("s:netrw_menucnt") + return + endif + + " 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") + 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 + 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 + 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 + 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) + 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 +endfunction + +" s:NetrwTreeDir: determine tree directory given current cursor position {{{2 +" (full path directory with trailing slash returned) +function 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 +endfunction + +" s:NetrwTreeDisplay: recursive tree display {{{2 +function 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 +endfunction + +" s:NetrwRefreshTreeDict: updates the contents information for a tree (w:netrw_treedict) {{{2 +function 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 = netrw#fs#Glob(direntry.'/','*',1) + let listdotstar= netrw#fs#Glob(direntry.'/','.*',1) + + else + endif + endfor +endfunction + +" s:NetrwTreeListing: displays tree listing from treetop on down, using NetrwTreeDisplay() {{{2 +" Called by s:PerformListing() +function 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) + \ && strdisplaywidth(a:dirname) < strdisplaywidth(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 +endfunction + +" 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 +function s:NetrwTreePath(treetop) + if line(".") < w:netrw_bannercnt + 2 + let treedir= a:treetop + if treedir !~ '/$' + let treedir= treedir.'/' + endif + return treedir + endif + + let svpos = winsaveview() + let depth = substitute(getline('.'),'^\(\%('.s:treedepthstring.'\)*\)[^'.s:treedepthstring.'].\{-}$','\1','e') + let depth = substitute(depth,'^'.s:treedepthstring,'','') + let curline= getline('.') + if curline =~ '/$' + let treedir= substitute(curline,'^\%('.s:treedepthstring.'\)*\([^'.s:treedepthstring.'].\{-}\)$','\1','e') + elseif curline =~ '@\s\+-->' + let treedir= substitute(curline,'^\%('.s:treedepthstring.'\)*\([^'.s:treedepthstring.'].\{-}\)$','\1','e') + let treedir= substitute(treedir,'@\s\+-->.*$','','e') + else + let treedir= "" + endif + " construct treedir by searching backwards at correct depth + while depth != "" && search('^'.depth.'[^'.s:treedepthstring.'].\{-}/$','bW') + let dirname= substitute(getline('.'),'^\('.s:treedepthstring.'\)*','','e') + let treedir= dirname.treedir + let depth = substitute(depth,'^'.s:treedepthstring,'','') + endwhile + if a:treetop =~ '/$' + let treedir= a:treetop.treedir + else + let treedir= a:treetop.'/'.treedir + endif + let treedir= substitute(treedir,'//$','/','') + call winrestview(svpos) + return treedir +endfunction + +" s:NetrwWideListing: {{{2 +function s:NetrwWideListing() + + if w:netrw_liststyle == s:WIDELIST + " 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('')] + 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) + return + endif + " allow for two spaces to separate columns + let b:netrw_cpf= b:netrw_cpf + 2 + + " 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 + + " 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 + if has("clipboard") && g:netrw_clipboard + 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("clipboard") + 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>" + exe "setl ".g:netrw_bufsettings + call s:RestoreRegister(dict) + return + else + if hasmapto("w","n") + sil! nunmap <buffer> w + endif + if hasmapto("b","n") + sil! nunmap <buffer> b + endif + endif +endfunction + +" s:PerformListing: {{{2 +function s:PerformListing(islocal) + sil! NetrwKeepj %d _ + " call DechoBuf(bufnr("%")) + + " set up syntax highlighting {{{3 + sil! setl ft=netrw + + NetrwKeepj call s:NetrwOptionsSafe(a:islocal) + setl noro ma + + + if exists("w:netrw_liststyle") && w:netrw_liststyle == s:TREELIST && exists("w:netrw_treedict") + " force a refresh for tree listings + 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 + 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 + NetrwKeepj 1 + let w:netrw_bannercnt= 1 + endif + + " 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 + if g:netrw_sort_by =~# "^n" + " 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 + " 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 + endif + + " show copy/move target, if any {{{3 + if g:netrw_banner + if exists("s:netrwmftgt") && exists("s:netrwmftgt_islocal") + 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 + endif + exe "sil! NetrwKeepj ".w:netrw_bannercnt + endif + + " Hiding... -or- Showing... {{{3 + if g:netrw_banner + 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 + + let quickhelp = g:netrw_quickhelp%len(s:QuickHelp) + NetrwKeepj put ='\" Quick Help: <F1>:help '.s:QuickHelp[quickhelp] + NetrwKeepj put ='\" ==============================================================================' + let w:netrw_bannercnt= w:netrw_bannercnt + 2 + 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 + endif + + " get list of files + if a:islocal + let filelist = s:NetrwLocalListingList(b:netrw_curdir, 1) + call append(w:netrw_bannercnt - 1, filelist) + silent! NetrwKeepj g/^$/d + silent! NetrwKeepj %s/\r$//e + execute printf("setl ts=%d", g:netrw_maxfilenamelen + 1) + else " remote + NetrwKeepj let badresult= s:NetrwRemoteListing() + if badresult + return + endif + endif + + " manipulate the directory listing (hide, sort) {{{3 + if !exists("w:netrw_bannercnt") + let w:netrw_bannercnt= 0 + endif + + if !g:netrw_banner || line("$") >= w:netrw_bannercnt + if g:netrw_hide && g:netrw_list_hide != "" + NetrwKeepj call s:NetrwListHide() + endif + if !g:netrw_banner || line("$") >= w:netrw_bannercnt + + if g:netrw_sort_by =~# "^n" + " sort by name + NetrwKeepj call s:NetrwSetSort() + + if !g:netrw_banner || w:netrw_bannercnt < line("$") + 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 + 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 + 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("$") + 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("$") + if g:netrw_sort_direction =~# 'n' + exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$sort'.' '.g:netrw_sort_options + else + exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$sort!'.' '.g:netrw_sort_options + endif + exe 'sil! NetrwKeepj '.w:netrw_bannercnt.',$s/^\d\{-}\///e' + NetrwKeepj call histdel("/",-1) + endif + endif + + elseif g:netrw_sort_direction =~# 'r' + 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 + + " convert to wide/tree listing {{{3 + NetrwKeepj call s:NetrwWideListing() + NetrwKeepj call s:NetrwTreeListing(b:netrw_curdir) + + " 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)) + 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 + exe 'sil! '.w:netrw_bannercnt + sil! NetrwKeepj norm! 0 + else + endif + + " record previous current directory + let w:netrw_prvdir= b:netrw_curdir + + " save certain window-oriented variables into buffer-oriented variables {{{3 + NetrwKeepj call s:SetBufWinVars() + NetrwKeepj call s:NetrwOptionsRestore("w:") + + " set display to netrw display settings + exe "setl ".g:netrw_bufsettings + if g:netrw_liststyle == s:LONGLIST + exe "setl ts=".(g:netrw_maxfilenamelen+1) + endif + " call DechoBuf(bufnr("%")) + + if exists("s:treecurpos") + NetrwKeepj call winrestview(s:treecurpos) + unlet s:treecurpos + endif + +endfunction + +" s:SetupNetrwStatusLine: {{{2 +function s:SetupNetrwStatusLine(statline) + + if !exists("s:netrw_setup_statline") + let s:netrw_setup_statline= 1 + + 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 + redraw + +endfunction + +" 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. +function s:NetrwRemoteFtpCmd(path,listcmd) + " sanity check: {{{3 + if !exists("w:netrw_method") + if exists("b:netrw_method") + let w:netrw_method= b:netrw_method + else + call netrw#msg#Notify('ERROR', '(s:NetrwRemoteFtpCmd) internal netrw error') + return + endif + endif + + " WinXX ftp uses unix style input, so set ff to unix " {{{3 + let ffkeep= &ff + setl ma ff=unix noro + + " clear off any older non-banner lines " {{{3 + " note that w:netrw_bannercnt indexes the line after the banner + 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 + endif + NetrwKeepj call setline(line("$")+1,a:listcmd) + if exists("g:netrw_port") && g:netrw_port != "" + exe s:netrw_silentxfer." NetrwKeepj ".w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".netrw#os#Escape(g:netrw_machine,1)." ".netrw#os#Escape(g:netrw_port,1) + else + exe s:netrw_silentxfer." NetrwKeepj ".w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." -i ".netrw#os#Escape(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,'\..*$','','') + if exists("s:netrw_hup") && exists("s:netrw_hup[host]") + call netrw#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 + 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") + call netrw#os#Execute(s:netrw_silentxfer.w:netrw_bannercnt.",$!".s:netrw_ftp_cmd." ".g:netrw_ftp_options) + 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 + return + + "......................................... + else " {{{3 + call netrw#msg#Notify('WARNING', printf('unable to comply with your request<%s>', bufname("%"))) + 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 +endfunction + +" s:NetrwRemoteListing: {{{2 +function s:NetrwRemoteListing() + + 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]' + if !executable("ftp") + call netrw#msg#Notify('ERROR', "this system doesn't support remote directory listing via ftp") + call s:NetrwOptionsRestore("w:") + return -1 + endif + + elseif !exists("g:netrw_list_cmd") || g:netrw_list_cmd == '' + if g:netrw_list_cmd == "" + call netrw#msg#Notify('ERROR', printf('your g:netrw_list_cmd is empty; perhaps %s is not executable on your system', g:netrw_ssh_cmd)) + else + call netrw#msg#Notify('ERROR', "this system doesn't support remote directory listing via ".g:netrw_list_cmd) + endif + + NetrwKeepj call s:NetrwOptionsRestore("w:") + return -1 + endif " (remote handling sanity check) + + if exists("b:netrw_method") + let w:netrw_method= b:netrw_method + endif + + if s:method == "ftp" + " use ftp to get remote file listing {{{3 + 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 s:NetrwRemoteFtpCmd(s:path,listcmd) + + " 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#msg#Notify('WARNING', mesg) + 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 + 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') + if line2 == 0 + sil! NetrwKeepj put='../' + endif + exe "sil! NetrwKeepj ".line1 + sil! NetrwKeepj norm! 0 + + if search('^\d\{2}-\d\{2}-\d\{2}\s','n') " M$ ftp site cleanup + 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 + 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 + let listcmd= s:MakeSshCmd(g:netrw_list_cmd) + if g:netrw_scp_cmd =~ '^pscp' + exe "NetrwKeepj r! ".listcmd.netrw#os#Escape(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 == "" + exe "NetrwKeepj keepalt r! ".listcmd + else + exe "NetrwKeepj keepalt r! ".listcmd.' '.netrw#os#Escape(fnameescape(s:path),1) + endif + endif + + " cleanup + if g:netrw_ssh_browse_reject != "" + 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 + + 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 + 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("$") + 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 + + + return 0 +endfunction + +" s:NetrwRemoteRm: remove/delete a remote file or directory {{{2 +function 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) +endfunction + +" s:NetrwRemoteRmFile: {{{2 +function s:NetrwRemoteRmFile(path,rmfile,all) + + let all= a:all + let ok = "" + + if a:rmfile !~ '^"' && (a:rmfile =~ '@$' || a:rmfile !~ '[\/]$') + " attempt to remove file + if !all + echohl Statement + 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 == "" + if exists("w:netrw_method") && (w:netrw_method == 2 || w:netrw_method == 3) + 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 + let netrw_rm_cmd= s:MakeSshCmd(g:netrw_rm_cmd) + if !exists("b:netrw_curdir") + call netrw#msg#Notify('ERROR', "for some reason b:netrw_curdir doesn't exist!") + let ok="q" + else + let remotedir= substitute(b:netrw_curdir,'^.\{-}//[^/]\+/\(.*\)$','\1','') + if remotedir != "" + let netrw_rm_cmd= netrw_rm_cmd." ".netrw#os#Escape(fnameescape(remotedir.a:rmfile)) + else + let netrw_rm_cmd= netrw_rm_cmd." ".netrw#os#Escape(fnameescape(a:rmfile)) + endif + 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#msg#Notify('ERROR', printf("remove failed; perhaps due to vim's current directory<%s> not matching netrw's (%s) (see :help netrw-cd)", getcwd(), b:netrw_curdir)) + else + call netrw#msg#Notify('WARNING', printf('cmd<%s> failed', netrw_rm_cmd)) + endif + elseif ret != 0 + call netrw#msg#Notify('WARNING', printf('cmd<%s> failed', netrw_rm_cmd)) + endif + endif + endif + elseif ok =~# 'q\%[uit]' + endif + + else + " attempt to remove directory + 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#fs#WinPath(g:netrw_rmdir_cmd)).' '.netrw#os#Escape(netrw#fs#WinPath(rmfile)) + let ret= system(netrw_rmdir_cmd) + + if v:shell_error != 0 + let netrw_rmf_cmd= s:MakeSshCmd(netrw#fs#WinPath(g:netrw_rmf_cmd)).' '.netrw#os#Escape(netrw#fs#WinPath(substitute(rmfile,'[\/]$','','e'))) + let ret= system(netrw_rmf_cmd) + + if v:shell_error != 0 + call netrw#msg#Notify('ERROR', printf('unable to remove directory<%s> -- is it empty?', rmfile)) + endif + endif + endif + + elseif ok =~# 'q\%[uit]' + endif + endif + + return ok +endfunction + +" s:NetrwRemoteRename: rename a remote file or directory {{{2 +function s:NetrwRemoteRename(usrhost,path) range + + " preparation for removing multiple files/directories + let svpos = winsaveview() + 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= netrw#os#Escape(a:path.oldname) + let newname= netrw#os#Escape(a:path.newname) + let ret = system(netrw#fs#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= netrw#os#Escape(a:path.oldname) + let newname= netrw#os#Escape(a:path.newname) + let ret = system(netrw#fs#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) +endfunction + +" 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 +function netrw#FileUrlEdit(fname) + let fname = a:fname + if fname =~ '^file://localhost/' + let fname= substitute(fname,'^file://localhost/','file:///','') + endif + if has("win32") + if fname =~ '^file:///\=\a[|:]/' + let fname = substitute(fname,'^file:///\=\(\a\)[|:]/','file://\1:/','') + endif + endif + let fname2396 = netrw#RFC2396(fname) + let fname2396e= fnameescape(fname2396) + let plainfname= substitute(fname2396,'file://\(.*\)','\1',"") + if has("win32") + if plainfname =~ '^/\+\a:' + let plainfname= substitute(plainfname,'^/\+\(\a:\)','\1','') + endif + endif + + exe "sil doau BufReadPre ".fname2396e + exe 'NetrwKeepj keepalt edit '. fnameescape(plainfname) + exe 'sil! NetrwKeepj keepalt bdelete '.fnameescape(a:fname) + + exe "sil doau BufReadPost ".fname2396e +endfunction + +" netrw#LocalBrowseCheck: {{{2 +function 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) + " getting E930: Cannot use :redir inside execute + + let ykeep= @@ + if isdirectory(s:NetrwFile(a:dirname)) + + if &ft != "netrw" || (exists("b:netrw_curdir") && b:netrw_curdir != a:dirname) || g:netrw_fastbrowse <= 1 + sil! NetrwKeepj keepalt call s:NetrwBrowse(1,a:dirname) + + elseif &ft == "netrw" && line("$") == 1 + sil! NetrwKeepj keepalt call s:NetrwBrowse(1,a:dirname) + + elseif exists("s:treeforceredraw") + unlet s:treeforceredraw + sil! NetrwKeepj keepalt call s:NetrwBrowse(1,a:dirname) + endif + 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 + let ibuf = 1 + let buflast = bufnr("$") + while ibuf <= buflast + if bufwinnr(ibuf) == -1 && !empty(bufname(ibuf)) && isdirectory(s:NetrwFile(bufname(ibuf))) + exe "sil! keepj keepalt ".ibuf."bw!" + endif + let ibuf= ibuf + 1 + endwhile + endif + let @@= ykeep + " not a directory, ignore it +endfunction + +" 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. +function 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 +endfunction + +" 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) +function 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 +endfunction + +function s:NetrwLocalListingList(dirname,setmaxfilenamelen) + " get the list of files contained in the current directory + let dirname = a:dirname + let dirnamelen = strlen(dirname) + let filelist = map(['.', '..'] + readdir(dirname), 'netrw#fs#PathJoin(dirname, v:val)') + + if g:netrw_cygwin == 0 && has("win32") + elseif index(filelist,'..') == -1 && dirname !~ '/' + " include ../ in the glob() entry if its missing + let filelist= filelist+[netrw#fs#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 + + let ftype = getftype(filename) + if ftype ==# "link" + " indicate a symbolic link + let pfile= filename."@" + + elseif ftype ==# "socket" + " indicate a socket + let pfile= filename."=" + + elseif ftype ==# "fifo" + " indicate a fifo + let pfile= filename."|" + + elseif ftype ==# "dir" + " 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 +endfunction + +" s:NetrwLocalExecute: uses system() to execute command under cursor ("X" command support) {{{2 +function s:NetrwLocalExecute(cmd) + let ykeep= @@ + " sanity check + if !executable(a:cmd) + call netrw#msg#Notify('ERROR', printf("the file<%s> is not executable!", a:cmd)) + let @@= ykeep + return + endif + + let optargs= input(":!".a:cmd,"","file") + let result= system(a:cmd.optargs) + + " strip any ansi escape sequences off + let result = substitute(result,"\e\\[[0-9;]*m","","g") + + " show user the result(s) + echomsg result + let @@= ykeep + +endfunction + +" s:NetrwLocalRename: rename a local file or directory {{{2 +function 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= netrw#fs#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 +endfunction + +" s:NetrwLocalRm: {{{2 +function 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 =~# '^a\%[ll]$' + let all = 1 + elseif ok =~# "n\%[o]" + break + 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 =~# '^a\%[ll]$' + let all = 1 + elseif ok =~# "n\%[o]" + break + 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 +endfunction + +" s:NetrwLocalRmFile: remove file fname given the path {{{2 +" Give confirmation prompt unless all==1 +function s:NetrwLocalRmFile(path, fname, all) + let all = a:all + let ok = "" + let dir = 0 + NetrwKeepj norm! 0 + let rmfile = s:NetrwFile(netrw#fs#ComposePath(a:path, escape(a:fname, '\\')))->fnamemodify(':.') + + " if not a directory + if rmfile !~ '^"' && (rmfile =~ '@$' || rmfile !~ '[\/]$') + let msg = "Confirm deletion of file <%s> [{y(es)},n(o),a(ll)]: " + else + let msg = "Confirm *recursive* deletion of directory <%s> [{y(es)},n(o),a(ll)]: " + let dir = 1 + endif + + " Ask confirmation + if !all + echohl Statement + call inputsave() + let ok = input(printf(msg, rmfile)) + call inputrestore() + echohl NONE + if ok =~# '^a\%[ll]$' || ok =~# '^y\%[es]$' + let all = 1 + else + let ok = 'no' + endif + endif + + if !dir && (all || empty(ok)) + " This works because delete return 0 if successful + if netrw#fs#Remove(rmfile) + call netrw#msg#Notify('ERROR', printf("unable to delete <%s>!", rmfile)) + else + " Remove file only if there are no pending changes + execute printf('silent! bwipeout %s', rmfile) + endif + + elseif dir && (all || empty(ok)) + " Remove trailing / + let rmfile = substitute(rmfile, '[\/]$', '', 'e') + if delete(rmfile, "rf") + call netrw#msg#Notify('ERROR', printf("unable to delete directory <%s>!", rmfile)) + endif + + endif + + return ok +endfunction + +" Support Functions: {{{1 + +" netrw#Call: allows user-specified mappings to call internal netrw functions {{{2 +function netrw#Call(funcname,...) + return call("s:".a:funcname,a:000) +endfunction + +" 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. +function netrw#Expose(varname) + if exists("s:".a:varname) + exe "let retval= s:".a:varname + if exists("g:netrw_pchk") + if type(retval) == 3 + let retval = copy(retval) + let i = 0 + while i < len(retval) + let retval[i]= substitute(retval[i],expand("$HOME"),'~','') + let i = i + 1 + endwhile + endif + return string(retval) + else + endif + else + let retval= "n/a" + endif + + return retval +endfunction + +" netrw#Modify: allows UserMaps to set (modify) script-local variables {{{2 +function netrw#Modify(varname,newvalue) + exe "let s:".a:varname."= ".string(a:newvalue) +endfunction + +" netrw#RFC2396: converts %xx into characters {{{2 +function netrw#RFC2396(fname) + let fname = escape(substitute(a:fname,'%\(\x\x\)','\=printf("%c","0x".submatch(1))','ge')," \t") + return fname +endfunction + +" 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 +function netrw#UserMaps(islocal) + + " set up usermaplist + if exists("g:Netrw_UserMaps") && type(g:Netrw_UserMaps) == 3 + for umap in g:Netrw_UserMaps + " 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 + exe "nno <buffer> <silent> ".umap[0]." :call <SID>UserMaps(".a:islocal.",'".umap[1]."')<cr>" + else + call netrw#msg#Notify('WARNING', printf('ignoring usermap <%s> -- not a [string,funcref] entry', string(umap[0]))) + endif + endfor + endif +endfunction + +" 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) +function s:NetrwBadd(islocal,bl2mf) + 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("%")} + 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 +endfunction + +" s:DeleteBookmark: deletes a file/directory from Netrw's bookmark system {{{2 +" Related Functions: s:MakeBookmark() s:NetrwBookHistHandler() s:NetrwBookmark() +function s:DeleteBookmark(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 + +endfunction + +" s:FileReadable: o/s independent filereadable {{{2 +function s:FileReadable(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 + + return ret +endfunction + +" 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 +function s:GetTempfile(fname) + + if !exists("b:netrw_tmpfile") + " get a brand new temporary filename + let tmpfile= tempname() + + let tmpfile= substitute(tmpfile,'\','/','ge') + + " sanity check -- does the temporary file's directory exist? + if !isdirectory(s:NetrwFile(substitute(tmpfile,'[^/]\+$','','e'))) + call netrw#msg#Notify('ERROR', printf('your <%s> directory is missing!', substitute(tmpfile,'[^/]\+$','','e'))) + return "" + endif + + " let netrw#NetSource() know about the tmpfile + let s:netrw_tmpfile= tmpfile " used by netrw#NetSource() and netrw#BrowseX() + + " 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 + else + " re-use temporary filename + let tmpfile= b:netrw_tmpfile + endif + + " use fname's suffix for the temporary file + if a:fname != "" + if a:fname =~ '\.[^./]\+$' + 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 + let tmpfile= substitute(tmpfile,'\.tmp$','','e') + let tmpfile .= suffix + let s:netrw_tmpfile= tmpfile " supports netrw#NetSource() + endif + endif + + return tmpfile +endfunction + +" s:MakeSshCmd: transforms input command using USEPORT HOSTNAME into {{{2 +" a correct command for use with a system() call +function s:MakeSshCmd(sshcmd) + let machine = shellescape(s:machine, 1) + if s:user != '' + let machine = shellescape(s:user, 1).'@'.machine + endif + let sshcmd = substitute(a:sshcmd,'\<HOSTNAME\>',machine,'') + if exists("g:netrw_port") && g:netrw_port != "" + let sshcmd= substitute(sshcmd,"USEPORT",g:netrw_sshport.' '.shellescape(g:netrw_port,1),'') + elseif exists("s:port") && s:port != "" + let sshcmd= substitute(sshcmd,"USEPORT",g:netrw_sshport.' '.shellescape(s:port,1),'') + else + let sshcmd= substitute(sshcmd,"USEPORT ",'','') + endif + return sshcmd +endfunction + +" s:MakeBookmark: enters a bookmark into Netrw's bookmark system {{{2 +function s:MakeBookmark(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 + +endfunction + +" s:MergeBookmarks: merge current bookmarks with saved bookmarks {{{2 +function s:MergeBookmarks() + " get bookmarks from .netrwbook file + let savefile= s:NetrwHome()."/.netrwbook" + if filereadable(s:NetrwFile(savefile)) + NetrwKeepj call s:NetrwBookHistSave() + NetrwKeepj call delete(savefile) + endif +endfunction + +" s:NetrwBMShow: {{{2 +function 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 +endfunction + +" s:NetrwCursor: responsible for setting cursorline/cursorcolumn based upon g:netrw_cursor {{{2 +function s:NetrwCursor(editfile) + if !exists("w:netrw_liststyle") + let w:netrw_liststyle= g:netrw_liststyle + endif + + + 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. + + 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 + 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 + setl cursorline + setl cursorcolumn + else + setl cursorline + endif + + elseif g:netrw_cursor == 2 + " thin-long-tree: cursorline, user's cursorcolumn + " wide : cursorline, user's cursorcolumn + 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 + setl cursorline + else + endif + + else + " all styles: user's cursorline, user's cursorcolumn + let &l:cursorline = s:netrw_usercul + let &l:cursorcolumn = s:netrw_usercuc + endif + +endfunction + +" s:RestoreCursorline: restores cursorline/cursorcolumn to original user settings {{{2 +function s:RestoreCursorline() + if exists("s:netrw_usercul") + let &l:cursorline = s:netrw_usercul + endif + if exists("s:netrw_usercuc") + let &l:cursorcolumn = s:netrw_usercuc + endif +endfunction + +" s:RestoreRegister: restores all registers given in the dict {{{2 +function s:RestoreRegister(dict) + for [key, val] in items(a:dict) + if key == 'unnamed' + let key = '' + endif + call setreg(key, val[0], val[1]) + endfor +endfunction + +" s:NetrwEnew: opens a new buffer, passes netrw buffer variables through {{{2 +function s:NetrwEnew(...) + + " 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. + let bufid = bufnr('$') + if bufid > 1 && !buflisted(bufid) && bufloaded(bufid) && bufname(bufid) == "" && bufwinid(bufid) == -1 + execute printf("silent! bdelete! %s", bufid) + endif + + " grab a function-local-variable copy of buffer variables + 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:") + " 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 + NetrwKeepj call s:NetrwOptionsSave("w:") + + " copy function-local-variables to buffer variable equivalents + 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 +endfunction + +" s:NetrwInsureWinVars: insure that a netrw buffer has its w: variables in spite of a wincmd v or s {{{2 +function s:NetrwInsureWinVars() + if !exists("w:netrw_liststyle") + 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") + for k in keys(winvars) + let w:{k}= winvars[k] + endfor + endif + endif +endfunction + +" s:NetrwLcd: handles changing the (local) directory {{{2 +" Returns: 0=success +" -1=failed +function s:NetrwLcd(newdir) + + 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#msg#Notify('ERROR', printf('unable to change directory to <%s> (permissions?)', a:newdir)) + if exists("w:netrw_prvdir") + let a:newdir= w:netrw_prvdir + else + call s:NetrwOptionsRestore("w:") + exe "setl ".g:netrw_bufsettings + let a:newdir= dirname + endif + return -1 + endif + + return 0 +endfunction + +" s:NetrwSaveWordPosn: used to keep cursor on same word after refresh, {{{2 +" changed sorting, etc. Also see s:NetrwRestoreWordPosn(). +function s:NetrwSaveWordPosn() + let s:netrw_saveword= '^'.fnameescape(getline('.')).'$' +endfunction + +" s:NetrwHumanReadable: takes a number and makes it "human readable" {{{2 +" 1000 -> 1K, 1000000 -> 1M, 1000000000 -> 1G +function s:NetrwHumanReadable(sz) + + 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 + + return sz +endfunction + +" s:NetrwRestoreWordPosn: used to keep cursor on same word after refresh, {{{2 +" changed sorting, etc. Also see s:NetrwSaveWordPosn(). +function s:NetrwRestoreWordPosn() + sil! call search(s:netrw_saveword,'w') +endfunction + +" s:RestoreBufVars: {{{2 +function 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 + +endfunction + +" s:RemotePathAnalysis: {{{2 +function s:RemotePathAnalysis(dirname) + + " method :// user @ machine :port /path + let dirpat = '^\(\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 + + +endfunction + +" 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 netrw#os#Escape(a:cmd)! +function s:RemoteSystem(cmd) + if !executable(g:netrw_ssh_cmd) + call netrw#msg#Notify('ERROR', printf('g:netrw_ssh_cmd<%s> is not executable!', g:netrw_ssh_cmd)) + elseif !exists("b:netrw_curdir") + call netrw#msg#Notify('ERROR', "for some reason b:netrw_curdir doesn't exist!") + else + let cmd = s:MakeSshCmd(g:netrw_ssh_cmd." USEPORT HOSTNAME") + let remotedir= substitute(b:netrw_curdir,'^.*//[^/]\+/\(.*\)$','\1','') + if remotedir != "" + let cmd= cmd.' cd '.netrw#os#Escape(remotedir).";" + else + let cmd= cmd.' ' + endif + let cmd= cmd.a:cmd + let ret= system(cmd) + endif + return ret +endfunction + +" s:RestoreWinVars: (used by Explore() and NetrwSplit()) {{{2 +function 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 +endfunction + +" 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 +function s:NetrwRexplore(islocal,dirname) + if exists("s:netrwdrag") + return + endif + + 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 + exe "NetrwKeepj e ".w:netrw_rexfile + unlet w:netrw_rexfile + return + endif + + " --------------------------- + " :Rex issued while in a file + " --------------------------- + + " record current file so :Rex can return to it from netrw + let w:netrw_rexfile= expand("%") + + if !exists("w:netrw_rexlocal") + return + endif + 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("%")) + " restore position in directory listing + NetrwKeepj call winrestview(s:rexposn_{bufnr('%')}) + if exists("s:rexposn_".bufnr('%')) + unlet s:rexposn_{bufnr('%')} + endif + else + endif + + if has("syntax") && exists("g:syntax_on") && g:syntax_on + if exists("s:explore_match") + exe "2match netrwMarkFile /".s:explore_match."/" + endif + endif + +endfunction + +" s:SaveBufVars: save selected b: variables to s: variables {{{2 +" use s:RestoreBufVars() to restore b: variables from s: variables +function s:SaveBufVars() + + 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 + +endfunction + +" s:SavePosn: saves position associated with current buffer into a dictionary {{{2 +function s:SavePosn(posndict) + + if !exists("a:posndict[bufnr('%')]") + let a:posndict[bufnr("%")]= [] + endif + call add(a:posndict[bufnr("%")],winsaveview()) + + return a:posndict +endfunction + +" s:RestorePosn: restores position associated with current buffer using dictionary {{{2 +function s:RestorePosn(posndict) + if exists("a:posndict") + if has_key(a:posndict,bufnr("%")) + let posnlen= len(a:posndict[bufnr("%")]) + if posnlen > 0 + let posnlen= posnlen - 1 + call winrestview(a:posndict[bufnr("%")][posnlen]) + call remove(a:posndict[bufnr("%")],posnlen) + endif + endif + endif +endfunction + +" s:SaveWinVars: (used by Explore() and NetrwSplit()) {{{2 +function s:SaveWinVars() + 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 +endfunction + +" 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. +function s:SetBufWinVars() + 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 +endfunction + +" s:SetRexDir: set directory for :Rexplore {{{2 +function s:SetRexDir(islocal,dirname) + let w:netrw_rexdir = a:dirname + let w:netrw_rexlocal = a:islocal + let s:rexposn_{bufnr("%")} = winsaveview() +endfunction + +" s:ShowLink: used to modify thin and tree listings to show links {{{2 +function 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 +endfunction + +" s:ShowStyle: {{{2 +function 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 +endfunction + +" s:TreeListMove: supports [[, ]], [], and ][ in tree mode {{{2 +function s:TreeListMove(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 + " 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 + elseif a:dir == '[]' && nxtline != '' + NetrwKeepj norm! 0 + let nl = search('^\%('.curindent.'\)\@!','We') " search forwards + if nl != 0 + NetrwKeepj norm! k + else + NetrwKeepj norm! G + endif + endif + endif + +endfunction + +" 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. +function 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 +endfunction + +" s:UseBufWinVars: (used by NetrwBrowse() and LocalBrowseCheck() {{{2 +" Matching function to s:SetBufWinVars() +function 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 +endfunction + +" s:UserMaps: supports user-defined UserMaps {{{2 +" * calls a user-supplied funcref(islocal,curdir) +" * interprets result +" See netrw#UserMaps() +function 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 +endfunction + +" Deprecated: {{{1 + +" }}} +" Settings Restoration: {{{1 + +let &cpo= s:keepcpo +unlet s:keepcpo + +" }}} +" vim:ts=8 sts=4 sw=4 et fdm=marker diff --git a/uvim/runtime/pack/dist/opt/netrw/autoload/netrw/fs.vim b/uvim/runtime/pack/dist/opt/netrw/autoload/netrw/fs.vim new file mode 100644 index 0000000000..8c002a88c5 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/netrw/autoload/netrw/fs.vim @@ -0,0 +1,189 @@ +" FUNCTIONS IN THIS FILE ARE MEANT TO BE USED BY NETRW.VIM AND NETRW.VIM ONLY. +" THESE FUNCTIONS DON'T COMMIT TO ANY BACKWARDS COMPATIBILITY. SO CHANGES AND +" BREAKAGES IF USED OUTSIDE OF NETRW.VIM ARE EXPECTED. + + +" netrw#fs#PathJoin: Appends a new part to a path taking different systems into consideration {{{ + +function! netrw#fs#PathJoin(...) + const slash = !exists('+shellslash') || &shellslash ? '/' : '\' + let path = "" + + for arg in a:000 + if empty(path) + let path = arg + else + let path .= slash . arg + endif + endfor + + return path +endfunction + +" }}} +" netrw#fs#ComposePath: Appends a new part to a path taking different systems into consideration {{{ + +function! netrw#fs#ComposePath(base, subdir) + const slash = !exists('+shellslash') || &shellslash ? '/' : '\' + if has('amiga') + let ec = a:base[strdisplaywidth(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') + let ret = a:subdir + + elseif a:base =~ '^\a:[/\\]\([^/\\]\|$\)' && has('win32') + if a:base =~ '[/\\]$' + let ret = a:base . a:subdir + else + let ret = a:base . slash . a:subdir + endif + + elseif a:base =~ '^\a\{3,}://' + 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 + + else + 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 + + return ret +endfunction + +" }}} +" netrw#fs#AbsPath: returns the full path to a directory and/or file {{{ + +function! netrw#fs#AbsPath(path) + let path = a:path->substitute('[\/]$', '', 'e') + + " Nothing to do + if isabsolutepath(path) + return path + endif + + return path->fnamemodify(':p')->substitute('[\/]$', '', 'e') +endfunction + +" }}} +" netrw#fs#Cwd: get the current directory. {{{ +" Change backslashes to forward slashes, if any. +" If doesc is true, escape certain troublesome characters + +function! netrw#fs#Cwd(doesc) + let curdir = substitute(getcwd(), '\\', '/', 'ge') + + if curdir !~ '[\/]$' + let curdir .= '/' + endif + + if a:doesc + let curdir = fnameescape(curdir) + endif + + return curdir +endfunction + +" }}} +" netrw#fs#Glob: does glob() if local, remote listing otherwise {{{ +" 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 netrw#fs#ComposePath() +" pare =1: remove the current directory from the resulting glob() filelist +" =0: leave the current directory in the resulting glob() filelist + +function! netrw#fs#Glob(direntry, expr, 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= netrw#fs#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 + let filelist = glob(path, 0, 1, 1) + if a:pare + let filelist = map(filelist,'substitute(v:val, "^.*/", "", "")') + endif + endif + + return filelist +endfunction + +" }}} +" netrw#fs#WinPath: tries to insure that the path is windows-acceptable, whether cygwin is used or not {{{ + +function! netrw#fs#WinPath(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 + + return path +endfunction + +" }}} +" netrw#fs#Remove: deletes a file. {{{ +" Uses Steve Hall's idea to insure that Windows paths stay +" acceptable. No effect on Unix paths. + +function! netrw#fs#Remove(path) + let path = netrw#fs#WinPath(a:path) + + if !g:netrw_cygwin && has("win32") && exists("+shellslash") + let sskeep = &shellslash + setl noshellslash + let result = delete(path) + let &shellslash = sskeep + else + let result = delete(path) + endif + + if result < 0 + call netrw#msg#Notify('WARNING', printf('delete("%s") failed!', path)) + endif + + return result +endfunction + +" }}} + +" vim:ts=8 sts=4 sw=4 et fdm=marker diff --git a/uvim/runtime/pack/dist/opt/netrw/autoload/netrw/msg.vim b/uvim/runtime/pack/dist/opt/netrw/autoload/netrw/msg.vim new file mode 100644 index 0000000000..5f8c13a8d0 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/netrw/autoload/netrw/msg.vim @@ -0,0 +1,70 @@ +" FUNCTIONS IN THIS FILE ARE MEANT TO BE USED BY NETRW.VIM AND NETRW.VIM ONLY. +" THESE FUNCTIONS DON'T COMMIT TO ANY BACKWARDS COMPATIBILITY. SO CHANGES AND +" BREAKAGES IF USED OUTSIDE OF NETRW.VIM ARE EXPECTED. + +let s:deprecation_msgs = [] +function! netrw#msg#Deprecate(name, version, alternatives) + " If running on neovim use vim.deprecate + if has('nvim') + let s:alternative = a:alternatives->get('nvim', v:null) + call v:lua.vim.deprecate(a:name, s:alternative, a:version, "netrw", v:false) + return + endif + + " If we did notify for something only do it once + if s:deprecation_msgs->index(a:name) >= 0 + return + endif + + let s:alternative = a:alternatives->get('vim', v:null) + echohl WarningMsg + echomsg s:alternative != v:null + \ ? printf('%s is deprecated, use %s instead.', a:name, s:alternative) + \ : printf('%s is deprecated.', a:name) + echomsg printf('Feature will be removed in netrw %s', a:version) + echohl None + + call add(s:deprecation_msgs, a:name) +endfunction + +" netrw#msg#Notify: {{{ +" Usage: netrw#msg#Notify('ERROR'|'WARNING'|'NOTE', 'some message') +" netrw#msg#Notify('ERROR'|'WARNING'|'NOTE', ["message1","message2",...]) +" (this function can optionally take a list of messages) +function! netrw#msg#Notify(level, msg) + if has('nvim') + " Convert string to corresponding vim.log.level value + if a:level ==# 'ERROR' + let level = 4 + elseif a:level ==# 'WARNING' + let level = 3 + elseif a:level ==# 'NOTE' + let level = 2 + endif + call v:lua.vim.notify(a:msg, level) + return + endif + + if a:level ==# 'WARNING' + echohl WarningMsg + elseif a:level ==# 'ERROR' + echohl ErrorMsg + else + echoerr printf('"%s" is not a valid level', a:level) + return + endif + + if type(a:msg) == v:t_list + for msg in a:msg + echomsg msg + endfor + else + echomsg a:msg + endif + + echohl None +endfunction + +" }}} + +" vim:ts=8 sts=4 sw=4 et fdm=marker diff --git a/uvim/runtime/pack/dist/opt/netrw/autoload/netrw/os.vim b/uvim/runtime/pack/dist/opt/netrw/autoload/netrw/os.vim new file mode 100644 index 0000000000..455ea5a513 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/netrw/autoload/netrw/os.vim @@ -0,0 +1,48 @@ +" FUNCTIONS IN THIS FILE ARE MEANT TO BE USED BY NETRW.VIM AND NETRW.VIM ONLY. +" THESE FUNCTIONS DON'T COMMIT TO ANY BACKWARDS COMPATIBILITY. SO CHANGES AND +" BREAKAGES IF USED OUTSIDE OF NETRW.VIM ARE EXPECTED. + +" netrw#os#Execute: executes a string using "!" {{{ + +function! netrw#os#Execute(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 + execute a:cmd + finally + let [&shell,&shellcmdflag,&shellxquote,&shellxescape,&shellquote,&shellpipe,&shellredir,&shellslash] = savedShell + endtry + else + execute a:cmd + endif + + if v:shell_error + call netrw#msg#Notify('ERROR', "shell signalled an error") + endif +endfunction + +" }}} +" netrw#os#Escape: shellescape(), or special windows handling {{{ + +function! netrw#os#Escape(string, ...) + return has('win32') && empty($SHELL) && &shellslash + \ ? printf('"%s"', substitute(a:string, '"', '""', 'g')) + \ : shellescape(a:string, a:0 > 0 ? a:1 : 0) +endfunction + +" }}} +" netrw#os#Open: open file with os viewer (eg. xdg-open) {{{ + +function! netrw#os#Open(file) abort + if has('nvim') + call luaeval('vim.ui.open(_A) and nil', a:file) + else + call dist#vim9#Open(a:file) + endif +endfunction + +" }}} + +" vim:ts=8 sts=4 sw=4 et fdm=marker diff --git a/uvim/runtime/pack/dist/opt/netrw/autoload/netrw_gitignore.vim b/uvim/runtime/pack/dist/opt/netrw/autoload/netrw_gitignore.vim new file mode 100644 index 0000000000..75aadf2aaa --- /dev/null +++ b/uvim/runtime/pack/dist/opt/netrw/autoload/netrw_gitignore.vim @@ -0,0 +1,28 @@ +" Previous Maintainer: Luca Saccarola <github.e41mv@aleeas.com> +" Maintainer: This runtime file is looking for a new maintainer. + +" netrw_gitignore#Hide: gitignore-based hiding +" Function returns a string of comma separated patterns convenient for +" assignment to `g:netrw_list_hide` option. +" Function can take additional filenames as arguments, example: +" netrw_gitignore#Hide('custom_gitignore1', 'custom_gitignore2') +" +" Usage examples: +" 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 {{{ +" 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_gitignore.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. }}} + +function! netrw_gitignore#Hide(...) + 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/uvim/runtime/pack/dist/opt/netrw/doc/netrw.txt b/uvim/runtime/pack/dist/opt/netrw/doc/netrw.txt new file mode 100644 index 0000000000..01a5bda597 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/netrw/doc/netrw.txt @@ -0,0 +1,3608 @@ +*netrw.txt* + + ------------------------------------------------ + NETRW REFERENCE MANUAL by Charles E. Campbell + ------------------------------------------------ +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, netrw.txt, 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 + merchantability. No guarantees of suitability for any purpose. 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. Use at your own risk! For bug reports, see |bugs|. + + *netrw* + *dav* *ftp* *netrw-file* *rcp* *scp* + *davs* *http* *netrw.vim* *rsync* *sftp* + *fetch* *network* + +============================================================================== +1. Contents *netrw-contents* {{{1 + +1. Contents..............................................|netrw-contents| +2. Starting With Netrw...................................|netrw-start| +3. Netrw Reference.......................................|netrw-ref| + EXTERNAL APPLICATIONS AND PROTOCOLS.................|netrw-externapp| + READING.............................................|netrw-read| + WRITING.............................................|netrw-write| + SOURCING............................................|netrw-source| + DIRECTORY LISTING...................................|netrw-dirlist| + CHANGING THE USERID AND PASSWORD....................|netrw-chgup| + VARIABLES AND SETTINGS..............................|netrw-variables| + PATHS...............................................|netrw-path| +4. Network-Oriented File Transfer........................|netrw-xfer| + NETRC...............................................|netrw-netrc| + PASSWORD............................................|netrw-passwd| +5. Activation............................................|netrw-activate| +6. Transparent Remote File Editing.......................|netrw-transparent| +7. Ex Commands...........................................|netrw-ex| +8. Variables and Options.................................|netrw-variables| +9. Browsing..............................................|netrw-browse| + Introduction To Browsing............................|netrw-intro-browse| + Quick Reference: Maps...............................|netrw-browse-maps| + Quick Reference: Commands...........................|netrw-browse-cmds| + Banner Display......................................|netrw-I| + Bookmarking A Directory.............................|netrw-mb| + Browsing............................................|netrw-cr| + Squeezing the Current Tree-Listing Directory........|netrw-s-cr| + Browsing With A Horizontally Split Window...........|netrw-o| + Browsing With A New Tab.............................|netrw-t| + Browsing With A Vertically Split Window.............|netrw-v| + Change Listing Style (thin wide long tree)..........|netrw-i| + Changing To A Bookmarked Directory..................|netrw-gb| + Quick hide/unhide of dot-files......................|netrw-gh| + Changing local-only File Permission.................|netrw-gp| + Changing To A Predecessor Directory.................|netrw-u| + Changing To A Successor Directory...................|netrw-U| + Deleting Bookmarks..................................|netrw-mB| + Deleting Files Or Directories.......................|netrw-D| + Directory Exploring Commands........................|netrw-explore| + Exploring With Stars and Patterns...................|netrw-star| + Displaying Information About File...................|netrw-qf| + Edit File Or Directory Hiding List..................|netrw-ctrl-h| + Editing The Sorting Sequence........................|netrw-S| + Forcing treatment as a file or directory............|netrw-gd| |netrw-gf| + Going Up............................................|netrw--| + Hiding Files Or Directories.........................|netrw-a| + Improving Browsing..................................|netrw-ssh-hack| + Listing Bookmarks And History.......................|netrw-qb| + Making A New Directory..............................|netrw-d| + Making The Browsing Directory The Current Directory.|netrw-cd| + Marking Files.......................................|netrw-mf| + Unmarking Files.....................................|netrw-mF| + Marking Files By Location List......................|netrw-qL| + Marking Files By QuickFix List......................|netrw-qF| + Marking Files By Regular Expression.................|netrw-mr| + Marked Files: Arbitrary Shell Command...............|netrw-mx| + Marked Files: Arbitrary Shell Command, En Bloc......|netrw-mX| + Marked Files: Arbitrary Vim Command.................|netrw-mv| + Marked Files: Argument List.........................|netrw-ma| |netrw-mA| + Marked Files: Buffer List...........................|netrw-cb| |netrw-cB| + Marked Files: Compression And Decompression.........|netrw-mz| + Marked Files: Copying...............................|netrw-mc| + Marked Files: Diff..................................|netrw-md| + Marked Files: Editing...............................|netrw-me| + Marked Files: Grep..................................|netrw-mg| + Marked Files: Hiding and Unhiding by Suffix.........|netrw-mh| + Marked Files: Moving................................|netrw-mm| + Marked Files: Sourcing..............................|netrw-ms| + Marked Files: Setting the Target Directory..........|netrw-mt| + Marked Files: Tagging...............................|netrw-mT| + Marked Files: Target Directory Using Bookmarks......|netrw-Tb| + Marked Files: Target Directory Using History........|netrw-Th| + Marked Files: Unmarking.............................|netrw-mu| + Netrw Browser Variables.............................|netrw-browser-var| + Netrw Browsing And Option Incompatibilities.........|netrw-incompatible| + Obtaining A File....................................|netrw-O| + Preview Window......................................|netrw-p| + Previous Window.....................................|netrw-P| + Refreshing The Listing..............................|netrw-ctrl-l| + Reversing Sorting Order.............................|netrw-r| + Renaming Files Or Directories.......................|netrw-R| + Selecting Sorting Style.............................|netrw-s| + Setting Editing Window..............................|netrw-C| +10. Problems and Fixes....................................|netrw-problems| +11. Credits...............................................|netrw-credits| + +============================================================================== +2. Starting With Netrw *netrw-start* {{{1 + +Netrw makes reading files, writing files, browsing over a network, and +local browsing easy! First, make sure that you have plugins enabled, so +you'll need to have at least the following in your <.vimrc>: +(or see |netrw-activate|) > + + set nocp " 'compatible' is not set + filetype plugin on " plugins are enabled +< +(see 'cp' and |:filetype-plugin-on|) + +Netrw supports "transparent" editing of files on other machines using urls +(see |netrw-transparent|). As an example of this, let's assume you have an +account on some other machine; if you can use scp, try: > + + vim scp://hostname/path/to/file +< +Want to make ssh/scp easier to use? Check out |netrw-ssh-hack|! + +So, what if you have ftp, not ssh/scp? That's easy, too; try > + + vim ftp://hostname/path/to/file +< +Want to make ftp simpler to use? See if your ftp supports a file called +<.netrc> -- typically it goes in your home directory, has read/write +permissions for only the user to read (ie. not group, world, other, etc), +and has lines resembling > + + machine HOSTNAME login USERID password "PASSWORD" + machine HOSTNAME login USERID password "PASSWORD" + ... + default login USERID password "PASSWORD" +< +Windows' ftp doesn't support .netrc; however, one may have in one's .vimrc: > + + let g:netrw_ftp_cmd= 'c:\Windows\System32\ftp -s:C:\Users\MyUserName\MACHINE' +< +Netrw will substitute the host's machine name for "MACHINE" from the URL it is +attempting to open, and so one may specify > + userid + password +for each site in a separate file: c:\Users\MyUserName\MachineName. + +Now about browsing -- when you just want to look around before editing a +file. For browsing on your current host, just "edit" a directory: > + + vim . + vim /home/userid/path +< +For browsing on a remote host, "edit" a directory (but make sure that +the directory name is followed by a "/"): > + + vim scp://hostname/ + vim ftp://hostname/path/to/dir/ +< +See |netrw-browse| for more! + +There are more protocols supported by netrw than just scp and ftp, too: see the +next section, |netrw-externapp|, on how to use these external applications with +netrw and vim. + +PREVENTING LOADING *netrw-noload* + +If you want to use plugins, but for some reason don't wish to use netrw, then +you need to avoid loading both the plugin and the autoload portions of netrw. +You may do so by placing the following two lines in your <.vimrc>: > + + :let g:loaded_netrw = 1 + :let g:loaded_netrwPlugin = 1 +< + +============================================================================== +3. Netrw Reference *netrw-ref* {{{1 + + Netrw supports several protocols in addition to scp and ftp as mentioned + in |netrw-start|. These include dav, fetch, http,... well, just look + at the list in |netrw-externapp|. Each protocol is associated with a + variable which holds the default command supporting that protocol. + +EXTERNAL APPLICATIONS AND PROTOCOLS *netrw-externapp* {{{2 + + Protocol Variable Default Value + -------- ---------------- ------------- + dav: *g:netrw_dav_cmd* = "cadaver" if cadaver is executable + dav: g:netrw_dav_cmd = "curl -o" elseif curl is available + fetch: *g:netrw_fetch_cmd* = "fetch -o" if fetch is available + ftp: *g:netrw_ftp_cmd* = "ftp" + http: *g:netrw_http_cmd* = "elinks" if elinks is available + http: g:netrw_http_cmd = "links" elseif links is available + http: g:netrw_http_cmd = "curl" elseif curl is available + http: g:netrw_http_cmd = "wget" elseif wget is available + http: g:netrw_http_cmd = "fetch" elseif fetch is available + http: *g:netrw_http_put_cmd* = "curl -T" + rcp: *g:netrw_rcp_cmd* = "rcp" + rsync: *g:netrw_rsync_cmd* = "rsync" (see |g:netrw_rsync_sep|) + scp: *g:netrw_scp_cmd* = "scp -q" + sftp: *g:netrw_sftp_cmd* = "sftp" + file: *g:netrw_file_cmd* = "elinks" or "links" + + *g:netrw_http_xcmd* : the option string for http://... protocols are + specified via this variable and may be independently overridden. By + default, the option arguments for the http-handling commands are: > + + elinks : "-source >" + links : "-dump >" + curl : "-L -o" + wget : "-q -O" + fetch : "-o" +< + For example, if your system has elinks, and you'd rather see the + page using an attempt at rendering the text, you may wish to have > + let g:netrw_http_xcmd= "-dump >" +< in your .vimrc. + + g:netrw_http_put_cmd: this option specifies both the executable and + any needed options. This command does a PUT operation to the url. + + +READING *netrw-read* *netrw-nread* {{{2 + + Generally, one may just use the URL notation with a normal editing + command, such as > + + :e ftp://[user@]machine/path +< + Netrw also provides the Nread command: + + :Nread ? give help + :Nread "machine:path" uses rcp + :Nread "machine path" uses ftp w/ <.netrc> + :Nread "machine id password path" uses ftp + :Nread "dav://machine[:port]/path" uses cadaver + :Nread "fetch://[user@]machine/path" uses fetch + :Nread "ftp://[user@]machine[[:#]port]/path" uses ftp w/ <.netrc> + :Nread "http://[user@]machine/path" uses http uses wget + :Nread "rcp://[user@]machine/path" uses rcp + :Nread "rsync://[user@]machine[:port]/path" uses rsync + :Nread "scp://[user@]machine[[:#]port]/path" uses scp + :Nread "sftp://[user@]machine/path" uses sftp + +WRITING *netrw-write* *netrw-nwrite* {{{2 + + One may just use the URL notation with a normal file writing + command, such as > + + :w ftp://[user@]machine/path +< + Netrw also provides the Nwrite command: + + :Nwrite ? give help + :Nwrite "machine:path" uses rcp + :Nwrite "machine path" uses ftp w/ <.netrc> + :Nwrite "machine id password path" uses ftp + :Nwrite "dav://machine[:port]/path" uses cadaver + :Nwrite "ftp://[user@]machine[[:#]port]/path" uses ftp w/ <.netrc> + :Nwrite "rcp://[user@]machine/path" uses rcp + :Nwrite "rsync://[user@]machine[:port]/path" uses rsync + :Nwrite "scp://[user@]machine[[:#]port]/path" uses scp + :Nwrite "sftp://[user@]machine/path" uses sftp + http: not supported! + +SOURCING *netrw-source* {{{2 + + One may just use the URL notation with the normal file sourcing + command, such as > + + :so ftp://[user@]machine/path +< + Netrw also provides the Nsource command: + + :Nsource ? give help + :Nsource "dav://machine[:port]/path" uses cadaver + :Nsource "fetch://[user@]machine/path" uses fetch + :Nsource "ftp://[user@]machine[[:#]port]/path" uses ftp w/ <.netrc> + :Nsource "http://[user@]machine/path" uses http uses wget + :Nsource "rcp://[user@]machine/path" uses rcp + :Nsource "rsync://[user@]machine[:port]/path" uses rsync + :Nsource "scp://[user@]machine[[:#]port]/path" uses scp + :Nsource "sftp://[user@]machine/path" uses sftp + +DIRECTORY LISTING *netrw-trailingslash* *netrw-dirlist* {{{2 + + One may browse a directory to get a listing by simply attempting to + edit the directory: > + + :e scp://[user]@hostname/path/ + :e ftp://[user]@hostname/path/ +< + For remote directory listings (ie. those using scp or ftp), that + trailing "/" is necessary (the slash tells netrw to treat the argument + as a directory to browse instead of as a file to download). + + The Nread command may also be used to accomplish this (again, that + trailing slash is necessary): > + + :Nread [protocol]://[user]@hostname/path/ +< + *netrw-login* *netrw-password* +CHANGING USERID AND PASSWORD *netrw-chgup* *netrw-userpass* {{{2 + + Attempts to use ftp will prompt you for a user-id and a password. + These will be saved in global variables |g:netrw_uid| and + |s:netrw_passwd|; subsequent use of ftp will re-use those two strings, + thereby simplifying use of ftp. However, if you need to use a + different user id and/or password, you'll want to call |NetUserPass()| + first. To work around the need to enter passwords, check if your ftp + supports a <.netrc> file in your home directory. Also see + |netrw-passwd| (and if you're using ssh/scp hoping to figure out how + to not need to use passwords for scp, look at |netrw-ssh-hack|). + + :NetUserPass [uid [password]] -- prompts as needed + :call NetUserPass() -- prompts for uid and password + :call NetUserPass("uid") -- prompts for password + :call NetUserPass("uid","password") -- sets global uid and password + +(Related topics: |ftp| |netrw-userpass| |netrw-start|) + +NETRW VARIABLES AND SETTINGS *netrw-variables* {{{2 + (Also see: + |netrw-browser-var| : netrw browser option variables + |netrw-protocol| : file transfer protocol option variables + |netrw-settings| : additional file transfer options + |netrw-browser-options| : these options affect browsing directories + ) + +Netrw provides a lot of variables which allow you to customize netrw to your +preferences. Most such settings are described below, in +|netrw-browser-options|, and in |netrw-externapp|: + + *b:netrw_lastfile* last file Network-read/written retained on a + per-buffer basis (supports plain :Nw ) + + *g:netrw_bufsettings* the settings that netrw buffers have + (default) noma nomod nonu nowrap ro nobl + + *g:netrw_chgwin* specifies a window number where subsequent file edits + will take place. (also see |netrw-C|) + (default) -1 + + *g:Netrw_funcref* specifies a function (or functions) to be called when + netrw edits a file. The file is first edited, and + then the function reference (|Funcref|) is called. + This variable may also hold a |List| of Funcrefs. + (default) not defined. (the capital in g:Netrw... + is required by its holding a function reference) +> + Example: place in .vimrc; affects all file opening + fun! MyFuncRef() + endfun + let g:Netrw_funcref= function("MyFuncRef") + +< + *g:Netrw_UserMaps* specifies a function or |List| of functions which can + be used to set up user-specified maps and functionality. + See |netrw-usermaps| + + *g:netrw_ftp* if it doesn't exist, use default ftp + =0 use default ftp (uid password) + =1 use alternate ftp method (user uid password) + If you're having trouble with ftp, try changing the + value of this variable to see if the alternate ftp + method works for your setup. + + *g:netrw_ftp_options* Chosen by default, these options are supposed to + turn interactive prompting off and to restrain ftp + from attempting auto-login upon initial connection. + However, it appears that not all ftp implementations + support this (ex. ncftp). + ="-i -n" + + *g:netrw_ftpextracmd* default: doesn't exist + If this variable exists, then any string it contains + will be placed into the commands set to your ftp + client. As an example: + ="passive" + + *g:netrw_ftpmode* ="binary" (default) + ="ascii" + + *g:netrw_ignorenetrc* =0 (default for linux, cygwin) + =1 If you have a <.netrc> file but it doesn't work and + you want it ignored, then set this variable as + shown. (default for Windows + cmd.exe) + + *g:netrw_menu* =0 disable netrw's menu + =1 (default) netrw's menu enabled + + *g:netrw_uid* (ftp) user-id, retained on a per-vim-session basis + *s:netrw_passwd* (ftp) password, retained on a per-vim-session basis + + *g:netrw_preview* =0 (default) preview window shown in a horizontally + split window + =1 preview window shown in a vertically split window. + Also affects the "previous window" (see |netrw-P|) + in the same way. + The |g:netrw_alto| variable may be used to provide + additional splitting control: + g:netrw_preview g:netrw_alto result + 0 0 |:aboveleft| + 0 1 |:belowright| + 1 0 |:topleft| + 1 1 |:botright| + To control sizing, see |g:netrw_winsize| + + *g:netrw_scpport* = "-P" : option to use to set port for scp + *g:netrw_sshport* = "-p" : option to use to set port for ssh + + *g:netrw_sepchr* =\0xff + =\0x01 for enc == euc-jp (and perhaps it should be for + others, too, please let me know) + Separates priority codes from filenames internally. + See |netrw-p12|. + + *g:netrw_silent* =0 : transfers done normally + =1 : transfers done silently + + *g:netrw_cygwin* =1 assume scp under windows is from cygwin. Also + permits network browsing to use ls with time and + size sorting (default if windows) + =0 assume Windows' scp accepts windows-style paths + Network browsing uses dir instead of ls + This option is ignored if you're using unix + + *g:netrw_use_nt_rcp* =0 don't use the rcp of WinNT, Win2000 and WinXP + =1 use WinNT's rcp in binary mode (default) + +PATHS *netrw-path* {{{2 + +Paths to files are generally user-directory relative for most protocols. +It is possible that some protocol will make paths relative to some +associated directory, however. +> + example: vim scp://user@host/somefile + example: vim scp://user@host/subdir1/subdir2/somefile +< +where "somefile" is in the "user"'s home directory. If you wish to get a +file using root-relative paths, use the full path: +> + example: vim scp://user@host//somefile + example: vim scp://user@host//subdir1/subdir2/somefile +< + +============================================================================== +4. Network-Oriented File Transfer *netrw-xfer* {{{1 + +Network-oriented file transfer under Vim is implemented by a Vim script +(<netrw.vim>) using plugin techniques. It currently supports both reading and +writing across networks using rcp, scp, ftp or ftp+<.netrc>, scp, fetch, +dav/cadaver, rsync, or sftp. + +http is currently supported read-only via use of wget or fetch. + +<netrw.vim> is a standard plugin which acts as glue between Vim and the +various file transfer programs. It uses autocommand events (BufReadCmd, +FileReadCmd, BufWriteCmd) to intercept reads/writes with url-like filenames. > + + ex. vim ftp://hostname/path/to/file +< +The characters preceding the colon specify the protocol to use; in the +example, it's ftp. The <netrw.vim> script then formulates a command or a +series of commands (typically ftp) which it issues to an external program +(ftp, scp, etc) which does the actual file transfer/protocol. Files are read +from/written to a temporary file (under Unix/Linux, /tmp/...) which the +<netrw.vim> script will clean up. + +Now, a word about Jan Minář's "FTP User Name and Password Disclosure"; first, +ftp is not a secure protocol. User names and passwords are transmitted "in +the clear" over the internet; any snooper tool can pick these up; this is not +a netrw thing, this is a ftp thing. If you're concerned about this, please +try to use scp or sftp instead. + +Netrw re-uses the user id and password during the same vim session and so long +as the remote hostname remains the same. + +Jan seems to be a bit confused about how netrw handles ftp; normally multiple +commands are performed in a "ftp session", and he seems to feel that the +uid/password should only be retained over one ftp session. However, netrw +does every ftp operation in a separate "ftp session"; so remembering the +uid/password for just one "ftp session" would be the same as not remembering +the uid/password at all. IMHO this would rapidly grow tiresome as one +browsed remote directories, for example. + +On the other hand, thanks go to Jan M. for pointing out the many +vulnerabilities that netrw (and vim itself) had had in handling "crafted" +filenames. The |shellescape()| and |fnameescape()| functions were written in +response by Bram Moolenaar to handle these sort of problems, and netrw has +been modified to use them. Still, my advice is, if the "filename" looks like +a vim command that you aren't comfortable with having executed, don't open it. + + *netrw-putty* *netrw-pscp* *netrw-psftp* +One may modify any protocol's implementing external application by setting a +variable (ex. scp uses the variable g:netrw_scp_cmd, which is defaulted to +"scp -q"). As an example, consider using PuTTY: > + + let g:netrw_scp_cmd = '"c:\Program Files\PuTTY\pscp.exe" -q -batch' + let g:netrw_sftp_cmd= '"c:\Program Files\PuTTY\psftp.exe"' +< +(note: it has been reported that windows 7 with putty v0.6's "-batch" option + doesn't work, so its best to leave it off for that system) + +See |netrw-p8| for more about putty, pscp, psftp, etc. + +Ftp, an old protocol, seems to be blessed by numerous implementations. +Unfortunately, some implementations are noisy (ie., add junk to the end of the +file). Thus, concerned users may decide to write a NetReadFixup() function +that will clean up after reading with their ftp. Some Unix systems (ie., +FreeBSD) provide a utility called "fetch" which uses the ftp protocol but is +not noisy and more convenient, actually, for <netrw.vim> to use. +Consequently, if "fetch" is available (ie. executable), it may be preferable +to use it for ftp://... based transfers. + +For rcp, scp, sftp, and http, one may use network-oriented file transfers +transparently; ie. +> + vim rcp://[user@]machine/path + vim scp://[user@]machine/path +< +If your ftp supports <.netrc>, then it too can be transparently used +if the needed triad of machine name, user id, and password are present in +that file. Your ftp must be able to use the <.netrc> file on its own, however. +> + vim ftp://[user@]machine[[:#]portnumber]/path +< +Windows provides an ftp (typically c:\Windows\System32\ftp.exe) which uses +an option, -s:filename (filename can and probably should be a full path) +which contains ftp commands which will be automatically run whenever ftp +starts. You may use this feature to enter a user and password for one site: > + userid + password +< *netrw-windows-netrc* *netrw-windows-s* +If |g:netrw_ftp_cmd| contains -s:[path/]MACHINE, then (on Windows machines +only) netrw will substitute the current machine name requested for ftp +connections for MACHINE. Hence one can have multiple machine.ftp files +containing login and password for ftp. Example: > + + let g:netrw_ftp_cmd= 'c:\Windows\System32\ftp -s:C:\Users\Myself\MACHINE' + vim ftp://myhost.somewhere.net/ + +will use a file > + + C:\Users\Myself\myhost.ftp +< +Often, ftp will need to query the user for the userid and password. +The latter will be done "silently"; ie. asterisks will show up instead of +the actually-typed-in password. Netrw will retain the userid and password +for subsequent read/writes from the most recent transfer so subsequent +transfers (read/write) to or from that machine will take place without +additional prompting. + + *netrw-urls* + +=================================+============================+============+ + | Reading | Writing | Uses | + +=================================+============================+============+ + | DAV: | | | + | dav://host/path | | cadaver | + | :Nread dav://host/path | :Nwrite dav://host/path | cadaver | + +---------------------------------+----------------------------+------------+ + | DAV + SSL: | | | + | davs://host/path | | cadaver | + | :Nread davs://host/path | :Nwrite davs://host/path | cadaver | + +---------------------------------+----------------------------+------------+ + | FETCH: | | | + | fetch://[user@]host/path | | | + | fetch://[user@]host:http/path | Not Available | fetch | + | :Nread fetch://[user@]host/path| | | + +---------------------------------+----------------------------+------------+ + | FILE: | | | + | file:///* | file:///* | | + | file://localhost/* | file://localhost/* | | + +---------------------------------+----------------------------+------------+ + | FTP: (*3) | (*3) | | + | ftp://[user@]host/path | ftp://[user@]host/path | ftp (*2) | + | :Nread ftp://host/path | :Nwrite ftp://host/path | ftp+.netrc | + | :Nread host path | :Nwrite host path | ftp+.netrc | + | :Nread host uid pass path | :Nwrite host uid pass path | ftp | + +---------------------------------+----------------------------+------------+ + | HTTP: wget is executable: (*4) | | | + | http://[user@]host/path | Not Available | wget | + +---------------------------------+----------------------------+------------+ + | HTTP: fetch is executable (*4) | | | + | http://[user@]host/path | Not Available | fetch | + +---------------------------------+----------------------------+------------+ + | RCP: | | | + | rcp://[user@]host/path | rcp://[user@]host/path | rcp | + +---------------------------------+----------------------------+------------+ + | RSYNC: | | | + | rsync://[user@]host/path | rsync://[user@]host/path | rsync | + | :Nread rsync://host/path | :Nwrite rsync://host/path | rsync | + | :Nread rcp://host/path | :Nwrite rcp://host/path | rcp | + +---------------------------------+----------------------------+------------+ + | SCP: | | | + | scp://[user@]host/path | scp://[user@]host/path | scp | + | :Nread scp://host/path | :Nwrite scp://host/path | scp (*1) | + +---------------------------------+----------------------------+------------+ + | SFTP: | | | + | sftp://[user@]host/path | sftp://[user@]host/path | sftp | + | :Nread sftp://host/path | :Nwrite sftp://host/path | sftp (*1) | + +=================================+============================+============+ + + (*1) For an absolute path use scp://machine//path. + + (*2) if <.netrc> is present, it is assumed that it will + work with your ftp client. Otherwise the script will + prompt for user-id and password. + + (*3) for ftp, "machine" may be machine#port or machine:port + if a different port is needed than the standard ftp port + + (*4) for http:..., if wget is available it will be used. Otherwise, + if fetch is available it will be used. + +Both the :Nread and the :Nwrite ex-commands can accept multiple filenames. + + +NETRC *netrw-netrc* + +The <.netrc> file, typically located in your home directory, contains lines +therein which map a hostname (machine name) to the user id and password you +prefer to use with it. + +The typical syntax for lines in a <.netrc> file is given as shown below. +Ftp under Unix usually supports <.netrc>; ftp under Windows usually doesn't. +> + machine {full machine name} login {user-id} password "{password}" + default login {user-id} password "{password}" + +Your ftp client must handle the use of <.netrc> on its own, but if the +<.netrc> file exists, an ftp transfer will not ask for the user-id or +password. + + Note: + Since this file contains passwords, make very sure nobody else can + read this file! Most programs will refuse to use a .netrc that is + readable for others. Don't forget that the system administrator can + still read the file! Ie. for Linux/Unix: chmod 600 .netrc + +Even though Windows' ftp clients typically do not support .netrc, netrw has +a work-around: see |netrw-windows-s|. + + +PASSWORD *netrw-passwd* + +The script attempts to get passwords for ftp invisibly using |inputsecret()|, +a built-in Vim function. See |netrw-userpass| for how to change the password +after one has set it. + +Unfortunately there doesn't appear to be a way for netrw to feed a password to +scp. Thus every transfer via scp will require re-entry of the password. +However, |netrw-ssh-hack| can help with this problem. + + +============================================================================== +5. Activation *netrw-activate* {{{1 + +Network-oriented file transfers are available by default whenever Vim's +'nocompatible' mode is enabled. Netrw's script files reside in your +system's plugin, autoload, and syntax directories; just the +plugin/netrwPlugin.vim script is sourced automatically whenever you bring up +vim. The main script in autoload/netrw.vim is only loaded when you actually +use netrw. I suggest that, at a minimum, you have at least the following in +your <.vimrc> customization file: > + + set nocp + if version >= 600 + filetype plugin indent on + endif +< +By also including the following lines in your .vimrc, one may have netrw +immediately activate when using [g]vim without any filenames, showing the +current directory: > + + " Augroup VimStartup: + augroup VimStartup + au! + au VimEnter * if expand("%") == "" | e . | endif + augroup END +< + +============================================================================== +6. Transparent Remote File Editing *netrw-transparent* {{{1 + +Transparent file transfers occur whenever a regular file read or write +(invoked via an |:autocmd| for |BufReadCmd|, |BufWriteCmd|, or |SourceCmd| +events) is made. Thus one may read, write, or source files across networks +just as easily as if they were local files! > + + vim ftp://[user@]machine/path + ... + :wq + +See |netrw-activate| for more on how to encourage your vim to use plugins +such as netrw. + +For password-free use of scp:, see |netrw-ssh-hack|. + + +============================================================================== +7. Ex Commands *netrw-ex* {{{1 + +The usual read/write commands are supported. There are also a few +additional commands available. Often you won't need to use Nwrite or +Nread as shown in |netrw-transparent| (ie. simply use > + :e URL + :r URL + :w URL +instead, as appropriate) -- see |netrw-urls|. In the explanations +below, a {netfile} is a URL to a remote file. + + *:Nwrite* *:Nw* +:[range]Nw[rite] Write the specified lines to the current + file as specified in b:netrw_lastfile. + (related: |netrw-nwrite|) + +:[range]Nw[rite] {netfile} [{netfile}]... + Write the specified lines to the {netfile}. + + *:Nread* *:Nr* +:Nr[ead] Read the lines from the file specified in b:netrw_lastfile + into the current buffer. (related: |netrw-nread|) + +:Nr[ead] {netfile} {netfile}... + Read the {netfile} after the current line. + + *:Nsource* *:Ns* +:Ns[ource] {netfile} + Source the {netfile}. + To start up vim using a remote .vimrc, one may use + the following (all on one line) (tnx to Antoine Mechelynck) > + vim -u NORC -N + --cmd "runtime plugin/netrwPlugin.vim" + --cmd "source scp://HOSTNAME/.vimrc" +< (related: |netrw-source|) + +:call NetUserPass() *NetUserPass()* + If g:netrw_uid and s:netrw_passwd don't exist, + this function will query the user for them. + (related: |netrw-userpass|) + +:call NetUserPass("userid") + This call will set the g:netrw_uid and, if + the password doesn't exist, will query the user for it. + (related: |netrw-userpass|) + +:call NetUserPass("userid","passwd") + This call will set both the g:netrw_uid and s:netrw_passwd. + The user-id and password are used by ftp transfers. One may + effectively remove the user-id and password by using empty + strings (ie. ""). + (related: |netrw-userpass|) + + +============================================================================== +8. Variables and Options *netrw-var* *netrw-settings* {{{1 + +(also see: |netrw-options| |netrw-variables| |netrw-protocol| + |netrw-browser-settings| |netrw-browser-options| ) + +The <netrw.vim> script provides several variables which act as options to +affect <netrw.vim>'s file transfer behavior. These variables typically may be +set in the user's <.vimrc> file: (see also |netrw-settings| |netrw-protocol|) + *netrw-options* +> + ------------- + Netrw Options + ------------- + Option Meaning + -------------- ----------------------------------------------- +< + b:netrw_col Holds current cursor position (during NetWrite) + g:netrw_cygwin =1 assume scp under windows is from cygwin + (default/windows) + =0 assume scp under windows accepts windows + style paths (default/else) + g:netrw_ftp =0 use default ftp (uid password) + g:netrw_ftpmode ="binary" (default) + ="ascii" (your choice) + g:netrw_ignorenetrc =1 (default) + if you have a <.netrc> file but you don't + want it used, then set this variable. Its + mere existence is enough to cause <.netrc> + to be ignored. + b:netrw_lastfile Holds latest method/machine/path. + b:netrw_line Holds current line number (during NetWrite) + g:netrw_silent =0 transfers done normally + =1 transfers done silently + g:netrw_uid Holds current user-id for ftp. + g:netrw_use_nt_rcp =0 don't use WinNT/2K/XP's rcp (default) + =1 use WinNT/2K/XP's rcp, binary mode + ----------------------------------------------------------------------- +< + *netrw-internal-variables* +The script will also make use of the following variables internally, albeit +temporarily. +> + ------------------- + Temporary Variables + ------------------- + Variable Meaning + -------- ------------------------------------ +< + b:netrw_method Index indicating rcp/ftp+.netrc/ftp + w:netrw_method (same as b:netrw_method) + g:netrw_machine Holds machine name parsed from input + b:netrw_fname Holds filename being accessed > + ------------------------------------------------------------ +< + *netrw-protocol* + +Netrw supports a number of protocols. These protocols are invoked using the +variables listed below, and may be modified by the user. +> + ------------------------ + Protocol Control Options + ------------------------ + Option Type Setting Meaning + --------- -------- -------------- --------------------------- +< netrw_ftp variable =doesn't exist userid set by "user userid" + =0 userid set by "user userid" + =1 userid set by "userid" + NetReadFixup function =doesn't exist no change + =exists Allows user to have files + read via ftp automatically + transformed however they wish + by NetReadFixup() + g:netrw_dav_cmd var ="cadaver" if cadaver is executable + g:netrw_dav_cmd var ="curl -o" elseif curl is executable + g:netrw_fetch_cmd var ="fetch -o" if fetch is available + g:netrw_ftp_cmd var ="ftp" + g:netrw_http_cmd var ="fetch -o" if fetch is available + g:netrw_http_cmd var ="wget -O" else if wget is available + g:netrw_http_put_cmd var ="curl -T" + |g:netrw_list_cmd| var ="ssh USEPORT HOSTNAME ls -Fa" + g:netrw_rcp_cmd var ="rcp" + g:netrw_rsync_cmd var ="rsync" + *g:netrw_rsync_sep* var ="/" used to separate the hostname + from the file spec + g:netrw_scp_cmd var ="scp -q" + g:netrw_sftp_cmd var ="sftp" > + ------------------------------------------------------------------------- +< + *netrw-ftp* + +The g:netrw_..._cmd options (|g:netrw_ftp_cmd| and |g:netrw_sftp_cmd|) +specify the external program to use handle the ftp protocol. They may +include command line options (such as -p for passive mode). Example: > + + let g:netrw_ftp_cmd= "ftp -p" +< +Browsing is supported by using the |g:netrw_list_cmd|; the substring +"HOSTNAME" will be changed via substitution with whatever the current request +is for a hostname. + +Two options (|g:netrw_ftp| and |netrw-fixup|) both help with certain ftp's +that give trouble . In order to best understand how to use these options if +ftp is giving you troubles, a bit of discussion is provided on how netrw does +ftp reads. + +For ftp, netrw typically builds up lines of one of the following formats in a +temporary file: +> + IF g:netrw_ftp !exists or is not 1 IF g:netrw_ftp exists and is 1 + ---------------------------------- ------------------------------ +< + open machine [port] open machine [port] + user userid password userid password + [g:netrw_ftpmode] password + [g:netrw_ftpextracmd] [g:netrw_ftpmode] + get filename tempfile [g:netrw_extracmd] + get filename tempfile > + --------------------------------------------------------------------- +< +The |g:netrw_ftpmode| and |g:netrw_ftpextracmd| are optional. + +Netrw then executes the lines above by use of a filter: +> + :%! {g:netrw_ftp_cmd} -i [-n] +< +where + g:netrw_ftp_cmd is usually "ftp", + -i tells ftp not to be interactive + -n means don't use netrc and is used for Method #3 (ftp w/o <.netrc>) + +If <.netrc> exists it will be used to avoid having to query the user for +userid and password. The transferred file is put into a temporary file. +The temporary file is then read into the main editing session window that +requested it and the temporary file deleted. + +If your ftp doesn't accept the "user" command and immediately just demands a +userid, then try putting "let netrw_ftp=1" in your <.vimrc>. + + *netrw-cadaver* +To handle the SSL certificate dialog for untrusted servers, one may pull +down the certificate and place it into /usr/ssl/cert.pem. This operation +renders the server treatment as "trusted". + + *netrw-fixup* *netreadfixup* +If your ftp for whatever reason generates unwanted lines (such as AUTH +messages) you may write a NetReadFixup() function: +> + function! NetReadFixup(method,line1,line2) + " a:line1: first new line in current file + " a:line2: last new line in current file + if a:method == 1 "rcp + elseif a:method == 2 "ftp + <.netrc> + elseif a:method == 3 "ftp + machine,uid,password,filename + elseif a:method == 4 "scp + elseif a:method == 5 "http/wget + elseif a:method == 6 "dav/cadaver + elseif a:method == 7 "rsync + elseif a:method == 8 "fetch + elseif a:method == 9 "sftp + else " complain + endif + endfunction +> +The NetReadFixup() function will be called if it exists and thus allows you to +customize your reading process. + +(Related topics: |ftp| |netrw-userpass| |netrw-start|) + +============================================================================== +9. Browsing *netrw-browsing* *netrw-browse* *netrw-help* {{{1 + *netrw-browser* *netrw-dir* *netrw-list* + +INTRODUCTION TO BROWSING *netrw-intro-browse* {{{2 + (Quick References: |netrw-quickmaps| |netrw-quickcoms|) + +Netrw supports the browsing of directories on your local system and on remote +hosts; browsing includes listing files and directories, entering directories, +editing files therein, deleting files/directories, making new directories, +moving (renaming) files and directories, copying files and directories, etc. +One may mark files and execute any system command on them! The Netrw browser +generally implements the previous explorer's maps and commands for remote +directories, although details (such as pertinent global variable names) +necessarily differ. To browse a directory, simply "edit" it! > + + vim /your/directory/ + vim . + vim c:\your\directory\ +< +(Related topics: |netrw-cr| |netrw-o| |netrw-p| |netrw-P| |netrw-t| + |netrw-mf| |netrw-mx| |netrw-D| |netrw-R| |netrw-v| ) + +The Netrw remote file and directory browser handles two protocols: ssh and +ftp. The protocol in the url, if it is ftp, will cause netrw also to use ftp +in its remote browsing. Specifying any other protocol will cause it to be +used for file transfers; but the ssh protocol will be used to do remote +browsing. + +To use Netrw's remote directory browser, simply attempt to read a "file" with +a trailing slash and it will be interpreted as a request to list a directory: +> + vim [protocol]://[user@]hostname/path/ +< +where [protocol] is typically scp or ftp. As an example, try: > + + vim ftp://ftp.home.vim.org/pub/vim/ +< +For local directories, the trailing slash is not required. Again, because it's +easy to miss: to browse remote directories, the URL must terminate with a +slash! + +If you'd like to avoid entering the password repeatedly for remote directory +listings with ssh or scp, see |netrw-ssh-hack|. To avoid password entry with +ftp, see |netrw-netrc| (if your ftp supports it). + +There are several things you can do to affect the browser's display of files: + + * To change the listing style, press the "i" key (|netrw-i|). + Currently there are four styles: thin, long, wide, and tree. + To make that change "permanent", see |g:netrw_liststyle|. + + * To hide files (don't want to see those xyz~ files anymore?) see + |netrw-ctrl-h|. + + * Press s to sort files by name, time, or size. + +See |netrw-browse-cmds| for all the things you can do with netrw! + + *netrw-getftype* *netrw-filigree* *netrw-ftype* +The |getftype()| function is used to append a bit of filigree to indicate +filetype to locally listed files: + + directory : / + executable : * + fifo : | + links : @ + sockets : = + +The filigree also affects the |g:netrw_sort_sequence|. + + +QUICK HELP *netrw-quickhelp* {{{2 + (Use ctrl-] to select a topic)~ + Intro to Browsing...............................|netrw-intro-browse| + Quick Reference: Maps.........................|netrw-quickmap| + Quick Reference: Commands.....................|netrw-browse-cmds| + Hiding + Edit hiding list..............................|netrw-ctrl-h| + Hiding Files or Directories...................|netrw-a| + Hiding/Unhiding by suffix.....................|netrw-mh| + Hiding dot-files.............................|netrw-gh| + Listing Style + Select listing style (thin/long/wide/tree)....|netrw-i| + Associated setting variable...................|g:netrw_liststyle| + Shell command used to perform listing.........|g:netrw_list_cmd| + Quick file info...............................|netrw-qf| + Sorted by + Select sorting style (name/time/size).........|netrw-s| + Editing the sorting sequence..................|netrw-S| + Sorting options...............................|g:netrw_sort_options| + Associated setting variable...................|g:netrw_sort_sequence| + Reverse sorting order.........................|netrw-r| + + + *netrw-quickmap* *netrw-quickmaps* +QUICK REFERENCE: MAPS *netrw-browse-maps* {{{2 +> + --- ----------------- ---- + Map Quick Explanation Link + --- ----------------- ---- +< <F1> Causes Netrw to issue help + <cr> Netrw will enter the directory or read the file |netrw-cr| + <del> Netrw will attempt to remove the file/directory |netrw-del| + <c-h> Edit file hiding list |netrw-ctrl-h| + <c-l> Causes Netrw to refresh the directory listing |netrw-ctrl-l| + <c-r> Browse using a gvim server |netrw-ctrl-r| + <c-tab> Shrink/expand a netrw/explore window |netrw-c-tab| + - Makes Netrw go up one directory |netrw--| + a Cycles between normal display, |netrw-a| + hiding (suppress display of files matching g:netrw_list_hide) + and showing (display only files which match g:netrw_list_hide) + cd Make browsing directory the current directory |netrw-cd| + C Setting the editing window |netrw-C| + d Make a directory |netrw-d| + D Attempt to remove the file(s)/directory(ies) |netrw-D| + gb Go to previous bookmarked directory |netrw-gb| + gd Force treatment as directory |netrw-gd| + gf Force treatment as file |netrw-gf| + gh Quick hide/unhide of dot-files |netrw-gh| + gn Make top of tree the directory below the cursor |netrw-gn| + gp Change local-only file permissions |netrw-gp| + i Cycle between thin, long, wide, and tree listings |netrw-i| + I Toggle the displaying of the banner |netrw-I| + mb Bookmark current directory |netrw-mb| + mc Copy marked files to marked-file target directory |netrw-mc| + md Apply diff to marked files (up to 3) |netrw-md| + me Place marked files on arg list and edit them |netrw-me| + mf Mark a file |netrw-mf| + mF Unmark files |netrw-mF| + mg Apply vimgrep to marked files |netrw-mg| + mh Toggle marked file suffices' presence on hiding list |netrw-mh| + mm Move marked files to marked-file target directory |netrw-mm| + mr Mark files using a shell-style |regexp| |netrw-mr| + mt Current browsing directory becomes markfile target |netrw-mt| + mT Apply ctags to marked files |netrw-mT| + mu Unmark all marked files |netrw-mu| + mv Apply arbitrary vim command to marked files |netrw-mv| + mx Apply arbitrary shell command to marked files |netrw-mx| + mX Apply arbitrary shell command to marked files en bloc|netrw-mX| + mz Compress/decompress marked files |netrw-mz| + o Enter the file/directory under the cursor in a new |netrw-o| + browser window. A horizontal split is used. + O Obtain a file specified by cursor |netrw-O| + p Preview the file |netrw-p| + P Browse in the previously used window |netrw-P| + qb List bookmarked directories and history |netrw-qb| + qf Display information on file |netrw-qf| + qF Mark files using a quickfix list |netrw-qF| + qL Mark files using a |location-list| |netrw-qL| + r Reverse sorting order |netrw-r| + R Rename the designated file(s)/directory(ies) |netrw-R| + s Select sorting style: by name, time, or file size |netrw-s| + S Specify suffix priority for name-sorting |netrw-S| + t Enter the file/directory under the cursor in a new tab|netrw-t| + u Change to recently-visited directory |netrw-u| + U Change to subsequently-visited directory |netrw-U| + v Enter the file/directory under the cursor in a new |netrw-v| + browser window. A vertical split is used. + x View file with an associated program |:Open| + X Execute filename under cursor via |system()| |netrw-X| + + % Open a new file in netrw's current directory |netrw-%| + + *netrw-mouse* *netrw-leftmouse* *netrw-middlemouse* *netrw-rightmouse* + <leftmouse> (gvim only) selects word under mouse as if a <cr> + had been pressed (ie. edit file, change directory) + <middlemouse> (gvim only) same as P selecting word under mouse; + see |netrw-P| + <rightmouse> (gvim only) delete file/directory using word under + mouse + <2-leftmouse> (gvim only) when: + * in a netrw-selected file, AND + * |g:netrw_retmap| == 1 AND + * the user doesn't already have a <2-leftmouse> + mapping defined before netrw is autoloaded, + then a double clicked leftmouse button will return + to the netrw browser window. See |g:netrw_retmap|. + <s-leftmouse> (gvim only) like mf, will mark files. Dragging + the shifted leftmouse will mark multiple files. + (see |netrw-mf|) + + (to disable mouse buttons while browsing: |g:netrw_mousemaps|) + + *netrw-quickcom* *netrw-quickcoms* +QUICK REFERENCE: COMMANDS *netrw-explore-cmds* *netrw-browse-cmds* {{{2 + :Ntree....................................................|netrw-ntree| + :Explore[!] [dir] Explore directory of current file......|netrw-explore| + :Hexplore[!] [dir] Horizontal Split & Explore.............|netrw-explore| + :Lexplore[!] [dir] Left Explorer Toggle...................|netrw-explore| + :Nexplore[!] [dir] Vertical Split & Explore...............|netrw-explore| + :Pexplore[!] [dir] Vertical Split & Explore...............|netrw-explore| + :Rexplore Return to Explorer.....................|netrw-explore| + :Sexplore[!] [dir] Split & Explore directory .............|netrw-explore| + :Texplore[!] [dir] Tab & Explore..........................|netrw-explore| + :Vexplore[!] [dir] Vertical Split & Explore...............|netrw-explore| + + +BANNER DISPLAY *netrw-I* + +One may toggle the displaying of the banner by pressing "I". + +Also See: |g:netrw_banner| + + +BOOKMARKING A DIRECTORY *netrw-mb* *netrw-bookmark* *netrw-bookmarks* {{{2 + +One may easily "bookmark" the currently browsed directory by using > + + mb +< + *.netrwbook* +Bookmarks are retained in between sessions of vim in a file called .netrwbook +as a |List|, which is typically stored in the first directory on the user's +'runtimepath'; entries are kept in sorted order. + +If there are marked files and/or directories, mb will add them to the bookmark +list. + + *netrw-:NetrwMB* +Additionally, one may use :NetrwMB to bookmark files or directories. > + + :NetrwMB[!] [files/directories] + +< 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 : |glob()|s each arg and bookmarks them + + With bang: deletes files/directories from Netrw's bookmark system + +The :NetrwMB command is available outside of netrw buffers (once netrw has been +invoked in the session). + +The file ".netrwbook" holds bookmarks when netrw (and vim) is not active. By +default, its stored on the first directory on the user's 'runtimepath'. + +Related Topics: + |netrw-gb| how to return (go) to a bookmark + |netrw-mB| how to delete bookmarks + |netrw-qb| how to list bookmarks + |g:netrw_home| controls where .netrwbook is kept + + +BROWSING *netrw-enter* *netrw-cr* {{{2 + +Browsing is simple: move the cursor onto a file or directory of interest. +Hitting the <cr> (the return key) will select the file or directory. +Directories will themselves be listed, and files will be opened using the +protocol given in the original read request. + + CAVEAT: There are four forms of listing (see |netrw-i|). Netrw assumes that + two or more spaces delimit filenames and directory names for the long and + wide listing formats. Thus, if your filename or directory name has two or + more sequential spaces embedded in it, or any trailing spaces, then you'll + need to use the "thin" format to select it. + +The |g:netrw_browse_split| option, which is zero by default, may be used to +cause the opening of files to be done in a new window or tab instead of the +default. When the option is one or two, the splitting will be taken +horizontally or vertically, respectively. When the option is set to three, a +<cr> will cause the file to appear in a new tab. + + +When using the gui (gvim), one may select a file by pressing the <leftmouse> +button. In addition, if + + * |g:netrw_retmap| == 1 AND (its default value is 0) + * in a netrw-selected file, AND + * the user doesn't already have a <2-leftmouse> mapping defined before + netrw is loaded + +then a doubly-clicked leftmouse button will return to the netrw browser +window. + +Netrw attempts to speed up browsing, especially for remote browsing where one +may have to enter passwords, by keeping and re-using previously obtained +directory listing buffers. The |g:netrw_fastbrowse| variable is used to +control this behavior; one may have slow browsing (no buffer re-use), medium +speed browsing (re-use directory buffer listings only for remote directories), +and fast browsing (re-use directory buffer listings as often as possible). +The price for such re-use is that when changes are made (such as new files +are introduced into a directory), the listing may become out-of-date. One may +always refresh directory listing buffers by pressing ctrl-L (see +|netrw-ctrl-l|). + + *netrw-s-cr* +Squeezing the Current Tree-Listing Directory~ + +When the tree listing style is enabled (see |netrw-i|) and one is using +gvim, then the <s-cr> mapping may be used to squeeze (close) the +directory currently containing the cursor. + +Otherwise, one may remap a key combination of one's own choice to get +this effect: > + + nmap <buffer> <silent> <nowait> YOURKEYCOMBO <Plug>NetrwTreeSqueeze +< +Put this line in $HOME/ftplugin/netrw/netrw.vim; it needs to be generated +for netrw buffers only. + +Related topics: + |netrw-ctrl-r| |netrw-o| |netrw-p| + |netrw-P| |netrw-t| |netrw-v| +Associated setting variables: + |g:netrw_browse_split| |g:netrw_fastbrowse| + |g:netrw_ftp_list_cmd| |g:netrw_ftp_sizelist_cmd| + |g:netrw_ftp_timelist_cmd| |g:netrw_ssh_browse_reject| + |g:netrw_ssh_cmd| |g:netrw_use_noswf| + + +BROWSING WITH A HORIZONTALLY SPLIT WINDOW *netrw-o* *netrw-horiz* {{{2 + +Normally one enters a file or directory using the <cr>. However, the "o" map +allows one to open a new window to hold the new directory listing or file. A +horizontal split is used. (for vertical splitting, see |netrw-v|) + +Normally, the o key splits the window horizontally with the new window and +cursor at the top. + +Associated setting variables: |g:netrw_alto| |g:netrw_winsize| + +Related topics: + |netrw-ctrl-r| |netrw-o| |netrw-p| + |netrw-P| |netrw-t| |netrw-v| +Associated setting variables: + |g:netrw_alto| control above/below splitting + |g:netrw_winsize| control initial sizing + +BROWSING WITH A NEW TAB *netrw-t* {{{2 + +Normally one enters a file or directory using the <cr>. The "t" map +allows one to open a new window holding the new directory listing or file in +a new tab. + +If you'd like to have the new listing in a background tab, use |gT|. + +Related topics: + |netrw-ctrl-r| |netrw-o| |netrw-p| + |netrw-P| |netrw-t| |netrw-v| +Associated setting variables: + |g:netrw_winsize| control initial sizing + +BROWSING WITH A VERTICALLY SPLIT WINDOW *netrw-v* {{{2 + +Normally one enters a file or directory using the <cr>. However, the "v" map +allows one to open a new window to hold the new directory listing or file. A +vertical split is used. (for horizontal splitting, see |netrw-o|) + +Normally, the v key splits the window vertically with the new window and +cursor at the left. + +There is only one tree listing buffer; using "v" on a displayed subdirectory +will split the screen, but the same buffer will be shown twice. + +Related topics: + |netrw-ctrl-r| |netrw-o| |netrw-p| + |netrw-P| |netrw-t| |netrw-v| +Associated setting variables: + |g:netrw_altv| control right/left splitting + |g:netrw_winsize| control initial sizing + + +BROWSING USING A GVIM SERVER *netrw-ctrl-r* {{{2 + +One may keep a browsing gvim separate from the gvim being used to edit. +Use the <c-r> map on a file (not a directory) in the netrw browser, and it +will use a gvim server (see |g:netrw_servername|). Subsequent use of <cr> +(see |netrw-cr|) will re-use that server for editing files. + +Related topics: + |netrw-ctrl-r| |netrw-o| |netrw-p| + |netrw-P| |netrw-t| |netrw-v| +Associated setting variables: + |g:netrw_servername| : sets name of server + |g:netrw_browse_split| : controls how <cr> will open files + + +CHANGE LISTING STYLE (THIN LONG WIDE TREE) *netrw-i* {{{2 + +The "i" map cycles between the thin, long, wide, and tree listing formats. + +The thin listing format gives just the files' and directories' names. + +The long listing is either based on the "ls" command via ssh for remote +directories or displays the filename, file size (in bytes), and the time and +date of last modification for local directories. With the long listing +format, netrw is not able to recognize filenames which have trailing spaces. +Use the thin listing format for such files. + +The wide listing format uses two or more contiguous spaces to delineate +filenames; when using that format, netrw won't be able to recognize or use +filenames which have two or more contiguous spaces embedded in the name or any +trailing spaces. The thin listing format will, however, work with such files. +The wide listing format is the most compact. + +The tree listing format has a top directory followed by files and directories +preceded by one or more "|"s, which indicate the directory depth. One may +open and close directories by pressing the <cr> key while atop the directory +name. + +One may make a preferred listing style your default; see |g:netrw_liststyle|. +As an example, by putting the following line in your .vimrc, > + let g:netrw_liststyle= 3 +the tree style will become your default listing style. + +One typical way to use the netrw tree display is to: > + + vim . + (use i until a tree display shows) + navigate to a file + v (edit as desired in vertically split window) + ctrl-w h (to return to the netrw listing) + P (edit newly selected file in the previous window) + ctrl-w h (to return to the netrw listing) + P (edit newly selected file in the previous window) + ...etc... +< +Associated setting variables: |g:netrw_liststyle| |g:netrw_maxfilenamelen| + |g:netrw_timefmt| |g:netrw_list_cmd| + +CHANGE FILE PERMISSION *netrw-gp* {{{2 + +"gp" will ask you for a new permission for the file named under the cursor. +Currently, this only works for local files. + +Associated setting variables: |g:netrw_chgperm| + + +CHANGING TO A BOOKMARKED DIRECTORY *netrw-gb* {{{2 + +To change directory back to a bookmarked directory, use + + {cnt}gb + +Any count may be used to reference any of the bookmarks. +Note that |netrw-qb| shows both bookmarks and history; to go +to a location stored in the history see |netrw-u| and |netrw-U|. + +Related Topics: + |netrw-mB| how to delete bookmarks + |netrw-mb| how to make a bookmark + |netrw-qb| how to list bookmarks + + +CHANGING TO A PREDECESSOR DIRECTORY *netrw-u* *netrw-updir* {{{2 + +Every time you change to a new directory (new for the current session), netrw +will save the directory in a recently-visited directory history list (unless +|g:netrw_dirhistmax| is zero; by default, it holds ten entries). With the "u" +map, one can change to an earlier directory (predecessor). To do the +opposite, see |netrw-U|. + +The "u" map also accepts counts to go back in the history several slots. For +your convenience, qb (see |netrw-qb|) lists the history number which may be +used in that count. + + *.netrwhist* +See |g:netrw_dirhistmax| for how to control the quantity of history stack +slots. The file ".netrwhist" holds history when netrw (and vim) is not +active. By default, its stored on the first directory on the user's +'runtimepath'. + +Related Topics: + |netrw-U| changing to a successor directory + |g:netrw_home| controls where .netrwhist is kept + + +CHANGING TO A SUCCESSOR DIRECTORY *netrw-U* *netrw-downdir* {{{2 + +With the "U" map, one can change to a later directory (successor). +This map is the opposite of the "u" map. (see |netrw-u|) Use the +qb map to list both the bookmarks and history. (see |netrw-qb|) + +The "U" map also accepts counts to go forward in the history several slots. + +See |g:netrw_dirhistmax| for how to control the quantity of history stack +slots. + + +CHANGING TREE TOP *netrw-ntree* *:Ntree* *netrw-gn* {{{2 + +One may specify a new tree top for tree listings using > + + :Ntree [dirname] + +Without a "dirname", the current line is used (and any leading depth +information is elided). +With a "dirname", the specified directory name is used. + +The "gn" map will take the word below the cursor and use that for +changing the top of the tree listing. + + *netrw-curdir* +DELETING BOOKMARKS *netrw-mB* {{{2 + +To delete a bookmark, use > + + {cnt}mB + +If there are marked files, then mB will remove them from the +bookmark list. + +Alternatively, one may use :NetrwMB! (see |netrw-:NetrwMB|). > + + :NetrwMB! [files/directories] + +Related Topics: + |netrw-gb| how to return (go) to a bookmark + |netrw-mb| how to make a bookmark + |netrw-qb| how to list bookmarks + + +DELETING FILES OR DIRECTORIES *netrw-delete* *netrw-D* *netrw-del* {{{2 + +If files have not been marked with |netrw-mf|: (local marked file list) + + Deleting/removing files and directories involves moving the cursor to the + file/directory to be deleted and pressing "D". Directories must be empty + first before they can be successfully removed. If the directory is a + softlink to a directory, then netrw will make two requests to remove the + directory before succeeding. Netrw will ask for confirmation before doing + the removal(s). You may select a range of lines with the "V" command + (visual selection), and then pressing "D". + +If files have been marked with |netrw-mf|: (local marked file list) + + Marked files (and empty directories) will be deleted; again, you'll be + asked to confirm the deletion before it actually takes place. + +A further approach is to delete files which match a pattern. + + * use :MF pattern (see |netrw-:MF|); then press "D". + + * use mr (see |netrw-mr|) which will prompt you for pattern. + This will cause the matching files to be marked. Then, + press "D". + +Please note that only empty directories may be deleted with the "D" mapping. +Regular files are deleted with |delete()|, too. + +The |g:netrw_rm_cmd|, |g:netrw_rmf_cmd|, and |g:netrw_rmdir_cmd| variables are +used to control the attempts to remove remote files and directories. The +g:netrw_rm_cmd is used with files, and its default value is: + + g:netrw_rm_cmd: ssh HOSTNAME rm + +The g:netrw_rmdir_cmd variable is used to support the removal of directories. +Its default value is: + + |g:netrw_rmdir_cmd|: ssh HOSTNAME rmdir + +If removing a directory fails with g:netrw_rmdir_cmd, netrw then will attempt +to remove it again using the g:netrw_rmf_cmd variable. Its default value is: + + |g:netrw_rmf_cmd|: ssh HOSTNAME rm -f + +Related topics: |netrw-d| +Associated setting variable: |g:netrw_rm_cmd| |g:netrw_ssh_cmd| + + +*netrw-explore* *netrw-hexplore* *netrw-nexplore* *netrw-pexplore* +*netrw-rexplore* *netrw-sexplore* *netrw-texplore* *netrw-vexplore* *netrw-lexplore* +DIRECTORY EXPLORATION COMMANDS {{{2 + + :[N]Explore[!] [dir]... Explore directory of current file *:Explore* + :[N]Hexplore[!] [dir]... Horizontal Split & Explore *:Hexplore* + :[N]Lexplore[!] [dir]... Left Explorer Toggle *:Lexplore* + :[N]Sexplore[!] [dir]... Split&Explore current file's directory *:Sexplore* + :[N]Vexplore[!] [dir]... Vertical Split & Explore *:Vexplore* + :Texplore [dir]... Tab & Explore *:Texplore* + :Rexplore ... Return to/from Explorer *:Rexplore* + + Used with :Explore **/pattern : (also see |netrw-starstar|) + :Nexplore............. go to next matching file *:Nexplore* + :Pexplore............. go to previous matching file *:Pexplore* + + *netrw-:Explore* +:Explore will open the local-directory browser on the current file's + directory (or on directory [dir] if specified). The window will be + split only if the file has been modified and 'hidden' is not set, + otherwise the browsing window will take over that window. Normally + the splitting is taken horizontally. + Also see: |netrw-:Rexplore| +:Explore! is like :Explore, but will use vertical splitting. + + *netrw-:Hexplore* +:Hexplore [dir] does an :Explore with |:belowright| horizontal splitting. +:Hexplore! [dir] does an :Explore with |:aboveleft| horizontal splitting. + + *netrw-:Lexplore* +:[N]Lexplore [dir] toggles a full height Explorer window on the left hand side + of the current tab. It will open a netrw window on the current + directory if [dir] is omitted; a :Lexplore [dir] will show the + specified directory in the left-hand side browser display no matter + from which window the command is issued. + + By default, :Lexplore will change an uninitialized |g:netrw_chgwin| + to 2; edits will thus preferentially be made in window#2. + + The [N] specifies a |g:netrw_winsize| just for the new :Lexplore + window. That means that + if [N] < 0 : use |N| columns for the Lexplore window + if [N] = 0 : a normal split is made + if [N] > 0 : use N% of the current window will be used for the + new window + + Those who like this method often also like tree style displays; + see |g:netrw_liststyle|. + +:[N]Lexplore! [dir] is similar to :Lexplore, except that the full-height + Explorer window will open on the right hand side and an + uninitialized |g:netrw_chgwin| will be set to 1 (eg. edits will + preferentially occur in the leftmost window). + + Also see: |netrw-C| |g:netrw_browse_split| |g:netrw_wiw| + |netrw-p| |netrw-P| |g:netrw_chgwin| + |netrw-c-tab| |g:netrw_winsize| + + *netrw-:Sexplore* +:[N]Sexplore will always split the window before invoking the local-directory + browser. As with Explore, the splitting is normally done + horizontally. +:[N]Sexplore! [dir] is like :Sexplore, but the splitting will be done vertically. + + *netrw-:Texplore* +:Texplore [dir] does a |:tabnew| before generating the browser window + + *netrw-:Vexplore* +:[N]Vexplore [dir] does an :Explore with |:leftabove| vertical splitting. +:[N]Vexplore! [dir] does an :Explore with |:rightbelow| vertical splitting. + +The optional parameters are: + + [N]: This parameter will override |g:netrw_winsize| to specify the quantity of + rows and/or columns the new explorer window should have. + Otherwise, the |g:netrw_winsize| variable, if it has been specified by the + user, is used to control the quantity of rows and/or columns new + explorer windows should have. + + [dir]: By default, these explorer commands use the current file's directory. + However, one may explicitly provide a directory (path) to use instead; + ie. > + + :Explore /some/path +< + *netrw-:Rexplore* +:Rexplore This command is a little different from the other Explore commands + as it doesn't necessarily open an Explorer window. + + Return to Explorer~ + When one edits a file using netrw which can occur, for example, + when pressing <cr> while the cursor is atop a filename in a netrw + browser window, a :Rexplore issued while editing that file will + return the display to that of the last netrw browser display in + that window. + + Return from Explorer~ + Conversely, when one is editing a directory, issuing a :Rexplore + will return to editing the file that was last edited in that + window. + + The <2-leftmouse> map (which is only available under gvim and + cooperative terms) does the same as :Rexplore. + +Also see: |g:netrw_alto| |g:netrw_altv| |g:netrw_winsize| + + +*netrw-star* *netrw-starpat* *netrw-starstar* *netrw-starstarpat* *netrw-grep* +EXPLORING WITH STARS AND PATTERNS {{{2 + +When Explore, Sexplore, Hexplore, or Vexplore are used with one of the +following four patterns Explore generates a list of files which satisfy the +request for the local file system. These exploration patterns will not work +with remote file browsing. + + */filepat files in current directory which satisfy filepat + **/filepat files in current directory or below which satisfy the + file pattern + *//pattern files in the current directory which contain the + pattern (vimgrep is used) + **//pattern files in the current directory or below which contain + the pattern (vimgrep is used) +< +The cursor will be placed on the first file in the list. One may then +continue to go to subsequent files on that list via |:Nexplore| or to +preceding files on that list with |:Pexplore|. Explore will update the +directory and place the cursor appropriately. + +A plain > + :Explore +will clear the explore list. + +If your console or gui produces recognizable shift-up or shift-down sequences, +then you'll likely find using shift-downarrow and shift-uparrow convenient. +They're mapped by netrw as follows: + + <s-down> == Nexplore, and + <s-up> == Pexplore. + +As an example, consider +> + :Explore */*.c + :Nexplore + :Nexplore + :Pexplore +< +The status line will show, on the right hand side of the status line, a +message like "Match 3 of 20". + +Associated setting variables: + |g:netrw_keepdir| |g:netrw_browse_split| + |g:netrw_fastbrowse| |g:netrw_ftp_browse_reject| + |g:netrw_ftp_list_cmd| |g:netrw_ftp_sizelist_cmd| + |g:netrw_ftp_timelist_cmd| |g:netrw_list_cmd| + |g:netrw_liststyle| + + +DISPLAYING INFORMATION ABOUT FILE *netrw-qf* {{{2 + +With the cursor atop a filename, pressing "qf" will reveal the file's size +and last modification timestamp. Currently this capability is only available +for local files. + + +EDIT FILE OR DIRECTORY HIDING LIST *netrw-ctrl-h* *netrw-edithide* {{{2 + +The "<ctrl-h>" map brings up a requestor allowing the user to change the +file/directory hiding list contained in |g:netrw_list_hide|. The hiding list +consists of one or more patterns delimited by commas. Files and/or +directories satisfying these patterns will either be hidden (ie. not shown) or +be the only ones displayed (see |netrw-a|). + +The "gh" mapping (see |netrw-gh|) quickly alternates between the usual +hiding list and the hiding of files or directories that begin with ".". + +As an example, > + let g:netrw_list_hide= '\(^\|\s\s\)\zs\.\S\+' +Effectively, this makes the effect of a |netrw-gh| command the initial setting. +What it means: + + \(^\|\s\s\) : if the line begins with the following, -or- + two consecutive spaces are encountered + \zs : start the hiding match now + \. : if it now begins with a dot + \S\+ : and is followed by one or more non-whitespace + characters + +Associated setting variables: |g:netrw_hide| |g:netrw_list_hide| +Associated topics: |netrw-a| |netrw-gh| |netrw-mh| + + *netrw-sort-sequence* +EDITING THE SORTING SEQUENCE *netrw-S* *netrw-sortsequence* {{{2 + +When "Sorted by" is name, one may specify priority via the sorting sequence +(g:netrw_sort_sequence). The sorting sequence typically prioritizes the +name-listing by suffix, although any pattern will do. Patterns are delimited +by commas. The default sorting sequence is (all one line): + +For Unix: > + '[\/]$,\<core\%(\.\d\+\)\=,\.[a-np-z]$,\.h$,\.c$,\.cpp$,*,\.o$,\.obj$, + \.info$,\.swp$,\.bak$,\~$' +< +Otherwise: > + '[\/]$,\.[a-np-z]$,\.h$,\.c$,\.cpp$,*,\.o$,\.obj$,\.info$, + \.swp$,\.bak$,\~$' +< +The lone * is where all filenames not covered by one of the other patterns +will end up. One may change the sorting sequence by modifying the +g:netrw_sort_sequence variable (either manually or in your <.vimrc>) or by +using the "S" map. + +Related topics: |netrw-s| |netrw-S| +Associated setting variables: |g:netrw_sort_sequence| |g:netrw_sort_options| + + +EXECUTING FILE UNDER CURSOR VIA SYSTEM() *netrw-X* {{{2 + +Pressing X while the cursor is atop an executable file will yield a prompt +using the filename asking for any arguments. Upon pressing a [return], netrw +will then call |system()| with that command and arguments. The result will be +displayed by |:echomsg|, and so |:messages| will repeat display of the result. +Ansi escape sequences will be stripped out. + +See |cmdline-window| for directions for more on how to edit the arguments. + + +FORCING TREATMENT AS A FILE OR DIRECTORY *netrw-gd* *netrw-gf* {{{2 + +Remote symbolic links (ie. those listed via ssh or ftp) are problematic +in that it is difficult to tell whether they link to a file or to a +directory. + +To force treatment as a file: use > + gf +< +To force treatment as a directory: use > + gd +< + +GOING UP *netrw--* {{{2 + +To go up a directory, press "-" or press the <cr> when atop the ../ directory +entry in the listing. + +Netrw will use the command in |g:netrw_list_cmd| to perform the directory +listing operation after changing HOSTNAME to the host specified by the +user-prpvided url. By default netrw provides the command as: > + + ssh HOSTNAME ls -FLa +< +where the HOSTNAME becomes the [user@]hostname as requested by the attempt to +read. Naturally, the user may override this command with whatever is +preferred. The NetList function which implements remote browsing +expects that directories will be flagged by a trailing slash. + + +HIDING FILES OR DIRECTORIES *netrw-a* *netrw-hiding* {{{2 + +Netrw's browsing facility allows one to use the hiding list in one of three +ways: ignore it, hide files which match, and show only those files which +match. + +If no files have been marked via |netrw-mf|: + +The "a" map allows the user to cycle through the three hiding modes. + +The |g:netrw_list_hide| variable holds a comma delimited list of patterns +based on regular expressions (ex. ^.*\.obj$,^\.) which specify the hiding list. +(also see |netrw-ctrl-h|) To set the hiding list, use the <c-h> map. As an +example, to hide files which begin with a ".", one may use the <c-h> map to +set the hiding list to '^\..*' (or one may put let g:netrw_list_hide= '^\..*' +in one's <.vimrc>). One may then use the "a" key to show all files, hide +matching files, or to show only the matching files. + + Example: \.[ch]$ + This hiding list command will hide/show all *.c and *.h files. + + Example: \.c$,\.h$ + This hiding list command will also hide/show all *.c and *.h + files. + +Don't forget to use the "a" map to select the mode (normal/hiding/show) you +want! + +If files have been marked using |netrw-mf|, then this command will: + + if showing all files or non-hidden files: + modify the g:netrw_list_hide list by appending the marked files to it + and showing only non-hidden files. + + else if showing hidden files only: + modify the g:netrw_list_hide list by removing the marked files from it + and showing only non-hidden files. + endif + + *netrw-gh* *netrw-hide* +As a quick shortcut, one may press > + gh +to toggle between hiding files which begin with a period (dot) and not hiding +them. + +Associated setting variables: |g:netrw_list_hide| |g:netrw_hide| +Associated topics: |netrw-a| |netrw-ctrl-h| |netrw-mh| + + *netrw-gitignore* +Netrw provides a helper function 'netrw_gitignore#Hide()' that, when used with +|g:netrw_list_hide| automatically hides all git-ignored files. + +'netrw_gitignore#Hide' searches for patterns in the following files: > + + './.gitignore' + './.git/info/exclude' + global gitignore file: `git config --global core.excludesfile` + system gitignore file: `git config --system core.excludesfile` +< +Files that do not exist, are ignored. +Git-ignore patterns are taken from existing files, and converted to patterns for +hiding files. For example, if you had '*.log' in your '.gitignore' file, it +would be converted to '.*\.log'. + +To use this function, simply assign its output to |g:netrw_list_hide| option. > + + Example: let g:netrw_list_hide= netrw_gitignore#Hide() + Git-ignored files are hidden in Netrw. + + Example: let g:netrw_list_hide= netrw_gitignore#Hide('my_gitignore_file') + Function can take additional files with git-ignore patterns. + + Example: let g:netrw_list_hide= netrw_gitignore#Hide() .. '.*\.swp$' + Combining 'netrw_gitignore#Hide' with custom patterns. +< + +IMPROVING BROWSING *netrw-listhack* *netrw-ssh-hack* {{{2 + +Especially with the remote directory browser, constantly entering the password +is tedious. + +For Linux/Unix systems, the book "Linux Server Hacks - 100 industrial strength +tips & tools" by Rob Flickenger (O'Reilly, ISBN 0-596-00461-3) gives a tip +for setting up no-password ssh and scp and discusses associated security +issues. It used to be available at http://hacks.oreilly.com/pub/h/66 , +but apparently that address is now being redirected to some "hackzine". +I'll attempt a summary based on that article and on a communication from +Ben Schmidt: + + 1. Generate a public/private key pair on the local machine + (ssh client): > + ssh-keygen -t rsa + (saving the file in ~/.ssh/id_rsa as prompted) +< + 2. Just hit the <CR> when asked for passphrase (twice) for no + passphrase. If you do use a passphrase, you will also need to use + ssh-agent so you only have to type the passphrase once per session. + If you don't use a passphrase, simply logging onto your local + computer or getting access to the keyfile in any way will suffice + to access any ssh servers which have that key authorized for login. + + 3. This creates two files: > + ~/.ssh/id_rsa + ~/.ssh/id_rsa.pub +< + 4. On the target machine (ssh server): > + cd + mkdir -p .ssh + chmod 0700 .ssh +< + 5. On your local machine (ssh client): (one line) > + ssh {serverhostname} + cat '>>' '~/.ssh/authorized_keys2' < ~/.ssh/id_rsa.pub +< + or, for OpenSSH, (one line) > + ssh {serverhostname} + cat '>>' '~/.ssh/authorized_keys' < ~/.ssh/id_rsa.pub +< +You can test it out with > + ssh {serverhostname} +and you should be log onto the server machine without further need to type +anything. + +If you decided to use a passphrase, do: > + ssh-agent $SHELL + ssh-add + ssh {serverhostname} +You will be prompted for your key passphrase when you use ssh-add, but not +subsequently when you use ssh. For use with vim, you can use > + ssh-agent vim +and, when next within vim, use > + :!ssh-add +Alternatively, you can apply ssh-agent to the terminal you're planning on +running vim in: > + ssh-agent xterm & +and do ssh-add whenever you need. + +For Windows, folks on the vim mailing list have mentioned that Pageant helps +with avoiding the constant need to enter the password. + +Kingston Fung wrote about another way to avoid constantly needing to enter +passwords: + + In order to avoid the need to type in the password for scp each time, you + provide a hack in the docs to set up a non password ssh account. I found a + better way to do that: I can use a regular ssh account which uses a + password to access the material without the need to key-in the password + each time. It's good for security and convenience. I tried ssh public key + authorization + ssh-agent, implementing this, and it works! + + + Ssh hints: + + Thomer Gil has provided a hint on how to speed up netrw+ssh: + http://thomer.com/howtos/netrw_ssh.html + + +LISTING BOOKMARKS AND HISTORY *netrw-qb* *netrw-listbookmark* {{{2 + +Pressing "qb" (query bookmarks) will list both the bookmarked directories and +directory traversal history. + +Related Topics: + |netrw-gb| how to return (go) to a bookmark + |netrw-mb| how to make a bookmark + |netrw-mB| how to delete bookmarks + |netrw-u| change to a predecessor directory via the history stack + |netrw-U| change to a successor directory via the history stack + +MAKING A NEW DIRECTORY *netrw-d* {{{2 + +With the "d" map one may make a new directory either remotely (which depends +on the global variable g:netrw_mkdir_cmd) or locally (which depends on the +global variable g:netrw_localmkdir). Netrw will issue a request for the new +directory's name. A bare <CR> at that point will abort the making of the +directory. Attempts to make a local directory that already exists (as either +a file or a directory) will be detected, reported on, and ignored. + +Related topics: |netrw-D| +Associated setting variables: |g:netrw_localmkdir| |g:netrw_mkdir_cmd| + |g:netrw_remote_mkdir| |netrw-%| + + +MAKING THE BROWSING DIRECTORY THE CURRENT DIRECTORY *netrw-cd* {{{2 + +By default, |g:netrw_keepdir| is 1. This setting means that the current +directory will not track the browsing directory. (done for backwards +compatibility with v6's file explorer). + +Setting g:netrw_keepdir to 0 tells netrw to make vim's current directory +track netrw's browsing directory. + +However, given the default setting for g:netrw_keepdir of 1 where netrw +maintains its own separate notion of the current directory, in order to make +the two directories the same, use the "cd" map (type cd). That map will +set Vim's notion of the current directory to netrw's current browsing +directory. + +|netrw-cd| : This map's name was changed from "c" to cd (see |netrw-cd|). + This change was done to allow for |netrw-cb| and |netrw-cB| maps. + +Associated setting variable: |g:netrw_keepdir| + +MARKING FILES *netrw-:MF* *netrw-mf* {{{2 + (also see |netrw-mr|) + +Netrw provides several ways to mark files: + + * One may mark files with the cursor atop a filename and + then pressing "mf". + + * With gvim, in addition one may mark files with + <s-leftmouse>. (see |netrw-mouse|) + + * One may use the :MF command, which takes a list of + files (for local directories, the list may include + wildcards -- see |glob()|) > + + :MF *.c +< + (Note that :MF uses |<f-args>| to break the line + at spaces) + + * Mark files using the |argument-list| (|netrw-mA|) + + * Mark files based upon a |location-list| (|netrw-qL|) + + * Mark files based upon the quickfix list (|netrw-qF|) + (|quickfix-error-lists|) + +The following netrw maps make use of marked files: + + |netrw-a| Hide marked files/directories + |netrw-D| Delete marked files/directories + |netrw-ma| Move marked files' names to |arglist| + |netrw-mA| Move |arglist| filenames to marked file list + |netrw-mb| Append marked files to bookmarks + |netrw-mB| Delete marked files from bookmarks + |netrw-mc| Copy marked files to target + |netrw-md| Apply vimdiff to marked files + |netrw-me| Edit marked files + |netrw-mF| Unmark marked files + |netrw-mg| Apply vimgrep to marked files + |netrw-mm| Move marked files to target + |netrw-ms| Netrw will source marked files + |netrw-mt| Set target for |netrw-mm| and |netrw-mc| + |netrw-mT| Generate tags using marked files + |netrw-mv| Apply vim command to marked files + |netrw-mx| Apply shell command to marked files + |netrw-mX| Apply shell command to marked files, en bloc + |netrw-mz| Compress/Decompress marked files + |netrw-O| Obtain marked files + |netrw-R| Rename marked files + +One may unmark files one at a time the same way one marks them; ie. place +the cursor atop a marked file and press "mf". This process also works +with <s-leftmouse> using gvim. One may unmark all files by pressing +"mu" (see |netrw-mu|). + +Marked files are highlighted using the "netrwMarkFile" highlighting group, +which by default is linked to "Identifier" (see Identifier under +|group-name|). You may change the highlighting group by putting something +like > + + highlight clear netrwMarkFile + hi link netrwMarkFile ..whatever.. +< +into $HOME/.vim/after/syntax/netrw.vim . + +If the mouse is enabled and works with your vim, you may use <s-leftmouse> to +mark one or more files. You may mark multiple files by dragging the shifted +leftmouse. (see |netrw-mouse|) + + *markfilelist* *global_markfilelist* *local_markfilelist* +All marked files are entered onto the global marked file list; there is only +one such list. In addition, every netrw buffer also has its own buffer-local +marked file list; since netrw buffers are associated with specific +directories, this means that each directory has its own local marked file +list. The various commands which operate on marked files use one or the other +of the marked file lists. + +Known Problem: if one is using tree mode (|g:netrw_liststyle|) and several +directories have files with the same name, then marking such a file will +result in all such files being highlighted as if they were all marked. The +|markfilelist|, however, will only have the selected file in it. This problem +is unlikely to be fixed. + + +UNMARKING FILES *netrw-mF* {{{2 + (also see |netrw-mf|, |netrw-mu|) + +The "mF" command will unmark all files in the current buffer. One may also use +mf (|netrw-mf|) on a specific, already marked, file to unmark just that file. + +MARKING FILES BY LOCATION LIST *netrw-qL* {{{2 + (also see |netrw-mf|) + +One may convert |location-list|s into a marked file list using "qL". +You may then proceed with commands such as me (|netrw-me|) to edit them. + + +MARKING FILES BY QUICKFIX LIST *netrw-qF* {{{2 + (also see |netrw-mf|) + +One may convert |quickfix-error-lists| into a marked file list using "qF". +You may then proceed with commands such as me (|netrw-me|) to edit them. +Quickfix error lists are generated, for example, by calls to |:vimgrep|. + + +MARKING FILES BY REGULAR EXPRESSION *netrw-mr* {{{2 + (also see |netrw-mf|) + +One may also mark files by pressing "mr"; netrw will then issue a prompt, +"Enter regexp: ". You may then enter a shell-style regular expression such +as *.c$ (see |glob()|). For remote systems, glob() doesn't work -- so netrw +converts "*" into ".*" (see |regexp|) and marks files based on that. In the +future I may make it possible to use |regexp|s instead of glob()-style +expressions (yet-another-option). + +See |cmdline-window| for directions on more on how to edit the regular +expression. + + +MARKED FILES, ARBITRARY VIM COMMAND *netrw-mv* {{{2 + (See |netrw-mf| and |netrw-mr| for how to mark files) + (uses the local marked-file list) + +The "mv" map causes netrw to execute an arbitrary vim command on each file on +the local marked file list, individually: + + * 1split + * sil! keepalt e file + * run vim command + * sil! keepalt wq! + +A prompt, "Enter vim command: ", will be issued to elicit the vim command you +wish used. See |cmdline-window| for directions for more on how to edit the +command. + + +MARKED FILES, ARBITRARY SHELL COMMAND *netrw-mx* {{{2 + (See |netrw-mf| and |netrw-mr| for how to mark files) + (uses the local marked-file list) + +Upon activation of the "mx" map, netrw will query the user for some (external) +command to be applied to all marked files. All "%"s in the command will be +substituted with the name of each marked file in turn. If no "%"s are in the +command, then the command will be followed by a space and a marked filename. + +Example: + (mark files) + mx + Enter command: cat + + The result is a series of shell commands: + cat 'file1' + cat 'file2' + ... + + +MARKED FILES, ARBITRARY SHELL COMMAND, EN BLOC *netrw-mX* {{{2 + (See |netrw-mf| and |netrw-mr| for how to mark files) + (uses the global marked-file list) + +Upon activation of the 'mX' map, netrw will query the user for some (external) +command to be applied to all marked files on the global marked file list. The +"en bloc" means that one command will be executed on all the files at once: > + + command files + +This approach is useful, for example, to select files and make a tarball: > + + (mark files) + mX + Enter command: tar cf mynewtarball.tar +< +The command that will be run with this example: + + tar cf mynewtarball.tar 'file1' 'file2' ... + + +MARKED FILES: ARGUMENT LIST *netrw-ma* *netrw-mA* + (See |netrw-mf| and |netrw-mr| for how to mark files) + (uses the global marked-file list) + +Using ma, one moves filenames from the marked file list to the argument list. +Using mA, one moves filenames from the argument list to the marked file list. + +See Also: |netrw-cb| |netrw-cB| |netrw-qF| |argument-list| |:args| + + +MARKED FILES: BUFFER LIST *netrw-cb* *netrw-cB* + (See |netrw-mf| and |netrw-mr| for how to mark files) + (uses the global marked-file list) + +Using cb, one moves filenames from the marked file list to the buffer list. +Using cB, one copies filenames from the buffer list to the marked file list. + +See Also: |netrw-ma| |netrw-mA| |netrw-qF| |buffer-list| |:buffers| + + +MARKED FILES: COMPRESSION AND DECOMPRESSION *netrw-mz* {{{2 + (See |netrw-mf| and |netrw-mr| for how to mark files) + (uses the local marked file list) + +If any marked files are compressed, then "mz" will decompress them. +If any marked files are decompressed, then "mz" will compress them +using the command specified by |g:netrw_compress|; by default, +that's "gzip". + +For decompression, netrw uses a |Dictionary| of suffices and their +associated decompressing utilities; see |g:netrw_decompress|. + +Remember that one can mark multiple files by regular expression +(see |netrw-mr|); this is particularly useful to facilitate compressing and +decompressing a large number of files. + +Associated setting variables: |g:netrw_compress| |g:netrw_decompress| + +MARKED FILES: COPYING *netrw-mc* {{{2 + (See |netrw-mf| and |netrw-mr| for how to mark files) + (Uses the global marked file list) + +Select a target directory with mt (|netrw-mt|). Then change directory, +select file(s) (see |netrw-mf|), and press "mc". The copy is done +from the current window (where one does the mf) to the target. + +If one does not have a target directory set with |netrw-mt|, then netrw +will query you for a directory to copy to. + +One may also copy directories and their contents (local only) to a target +directory. + +Associated setting variables: + |g:netrw_localcopycmd| |g:netrw_localcopycmdopt| + |g:netrw_localcopydircmd| |g:netrw_localcopydircmdopt| + |g:netrw_ssh_cmd| + +MARKED FILES: DIFF *netrw-md* {{{2 + (See |netrw-mf| and |netrw-mr| for how to mark files) + (uses the global marked file list) + +Use |vimdiff| to visualize difference between selected files (two or +three may be selected for this). Uses the global marked file list. + +MARKED FILES: EDITING *netrw-me* {{{2 + (See |netrw-mf| and |netrw-mr| for how to mark files) + (uses the global marked file list) + +The "me" command will place the marked files on the |arglist| and commence +editing them. One may return the to explorer window with |:Rexplore|. +(use |:n| and |:p| to edit next and previous files in the arglist) + +MARKED FILES: GREP *netrw-mg* {{{2 + (See |netrw-mf| and |netrw-mr| for how to mark files) + (uses the global marked file list) + +The "mg" command will apply |:vimgrep| to the marked files. +The command will ask for the requested pattern; one may then enter: > + + /pattern/[g][j] + ! /pattern/[g][j] + pattern +< +With /pattern/, editing will start with the first item on the |quickfix| list +that vimgrep sets up (see |:copen|, |:cnext|, |:cprevious|, |:cclose|). The |:vimgrep| +command is in use, so without 'g' each line is added to quickfix list only +once; with 'g' every match is included. + +With /pattern/j, "mg" will winnow the current marked file list to just those +marked files also possessing the specified pattern. Thus, one may use > + + mr ...file-pattern... + mg /pattern/j +< +to have a marked file list satisfying the file-pattern but also restricted to +files containing some desired pattern. + + +MARKED FILES: HIDING AND UNHIDING BY SUFFIX *netrw-mh* {{{2 + (See |netrw-mf| and |netrw-mr| for how to mark files) + (uses the local marked file list) + +The "mh" command extracts the suffices of the marked files and toggles their +presence on the hiding list. Please note that marking the same suffix +this way multiple times will result in the suffix's presence being toggled +for each file (so an even quantity of marked files having the same suffix +is the same as not having bothered to select them at all). + +Related topics: |netrw-a| |g:netrw_list_hide| + +MARKED FILES: MOVING *netrw-mm* {{{2 + (See |netrw-mf| and |netrw-mr| for how to mark files) + (uses the global marked file list) + + WARNING: moving files is more dangerous than copying them. + A file being moved is first copied and then deleted; if the + copy operation fails and the delete succeeds, you will lose + the file. Either try things out with unimportant files + first or do the copy and then delete yourself using mc and D. + Use at your own risk! + +Select a target directory with mt (|netrw-mt|). Then change directory, +select file(s) (see |netrw-mf|), and press "mm". The move is done +from the current window (where one does the mf) to the target. + +Associated setting variable: |g:netrw_localmovecmd| |g:netrw_ssh_cmd| + +MARKED FILES: SOURCING *netrw-ms* {{{2 + (See |netrw-mf| and |netrw-mr| for how to mark files) + (uses the local marked file list) + +With "ms", netrw will source the marked files (using vim's |:source| command) + + +MARKED FILES: SETTING THE TARGET DIRECTORY *netrw-mt* {{{2 + (See |netrw-mf| and |netrw-mr| for how to mark files) + +Set the marked file copy/move-to target (see |netrw-mc| and |netrw-mm|): + + * If the cursor is atop a file name, then the netrw window's currently + displayed directory is used for the copy/move-to target. + + * Also, if the cursor is in the banner, then the netrw window's currently + displayed directory is used for the copy/move-to target. + Unless the target already is the current directory. In which case, + typing "mf" clears the target. + + * However, if the cursor is atop a directory name, then that directory is + used for the copy/move-to target + + * One may use the :MT [directory] command to set the target *netrw-:MT* + This command uses |<q-args>|, so spaces in the directory name are + permitted without escaping. + + * With mouse-enabled vim or with gvim, one may select a target by using + <c-leftmouse> + +There is only one copy/move-to target at a time in a vim session; ie. the +target is a script variable (see |s:var|) and is shared between all netrw +windows (in an instance of vim). + +When using menus and gvim, netrw provides a "Targets" entry which allows one +to pick a target from the list of bookmarks and history. + +Related topics: + Marking Files......................................|netrw-mf| + Marking Files by Regular Expression................|netrw-mr| + Marked Files: Target Directory Using Bookmarks.....|netrw-Tb| + Marked Files: Target Directory Using History.......|netrw-Th| + + +MARKED FILES: TAGGING *netrw-mT* {{{2 + (See |netrw-mf| and |netrw-mr| for how to mark files) + (uses the global marked file list) + +The "mT" mapping will apply the command in |g:netrw_ctags| (by default, it is +"ctags") to marked files. For remote browsing, in order to create a tags file +netrw will use ssh (see |g:netrw_ssh_cmd|), and so ssh must be available for +this to work on remote systems. For your local system, see |ctags| on how to +get a version. I myself use hdrtags, currently available at +http://www.drchip.org/astronaut/src/index.html , and have > + + let g:netrw_ctags= "hdrtag" +< +in my <.vimrc>. + +When a remote set of files are tagged, the resulting tags file is "obtained"; +ie. a copy is transferred to the local system's directory. The now local tags +file is then modified so that one may use it through the network. The +modification made concerns the names of the files in the tags; each filename is +preceded by the netrw-compatible URL used to obtain it. When one subsequently +uses one of the go to tag actions (|tags|), the URL will be used by netrw to +edit the desired file and go to the tag. + +Associated setting variables: |g:netrw_ctags| |g:netrw_ssh_cmd| + +MARKED FILES: TARGET DIRECTORY USING BOOKMARKS *netrw-Tb* {{{2 + +Sets the marked file copy/move-to target. + +The |netrw-qb| map will give you a list of bookmarks (and history). +One may choose one of the bookmarks to become your marked file +target by using [count]Tb (default count: 1). + +Related topics: + Copying files to target............................|netrw-mc| + Listing Bookmarks and History......................|netrw-qb| + Marked Files: Setting The Target Directory.........|netrw-mt| + Marked Files: Target Directory Using History.......|netrw-Th| + Marking Files......................................|netrw-mf| + Marking Files by Regular Expression................|netrw-mr| + Moving files to target.............................|netrw-mm| + + +MARKED FILES: TARGET DIRECTORY USING HISTORY *netrw-Th* {{{2 + +Sets the marked file copy/move-to target. + +The |netrw-qb| map will give you a list of history (and bookmarks). +One may choose one of the history entries to become your marked file +target by using [count]Th (default count: 0; ie. the current directory). + +Related topics: + Copying files to target............................|netrw-mc| + Listing Bookmarks and History......................|netrw-qb| + Marked Files: Setting The Target Directory.........|netrw-mt| + Marked Files: Target Directory Using Bookmarks.....|netrw-Tb| + Marking Files......................................|netrw-mf| + Marking Files by Regular Expression................|netrw-mr| + Moving files to target.............................|netrw-mm| + + +MARKED FILES: UNMARKING *netrw-mu* {{{2 + (See |netrw-mf|, |netrw-mF|) + +The "mu" mapping will unmark all currently marked files. This command differs +from "mF" as the latter only unmarks files in the current directory whereas +"mu" will unmark global and all buffer-local marked files. +(see |netrw-mF|) + + + *netrw-browser-settings* +NETRW BROWSER VARIABLES *netrw-browser-options* *netrw-browser-var* {{{2 + +(if you're interested in the netrw file transfer settings, see |netrw-options| + and |netrw-protocol|) + +The <netrw.vim> browser provides settings in the form of variables which +you may modify; by placing these settings in your <.vimrc>, you may customize +your browsing preferences. (see also: |netrw-settings|) +> + --- ----------- + Var Explanation + --- ----------- +< *g:netrw_altfile* some like |CTRL-^| to return to the last + edited file. Choose that by setting this + parameter to 1. + Others like |CTRL-^| to return to the + netrw browsing buffer. Choose that by setting + this parameter to 0. + default: =0 + + *g:netrw_alto* change from above splitting to below splitting + by setting this variable (see |netrw-o|) + default: =&sb (see 'sb') + + *g:netrw_altv* change from left splitting to right splitting + by setting this variable (see |netrw-v|) + default: =&spr (see 'spr') + + *g:netrw_banner* enable/suppress the banner + =0: suppress the banner + =1: banner is enabled (default) + + *g:netrw_bannerbackslash* if this variable exists and is not zero, the + banner will be displayed with backslashes + rather than forward slashes. + + *g:netrw_browse_split* when browsing, <cr> will open the file by: + =0: re-using the same window (default) + =1: horizontally splitting the window first + =2: vertically splitting the window first + =3: open file in new tab + =4: act like "P" (ie. open previous window) + Note that |g:netrw_preview| may be used + to get vertical splitting instead of + horizontal splitting. + =[servername,tab-number,window-number] + Given a |List| such as this, a remote server + named by the "servername" will be used for + editing. It will also use the specified tab + and window numbers to perform editing + (see |clientserver|, |netrw-ctrl-r|) + This option does not affect the production of + |:Lexplore| windows. + + Related topics: + |g:netrw_alto| |g:netrw_altv| + |netrw-C| |netrw-cr| + |netrw-ctrl-r| + + *g:netrw_chgperm* Unix/Linux: "chmod PERM FILENAME" + Windows: "cacls FILENAME /e /p PERM" + Used to change access permission for a file. + + *g:netrw_clipboard* =1 + By default, netrw will attempt to insure that + the clipboard's values will remain unchanged. + However, some users report that they have + speed problems with this; consequently, this + option, when set to zero, lets such users + prevent netrw from saving and restoring the + clipboard (the latter is done only as needed). + That means that if the clipboard is changed + (inadvertently) by normal netrw operation that + it will not be restored to its prior state. + + *g:netrw_compress* ="gzip" + Will compress marked files with this + command + + *g:Netrw_corehandler* Allows one to specify something additional + to do when handling <core> files via netrw's + browser's "x" command. If present, + g:Netrw_corehandler specifies either one or + more function references (see |Funcref|). + (the capital g:Netrw... is required its + holding a function reference) + + + *g:netrw_ctags* ="ctags" + The default external program used to create + tags + + *g:netrw_cursor* = 2 (default) + This option controls the use of the + 'cursorline' (cul) and 'cursorcolumn' + (cuc) settings by netrw: + + Value Thin-Long-Tree Wide + =0 u-cul u-cuc u-cul u-cuc + =1 u-cul u-cuc cul u-cuc + =2 cul u-cuc cul u-cuc + =3 cul u-cuc cul cuc + =4 cul cuc cul cuc + =5 U-cul U-cuc U-cul U-cuc + =6 U-cul U-cuc cul U-cuc + =7 cul U-cuc cul U-cuc + =8 cul U-cuc cul cuc + + Where + u-cul : user's 'cursorline' initial setting used + u-cuc : user's 'cursorcolumn' initial setting used + U-cul : user's 'cursorline' current setting used + U-cuc : user's 'cursorcolumn' current setting used + cul : 'cursorline' will be locally set + cuc : 'cursorcolumn' will be locally set + + The "initial setting" means the values of + the 'cuc' and 'cul' settings in effect when + netrw last saw |g:netrw_cursor| >= 5 or when + netrw was initially run. + + *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. + + *g:netrw_dirhistmax* =10: controls maximum quantity of past + history. May be zero to suppress + history. + (related: |netrw-qb| |netrw-u| |netrw-U|) + + *g:netrw_dynamic_maxfilenamelen* =32: enables dynamic determination of + |g:netrw_maxfilenamelen|, which affects + local file long listing. + + *g:netrw_fastbrowse* =0: slow speed directory browsing; + never re-uses directory listings; + always obtains directory listings. + =1: medium speed directory browsing; + re-use directory listings only + when remote directory browsing. + (default value) + =2: fast directory browsing; + only obtains directory listings when the + directory hasn't been seen before + (or |netrw-ctrl-l| is used). + + Fast browsing retains old directory listing + buffers so that they don't need to be + re-acquired. This feature is especially + important for remote browsing. However, if + a file is introduced or deleted into or from + such directories, the old directory buffer + becomes out-of-date. One may always refresh + such a directory listing with |netrw-ctrl-l|. + This option gives the user the choice of + trading off accuracy (ie. up-to-date listing) + versus speed. + + *g:netrw_ffkeep* (default: doesn't exist) + If this variable exists and is zero, then + netrw will not do a save and restore for + 'fileformat'. + + *g:netrw_fname_escape* =' ?&;%' + Used on filenames before remote reading/writing + + *g:netrw_ftp_browse_reject* ftp can produce a number of errors and warnings + that can show up as "directories" and "files" + in the listing. This pattern is used to + remove such embedded messages. By default its + value is: + '^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$' + + *g:netrw_ftp_list_cmd* options for passing along to ftp for directory + listing. Defaults: + unix or g:netrw_cygwin set: : "ls -lF" + otherwise "dir" + + + *g:netrw_ftp_sizelist_cmd* options for passing along to ftp for directory + listing, sorted by size of file. + Defaults: + unix or g:netrw_cygwin set: : "ls -slF" + otherwise "dir" + + *g:netrw_ftp_timelist_cmd* options for passing along to ftp for directory + listing, sorted by time of last modification. + Defaults: + unix or g:netrw_cygwin set: : "ls -tlF" + otherwise "dir" + + *g:netrw_glob_escape* ='[]*?`{~$' (unix) + ='[]*?`{$' (windows + These characters in directory names are + escaped before applying glob() + + *g:netrw_hide* Controlled by the "a" map (see |netrw-a|) + =0 : show all + =1 : show not-hidden files + =2 : show hidden files only + default: =1 + + *g:netrw_home* The home directory for where bookmarks and + history are saved (as .netrwbook and + .netrwhist). + Netrw uses |expand()|on the string. + default: the first directory on the + 'runtimepath' + + *g:netrw_keepdir* =1 (default) keep current directory immune from + the browsing directory. + =0 keep the current directory the same as the + browsing directory. + The current browsing directory is contained in + b:netrw_curdir (also see |netrw-cd|) + + *g:netrw_keepj* ="keepj" (default) netrw attempts to keep the + |:jumps| table unaffected. + ="" netrw will not use |:keepjumps| with + exceptions only for the + saving/restoration of position. + + *g:netrw_list_cmd* command for listing remote directories + default: (if ssh is executable) + "ssh HOSTNAME ls -FLa" + + *g:netrw_list_cmd_options* If this variable exists, then its contents are + appended to the g:netrw_list_cmd. For + example, use "2>/dev/null" to get rid of banner + messages on unix systems. + + + *g:netrw_liststyle* Set the default listing style: + = 0: thin listing (one file per line) + = 1: long listing (one file per line with time + stamp information and file size) + = 2: wide listing (multiple files in columns) + = 3: tree style listing + + *g:netrw_list_hide* comma-separated pattern list for hiding files + Patterns are regular expressions (see |regexp|) + There's some special support for git-ignore + files: you may add the output from the helper + function 'netrw_gitignore#Hide() automatically + hiding all gitignored files. + For more details see |netrw-gitignore|. + + Examples: + let g:netrw_list_hide= '.*\.swp$' + let g:netrw_list_hide= netrw_gitignore#Hide() .. '.*\.swp$' + default: "" + + *g:netrw_localcopycmd* ="cp" Linux/Unix/MacOS/Cygwin + =expand("$COMSPEC") Windows + Copies marked files (|netrw-mf|) to target + directory (|netrw-mt|, |netrw-mc|) + + *g:netrw_localcopycmdopt* ='' Linux/Unix/MacOS/Cygwin + =' \c copy' Windows + Options for the |g:netrw_localcopycmd| + + *g:netrw_localcopydircmd* ="cp" Linux/Unix/MacOS/Cygwin + =expand("$COMSPEC") Windows + Copies directories to target directory. + (|netrw-mc|, |netrw-mt|) + + *g:netrw_localcopydircmdopt* =" -R" Linux/Unix/MacOS/Cygwin + =" /c xcopy /e /c /h/ /i /k" Windows + Options for |g:netrw_localcopydircmd| + + *g:netrw_localmkdir* ="mkdir" Linux/Unix/MacOS/Cygwin + =expand("$COMSPEC") Windows + command for making a local directory + + *g:netrw_localmkdiropt* ="" Linux/Unix/MacOS/Cygwin + =" /c mkdir" Windows + Options for |g:netrw_localmkdir| + + *g:netrw_localmovecmd* ="mv" Linux/Unix/MacOS/Cygwin + =expand("$COMSPEC") Windows + Moves marked files (|netrw-mf|) to target + directory (|netrw-mt|, |netrw-mm|) + + *g:netrw_localmovecmdopt* ="" Linux/Unix/MacOS/Cygwin + =" /c move" Windows + Options for |g:netrw_localmovecmd| + + *g:netrw_maxfilenamelen* =32 by default, selected so as to make long + listings fit on 80 column displays. + If your screen is wider, and you have file + or directory names longer than 32 bytes, + you may set this option to keep listings + columnar. + + *g:netrw_mkdir_cmd* command for making a remote directory + via ssh (also see |g:netrw_remote_mkdir|) + default: "ssh USEPORT HOSTNAME mkdir" + + *g:netrw_mousemaps* =1 (default) enables mouse buttons while + browsing to: + leftmouse : open file/directory + shift-leftmouse : mark file + middlemouse : same as P + rightmouse : remove file/directory + =0: disables mouse maps + + *g:netrw_sizestyle* not defined: actual bytes (default) + ="b" : actual bytes (default) + ="h" : human-readable (ex. 5k, 4m, 3g) + uses 1000 base + ="H" : human-readable (ex. 5K, 4M, 3G) + uses 1024 base + The long listing (|netrw-i|) and query-file + maps (|netrw-qf|) will display file size + using the specified style. + + *g:netrw_usetab* if this variable exists and is non-zero, then + the <tab> map supporting shrinking/expanding a + Lexplore or netrw window will be enabled. + (see |netrw-c-tab|) + + *g:netrw_remote_mkdir* command for making a remote directory + via ftp (also see |g:netrw_mkdir_cmd|) + default: "mkdir" + + *g:netrw_retmap* if it exists and is set to one, then: + * if in a netrw-selected file, AND + * no normal-mode <2-leftmouse> mapping exists, + then the <2-leftmouse> will be mapped for easy + return to the netrw browser window. + example: click once to select and open a file, + double-click to return. + + Note that one may instead choose to: + * let g:netrw_retmap= 1, AND + * nmap <silent> YourChoice <Plug>NetrwReturn + and have another mapping instead of + <2-leftmouse> to invoke the return. + + You may also use the |:Rexplore| command to do + the same thing. + + default: =0 + + *g:netrw_rm_cmd* command for removing remote files + default: "ssh USEPORT HOSTNAME rm" + + *g:netrw_rmdir_cmd* command for removing remote directories + default: "ssh USEPORT HOSTNAME rmdir" + + *g:netrw_rmf_cmd* command for removing remote softlinks + default: "ssh USEPORT HOSTNAME rm -f" + + *g:netrw_servername* use this variable to provide a name for + |netrw-ctrl-r| to use for its server. + default: "NETRWSERVER" + + *g:netrw_sort_by* sort by "name", "time", "size", or + "exten". + default: "name" + + *g:netrw_sort_direction* sorting direction: "normal" or "reverse" + default: "normal" + + *g:netrw_sort_options* sorting is done using |:sort|; this + variable's value is appended to the + sort command. Thus one may ignore case, + for example, with the following in your + .vimrc: > + let g:netrw_sort_options="i" +< default: "" + + *g:netrw_sort_sequence* when sorting by name, first sort by the + comma-separated pattern sequence. Note that + any filigree added to indicate filetypes + should be accounted for in your pattern. + default: '[\/]$,*,\.bak$,\.o$,\.h$, + \.info$,\.swp$,\.obj$' + + *g:netrw_special_syntax* If true, then certain files will be shown + using special syntax in the browser: + + netrwBak : *.bak + netrwCompress: *.gz *.bz2 *.Z *.zip + netrwCoreDump: core.\d\+ + netrwData : *.dat + netrwDoc : *.doc,*.txt,*.pdf, + *.pdf,*.docx + netrwHdr : *.h + netrwLex : *.l *.lex + netrwLib : *.a *.so *.lib *.dll + netrwMakefile: [mM]akefile *.mak + netrwObj : *.o *.obj + netrwPix : *.bmp,*.fit,*.fits,*.gif, + *.jpg,*.jpeg,*.pcx,*.ppc + *.pgm,*.png,*.psd,*.rgb + *.tif,*.xbm,*.xcf + netrwTags : tags ANmenu ANtags + netrwTilde : * + netrwTmp : tmp* *tmp + netrwYacc : *.y + + In addition, those groups mentioned in + 'suffixes' are also added to the special + file highlighting group. + These syntax highlighting groups are linked + to netrwGray or Folded by default + (see |hl-Folded|), but one may put lines like > + hi link netrwCompress Visual +< into one's <.vimrc> to use one's own + preferences. Alternatively, one may + put such specifications into > + .vim/after/syntax/netrw.vim. +< The netrwGray highlighting is set up by + netrw when > + * netrwGray has not been previously + defined + * the gui is running +< As an example, I myself use a dark-background + colorscheme with the following in + .vim/after/syntax/netrw.vim: > + + hi netrwCompress term=NONE cterm=NONE gui=NONE ctermfg=10 guifg=green ctermbg=0 guibg=black + hi netrwData term=NONE cterm=NONE gui=NONE ctermfg=9 guifg=blue ctermbg=0 guibg=black + hi netrwHdr term=NONE cterm=NONE,italic gui=NONE guifg=SeaGreen1 + hi netrwLex term=NONE cterm=NONE,italic gui=NONE guifg=SeaGreen1 + hi netrwYacc term=NONE cterm=NONE,italic gui=NONE guifg=SeaGreen1 + hi netrwLib term=NONE cterm=NONE gui=NONE ctermfg=14 guifg=yellow + hi netrwObj term=NONE cterm=NONE gui=NONE ctermfg=12 guifg=red + hi netrwTilde term=NONE cterm=NONE gui=NONE ctermfg=12 guifg=red + hi netrwTmp term=NONE cterm=NONE gui=NONE ctermfg=12 guifg=red + hi netrwTags term=NONE cterm=NONE gui=NONE ctermfg=12 guifg=red + hi netrwDoc term=NONE cterm=NONE gui=NONE ctermfg=220 ctermbg=27 guifg=yellow2 guibg=Blue3 + hi netrwSymLink term=NONE cterm=NONE gui=NONE ctermfg=220 ctermbg=27 guifg=grey60 +< + *g:netrw_ssh_browse_reject* ssh can sometimes produce unwanted lines, + messages, banners, and whatnot that one doesn't + want masquerading as "directories" and "files". + Use this pattern to remove such embedded + messages. By default its value is: + '^total\s\+\d\+$' + + *g:netrw_ssh_cmd* One may specify an executable command + to use instead of ssh for remote actions + such as listing, file removal, etc. + default: ssh + + *g:netrw_tmpfile_escape* =' &;' + escape() is applied to all temporary files + to escape these characters. + + *g:netrw_timefmt* specify format string to vim's strftime(). + The default, "%c", is "the preferred date + and time representation for the current + locale" according to my manpage entry for + strftime(); however, not all are satisfied + with it. Some alternatives: + "%a %d %b %Y %T", + " %a %Y-%m-%d %I-%M-%S %p" + default: "%c" + + *g:netrw_use_noswf* netrw normally avoids writing swapfiles + for browser buffers. However, under some + systems this apparently is causing nasty + ml_get errors to appear; if you're getting + ml_get errors, try putting + let g:netrw_use_noswf= 0 + in your .vimrc. + default: 1 + + *g:netrw_winsize* specify initial size of new windows made with + "o" (see |netrw-o|), "v" (see |netrw-v|), + |:Hexplore| or |:Vexplore|. The g:netrw_winsize + is an integer describing the percentage of the + current netrw buffer's window to be used for + the new window. + If g:netrw_winsize is less than zero, then + the absolute value of g:netrw_winsize will be + used to specify the quantity of lines or + columns for the new window. + If g:netrw_winsize is zero, then a normal + split will be made (ie. 'equalalways' will + take effect, for example). + default: 50 (for 50%) + + *g:netrw_wiw* =1 specifies the minimum window width to use + when shrinking a netrw/Lexplore window + (see |netrw-c-tab|). + + *g:netrw_xstrlen* Controls how netrw computes string lengths, + including multi-byte characters' string + length. (thanks to N Weibull, T Mechelynck) + =0: uses Vim's built-in strlen() + =1: number of codepoints (Latin a + combining + circumflex is two codepoints) (DEFAULT) + =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.) + =3: virtual length (counting 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) + + *g:NetrwTopLvlMenu* This variable specifies the top level + menu name; by default, it's "Netrw.". If + you wish to change this, do so in your + .vimrc. + +NETRW BROWSING AND OPTION INCOMPATIBILITIES *netrw-incompatible* {{{2 + +Netrw has been designed to handle user options by saving them, setting the +options to something that's compatible with netrw's needs, and then restoring +them. However, the autochdir option: > + :set acd +is problematic. Autochdir sets the current directory to that containing the +file you edit; this apparently also applies to directories. In other words, +autochdir sets the current directory to that containing the "file" (even if +that "file" is itself a directory). + + +============================================================================== +OBTAINING A FILE *netrw-obtain* *netrw-O* {{{2 + +If there are no marked files: + + When browsing a remote directory, one may obtain a file under the cursor + (ie. get a copy on your local machine, but not edit it) by pressing the O + key. + +If there are marked files: + + The marked files will be obtained (ie. a copy will be transferred to your + local machine, but not set up for editing). + +Only ftp and scp are supported for this operation (but since these two are +available for browsing, that shouldn't be a problem). The status bar will +then show, on its right hand side, a message like "Obtaining filename". The +statusline will be restored after the transfer is complete. + +Netrw can also "obtain" a file using the local browser. Netrw's display +of a directory is not necessarily the same as Vim's "current directory", +unless |g:netrw_keepdir| is set to 0 in the user's <.vimrc>. One may select +a file using the local browser (by putting the cursor on it) and pressing +"O" will then "obtain" the file; ie. copy it to Vim's current directory. + +Related topics: + * To see what the current directory is, use |:pwd| + * To make the currently browsed directory the current directory, see + |netrw-cd| + * To automatically make the currently browsed directory the current + directory, see |g:netrw_keepdir|. + + *netrw-newfile* *netrw-createfile* +OPEN A NEW FILE IN NETRW'S CURRENT DIRECTORY *netrw-%* {{{2 + +To open a new file in netrw's current directory, press "%". This map +will query the user for a new filename; an empty file by that name will +be placed in the netrw's current directory (ie. b:netrw_curdir). + +If Lexplore (|netrw-:Lexplore|) is in use, the new file will be generated +in the |g:netrw_chgwin| window. + +Related topics: |netrw-d| + + +PREVIEW WINDOW *netrw-p* *netrw-preview* {{{2 + +One may use a preview window by using the "p" key when the cursor is atop the +desired filename to be previewed. The display will then split to show both +the browser (where the cursor will remain) and the file (see |:pedit|). By +default, the split will be taken horizontally; one may use vertical splitting +if one has set |g:netrw_preview| first. + +An interesting set of netrw settings is: > + + let g:netrw_preview = 1 + let g:netrw_liststyle = 3 + let g:netrw_winsize = 30 + +These will: + + 1. Make vertical splitting the default for previewing files + 2. Make the default listing style "tree" + 3. When a vertical preview window is opened, the directory listing + will use only 30% of the columns available; the rest of the window + is used for the preview window. + + Related: if you like this idea, you may also find :Lexplore + (|netrw-:Lexplore|) or |g:netrw_chgwin| of interest + +Also see: |g:netrw_chgwin| |netrw-P| 'previewwindow' |CTRL-W_z| |:pclose| + + +PREVIOUS WINDOW *netrw-P* *netrw-prvwin* {{{2 + +To edit a file or directory under the cursor in the previously used (last +accessed) window (see :he |CTRL-W_p|), press a "P". If there's only one +window, then the one window will be horizontally split (by default). + +If there's more than one window, the previous window will be re-used on +the selected file/directory. If the previous window's associated buffer +has been modified, and there's only one window with that buffer, then +the user will be asked if s/he wishes to save the buffer first (yes, +no, or cancel). + +Related Actions |netrw-cr| |netrw-o| |netrw-t| |netrw-v| +Associated setting variables: + |g:netrw_alto| control above/below splitting + |g:netrw_altv| control right/left splitting + |g:netrw_preview| control horizontal vs vertical splitting + |g:netrw_winsize| control initial sizing + +Also see: |g:netrw_chgwin| |netrw-p| + + +REFRESHING THE LISTING *netrw-refresh* *netrw-ctrl-l* *netrw-ctrl_l* {{{2 + +To refresh either a local or remote directory listing, press ctrl-l (<c-l>) or +hit the <cr> when atop the ./ directory entry in the listing. One may also +refresh a local directory by using ":e .". + + +REVERSING SORTING ORDER *netrw-r* *netrw-reverse* {{{2 + +One may toggle between normal and reverse sorting order by pressing the +"r" key. + +Related topics: |netrw-s| +Associated setting variable: |g:netrw_sort_direction| + + +RENAMING FILES OR DIRECTORIES *netrw-move* *netrw-rename* *netrw-R* {{{2 + +If there are no marked files: (see |netrw-mf|) + + Renaming files and directories involves moving the cursor to the + file/directory to be moved (renamed) and pressing "R". You will then be + queried for what you want the file/directory to be renamed to. You may + select a range of lines with the "V" command (visual selection), and then + press "R"; you will be queried for each file as to what you want it + renamed to. + +If there are marked files: (see |netrw-mf|) + + Marked files will be renamed (moved). You will be queried as above in + order to specify where you want the file/directory to be moved. + + If you answer a renaming query with a "s/frompattern/topattern/", then + subsequent files on the marked file list will be renamed by taking each + name, applying that substitute, and renaming each file to the result. + As an example : > + + mr [query: reply with *.c] + R [query: reply with s/^\(.*\)\.c$/\1.cpp/] +< + This example will mark all *.c files and then rename them to *.cpp + files. Netrw will protect you from overwriting local files without + confirmation, but not remote ones. + + The ctrl-X character has special meaning for renaming files: > + + <c-x> : a single ctrl-x tells netrw to ignore the portion of the response + lying between the last '/' and the ctrl-x. + + <c-x><c-x> : a pair of contiguous ctrl-x's tells netrw to ignore any + portion of the string preceding the double ctrl-x's. +< + WARNING:~ + + Note that moving files is a dangerous operation; copies are safer. That's + because a "move" for remote files is actually a copy + delete -- and if + the copy fails and the delete succeeds you may lose the file. + Use at your own risk. + +The *g:netrw_rename_cmd* variable is used to implement remote renaming. By +default its value is: > + + ssh HOSTNAME mv +< +One may rename a block of files and directories by selecting them with +V (|linewise-visual|) when using thin style. + +See |cmdline-editing| for more on how to edit the command line; in particular, +you'll find <ctrl-f> (initiates cmdline window editing) and <ctrl-c> (uses the +command line under the cursor) useful in conjunction with the R command. + + +SELECTING SORTING STYLE *netrw-s* *netrw-sort* {{{2 + +One may select the sorting style by name, time, or (file) size. The "s" map +allows one to circulate amongst the three choices; the directory listing will +automatically be refreshed to reflect the selected style. + +Related topics: |netrw-r| |netrw-S| +Associated setting variables: |g:netrw_sort_by| |g:netrw_sort_sequence| + + +SETTING EDITING WINDOW *netrw-editwindow* *netrw-C* *netrw-:NetrwC* {{{2 + +One may select a netrw window for editing with the "C" mapping, using the +:NetrwC [win#] command, or by setting |g:netrw_chgwin| to the selected window +number. Subsequent selection of a file to edit (|netrw-cr|) will use that +window. + + * C : by itself, will select the current window holding a netrw buffer + for subsequent editing via |netrw-cr|. The C mapping is only available + while in netrw buffers. + + * [count]C : the count will be used as the window number to be used + for subsequent editing via |netrw-cr|. + + * :NetrwC will set |g:netrw_chgwin| to the current window + + * :NetrwC win# will set |g:netrw_chgwin| to the specified window + number + +Using > + let g:netrw_chgwin= -1 +will restore the default editing behavior +(ie. subsequent editing will use the current window). + +Related topics: |netrw-cr| |g:netrw_browse_split| +Associated setting variables: |g:netrw_chgwin| + + +SHRINKING OR EXPANDING A NETRW OR LEXPLORE WINDOW *netrw-c-tab* {{{2 + +The <c-tab> key will toggle a netrw or |:Lexplore| window's width, +but only if |g:netrw_usetab| exists and is non-zero (and, of course, +only if your terminal supports differentiating <c-tab> from a plain +<tab>). + + * If the current window is a netrw window, toggle its width + (between |g:netrw_wiw| and its original width) + + * Else if there is a |:Lexplore| window in the current tab, toggle + its width + + * Else bring up a |:Lexplore| window + +If |g:netrw_usetab| exists and is zero, or if there is a pre-existing mapping +for <c-tab>, then the <c-tab> will not be mapped. One may map something other +than a <c-tab>, too: (but you'll still need to have had |g:netrw_usetab| set). > + + nmap <unique> (whatever) <Plug>NetrwShrink +< +Related topics: |:Lexplore| +Associated setting variable: |g:netrw_usetab| + + +USER SPECIFIED MAPS *netrw-usermaps* {{{1 + +One may make customized user maps. Specify a variable, |g:Netrw_UserMaps|, +to hold a |List| of lists of keymap strings and function names: > + + [["keymap-sequence","ExampleUserMapFunc"],...] +< +When netrw is setting up maps for a netrw buffer, if |g:Netrw_UserMaps| +exists, then the internal function netrw#UserMaps(islocal) is called. +This function goes through all the entries in the |g:Netrw_UserMaps| list: + + * sets up maps: > + nno <buffer> <silent> KEYMAP-SEQUENCE + :call s:UserMaps(islocal,"ExampleUserMapFunc") +< * refreshes if result from that function call is the string + "refresh" + * if the result string is not "", then that string will be + executed (:exe result) + * if the result is a List, then the above two actions on results + will be taken for every string in the result List + +The user function is passed one argument; it resembles > + + fun! ExampleUserMapFunc(islocal) +< +where a:islocal is 1 if its a local-directory system call or 0 when +remote-directory system call. + + *netrw-call* *netrw-expose* *netrw-modify* +Use netrw#Expose("varname") to access netrw-internal (script-local) + variables. +Use netrw#Modify("varname",newvalue) to change netrw-internal variables. +Use netrw#Call("funcname"[,args]) to call a netrw-internal function with + specified arguments. + +Example: Get a copy of netrw's marked file list: > + + let netrwmarkfilelist= netrw#Expose("netrwmarkfilelist") +< +Example: Modify the value of netrw's marked file list: > + + call netrw#Modify("netrwmarkfilelist",[]) +< +Example: Clear netrw's marked file list via a mapping on gu > + " ExampleUserMap: {{{2 + fun! ExampleUserMap(islocal) + call netrw#Modify("netrwmarkfilelist",[]) + call netrw#Modify('netrwmarkfilemtch_{bufnr("%")}',"") + let retval= ["refresh"] + return retval + endfun + let g:Netrw_UserMaps= [["gu","ExampleUserMap"]] +< + +10. Problems and Fixes *netrw-problems* {{{1 + + (This section is likely to grow as I get feedback) + *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 + don't I get sorts by time or size? + + Windows' ftp has a minimal support for ls (ie. it doesn't + accept sorting options). It doesn't support the -F which + gives an explanatory character (ABC/ for "ABC is a directory"). + Netrw then uses "dir" to get both its thin and long listings. + If you think your ftp does support a full-up ls, put the + following into your <.vimrc>: > + + let g:netrw_ftp_list_cmd = "ls -lF" + let g:netrw_ftp_timelist_cmd= "ls -tlF" + let g:netrw_ftp_sizelist_cmd= "ls -slF" +< + Alternatively, if you have cygwin on your Windows box, put + into your <.vimrc>: > + + let g:netrw_cygwin= 1 +< + This problem also occurs when the remote system is Windows. + In this situation, the various g:netrw_ftp_[time|size]list_cmds + are as shown above, but the remote system will not correctly + modify its listing behavior. + + + *netrw-p2* + P2. I tried rcp://user@host/ (or protocol other than ftp) and netrw {{{2 + used ssh! That wasn't what I asked for... + + Netrw has two methods for browsing remote directories: ssh + and ftp. Unless you specify ftp specifically, ssh is used. + When it comes time to do download a file (not just a directory + listing), netrw will use the given protocol to do so. + + *netrw-p3* + P3. I would like long listings to be the default. {{{2 + + Put the following statement into your |.vimrc|: > + + let g:netrw_liststyle= 1 +< + Check out |netrw-browser-var| for more customizations that + you can set. + + *netrw-p4* + P4. My times come up oddly in local browsing {{{2 + + Does your system's strftime() accept the "%c" to yield dates + such as "Sun Apr 27 11:49:23 1997"? If not, do a + "man strftime" and find out what option should be used. Then + put it into your |.vimrc|: > + + let g:netrw_timefmt= "%X" (where X is the option) +< + *netrw-p5* + P5. I want my current directory to track my browsing. {{{2 + How do I do that? + + Put the following line in your |.vimrc|: +> + let g:netrw_keepdir= 0 +< + *netrw-p6* + P6. I use Chinese (or other non-ascii) characters in my filenames, {{{2 + and netrw (Explore, Sexplore, Hexplore, etc) doesn't display them! + + (taken from an answer provided by Wu Yongwei on the vim + mailing list) + I now see the problem. Your code page is not 936, right? Vim + seems only able to open files with names that are valid in the + current code page, as are many other applications that do not + use the Unicode version of Windows APIs. This is an OS-related + issue. You should not have such problems when the system + locale uses UTF-8, such as modern Linux distros. + + (...it is one more reason to recommend that people use utf-8!) + + *netrw-p7* + P7. I'm getting "ssh is not executable on your system" -- what do I {{{2 + do? + + (Dudley Fox) Most people I know use putty for windows ssh. It + is a free ssh/telnet application. You can read more about it + here: + + http://www.chiark.greenend.org.uk/~sgtatham/putty/ Also: + + (Marlin Unruh) This program also works for me. It's a single + executable, so he/she can copy it into the Windows\System32 + folder and create a shortcut to it. + + (Dudley Fox) You might also wish to consider plink, as it + sounds most similar to what you are looking for. plink is an + application in the putty suite. + + http://the.earth.li/~sgtatham/putty/0.58/htmldoc/Chapter7.html#plink + + (Vissale Neang) Maybe you can try OpenSSH for windows, which + can be obtained from: + + http://sshwindows.sourceforge.net/ + + It doesn't need the full Cygwin package. + + (Antoine Mechelynck) For individual Unix-like programs needed + for work in a native-Windows environment, I recommend getting + them from the GnuWin32 project on sourceforge if it has them: + + http://gnuwin32.sourceforge.net/ + + Unlike Cygwin, which sets up a Unix-like virtual machine on + top of Windows, GnuWin32 is a rewrite of Unix utilities with + Windows system calls, and its programs works quite well in the + cmd.exe "Dos box". + + (dave) Download WinSCP and use that to connect to the server. + In Preferences > Editors, set gvim as your editor: + + - Click "Add..." + - Set External Editor (adjust path as needed, include + the quotes and !.! at the end): + "c:\Program Files\Vim\vim82\gvim.exe" !.! + - Check that the filetype in the box below is + {asterisk}.{asterisk} (all files), or whatever types + you want (cec: change {asterisk} to * ; I had to + write it that way because otherwise the helptags + system thinks it's a tag) + - Make sure it's at the top of the listbox (click it, + then click "Up" if it's not) + If using the Norton Commander style, you just have to hit <F4> + to edit a file in a local copy of gvim. + + (Vit Gottwald) How to generate public/private key and save + public key it on server: > + http://www.chiark.greenend.org.uk/~sgtatham/putty/0.60/htmldoc/Chapter8.html#pubkey-gettingready + (8.3 Getting ready for public key authentication) +< + How to use a private key with 'pscp': > + + http://www.chiark.greenend.org.uk/~sgtatham/putty/0.60/htmldoc/Chapter5.html + (5.2.4 Using public key authentication with PSCP) +< + (Ben Schmidt) I find the ssh included with cwRsync is + brilliant, and install cwRsync or cwRsyncServer on most + Windows systems I come across these days. I guess COPSSH, + packed by the same person, is probably even better for use as + just ssh on Windows, and probably includes sftp, etc. which I + suspect the cwRsync doesn't, though it might + + (cec) To make proper use of these suggestions above, you will + need to modify the following user-settable variables in your + .vimrc: + + |g:netrw_ssh_cmd| |g:netrw_list_cmd| |g:netrw_mkdir_cmd| + |g:netrw_rm_cmd| |g:netrw_rmdir_cmd| |g:netrw_rmf_cmd| + + The first one (|g:netrw_ssh_cmd|) is the most important; most + of the others will use the string in g:netrw_ssh_cmd by + default. + + *netrw-p8* *netrw-ml_get* + P8. I'm browsing, changing directory, and bang! ml_get errors {{{2 + appear and I have to kill vim. Any way around this? + + Normally netrw attempts to avoid writing swapfiles for + its temporary directory buffers. However, on some systems + this attempt appears to be causing ml_get errors to + appear. Please try setting |g:netrw_use_noswf| to 0 + in your <.vimrc>: > + let g:netrw_use_noswf= 0 +< + *netrw-p9* + P9. I'm being pestered with "[something] is a directory" and {{{2 + "Press ENTER or type command to continue" prompts... + + The "[something] is a directory" prompt is issued by Vim, + not by netrw, and there appears to be no way to work around + it. Coupled with the default cmdheight of 1, this message + causes the "Press ENTER..." prompt. So: read |hit-enter|; + I also suggest that you set your 'cmdheight' to 2 (or more) in + your <.vimrc> file. + + *netrw-p10* + P10. I want to have two windows; a thin one on the left and my {{{2 + editing window on the right. How may I accomplish this? + + You probably want netrw running as in a side window. If so, you + will likely find that ":[N]Lexplore" does what you want. The + optional "[N]" allows you to select the quantity of columns you + wish the |:Lexplore|r window to start with (see |g:netrw_winsize| + for how this parameter works). + + Previous solution: + + * Put the following line in your <.vimrc>: + let g:netrw_altv = 1 + * Edit the current directory: :e . + * Select some file, press v + * Resize the windows as you wish (see |CTRL-W_<| and + |CTRL-W_>|). If you're using gvim, you can drag + the separating bar with your mouse. + * When you want a new file, use ctrl-w h to go back to the + netrw browser, select a file, then press P (see |CTRL-W_h| + and |netrw-P|). If you're using gvim, you can press + <leftmouse> in the browser window and then press the + <middlemouse> to select the file. + + + *netrw-p11* + P11. My directory isn't sorting correctly, or unwanted letters are {{{2 + appearing in the listed filenames, or things aren't lining + up properly in the wide listing, ... + + This may be due to an encoding problem. I myself usually use + utf-8, but really only use ascii (ie. bytes from 32-126). + Multibyte encodings use two (or more) bytes per character. + You may need to change |g:netrw_sepchr| and/or |g:netrw_xstrlen|. + + *netrw-p12* + P12. I'm a Windows + putty + ssh user, and when I attempt to {{{2 + browse, the directories are missing trailing "/"s so netrw treats + them as file transfers instead of as attempts to browse + subdirectories. How may I fix this? + + (mikeyao) If you want to use vim via ssh and putty under Windows, + try combining the use of pscp/psftp with plink. pscp/psftp will + be used to connect and plink will be used to execute commands on + the server, for example: list files and directory using 'ls'. + + These are the settings I use to do this: +> + " list files, it's the key setting, if you haven't set, + " you will get a blank buffer + let g:netrw_list_cmd = "plink HOSTNAME ls -Fa" + " if you haven't add putty directory in system path, you should + " specify scp/sftp command. For examples: + "let g:netrw_sftp_cmd = "d:\\dev\\putty\\PSFTP.exe" + "let g:netrw_scp_cmd = "d:\\dev\\putty\\PSCP.exe" +< + *netrw-p13* + P13. I would like to speed up writes using Nwrite and scp/ssh {{{2 + style connections. How? (Thomer M. Gil) + + Try using ssh's ControlMaster and ControlPath (see the ssh_config + man page) to share multiple ssh connections over a single network + connection. That cuts out the cryptographic handshake on each + file write, sometimes speeding it up by an order of magnitude. + (see http://thomer.com/howtos/netrw_ssh.html) + (included by permission) + + Add the following to your ~/.ssh/config: > + + # you change "*" to the hostname you care about + Host * + ControlMaster auto + ControlPath /tmp/%r@%h:%p + +< Then create an ssh connection to the host and leave it running: > + + ssh -N host.domain.com + +< Now remotely open a file with Vim's Netrw and enjoy the + zippiness: > + + vim scp://host.domain.com//home/user/.bashrc +< + *netrw-p14* + P14. How may I use a double-click instead of netrw's usual single {{{2 + click to open a file or directory? (Ben Fritz) + + First, disable netrw's mapping with > + let g:netrw_mousemaps= 0 +< and then create a netrw buffer only mapping in + $HOME/.vim/after/ftplugin/netrw.vim: > + nmap <buffer> <2-leftmouse> <CR> +< Note that setting g:netrw_mousemaps to zero will turn off + all netrw's mouse mappings, not just the <leftmouse> one. + (see |g:netrw_mousemaps|) + + *netrw-p15* + P15. When editing remote files (ex. :e ftp://hostname/path/file), {{{2 + under Windows I get an |E303| message complaining that its unable + to open a swap file. + + (romainl) It looks like you are starting Vim from a protected + directory. Start netrw from your $HOME or other writable + directory. + + *netrw-p16* + P16. Netrw is closing buffers on its own. {{{2 + What steps will reproduce the problem? + 1. :Explore, navigate directories, open a file + 2. :Explore, open another file + 3. Buffer opened in step 1 will be closed. o + What is the expected output? What do you see instead? + I expect both buffers to exist, but only the last one does. + + (Lance) Problem is caused by "set autochdir" in .vimrc. + (drchip) I am able to duplicate this problem with 'acd' set. + It appears that the buffers are not exactly closed; + a ":ls!" will show them (although ":ls" does not). + + *netrw-P17* + P17. How to locally edit a file that's only available via {{{2 + another server accessible via ssh? + See http://stackoverflow.com/questions/12469645/ + "Using Vim to Remotely Edit A File on ServerB Only + Accessible From ServerA" + + *netrw-P18* + P18. How do I get numbering on in directory listings? {{{2 + With |g:netrw_bufsettings|, you can control netrw's buffer + settings; try putting > + let g:netrw_bufsettings="noma nomod nu nobl nowrap ro nornu" +< in your .vimrc. If you'd like to have relative numbering + instead, try > + let g:netrw_bufsettings="noma nomod nonu nobl nowrap ro rnu" +< + *netrw-P19* + P19. How may I have gvim start up showing a directory listing? {{{2 + Try putting the following code snippet into your .vimrc: > + augroup VimStartup + au! + au VimEnter * if expand("%") == "" && argc() == 0 && + \ (v:servername =~ 'GVIM\d*' || v:servername == "") + \ | e . | endif + augroup END +< You may use Lexplore instead of "e" if you're so inclined. + This snippet assumes that you have client-server enabled + (ie. a "huge" vim version). + + *netrw-P20* + P20. I've made a directory (or file) with an accented character, {{{2 + but netrw isn't letting me enter that directory/read that file: + + Its likely that the shell or o/s is using a different encoding + than you have vim (netrw) using. A patch to vim supporting + "systemencoding" may address this issue in the future; for + now, just have netrw use the proper encoding. For example: > + + au FileType netrw set enc=latin1 +< + *netrw-P21* + P21. I get an error message when I try to copy or move a file: {{{2 + + **error** (netrw) tried using g:netrw_localcopycmd<cp>; it doesn't work! + + What's wrong? + + Netrw uses several system level commands to do things (see + + |g:netrw_localcopycmd|, |g:netrw_localmovecmd|, + |g:netrw_mkdir_cmd|). + + You may need to adjust the default commands for one or more of + these commands by setting them properly in your .vimrc. Another + source of difficulty is that these commands use vim's local + directory, which may not be the same as the browsing directory + shown by netrw (see |g:netrw_keepdir|). + + +============================================================================== +11. Credits *netrw-credits* {{{1 + + Vim editor by Bram Moolenaar (Thanks, Bram!) + dav support by C Campbell + fetch support by Bram Moolenaar and C Campbell + ftp support by C Campbell <NcampObell@SdrPchip.AorgM-NOSPAM> + http support by Bram Moolenaar <bram@moolenaar.net> + rcp + rsync support by C Campbell (suggested by Erik Warendorph) + scp support by raf <raf@comdyn.com.au> + sftp support by C Campbell + + inputsecret(), BufReadCmd, BufWriteCmd contributed by C Campbell + + Jérôme Augé -- also using new buffer method with ftp+.netrc + Bram Moolenaar -- obviously vim itself, :e and v:cmdarg use, + fetch,... + Yasuhiro Matsumoto -- pointing out undo+0r problem and a solution + Erik Warendorph -- for several suggestions (g:netrw_..._cmd + variables, rsync etc) + Doug Claar -- modifications to test for success with ftp + operation + +============================================================================== +Modelines: {{{1 +vim:tw=78:ts=8:ft=help:noet:norl:fdm=marker diff --git a/uvim/runtime/pack/dist/opt/netrw/plugin/netrwPlugin.vim b/uvim/runtime/pack/dist/opt/netrw/plugin/netrwPlugin.vim new file mode 100644 index 0000000000..da0db3f925 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/netrw/plugin/netrwPlugin.vim @@ -0,0 +1,165 @@ +" Creator: Charles E Campbell +" Previous Maintainer: Luca Saccarola <github.e41mv@aleeas.com> +" Maintainer: This runtime file is looking for a new maintainer. +" 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 = "v184" + +let s:keepcpo = &cpo +set cpo&vim + +" 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 netrw#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>) + +" }}} +" Maps: {{{ + +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 + +" }}} +" Deprecated: {{{ + +function NetUserPass(...) + call netrw#msg#Deprecate('NetUserPass', 'v185', { + \ 'vim': 'netrw#NetUserPass()', + \ 'nvim': 'netrw#NetUserPass()' + \}) + if a:0 + call netrw#NetUserPass(a:000) + else + call netrw#NetUserPass() + endif +endfunction + +" }}} + +let &cpo= s:keepcpo +unlet s:keepcpo + +" vim:ts=8 sts=4 sw=4 et fdm=marker diff --git a/uvim/runtime/pack/dist/opt/netrw/syntax/netrw.vim b/uvim/runtime/pack/dist/opt/netrw/syntax/netrw.vim new file mode 100644 index 0000000000..2c0b9acb75 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/netrw/syntax/netrw.vim @@ -0,0 +1,150 @@ +" Creator: Charles E Campbell +" Previous Maintainer: Luca Saccarola <github.e41mv@aleeas.com> +" Maintainer: This runtime file is looking for a new maintainer. +" 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/uvim/runtime/pack/dist/opt/nohlsearch/plugin/nohlsearch.vim b/uvim/runtime/pack/dist/opt/nohlsearch/plugin/nohlsearch.vim new file mode 100644 index 0000000000..58613a2f03 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/nohlsearch/plugin/nohlsearch.vim @@ -0,0 +1,24 @@ +" nohlsearch.vim: Auto turn off hlsearch +" Last Change: 2025-03-08 +" Maintainer: Maxim Kim <habamax@gmail.com> +" +" turn off hlsearch after: +" - doing nothing for 'updatetime' +" - getting into insert mode + +if exists('g:loaded_nohlsearch') + finish +endif +let g:loaded_nohlsearch = 1 + +func! s:Nohlsearch() + if v:hlsearch + call feedkeys("\<cmd>nohlsearch\<cr>", 'm') + endif +endfunc + +augroup nohlsearch + au! + au CursorHold * call s:Nohlsearch() + au InsertEnter * call s:Nohlsearch() +augroup END diff --git a/uvim/runtime/pack/dist/opt/osc52/autoload/osc52.vim b/uvim/runtime/pack/dist/opt/osc52/autoload/osc52.vim new file mode 100644 index 0000000000..3471329d7f --- /dev/null +++ b/uvim/runtime/pack/dist/opt/osc52/autoload/osc52.vim @@ -0,0 +1,97 @@ +vim9script + +export var allowed: bool = false + +export def Available(): bool + if get(g:, 'osc52_force_avail', 0) + return true + endif + return allowed +enddef + +var sent_message: bool = false + +def OSCMessage(id: number) + echo "Waiting for OSC52 response... Press CTRL-C to cancel" + sent_message = true +enddef + +export def Paste(reg: string): tuple<string, list<string>> + # Check if user has indicated that the terminal does not support OSC 52 paste + # (or has disabled it) + if get(g:, 'osc52_disable_paste', 0) + return ("c", []) + endif + + # Some terminals like Kitty respect the selection type parameter on both X11 + # and Wayland. If the terminal doesn't then the selection type parameter + # should be ignored (no-op) + if reg == "+" + echoraw("\<Esc>]52;c;?\<Esc>\\") + else + echoraw("\<Esc>]52;p;?\<Esc>\\") + endif + + var timerid: number = timer_start(1000, OSCMessage) + var interrupt: bool = false + + # Wait for response from terminal. If we got interrupted (Ctrl-C), then do a + # redraw if we already sent the message, and return an empty string. + try + while true + var key: string = getcharstr(-1, {cursor: "hide"}) + + if key == "\<xOSC>" && match(v:termosc, '52;') != -1 + break + elseif key == "\<C-c>" + interrupt = true + break + endif + endwhile + + # This doesn't seem to catch Ctrl-C sent via term_sendkeys(), which is used in + # tests. So also check the result of getcharstr()/getchar(). + catch /^Vim:Interrupt$/ + interrupt = true + finally + timer_stop(timerid) + if sent_message + sent_message = false + :redraw! + endif + endtry + + if interrupt + echo "Interrupted while waiting for OSC 52 response" + return ("c", [""]) + endif + + # Extract the base64 stuff + var stuff: string = matchstr(v:termosc, '52;.*;\zs[A-Za-z0-9+/=]*') + + if len(stuff) == 0 + return ("c", []) + endif + + var ret: list<string> + + # "stuff" may be an invalid base64 string, so catch any errors + try + ret = blob2str(base64_decode(stuff)) + catch /E\(475\|1515\)/ + echo "Invalid OSC 52 response received" + return ("c", [""]) + endtry + + return ("", ret) +enddef + +export def Copy(reg: string, type: string, lines: list<string>): void + if reg == "+" + echoraw("\<Esc>]52;c;" .. base64_encode(str2blob(lines)) .. "\<Esc>\\") + else + echoraw("\<Esc>]52;p;" .. base64_encode(str2blob(lines)) .. "\<Esc>\\") + endif +enddef + +# vim: set sw=2 sts=2 : diff --git a/uvim/runtime/pack/dist/opt/osc52/doc/osc52.txt b/uvim/runtime/pack/dist/opt/osc52/doc/osc52.txt new file mode 100644 index 0000000000..7ac73c076d --- /dev/null +++ b/uvim/runtime/pack/dist/opt/osc52/doc/osc52.txt @@ -0,0 +1,71 @@ +*osc52.txt* For Vim version 9.1. Last change: 2025 Dec 18 + + + VIM REFERENCE MANUAL + +Use the OSC 52 terminal command for clipboard support +============================================================================== + +1. OVERVIEW *osc52-overview* + +The osc52.vim plugin provides support for the OSC 52 terminal command, which +allows an application to access the clipboard by communicating with the +terminal. This is useful in situations such as if you are in a SSH session. + + *osc52-support* +In order for this plugin to work, the terminal Vim is running in must +recognize and handle the OSC 52 escape sequence. You can easily check this +online. Additionally, while yanking is guaranteed to work, some terminals +don't implement the paste functionality. If the terminal doesn't support +pasting, then Vim will just block waiting for the data which will never come. +In this case just press Ctrl-C to cancel the operation. + + *osc52-selections* +Note that this only applies to users on Wayland or X11 platforms + +Some terminals support the selection type parameter in the OSC 52 command. +This originates from X11, and some terminals check this parameter and handle +it accordingly. If your terminal handles this parameter, then the "+" +register corresponds to the regular selection, and the "*" register +corresponds to the primary selection. If your terminal does not handle it, +then it is up to the terminal to handle what selection to use. + +2. HOW TO USE THE PLUGIN *osc52-how-to-use* + +The osc52.vim package relies on Vim's clipboard provider functionality, see +|clipboard-providers|. To enable it, add the following to your |vimrc|: >vim + packadd osc52 + set clipmethod+=osc52 +< +This appends "osc52" to |clipmethod|, causing Vim to try it only after any +earlier clipboard methods. This allows Vim to use the system clipboard +directly when available, and automatically fall back to OSC 52 method when it +is not (for example, when running over SSH). + +Note: that this fallback behavior applies only on platforms that use +|clipmethod| for accessing the clipboard. On macOS and Windows, Vim does not +use |clipmethod|, so this behaviour won't happen. Instead if OSC 52 support is +detected and "osc52" is the only value in |clipmethod|, then it will always be +used. + +You can check whether the osc52.vim provider is active by inspecting +|v:clipmethod|. If it contains "osc52", the plugin is enabled. + +Note: terminal multiplexers such as tmux may interfere with automatic OSC 52 +detection. + + *g:osc52_force_avail* +In most cases, the plugin should automatically detect and work if your +terminal supports the OSC 52 command. Internally, it does this by sending the +Primary Device Attributes (DA1) query. You may force enable the plugin by +setting |g:osc52_force_avail| to true. + + *g:osc52_disable_paste* +If your terminal does not support pasting via OSC 52, or has it disabled, then +it is a good idea to set g:osc52_disable_paste to TRUE. This will register +only the "copy" method for the osc52.vim clipboard provider, so Vim will not +attempt an OSC 52 paste query and avoids the blocking wait described in +|osc52-support|. + +============================================================================== +vim:tw=78:ts=8:fo=tcq2:ft=help: diff --git a/uvim/runtime/pack/dist/opt/osc52/doc/tags b/uvim/runtime/pack/dist/opt/osc52/doc/tags new file mode 100644 index 0000000000..9d723973b6 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/osc52/doc/tags @@ -0,0 +1,7 @@ +g:osc52_disable_paste osc52.txt /*g:osc52_disable_paste* +g:osc52_force_avail osc52.txt /*g:osc52_force_avail* +osc52-how-to-use osc52.txt /*osc52-how-to-use* +osc52-overview osc52.txt /*osc52-overview* +osc52-selections osc52.txt /*osc52-selections* +osc52-support osc52.txt /*osc52-support* +osc52.txt osc52.txt /*osc52.txt* diff --git a/uvim/runtime/pack/dist/opt/osc52/plugin/osc52.vim b/uvim/runtime/pack/dist/opt/osc52/plugin/osc52.vim new file mode 100644 index 0000000000..0ae16376a5 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/osc52/plugin/osc52.vim @@ -0,0 +1,59 @@ +vim9script + +# Vim plugin for OSC52 clipboard support +# +# Maintainer: The Vim Project <https://github.com/vim/vim> +# Last Change: 2026 Mar 01 + +if !has("timers") + finish +endif + +import autoload "../autoload/osc52.vim" as osc + +var provider: dict<any> = { + "available": osc.Available, + "copy": { + "*": osc.Copy, + "+": osc.Copy + }, +} + +if !get(g:, 'osc52_disable_paste', 0) + provider->extend({ + "paste": { + "*": osc.Paste, + "+": osc.Paste + } + }) +endif + +v:clipproviders["osc52"] = provider + +def SendDA1(): void + if !has("gui_running") && !get(g:, 'osc52_force_avail', 0) + && !get(g:, 'osc52_no_da1', 0) + echoraw("\<Esc>[c") + endif +enddef + +if v:vim_did_enter + SendDA1() +endif + +augroup VimOSC52Plugin + autocmd! + # Query support for OSC 52 using a DA1 query + autocmd TermResponseAll da1 { + if match(v:termda1, '?\zs.*52\ze') != -1 + osc.allowed = true + :silent! clipreset + else + osc.allowed = false + :silent! clipreset + endif + } + autocmd VimEnter * SendDA1() +augroup END + +# vim: set sw=2 sts=2 : diff --git a/uvim/runtime/pack/dist/opt/shellmenu/plugin/shellmenu.vim b/uvim/runtime/pack/dist/opt/shellmenu/plugin/shellmenu.vim new file mode 100644 index 0000000000..04b48b9ce8 --- /dev/null +++ b/uvim/runtime/pack/dist/opt/shellmenu/plugin/shellmenu.vim @@ -0,0 +1,104 @@ +" When you're writing shell scripts and you are in doubt which test to use, +" which shell environment variables are defined, what the syntax of the case +" statement is, and you need to invoke 'man sh'? +" +" Your problems are over now! +" +" Attached is a Vim script file for turning gvim into a shell script editor. +" It may also be used as an example how to use menus in Vim. +" +" Maintainer: Ada (Haowen) Yu <me@yuhaowen.com> +" Original author: Lennart Schultz <les@dmi.min.dk> (mail unreachable) + +" Make sure the '<' and 'C' flags are not included in 'cpoptions', otherwise +" <CR> would not be recognized. See ":help 'cpoptions'". +let s:cpo_save = &cpo +set cpo&vim + +imenu ShellMenu.Statements.for for in <CR>do<CR><CR>done<esc>ki <esc>kk0elli +imenu ShellMenu.Statements.case case in<CR>) ;;<CR>esac<esc>bki <esc>k0elli +imenu ShellMenu.Statements.if if <CR>then<CR><CR>fi<esc>ki <esc>kk0elli +imenu ShellMenu.Statements.if-else if <CR>then<CR><CR>else<CR><CR>fi<esc>ki <esc>kki <esc>kk0elli +imenu ShellMenu.Statements.elif elif <CR>then<CR><CR><esc>ki <esc>kk0elli +imenu ShellMenu.Statements.while while do<CR><CR>done<esc>ki <esc>kk0elli +imenu ShellMenu.Statements.break break +imenu ShellMenu.Statements.continue continue +imenu ShellMenu.Statements.function () {<CR><CR>}<esc>ki <esc>k0i +imenu ShellMenu.Statements.return return +imenu ShellMenu.Statements.return-true return 0 +imenu ShellMenu.Statements.return-false return 1 +imenu ShellMenu.Statements.exit exit +imenu ShellMenu.Statements.shift shift +imenu ShellMenu.Statements.trap trap +imenu ShellMenu.Test.Existence [ -e ]<esc>hi +imenu ShellMenu.Test.Existence\ -\ file [ -f ]<esc>hi +imenu ShellMenu.Test.Existence\ -\ file\ (not\ empty) [ -s ]<esc>hi +imenu ShellMenu.Test.Existence\ -\ directory [ -d ]<esc>hi +imenu ShellMenu.Test.Existence\ -\ executable [ -x ]<esc>hi +imenu ShellMenu.Test.Existence\ -\ readable [ -r ]<esc>hi +imenu ShellMenu.Test.Existence\ -\ writable [ -w ]<esc>hi +imenu ShellMenu.Test.String\ is\ empty [ x = "x$" ]<esc>hhi +imenu ShellMenu.Test.String\ is\ not\ empty [ x != "x$" ]<esc>hhi +imenu ShellMenu.Test.Strings\ are\ equal [ "" = "" ]<esc>hhhhhhhi +imenu ShellMenu.Test.Strings\ are\ not\ equal [ "" != "" ]<esc>hhhhhhhhi +imenu ShellMenu.Test.Value\ is\ greater\ than [ -gt ]<esc>hhhhhhi +imenu ShellMenu.Test.Value\ is\ greater\ equal [ -ge ]<esc>hhhhhhi +imenu ShellMenu.Test.Values\ are\ equal [ -eq ]<esc>hhhhhhi +imenu ShellMenu.Test.Values\ are\ not\ equal [ -ne ]<esc>hhhhhhi +imenu ShellMenu.Test.Value\ is\ less\ than [ -lt ]<esc>hhhhhhi +imenu ShellMenu.Test.Value\ is\ less\ equal [ -le ]<esc>hhhhhhi +imenu ShellMenu.ParmSub.Substitute\ word\ if\ parm\ not\ set ${:-}<esc>hhi +imenu ShellMenu.ParmSub.Set\ parm\ to\ word\ if\ not\ set ${:=}<esc>hhi +imenu ShellMenu.ParmSub.Substitute\ word\ if\ parm\ set\ else\ nothing ${:+}<esc>hhi +imenu ShellMenu.ParmSub.If\ parm\ not\ set\ print\ word\ and\ exit ${:?}<esc>hhi +imenu ShellMenu.SpShVars.Number\ of\ positional\ parameters ${#} +imenu ShellMenu.SpShVars.All\ positional\ parameters\ (quoted\ spaces) ${*} +imenu ShellMenu.SpShVars.All\ positional\ parameters\ (unquoted\ spaces) ${@} +imenu ShellMenu.SpShVars.Flags\ set ${-} +imenu ShellMenu.SpShVars.Return\ code\ of\ last\ command ${?} +imenu ShellMenu.SpShVars.Process\ number\ of\ this\ shell ${$} +imenu ShellMenu.SpShVars.Process\ number\ of\ last\ background\ command ${!} +imenu ShellMenu.Environ.HOME ${HOME} +imenu ShellMenu.Environ.PATH ${PATH} +imenu ShellMenu.Environ.CDPATH ${CDPATH} +imenu ShellMenu.Environ.MAIL ${MAIL} +imenu ShellMenu.Environ.MAILCHECK ${MAILCHECK} +imenu ShellMenu.Environ.PS1 ${PS1} +imenu ShellMenu.Environ.PS2 ${PS2} +imenu ShellMenu.Environ.IFS ${IFS} +imenu ShellMenu.Environ.SHACCT ${SHACCT} +imenu ShellMenu.Environ.SHELL ${SHELL} +imenu ShellMenu.Environ.LC_CTYPE ${LC_CTYPE} +imenu ShellMenu.Environ.LC_MESSAGES ${LC_MESSAGES} +imenu ShellMenu.Builtins.cd cd +imenu ShellMenu.Builtins.echo echo +imenu ShellMenu.Builtins.eval eval +imenu ShellMenu.Builtins.exec exec +imenu ShellMenu.Builtins.export export +imenu ShellMenu.Builtins.getopts getopts +imenu ShellMenu.Builtins.hash hash +imenu ShellMenu.Builtins.newgrp newgrp +imenu ShellMenu.Builtins.pwd pwd +imenu ShellMenu.Builtins.read read +imenu ShellMenu.Builtins.readonly readonly +imenu ShellMenu.Builtins.return return +imenu ShellMenu.Builtins.times times +imenu ShellMenu.Builtins.type type +imenu ShellMenu.Builtins.umask umask +imenu ShellMenu.Builtins.wait wait +imenu ShellMenu.Set.set set +imenu ShellMenu.Set.unset unset +imenu ShellMenu.Set.Mark\ created\ or\ modified\ variables\ for\ export set -a +imenu ShellMenu.Set.Exit\ when\ command\ returns\ non-zero\ status set -e +imenu ShellMenu.Set.Disable\ file\ name\ expansion set -f +imenu ShellMenu.Set.Locate\ and\ remember\ commands\ when\ being\ looked\ up set -h +imenu ShellMenu.Set.All\ assignment\ statements\ are\ placed\ in\ the\ environment\ for\ a\ command set -k +imenu ShellMenu.Set.Read\ commands\ but\ do\ not\ execute\ them set -n +imenu ShellMenu.Set.Exit\ after\ reading\ and\ executing\ one\ command set -t +imenu ShellMenu.Set.Treat\ unset\ variables\ as\ an\ error\ when\ substituting set -u +imenu ShellMenu.Set.Print\ shell\ input\ lines\ as\ they\ are\ read set -v +imenu ShellMenu.Set.Print\ commands\ and\ their\ arguments\ as\ they\ are\ executed set -x + +" Restore the previous value of 'cpoptions'. +let &cpo = s:cpo_save +unlet s:cpo_save diff --git a/uvim/runtime/pack/dist/opt/swapmouse/plugin/swapmouse.vim b/uvim/runtime/pack/dist/opt/swapmouse/plugin/swapmouse.vim new file mode 100644 index 0000000000..8b85be050b --- /dev/null +++ b/uvim/runtime/pack/dist/opt/swapmouse/plugin/swapmouse.vim @@ -0,0 +1,22 @@ +" These macros swap the left and right mouse buttons (for left handed) +" Don't forget to do ":set mouse=a" or the mouse won't work at all +noremap <LeftMouse> <RightMouse> +noremap <2-LeftMouse> <2-RightMouse> +noremap <3-LeftMouse> <3-RightMouse> +noremap <4-LeftMouse> <4-RightMouse> +noremap <LeftDrag> <RightDrag> +noremap <LeftRelease> <RightRelease> +noremap <RightMouse> <LeftMouse> +noremap <2-RightMouse> <2-LeftMouse> +noremap <3-RightMouse> <3-LeftMouse> +noremap <4-RightMouse> <4-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> diff --git a/uvim/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim b/uvim/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim new file mode 100644 index 0000000000..3ac5c71d5b --- /dev/null +++ b/uvim/runtime/pack/dist/opt/termdebug/plugin/termdebug.vim @@ -0,0 +1,2431 @@ +vim9script + +# Debugger plugin using gdb. + +# Author: Bram Moolenaar +# Copyright: Vim license applies, see ":help license" +# Last Change: 2026 Mar 11 +# Converted to Vim9: Ubaldo Tiberi <ubaldo.tiberi@gmail.com> + +# WORK IN PROGRESS - The basics works stable, more to come +# Note: In general you need at least GDB 7.12 because this provides the +# frame= response in MI thread-selected events we need to sync stack to file. +# The one included with "old" MingW is too old (7.6.1), you may upgrade it or +# use a newer version from http://www.equation.com/servlet/equation.cmd?fa=gdb + +# There are two ways to run gdb: +# - In a terminal window; used if possible, does not work on MS-Windows +# Not used when g:termdebug_use_prompt is set to true. +# - Using a "prompt" buffer; may use a terminal window for the program + +# For both the current window is used to view source code and shows the +# current statement from gdb. + +# USING A TERMINAL WINDOW + +# Opens two visible terminal windows: +# 1. runs a pty for the debugged program, as with ":term NONE" +# 2. runs gdb, passing the pty of the debugged program +# A third terminal window is hidden, it is used for communication with gdb. + +# USING A PROMPT BUFFER + +# Opens a window with a prompt buffer to communicate with gdb. +# Gdb is run as a job with callbacks for I/O. +# On Unix another terminal window is opened to run the debugged program +# On MS-Windows a separate console is opened to run the debugged program +# but a terminal window is used to run remote debugged programs. + +# The communication with gdb uses GDB/MI. See: +# https://sourceware.org/gdb/current/onlinedocs/gdb/GDB_002fMI.html + +var DEBUG = false +if exists('g:termdebug_config') + DEBUG = get(g:termdebug_config, 'debug', false) +endif + +def Echoerr(msg: string) + echohl ErrorMsg | echom $'[termdebug] {msg}' | echohl None +enddef + +def Echowarn(msg: string) + echohl WarningMsg | echom $'[termdebug] {msg}' | echohl None +enddef + +# Variables to keep their status among multiple instances of Termdebug +g:termdebug_is_running = false + + +# The command that starts debugging, e.g. ":Termdebug vim". +# To end type "quit" in the gdb window. +command -nargs=* -complete=file -bang Termdebug StartDebug(<bang>0, <f-args>) +command -nargs=+ -complete=file -bang TermdebugCommand StartDebugCommand(<bang>0, <f-args>) + +enum Way + Prompt, + Terminal +endenum + +# Script variables declaration. These variables are re-initialized at every +# Termdebug instance +var way: Way +var err: string + +var pc_id: number +var asm_id: number +var break_id: number +var stopped: bool +var running: bool + +var parsing_disasm_msg: number +var asm_lines: list<string> +var asm_addr: string + +# These shall be constants but cannot be initialized here +# They indicate the buffer numbers of the main buffers used +var gdbbufnr: number +var gdbbufname: string +var varbufnr: number +var varbufname: string +var asmbufnr: number +var asmbufname: string +var promptbufnr: number +# 'pty' refers to the "debugged-program" pty +var ptybufnr: number +var ptybufname: string +var commbufnr: number +var commbufname: string + +var gdbjob: job +var gdb_channel: channel +# These changes because they relate to windows +var pid: number +var gdbwin: number +var varwin: number +var asmwin: number +var ptywin: number +var sourcewin: number + +# Contains breakpoints that have been placed, key is a string with the GDB +# breakpoint number. +# Each entry is a dict, containing the sub-breakpoints. Key is the subid. +# For a breakpoint that is just a number the subid is zero. +# For a breakpoint "123.4" the id is "123" and subid is "4". +# Example, when breakpoint "44", "123", "123.1" and "123.2" exist: +# {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}} +var breakpoints: dict<any> + +# Contains breakpoints by file/lnum. The key is "fname:lnum". +# Each entry is a list of breakpoint IDs at that position. +var breakpoint_locations: dict<any> +var BreakpointSigns: list<string> + +var evalFromBalloonExpr: bool +var evalInPopup: bool +var evalPopupId: number +var evalExprResult: string +var ignoreEvalError: bool +var evalexpr: string +# Remember the old value of 'signcolumn' for each buffer that it's set in, so +# that we can restore the value for all buffers. +var signcolumn_buflist: list<number> +var saved_columns: number + +var allleft: bool +# This was s:vertical but I cannot use vertical as variable name +var vvertical: bool + +var winbar_winids: list<number> + +var saved_mousemodel: string + +var saved_K_map: dict<any> +var saved_visual_K_map: dict<any> +var saved_plus_map: dict<any> +var saved_minus_map: dict<any> + +def InitScriptVariables() + if exists('g:termdebug_config') && has_key(g:termdebug_config, 'use_prompt') + way = g:termdebug_config['use_prompt'] ? Way.Prompt : Way.Terminal + elseif exists('g:termdebug_use_prompt') + way = g:termdebug_use_prompt ? Way.Prompt : Way.Terminal + elseif has('terminal') && !has('win32') + way = Way.Terminal + else + way = Way.Prompt + endif + err = '' + + pc_id = 12 + asm_id = 13 + break_id = 14 # breakpoint number is added to this + stopped = true + running = false + + parsing_disasm_msg = 0 + asm_lines = [] + asm_addr = '' + + # They indicate the buffer numbers of the main buffers used + gdbbufnr = 0 + gdbbufname = 'gdb' + varbufnr = 0 + varbufname = 'Termdebug-variables-listing' + asmbufnr = 0 + asmbufname = 'Termdebug-asm-listing' + promptbufnr = 0 + # This is for the "debugged-program" thing + ptybufname = "debugged-program" + ptybufnr = 0 + commbufname = "gdb-communication" + commbufnr = 0 + + gdbjob = null_job + gdb_channel = null_channel + # These changes because they relate to windows + pid = 0 + gdbwin = 0 + varwin = 0 + asmwin = 0 + ptywin = 0 + sourcewin = 0 + + # Contains breakpoints that have been placed, key is a string with the GDB + # breakpoint number. + # Each entry is a dict, containing the sub-breakpoints. Key is the subid. + # For a breakpoint that is just a number the subid is zero. + # For a breakpoint "123.4" the id is "123" and subid is "4". + # Example, when breakpoint "44", "123", "123.1" and "123.2" exist: + # {'44': {'0': entry}, '123': {'0': entry, '1': entry, '2': entry}} + breakpoints = {} + + # Contains breakpoints by file/lnum. The key is "fname:lnum". + # Each entry is a list of breakpoint IDs at that position. + breakpoint_locations = {} + BreakpointSigns = [] + + evalFromBalloonExpr = false + evalInPopup = false + evalPopupId = -1 + evalExprResult = '' + ignoreEvalError = false + evalexpr = '' + # Remember the old value of 'signcolumn' for each buffer that it's set in, so + # that we can restore the value for all buffers. + signcolumn_buflist = [bufnr()] + saved_columns = &columns + + winbar_winids = [] + + saved_K_map = maparg('K', 'n', false, true) + saved_plus_map = maparg('+', 'n', false, true) + saved_minus_map = maparg('-', 'n', false, true) + saved_visual_K_map = maparg('K', 'x', false, true) + + if has('menu') + saved_mousemodel = &mousemodel + endif +enddef + +def SanityCheck(): bool + var gdb_cmd = GetCommand()[0] + var cwd = $'{getcwd()}/' + if exists('+shellslash') && !&shellslash + # on windows, need to handle backslash + cwd->substitute('\\', '/', 'g') + endif + var is_check_ok = true + # Need either the +terminal feature or +channel and the prompt buffer. + # The terminal feature does not work with gdb on win32. + if (way is Way.Prompt) && !has('channel') + err = 'Cannot debug, +channel feature is not supported' + elseif (way is Way.Prompt) && !exists('*prompt_setprompt') + err = 'Cannot debug, missing prompt buffer support' + elseif (way is Way.Prompt) && !empty(glob($'{cwd}{gdb_cmd}')) + err = $"You have a file/folder named '{gdb_cmd}' in the current directory Termdebug may not work properly. Please exit and rename such a file/folder." + elseif !empty(glob($'{cwd}{asmbufname}')) + err = $"You have a file/folder named '{asmbufname}' in the current directory Termdebug may not work properly. Please exit and rename such a file/folder." + elseif !empty(glob($'{cwd}{varbufname}')) + err = $"You have a file/folder named '{varbufname}' in the current directory Termdebug may not work properly. Please exit and rename such a file/folder." + elseif !executable(gdb_cmd) + err = $"Cannot execute debugger program '{gdb_cmd}'" + endif + + if !empty(err) + Echoerr(err) + is_check_ok = false + endif + return is_check_ok +enddef + +def DeprecationWarnings() + # TODO Remove the deprecated features after 1 Jan 2025. + var config_param = '' + if exists('g:termdebug_wide') + config_param = 'g:termdebug_wide' + elseif exists('g:termdebug_popup') + config_param = 'g:termdebug_popup' + elseif exists('g:termdebugger') + config_param = 'g:termdebugger' + elseif exists('g:termdebug_variables_window') + config_param = 'g:termdebug_variables_window' + elseif exists('g:termdebug_disasm_window') + config_param = 'g:termdebug_disasm_window' + elseif exists('g:termdebug_map_K') + config_param = 'g:termdebug_map_K' + elseif exists('g:termdebug_use_prompt') + config_param = 'g:termdebug_use_prompt' + endif + + if !empty(config_param) + Echowarn($"Deprecation Warning: '{config_param}' parameter + \ is deprecated and will be removed in the future. See ':h g:termdebug_config' for alternatives.") + endif + + # termdebug config types + if exists('g:termdebug_config') && !empty(g:termdebug_config) + for key in keys(g:termdebug_config) + if index(['disasm_window', 'variables_window', 'use_prompt', 'map_K', 'map_minus', 'map_plus'], key) != -1 + if typename(g:termdebug_config[key]) == 'number' + var val = g:termdebug_config[key] + Echowarn($"Deprecation Warning: 'g:termdebug_config[\"{key}\"] = {val}' will be deprecated. + \ Please use 'g:termdebug_config[\"{key}\"] = {val != 0}'" ) + endif + endif + endfor + endif +enddef + +# Take a breakpoint number as used by GDB and turn it into an integer. +# The breakpoint may contain a dot: 123.4 -> 123004 +# The main breakpoint has a zero subid. +def Breakpoint2SignNumber(id: number, subid: number): number + return break_id + id * 1000 + subid +enddef + +# Define or adjust the default highlighting, using background "new". +# When the 'background' option is set then "old" has the old value. +def Highlight(init: bool, old: string, new: string) + var default = init ? 'default ' : '' + if new ==# 'light' && old !=# 'light' + exe $"hi {default}debugPC term=reverse ctermbg=lightblue guibg=lightblue" + elseif new ==# 'dark' && old !=# 'dark' + exe $"hi {default}debugPC term=reverse ctermbg=darkblue guibg=darkblue" + endif +enddef + +# Define the default highlighting, using the current 'background' value. +def InitHighlight() + Highlight(true, '', &background) + hi default debugBreakpoint term=reverse ctermbg=red guibg=red + hi default debugBreakpointDisabled term=reverse ctermbg=gray guibg=gray +enddef + +# Setup an autocommand to redefine the default highlight when the colorscheme +# is changed. +def InitAutocmd() + augroup TermDebug + autocmd! + autocmd ColorScheme * InitHighlight() + augroup END +enddef + +# Get the command to execute the debugger as a list, defaults to ["gdb"]. +def GetCommand(): list<string> + var cmd: any + if exists('g:termdebug_config') + cmd = get(g:termdebug_config, 'command', 'gdb') + elseif exists('g:termdebugger') + cmd = g:termdebugger + else + cmd = 'gdb' + endif + + return type(cmd) == v:t_list ? copy(cmd) : [cmd] +enddef + +def StartDebug(bang: bool, ...gdb_args: list<string>) + # First argument is the command to debug, second core file or process ID. + StartDebug_internal({gdb_args: gdb_args, bang: bang}) +enddef + +def StartDebugCommand(bang: bool, ...args: list<string>) + # First argument is the command to debug, rest are run arguments. + StartDebug_internal({gdb_args: [args[0]], proc_args: args[1 : ], bang: bang}) +enddef + +def StartDebug_internal(dict: dict<any>) + if g:termdebug_is_running + Echoerr('Terminal debugger already running, cannot run two') + return + endif + + InitScriptVariables() + if !SanityCheck() + return + endif + DeprecationWarnings() + + if exists('#User#TermdebugStartPre') + doauto <nomodeline> User TermdebugStartPre + endif + + # Uncomment this line to write logging in "debuglog". + # ch_logfile('debuglog', 'w') + + # Assume current window is the source code window + sourcewin = win_getid() + var wide = 0 + + if exists('g:termdebug_config') + wide = get(g:termdebug_config, 'wide', 0) + elseif exists('g:termdebug_wide') + wide = g:termdebug_wide + endif + if wide > 0 + if &columns < wide + &columns = wide + # If we make the Vim window wider, use the whole left half for the debug + # windows. + allleft = true + endif + vvertical = true + else + vvertical = false + endif + + if way is Way.Prompt + StartDebug_prompt(dict) + else + StartDebug_term(dict) + endif + + if GetDisasmWindow() + var curwinid = win_getid() + GotoAsmwinOrCreateIt() + win_gotoid(curwinid) + endif + + if GetVariablesWindow() + var curwinid = win_getid() + GotoVariableswinOrCreateIt() + win_gotoid(curwinid) + endif + + if exists('#User#TermdebugStartPost') + doauto <nomodeline> User TermdebugStartPost + endif + g:termdebug_is_running = true +enddef + +# Use when debugger didn't start or ended. +def CloseBuffers() + var buf_numbers = [promptbufnr, ptybufnr, commbufnr, asmbufnr, varbufnr] + for buf_nr in buf_numbers + if buf_nr > 0 && bufexists(buf_nr) + exe $'bwipe! {buf_nr}' + endif + endfor +enddef + +def IsGdbStarted(): bool + var gdbproc_status = job_status(term_getjob(gdbbufnr)) + if gdbproc_status !=# 'run' + return false + endif + return true +enddef + +# Check if the debugger is running remotely and return a suitable command to pty remotely +def GetRemotePtyCmd(gdb_cmd: list<string>): list<string> + # Check if the user provided a command to launch the program window + var term_cmd = null_list + if exists('g:termdebug_config') && has_key(g:termdebug_config, 'remote_window') + term_cmd = g:termdebug_config['remote_window'] + term_cmd = type(term_cmd) == v:t_list ? copy(term_cmd) : [term_cmd] + else + # Check if it is a remote gdb, the program terminal should be started + # on the remote machine. + const remote_pattern = '^\(ssh\|wsl\)' + if gdb_cmd[0] =~? remote_pattern + var gdb_pos = indexof(gdb_cmd, $'v:val =~? "^{GetCommand()[-1]}"') + if gdb_pos > 0 + # strip debugger call + term_cmd = gdb_cmd[0 : gdb_pos - 1] + # roundtrip to check if socat is available on the remote side + silent call system(join(term_cmd, ' ') .. ' socat -h') + if v:shell_error != 0 + Echowarn('Install socat on the remote machine for a program window better experience') + else + # create a devoted tty slave device and link to stdin/stdout + term_cmd += ['socat', '-dd', '-', 'PTY,raw,echo=0'] + ch_log($'launching remote ttys using "{join(term_cmd)}"') + endif + endif + endif + endif + return term_cmd +enddef + +# Retrieve the remote pty device from a remote terminal +# If interact is true, use remote tty command to get the pty device +def GetRemotePtyDev(bufnr: number, interact: bool): string + var pty: string = null_string + var line = null_string + + for j in range(5) + + if interact + term_sendkeys(bufnr, "tty\<CR>") + endif + + for i in range(0, term_getsize(bufnr)[0]) + line = term_getline(bufnr, i) + if line =~? "/dev/pts" + pty = line + break + endif + term_wait(bufnr, 100) + endfor # i + + if pty != null_string + # Clear the terminal window + if interact + term_sendkeys(bufnr, "clear\<CR>") + endif + break + endif + + endfor # j + + return pty +enddef + +def CreateProgramPty(cmd: list<string> = null_list): string + ptybufnr = term_start(!cmd ? 'NONE' : cmd, { + term_name: ptybufname, + vertical: vvertical}) + if ptybufnr == 0 + return null_string + endif + ptywin = win_getid() + + if vvertical + # Assuming the source code window will get a signcolumn, use two more + # columns for that, thus one less for the terminal window. + exe $":{(&columns / 2 - 1)}wincmd |" + if allleft + # use the whole left column + wincmd H + endif + endif + + if !cmd + return job_info(term_getjob(ptybufnr))['tty_out'] + else + var interact = indexof(cmd, 'v:val =~? "^socat"') < 0 + var pty = GetRemotePtyDev(ptybufnr, interact) + + if pty !~? "/dev/pts" + Echoerr('Failed to get the program window tty') + exe $'bwipe! {ptybufnr}' + pty = null_string + elseif pty !~? "^/dev/pts" + # remove the prompt + pty = pty->matchstr('/dev/pts/\d\+') + endif + + return pty + endif +enddef + +def CreateCommunicationPty(cmd: list<string> = null_list): string + # Create a hidden terminal window to communicate with gdb + var options: dict<any> = { term_name: commbufname, out_cb: CommOutput, hidden: 1 } + + if !cmd + commbufnr = term_start('NONE', options) + else + # avoid message wrapping that prevents proper parsing + options['term_cols'] = 500 + commbufnr = term_start(cmd, options) + endif + + if commbufnr == 0 + return null_string + endif + + if !cmd + return job_info(term_getjob(commbufnr))['tty_out'] + else + # CommunicationPty only will be reliable with socat + if indexof(cmd, 'v:val =~? "^socat"') < 0 + Echoerr('Communication window should be started with socat') + exe $'bwipe! {commbufnr}' + return null_string + endif + + var pty = GetRemotePtyDev(commbufnr, false) + + if pty !~? "/dev/pts" + Echoerr('Failed to get the communication window tty') + exe $'bwipe! {commbufnr}' + pty = null_string + elseif pty !~? "^/dev/pts" + # remove the prompt + pty = pty->matchstr('/dev/pts/\d\+') + endif + + return pty + endif +enddef + +# Convenient filter to workaround remote escaping issues. +# For example, ssh doesn't escape spaces for the gdb arguments. +# Workaround doing: +# let g:termdebug_config['command_filter'] = function('g:Termdebug_escape_whitespace') +def g:Termdebug_escape_whitespace(args: list<string>): list<string> + var new_args: list<string> = [] + for arg in args + new_args += [substitute(arg, ' ', '\\ ', 'g')] + endfor + return new_args +enddef + +def CreateGdbConsole(dict: dict<any>, pty: string, commpty: string): string + # Start the gdb buffer + var gdb_args = get(dict, 'gdb_args', []) + var proc_args = get(dict, 'proc_args', []) + + var gdb_cmd = GetCommand() + + gdbbufname = gdb_cmd[0] + + if exists('g:termdebug_config') && has_key(g:termdebug_config, 'command_add_args') + gdb_cmd = g:termdebug_config.command_add_args(gdb_cmd, pty) + else + # Add -quiet to avoid the intro message causing a hit-enter prompt. + gdb_cmd += ['-quiet'] + # Disable pagination, it causes everything to stop at the gdb + gdb_cmd += ['-iex', 'set pagination off'] + # Interpret commands while the target is running. This should usually only + # be exec-interrupt, since many commands don't work properly while the + # target is running (so execute during startup). + gdb_cmd += ['-iex', 'set mi-async on'] + # Open a terminal window to run the debugger. + gdb_cmd += ['-tty', pty] + # Command executed _after_ startup is done, provides us with the necessary + # feedback + gdb_cmd += ['-ex', 'echo startupdone\n'] + endif + + # Escape whitespaces in the gdb arguments for ssh remoting + if exists('g:termdebug_config') && !has_key(g:termdebug_config, 'command_filter') && + gdb_cmd[0] =~? '^ssh' + g:termdebug_config['command_filter'] = function('g:Termdebug_escape_whitespace') + endif + + if exists('g:termdebug_config') && has_key(g:termdebug_config, 'command_filter') + gdb_cmd = g:termdebug_config.command_filter(gdb_cmd) + endif + + # Adding arguments requested by the user + gdb_cmd += gdb_args + + ch_log($'executing "{join(gdb_cmd)}"') + gdbbufnr = term_start(gdb_cmd, { + term_name: gdbbufname, + term_finish: 'close', + }) + if gdbbufnr == 0 + return 'Failed to open the gdb terminal window' + endif + gdbwin = win_getid() + + # Wait for the "startupdone" message before sending any commands. + var counter = 0 + var counter_max = 300 + if exists('g:termdebug_config') && has_key(g:termdebug_config, 'timeout') + counter_max = g:termdebug_config['timeout'] + endif + + var success = false + while !success && counter < counter_max + if !IsGdbStarted() + return $'{gdbbufname} exited unexpectedly' + endif + + for lnum in range(1, 200) + if term_getline(gdbbufnr, lnum) =~ 'startupdone' + success = true + endif + endfor + + # Each count is 10ms + counter += 1 + sleep 10m + endwhile + + if !success + return 'Failed to startup the gdb program.' + endif + + # ---- gdb started. Next, let's set the MI interface. --- + # Set arguments to be run. + if !empty(proc_args) + term_sendkeys(gdbbufnr, $"server set args {join(proc_args)}\r") + endif + + # Connect gdb to the communication pty, using the GDB/MI interface. + # Prefix "server" to avoid adding this to the history. + term_sendkeys(gdbbufnr, $"server new-ui mi {commpty}\r") + + # Wait for the response to show up, users may not notice the error and wonder + # why the debugger doesn't work. + counter = 0 + counter_max = 300 + success = false + while !success && counter < counter_max + if !IsGdbStarted() + return $'{gdbbufname} exited unexpectedly' + endif + + var response = '' + for lnum in range(1, 200) + var line1 = term_getline(gdbbufnr, lnum) + var line2 = term_getline(gdbbufnr, lnum + 1) + if line1 =~ 'new-ui mi ' + # response can be in the same line or the next line + response = $"{line1}{line2}" + if response =~ 'Undefined command' + # CHECKME: possibly send a "server show version" here + return 'Sorry, your gdb is too old, gdb 7.12 is required' + endif + if response =~ 'New UI allocated' + # Success! + success = true + endif + elseif line1 =~ 'Reading symbols from' && line2 !~ 'new-ui mi ' + # Reading symbols might take a while, try more times + counter -= 1 + endif + endfor + if response =~ 'New UI allocated' + break + endif + counter += 1 + sleep 10m + endwhile + + if !success + return 'Cannot check if your gdb works, continuing anyway' + endif + return '' +enddef + + +# Open a terminal window without a job, to run the debugged program in. +def StartDebug_term(dict: dict<any>) + + # Retrieve command if remote pty is needed + var gdb_cmd = GetCommand() + var term_cmd = GetRemotePtyCmd(gdb_cmd) + + var programpty = CreateProgramPty(term_cmd) + if programpty is null_string + Echoerr('Failed to open the program terminal window') + CloseBuffers() + return + endif + + var commpty = CreateCommunicationPty(term_cmd) + if commpty is null_string + Echoerr('Failed to open the communication terminal window') + CloseBuffers() + return + endif + + var err_message = CreateGdbConsole(dict, programpty, commpty) + if !empty(err_message) + Echoerr(err_message) + CloseBuffers() + return + endif + + job_setoptions(term_getjob(gdbbufnr), {exit_cb: EndDebug}) + + # Set the filetype, this can be used to add mappings. + set filetype=termdebug + + StartDebugCommon(dict) +enddef + +# Open a window with a prompt buffer to run gdb in. +def StartDebug_prompt(dict: dict<any>) + var gdb_cmd = GetCommand() + gdbbufname = gdb_cmd[0] + + if vvertical + vertical new + else + new + endif + gdbwin = win_getid() + promptbufnr = bufnr('') + prompt_setprompt(promptbufnr, 'gdb> ') + set buftype=prompt + exe $"file {gdbbufname}" + + prompt_setcallback(promptbufnr, PromptCallback) + prompt_setinterrupt(promptbufnr, PromptInterrupt) + + if vvertical + # Assuming the source code window will get a signcolumn, use two more + # columns for that, thus one less for the terminal window. + exe $":{(&columns / 2 - 1)}wincmd |" + endif + + var gdb_args = get(dict, 'gdb_args', []) + var proc_args = get(dict, 'proc_args', []) + + # directly communicate via mi2. This option must precede any -iex options for proper + # interpretation. + gdb_cmd += ['--interpreter=mi2'] + # Disable pagination, it causes everything to stop at the gdb, needs to be run early + gdb_cmd += ['-iex', 'set pagination off'] + # Interpret commands while the target is running. This should usually only + # be exec-interrupt, since many commands don't work properly while the + # target is running (so execute during startup). + gdb_cmd += ['-iex', 'set mi-async on'] + # Add -quiet to avoid the intro message causing a hit-enter prompt. + gdb_cmd += ['-quiet'] + + # Escape whitespaces in the gdb arguments for ssh remoting + if exists('g:termdebug_config') && !has_key(g:termdebug_config, 'command_filter') && + gdb_cmd[0] =~? '^ssh' + g:termdebug_config['command_filter'] = function('g:Termdebug_escape_whitespace') + endif + + if exists('g:termdebug_config') && has_key(g:termdebug_config, 'command_filter') + gdb_cmd = g:termdebug_config.command_filter(gdb_cmd) + endif + + # Adding arguments requested by the user + gdb_cmd += gdb_args + + ch_log($'executing "{join(gdb_cmd)}"') + gdbjob = job_start(gdb_cmd, { + exit_cb: EndDebug, + out_cb: GdbOutCallback + }) + if job_status(gdbjob) != "run" + Echoerr('Failed to start gdb') + exe $'bwipe! {promptbufnr}' + return + endif + exe $'au BufUnload <buffer={promptbufnr}> ++once ' .. + 'call job_stop(gdbjob, ''kill'')' + # Mark the buffer modified so that it's not easy to close. + set modified + gdb_channel = job_getchannel(gdbjob) + + # Retrieve command if remote pty is needed + var term_cmd = GetRemotePtyCmd(gdb_cmd) + + # If we are not using socat maybe is a shell: + var interact = indexof(term_cmd, 'v:val =~? "^socat"') < 0 + + if has('terminal') && (term_cmd != null || !has('win32')) + + # Try open terminal twice because sync with gdbjob may not succeed + # the first time (docker daemon for example) + var trials: number = 2 + var pty: string = null_string + + while trials > 0 + + # Run the debugged program in a window. Open it below the + # gdb window. + belowright ptybufnr = term_start( + term_cmd != null ? term_cmd : 'NONE', { + term_name: 'debugged program', + vertical: vvertical + }) + + if ptybufnr == 0 + Echoerr('Failed to open the program terminal window') + job_stop(gdbjob) + return + endif + + ptywin = win_getid() + + if term_cmd is null + pty = job_info(term_getjob(ptybufnr))['tty_out'] + else + # Retrieve remote pty value + pty = GetRemotePtyDev(ptybufnr, interact) + endif + + if pty !~? "/dev/pts" + exe $'bwipe! {ptybufnr}' + --trials + pty = null_string + else + break + endif + endwhile + + if pty !~? "/dev/pts" + Echoerr('Failed to get the program windows tty') + job_stop(gdbjob) + elseif pty !~? "^/dev/pts" + # remove the prompt + pty = pty->matchstr('/dev/pts/\d\+') + endif + + SendCommand($'tty {pty}') + + # Since GDB runs in a prompt window, the environment has not been set to + # match a terminal window, need to do that now. + SendCommand('set env TERM = xterm-color') + SendCommand($'set env ROWS = {winheight(ptywin)}') + SendCommand($'set env LINES = {winheight(ptywin)}') + SendCommand($'set env COLUMNS = {winwidth(ptywin)}') + SendCommand($'set env COLORS = {&t_Co}') + SendCommand($'set env VIM_TERMINAL = {v:version}') + elseif has('win32') + # MS-Windows: run in a new console window for maximum compatibility + SendCommand('set new-console on') + else + # TODO: open a new terminal, get the tty name, pass on to gdb + SendCommand('show inferior-tty') + endif + SendCommand('set print pretty on') + SendCommand('set breakpoint pending on') + + # Set arguments to be run + if !empty(proc_args) + SendCommand($'set args {join(proc_args)}') + endif + + StartDebugCommon(dict) + startinsert +enddef + +def StartDebugCommon(dict: dict<any>) + # Sign used to highlight the line where the program has stopped. + # There can be only one. + sign_define('debugPC', {linehl: 'debugPC'}) + + # Install debugger commands in the text window. + win_gotoid(sourcewin) + InstallCommands() + win_gotoid(gdbwin) + + # Enable showing a balloon with eval info + if has("balloon_eval") || has("balloon_eval_term") + set balloonexpr=TermDebugBalloonExpr() + if has("balloon_eval") + set ballooneval + endif + if has("balloon_eval_term") + set balloonevalterm + endif + endif + + augroup TermDebug + au BufRead * BufRead() + au BufUnload * BufUnloaded() + au OptionSet background Highlight(0, v:option_old, v:option_new) + augroup END + + # Run the command if the bang attribute was given and got to the debug + # window. + if get(dict, 'bang', 0) + SendResumingCommand('-exec-run') + win_gotoid(ptywin) + endif +enddef + +# Send a command to gdb. "cmd" is the string without line terminator. +def SendCommand(cmd: string) + ch_log($'sending to gdb: {cmd}') + if way is Way.Prompt + ch_sendraw(gdb_channel, $"{cmd}\n") + else + term_sendkeys(commbufnr, $"{cmd}\r") + endif +enddef + +# Interrupt or stop the program +def StopCommand() + if way is Way.Prompt + PromptInterrupt() + else + SendCommand('-exec-interrupt') + endif +enddef + +# Continue the program +def ContinueCommand() + if way is Way.Prompt + SendCommand('continue') + else + # using -exec-continue results in CTRL-C in the gdb window not working, + # communicating via commbuf (= use of SendCommand) has the same result + SendCommand('-exec-continue') + # command Continue term_sendkeys(gdbbuf, "continue\r") + endif +enddef + +# This is global so that a user can create their mappings with this. +def g:TermDebugSendCommand(cmd: string) + if way is Way.Prompt + ch_sendraw(gdb_channel, $"{cmd}\n") + else + var do_continue = false + if !stopped + do_continue = true + StopCommand() + sleep 10m + endif + # TODO: should we prepend CTRL-U to clear the command? + term_sendkeys(gdbbufnr, $"{cmd}\r") + if do_continue + ContinueCommand() + endif + endif +enddef + +# Send a command that resumes the program. If the program isn't stopped the +# command is not sent (to avoid a repeated command to cause trouble). +# If the command is sent then reset stopped. +def SendResumingCommand(cmd: string) + if stopped + # reset stopped here, it may take a bit of time before we get a response + stopped = false + ch_log('assume that program is running after this command') + SendCommand(cmd) + else + ch_log($'dropping command, program is running: {cmd}') + endif +enddef + +# Function called when entering a line in the prompt buffer. +def PromptCallback(text: string) + SendCommand(text) +enddef + +# Function called when pressing CTRL-C in the prompt buffer and when placing a +# breakpoint. +def PromptInterrupt() + ch_log('Interrupting gdb') + if has('win32') + # Using job_stop() does not work on MS-Windows, need to send SIGTRAP to + # the debugger program so that gdb responds again. + if pid == 0 + Echoerr('Cannot interrupt gdb, did not find a process ID') + else + debugbreak(pid) + endif + else + job_stop(gdbjob, 'int') + endif +enddef + +# Function called when gdb outputs text. +def GdbOutCallback(channel: channel, text: string) + ch_log($'received from gdb: {text}') + + # Disassembly messages need to be forwarded as-is. + if parsing_disasm_msg > 0 + CommOutput(channel, text) + return + endif + + # Drop the gdb prompt, we have our own. + # Drop status and echo'd commands. + if text == '(gdb) ' || text == '^done' || + (text[0] == '&' && text !~ '^&"disassemble') + return + endif + + var decoded_text = '' + if text =~ '^\^error,msg=' + decoded_text = DecodeMessage(text[11 : ], false) + if !empty(evalexpr) && decoded_text =~ 'A syntax error in expression, near\|No symbol .* in current context' + # Silently drop evaluation errors. + evalexpr = '' + return + endif + elseif text[0] == '~' + decoded_text = DecodeMessage(text[1 : ], false) + else + CommOutput(channel, text) + return + endif + + var curwinid = win_getid() + win_gotoid(gdbwin) + + # Add the output above the current prompt. + append(line('$') - 1, decoded_text) + set modified + + win_gotoid(curwinid) +enddef + +# Decode a message from gdb. "quotedText" starts with a ", return the text up +# to the next unescaped ", unescaping characters: +# - remove line breaks (unless "literal" is true) +# - change \" to " +# - change \\t to \t (unless "literal" is true) +# - change \0xhh to \xhh (disabled for now) +# - change \ooo to octal +# - change \\ to \ +def DecodeMessage(quotedText: string, literal: bool): string + if quotedText[0] != '"' + Echoerr($'DecodeMessage(): missing quote in {quotedText}') + return '' + endif + var msg = quotedText + ->substitute('^"\|[^\\]\zs".*', '', 'g') + ->substitute('\\"', '"', 'g') + #\ multi-byte characters arrive in octal form + #\ NULL-values must be kept encoded as those break the string otherwise + ->substitute('\\000', NullRepl, 'g') + ->substitute('\\\(\o\o\o\)', (m) => nr2char(str2nr(m[1], 8)), 'g') + # You could also use ->substitute('\\\\\(\o\o\o\)', '\=nr2char(str2nr(submatch(1), 8))', "g") + #\ Note: GDB docs also mention hex encodings - the translations below work + #\ but we keep them out for performance-reasons until we actually see + #\ those in mi-returns + ->substitute('\\\\', '\', 'g') + ->substitute(NullRepl, '\\000', 'g') + if !literal + return msg + ->substitute('\\t', "\t", 'g') + ->substitute('\\n', '', 'g') + else + return msg + endif +enddef +const NullRepl = 'XXXNULLXXX' + +# Extract the "name" value from a gdb message with fullname="name". +def GetLocalFullname(msg: string): string + if msg !~ 'fullname' + return '' + endif + + var name = DecodeMessage(substitute(msg, '.*fullname=', '', ''), true) + if has('win32') && name =~ ':\\\\' + # sometimes the name arrives double-escaped + name = substitute(name, '\\\\', '\\', 'g') + endif + + return name +enddef + +# Turn a remote machine local path into a remote one. +def Local2RemotePath(path: string): string + # If no mappings are provided keep the path. + if !exists('g:termdebug_config') || !has_key(g:termdebug_config, 'substitute_path') + return path + endif + + var mappings: list<any> = items(g:termdebug_config['substitute_path']) + + # Try to match the longest local path first. + sort(mappings, (a, b) => len(b[0]) - len(a[0])) + + for [local, remote] in mappings + const pattern = '^' .. escape(local, '\.*~()') + if path =~ pattern + return substitute(path, pattern, escape(remote, '\.*~()'), '') + endif + endfor + + return path +enddef + +# Turn a remote path into a local one to the remote machine. +def Remote2LocalPath(path: string): string + # If no mappings are provided keep the path. + if !exists('g:termdebug_config') || !has_key(g:termdebug_config, 'substitute_path') + return path + endif + + var mappings: list<any> = items(g:termdebug_config['substitute_path']) + + # Try to match the longest remote path first. + sort(mappings, (a, b) => len(b[1]) - len(a[1])) + + for [local, remote] in mappings + const pattern = '^' .. escape(substitute(remote, '[\/]', '[\\/]', 'g'), '.*~()') + if path =~ pattern + return substitute(path, pattern, local, '') + endif + endfor + + return path +enddef + +# Extract the "addr" value from a gdb message with addr="0x0001234". +def GetAsmAddr(msg: string): string + if msg !~ 'addr=' + return '' + endif + + var addr = DecodeMessage(substitute(msg, '.*addr=', '', ''), false) + return addr +enddef + +def EndDebug(job: any, status: any) + if exists('#User#TermdebugStopPre') + doauto <nomodeline> User TermdebugStopPre + endif + + if way is Way.Prompt + ch_log("Returning from EndDebug()") + endif + + var curwinid = win_getid() + CloseBuffers() + + # Restore 'signcolumn' in all buffers for which it was set. + win_gotoid(sourcewin) + var was_buf = bufnr() + for bufnr in signcolumn_buflist + if bufexists(bufnr) + exe $":{bufnr}buf" + if exists('b:save_signcolumn') + &signcolumn = b:save_signcolumn + unlet b:save_signcolumn + endif + endif + endfor + if bufexists(was_buf) + exe $":{was_buf}buf" + endif + + DeleteCommands() + + win_gotoid(curwinid) + + &columns = saved_columns + + if has("balloon_eval") || has("balloon_eval_term") + set balloonexpr= + if has("balloon_eval") + set noballooneval + endif + if has("balloon_eval_term") + set noballoonevalterm + endif + endif + + if exists('#User#TermdebugStopPost') + doauto <nomodeline> User TermdebugStopPost + endif + + au! TermDebug + g:termdebug_is_running = false +enddef + +# Disassembly window - added by Michael Sartain +# +# - CommOutput: &"disassemble $pc\n" +# - CommOutput: ~"Dump of assembler code for function main(int, char**):\n" +# - CommOutput: ~" 0x0000555556466f69 <+0>:\tpush rbp\n" +# ... +# - CommOutput: ~" 0x0000555556467cd0:\tpop rbp\n" +# - CommOutput: ~" 0x0000555556467cd1:\tret \n" +# - CommOutput: ~"End of assembler dump.\n" +# - CommOutput: ^done + +# - CommOutput: &"disassemble $pc\n" +# - CommOutput: &"No function contains specified address.\n" +# - CommOutput: ^error,msg="No function contains specified address." +def HandleDisasmMsg(msg: string) + if msg =~ '^\^done' + var curwinid = win_getid() + if win_gotoid(asmwin) + silent! :%delete _ + setline(1, asm_lines) + set nomodified + set filetype=asm + + var lnum = search($'^{asm_addr}') + if lnum != 0 + sign_unplace('TermDebug', {id: asm_id}) + sign_place(asm_id, 'TermDebug', 'debugPC', '%', {lnum: lnum}) + endif + + win_gotoid(curwinid) + endif + + parsing_disasm_msg = 0 + asm_lines = [] + + elseif msg =~ '^\^error,msg=' + if parsing_disasm_msg == 1 + # Disassemble call ran into an error. This can happen when gdb can't + # find the function frame address, so let's try to disassemble starting + # at current PC + SendCommand('disassemble $pc,+100') + endif + parsing_disasm_msg = 0 + elseif msg =~ '^&"disassemble \$pc' + if msg =~ '+100' + # This is our second disasm attempt + parsing_disasm_msg = 2 + endif + elseif msg !~ '^&"disassemble' + var value = substitute(msg, '^\~\"[ ]*', '', '') + ->substitute('^=>[ ]*', '', '') + ->substitute('\\n\"\r$', '', '') + ->substitute('\\n\"$', '', '') + ->substitute('\r', '', '') + ->substitute('\\t', ' ', 'g') + + if value != '' || !empty(asm_lines) + add(asm_lines, value) + endif + endif +enddef + + +def ParseVarinfo(varinfo: string): dict<any> + var dict = {} + var nameIdx = matchstrpos(varinfo, '{name="\([^"]*\)"') + dict['name'] = varinfo[nameIdx[1] + 7 : nameIdx[2] - 2] + var typeIdx = matchstrpos(varinfo, ',type="\([^"]*\)"') + # 'type' maybe is a url-like string, + # try to shorten it and show only the /tail + dict['type'] = (varinfo[typeIdx[1] + 7 : typeIdx[2] - 2])->fnamemodify(':t') + var valueIdx = matchstrpos(varinfo, ',value="\(.*\)"}') + if valueIdx[1] == -1 + dict['value'] = 'Complex value' + else + dict['value'] = varinfo[valueIdx[1] + 8 : valueIdx[2] - 3] + endif + return dict +enddef + +def HandleVariablesMsg(msg: string) + var curwinid = win_getid() + if win_gotoid(varwin) + silent! :%delete _ + var spaceBuffer = 20 + var spaces = repeat(' ', 16) + setline(1, $'Type{spaces}Name{spaces}Value') + var cnt = 1 + var capture = '{name=".\{-}",\%(arg=".\{-}",\)\{0,1\}type=".\{-}"\%(,value=".\{-}"\)\{0,1\}}' + var varinfo = matchstr(msg, capture, 0, cnt) + + while varinfo != '' + var vardict = ParseVarinfo(varinfo) + setline(cnt + 1, vardict['type'] .. + repeat(' ', max([20 - len(vardict['type']), 1])) .. + vardict['name'] .. + repeat(' ', max([20 - len(vardict['name']), 1])) .. + vardict['value']) + cnt += 1 + varinfo = matchstr(msg, capture, 0, cnt) + endwhile + endif + win_gotoid(curwinid) +enddef + + +# Handle a message received from gdb on the GDB/MI interface. +def CommOutput(chan: channel, message: string) + # We may use the standard MI message formats? See #10300 on github that mentions + # the following links: + # https://sourceware.org/gdb/current/onlinedocs/gdb.html/GDB_002fMI-Input-Syntax.html#GDB_002fMI-Input-Syntax + # https://sourceware.org/gdb/current/onlinedocs/gdb.html/GDB_002fMI-Output-Syntax.html#GDB_002fMI-Output-Syntax + + var msgs = split(message, "\r") + + var msg = '' + for received_msg in msgs + # remove prefixed NL + if received_msg[0] == "\n" + msg = received_msg[1 : ] + else + msg = received_msg + endif + + if parsing_disasm_msg > 0 + HandleDisasmMsg(msg) + elseif msg != '' + if msg =~ '^\(\*stopped\|\*running\|=thread-selected\)' + HandleCursor(msg) + elseif msg =~ '^\^done,bkpt=' || msg =~ '^=breakpoint-created,' + HandleNewBreakpoint(msg, false) + elseif msg =~ '^=breakpoint-modified,' + HandleNewBreakpoint(msg, true) + elseif msg =~ '^=breakpoint-deleted,' + HandleBreakpointDelete(msg) + elseif msg =~ '^=thread-group-started' + HandleProgramRun(msg) + elseif msg =~ '^\^done,value=' + HandleEvaluate(msg) + elseif msg =~ '^\^error,msg=' + HandleError(msg) + elseif msg =~ '^&"disassemble' + parsing_disasm_msg = 1 + asm_lines = [] + HandleDisasmMsg(msg) + elseif msg =~ '^\^done,variables=' + HandleVariablesMsg(msg) + endif + endif + endfor +enddef + +def GotoProgram() + if has('win32') && !ptywin + if executable('powershell') + system(printf('powershell -Command "add-type -AssemblyName microsoft.VisualBasic;[Microsoft.VisualBasic.Interaction]::AppActivate(%d);"', pid)) + endif + else + win_gotoid(ptywin) + endif +enddef + +# Install commands in the current window to control the debugger. +def InstallCommands() + + command -nargs=? Break SetBreakpoint(<q-args>) + command -nargs=? Tbreak SetBreakpoint(<q-args>, true) + command ToggleBreak ToggleBreak() + command Clear ClearBreakpoint() + command Step SendResumingCommand('-exec-step') + command Over SendResumingCommand('-exec-next') + command -nargs=? Until Until(<q-args>) + command Finish SendResumingCommand('-exec-finish') + command -nargs=* Run Run(<q-args>) + command -nargs=* Arguments SendResumingCommand('-exec-arguments ' .. <q-args>) + command Stop StopCommand() + command Continue ContinueCommand() + command RunOrContinue RunOrContinue() + + command -nargs=* Frame Frame(<q-args>) + command -count=1 Up Up(<count>) + command -count=1 Down Down(<count>) + + command -range -nargs=* Evaluate Evaluate(<range>, <q-args>) + command Gdb win_gotoid(gdbwin) + command Program GotoProgram() + command Source GotoSourcewinOrCreateIt() + command Asm GotoAsmwinOrCreateIt() + command Var GotoVariableswinOrCreateIt() + command Winbar InstallWinbar(true) + + var map = true + if exists('g:termdebug_config') + map = get(g:termdebug_config, 'map_K', true) + elseif exists('g:termdebug_map_K') + map = g:termdebug_map_K + endif + + if map + if !empty(saved_K_map) && !saved_K_map.buffer || empty(saved_K_map) + nnoremap K :Evaluate<CR> + endif + if !empty(saved_visual_K_map) && !saved_visual_K_map.buffer || empty(saved_visual_K_map) + xnoremap K :Evaluate<CR> + endif + endif + + map = true + if exists('g:termdebug_config') + map = get(g:termdebug_config, 'map_plus', true) + endif + if map + if !empty(saved_plus_map) && !saved_plus_map.buffer || empty(saved_plus_map) + nnoremap <expr> + $'<Cmd>{v:count1}Up<CR>' + endif + endif + + map = true + if exists('g:termdebug_config') + map = get(g:termdebug_config, 'map_minus', true) + endif + if map + if !empty(saved_minus_map) && !saved_minus_map.buffer || empty(saved_minus_map) + nnoremap <expr> - $'<Cmd>{v:count1}Down<CR>' + endif + endif + + + if has('menu') && &mouse != '' + InstallWinbar(false) + + var pup = true + if exists('g:termdebug_config') + pup = get(g:termdebug_config, 'popup', true) + elseif exists('g:termdebug_popup') + pup = g:termdebug_popup + endif + + if pup + &mousemodel = 'popup_setpos' + an 1.200 PopUp.-SEP3- <Nop> + an 1.210 PopUp.Set\ breakpoint <cmd>Break<CR> + an 1.220 PopUp.Clear\ breakpoint <cmd>Clear<CR> + an 1.230 PopUp.Run\ until <cmd>Until<CR> + an 1.240 PopUp.Evaluate <cmd>Evaluate<CR> + endif + endif + +enddef + +# Install the window toolbar in the current window. +def InstallWinbar(force: bool) + # install the window toolbar by default, can be disabled in the config + var winbar = true + if exists('g:termdebug_config') + winbar = get(g:termdebug_config, 'winbar', true) + endif + + if has('menu') && &mouse != '' && (winbar || force) + nnoremenu WinBar.Step :Step<CR> + nnoremenu WinBar.Next :Over<CR> + nnoremenu WinBar.Finish :Finish<CR> + nnoremenu WinBar.Cont :Continue<CR> + nnoremenu WinBar.Stop :Stop<CR> + nnoremenu WinBar.Eval :Evaluate<CR> + add(winbar_winids, win_getid()) + endif +enddef + +# Delete installed debugger commands in the current window. +def DeleteCommands() + delcommand Break + delcommand Tbreak + delcommand Clear + delcommand Step + delcommand Over + delcommand Until + delcommand Finish + delcommand Run + delcommand Arguments + delcommand Stop + delcommand Continue + delcommand Frame + delcommand Up + delcommand Down + delcommand Evaluate + delcommand Gdb + delcommand Program + delcommand Source + delcommand Asm + delcommand Var + delcommand Winbar + delcommand RunOrContinue + delcommand ToggleBreak + + + if !empty(saved_K_map) && !saved_K_map.buffer + mapset(saved_K_map) + elseif empty(saved_K_map) + silent! nunmap K + endif + + if !empty(saved_visual_K_map) && !saved_visual_K_map.buffer + mapset(saved_visual_K_map) + elseif empty(saved_visual_K_map) + silent! xunmap K + endif + + if !empty(saved_plus_map) && !saved_plus_map.buffer + mapset(saved_plus_map) + elseif empty(saved_plus_map) + silent! nunmap + + endif + + if !empty(saved_minus_map) && !saved_minus_map.buffer + mapset(saved_minus_map) + elseif empty(saved_minus_map) + silent! nunmap - + endif + + + if has('menu') + # Remove the WinBar entries from all windows where it was added. + var curwinid = win_getid() + for winid in winbar_winids + if win_gotoid(winid) + aunmenu WinBar.Step + aunmenu WinBar.Next + aunmenu WinBar.Finish + aunmenu WinBar.Cont + aunmenu WinBar.Stop + aunmenu WinBar.Eval + endif + endfor + win_gotoid(curwinid) + + &mousemodel = saved_mousemodel + try + aunmenu PopUp.-SEP3- + aunmenu PopUp.Set\ breakpoint + aunmenu PopUp.Clear\ breakpoint + aunmenu PopUp.Run\ until + aunmenu PopUp.Evaluate + catch + # ignore any errors in removing the PopUp menu + endtry + endif + + sign_unplace('TermDebug') + sign_undefine('debugPC') + sign_undefine(BreakpointSigns->map("'debugBreakpoint' .. v:val")) +enddef + +def QuoteArg(x: string): string + # Find all the occurrences of " and \ and escape them and double quote + # the resulting string. + return printf('"%s"', x ->substitute('[\\"]', '\\&', 'g')) +enddef + +def DefaultBreakpointLocation(): string + # Use the fname:lnum format, older gdb can't handle --source. + var fname = Remote2LocalPath(expand('%:p')) + return QuoteArg($"{fname}:{line('.')}") +enddef + +def TokenizeBreakpointArguments(args: string): list<dict<any>> + var tokens: list<dict<any>> = [] + var start = -1 + var escaped = false + var in_quotes = false + + var i = 0 + for ch in args + if start < 0 && ch !~ '\s' + start = i + endif + if start >= 0 + if escaped + escaped = false + elseif ch == '\' + escaped = true + elseif ch == '"' + in_quotes = !in_quotes + elseif !in_quotes && ch =~ '\s' + tokens->add({text: args[start : i - 1], start: start, end: i - 1}) + start = -1 + endif + endif + i += 1 + endfor + + if start >= 0 + tokens->add({text: args[start :], start: start, end: i - 1}) + endif + return tokens +enddef + +def BuildBreakpointCommand(at: string, tbreak=false): string + var args = trim(at) + var cmd = '-break-insert' + if tbreak + cmd ..= ' -t' + endif + + if empty(args) + return $'{cmd} {DefaultBreakpointLocation()}' + endif + + var condition = '' + var prefix = args + for token in TokenizeBreakpointArguments(args) + if token.text == 'if' && token.end < strchars(args) - 1 + condition = trim(args[token.end + 1 :]) + prefix = token.start > 0 ? trim(args[: token.start - 1]) : '' + break + endif + endfor + + var prefix_tokens = TokenizeBreakpointArguments(prefix) + var location = prefix + var thread = '' + if len(prefix_tokens) >= 2 + && prefix_tokens[-2].text == 'thread' + && prefix_tokens[-1].text =~ '^\d\+$' + thread = prefix_tokens[-1].text + location = join(prefix_tokens[: -3]->mapnew('v:val.text'), ' ') + endif + + if empty(trim(location)) + location = DefaultBreakpointLocation() + endif + if !empty(thread) + cmd ..= $' -p {thread}' + endif + if !empty(condition) + cmd ..= $' -c {QuoteArg(condition)}' + endif + return $'{cmd} {trim(location)}' +enddef + +# :Until - Execute until past a specified position or current line +def Until(at: string) + + if stopped + # reset stopped here, it may take a bit of time before we get a response + stopped = false + ch_log('assume that program is running after this command') + + # Use the fname:lnum format + var fname = Remote2LocalPath(expand('%:p')) + var AT = empty(at) ? QuoteArg($"{fname}:{line('.')}") : at + SendCommand($'-exec-until {AT}') + else + ch_log('dropping command, program is running: exec-until') + endif +enddef + +# :Break - Set a breakpoint at the cursor position. +def SetBreakpoint(at: string, tbreak=false) + # Setting a breakpoint may not work while the program is running. + # Interrupt to make it work. + var do_continue = false + if !stopped + do_continue = true + StopCommand() + sleep 10m + endif + + var cmd = BuildBreakpointCommand(at, tbreak) + SendCommand(cmd) + if do_continue + ContinueCommand() + endif +enddef + +def ClearBreakpoint() + var fname = Remote2LocalPath(expand('%:p')) + fname = fnameescape(fname) + var lnum = line('.') + var bploc = printf('%s:%d', fname, lnum) + var nr = 0 + if has_key(breakpoint_locations, bploc) + var idx = 0 + for id in breakpoint_locations[bploc] + if has_key(breakpoints, id) + # Assume this always works, the reply is simply "^done". + SendCommand($'-break-delete {id}') + for subid in keys(breakpoints[id]) + sign_unplace('TermDebug', + {id: Breakpoint2SignNumber(id, str2nr(subid))}) + endfor + remove(breakpoints, id) + remove(breakpoint_locations[bploc], idx) + nr = id + break + else + idx += 1 + endif + endfor + + if nr != 0 + if empty(breakpoint_locations[bploc]) + remove(breakpoint_locations, bploc) + endif + echomsg $'Breakpoint {nr} cleared from line {lnum}.' + else + Echoerr($'Internal error trying to remove breakpoint at line {lnum}!') + endif + else + echomsg $'No breakpoint to remove at line {lnum}.' + endif +enddef + +def ToggleBreak() + var fname = Remote2LocalPath(expand('%:p')) + fname = fnameescape(fname) + var lnum = line('.') + var bploc = printf('%s:%d', fname, lnum) + if has_key(breakpoint_locations, bploc) + while has_key(breakpoint_locations, bploc) + ClearBreakpoint() + endwhile + else + SetBreakpoint("") + endif +enddef + +def Run(args: string) + if args != '' + SendResumingCommand($'-exec-arguments {args}') + endif + SendResumingCommand('-exec-run') +enddef + +def RunOrContinue() + if running + ContinueCommand() + else + Run('') + endif +enddef + +# :Frame - go to a specific frame in the stack +def Frame(arg: string) + # Note: we explicit do not use mi's command + # call SendCommand('-stack-select-frame "' . arg .'"') + # as we only get a "done" mi response and would have to open the file + # 'manually' - using cli command "frame" provides us with the mi response + # already parsed and allows for more formats + if arg =~ '^\d\+$' || arg == '' + # specify frame by number + SendCommand($'-interpreter-exec mi "frame {arg}"') + elseif arg =~ '^0x[0-9a-fA-F]\+$' + # specify frame by stack address + SendCommand($'-interpreter-exec mi "frame address {arg}"') + else + # specify frame by function name + SendCommand($'-interpreter-exec mi "frame function {arg}"') + endif +enddef + +# :Up - go count frames in the stack "higher" +def Up(count: number) + # the 'correct' one would be -stack-select-frame N, but we don't know N + SendCommand($'-interpreter-exec console "up {count}"') +enddef + +# :Down - go count frames in the stack "below" +def Down(count: number) + # the 'correct' one would be -stack-select-frame N, but we don't know N + SendCommand($'-interpreter-exec console "down {count}"') +enddef + +def SendEval(expr: string) + # check for "likely" boolean expressions, in which case we take it as lhs + var exprLHS = substitute(expr, ' *=.*', '', '') + if expr =~ "[=!<>]=" + exprLHS = expr + endif + + # encoding expression to prevent bad errors + var expr_escaped = expr + ->substitute('\\', '\\\\', 'g') + ->substitute('"', '\\"', 'g') + SendCommand($'-data-evaluate-expression "{expr_escaped}"') + evalexpr = exprLHS +enddef + +# Returns whether to evaluate in a popup or not, defaults to false. +def EvaluateInPopup(): bool + if exists('g:termdebug_config') + return get(g:termdebug_config, 'evaluate_in_popup', false) + endif + return false +enddef + +# :Evaluate - evaluate what is specified / under the cursor +def Evaluate(range: number, arg: string) + var expr = GetEvaluationExpression(range, arg) + if EvaluateInPopup() + evalInPopup = true + evalExprResult = '' + else + echomsg $'expr: {expr}' + endif + ignoreEvalError = false + SendEval(expr) +enddef + + +# get what is specified / under the cursor +def GetEvaluationExpression(range: number, arg: string): string + var expr = '' + if arg != '' + # user supplied evaluation + expr = CleanupExpr(arg) + expr = substitute(expr, '"\([^"]*\)": *', '\1=', 'g') + elseif range == 2 + # no evaluation but provided but range set + var pos = getcurpos() + var regst = getreg('v', 1, 1) + var regt = getregtype('v') + normal! gv"vy + expr = CleanupExpr(@v) + setpos('.', pos) + setreg('v', regst, regt) + else + # no evaluation provided: get from C-expression under cursor + # TODO: allow filetype specific lookup #9057 + expr = expand('<cexpr>') + endif + return expr +enddef + +# clean up expression that may get in because of range +# (newlines and surrounding whitespace) +# As it can also be specified via ex-command for assignments this function +# may not change the "content" parts (like replacing contained spaces) +def CleanupExpr(passed_expr: string): string + # replace all embedded newlines/tabs/... + var expr = substitute(passed_expr, '\_s', ' ', 'g') + + if &filetype ==# 'cobol' + # extra cleanup for COBOL: + # - a semicolon nmay be used instead of a space + # - a trailing comma or period is ignored as it commonly separates/ends + # multiple expr + expr = substitute(expr, ';', ' ', 'g') + expr = substitute(expr, '[,.]\+ *$', '', '') + endif + + # get rid of leading and trailing spaces + expr = substitute(expr, '^ *', '', '') + expr = substitute(expr, ' *$', '', '') + return expr +enddef + +def Balloon_show(expr: string) + if has("balloon_eval") || has("balloon_eval_term") + balloon_show(expr) + endif +enddef + +def Popup_format(expr: string): list<string> + var lines = expr + ->substitute('{', '{\n', 'g') + ->substitute('}', '\n}', 'g') + ->substitute(',', ',\n', 'g') + ->split('\n') + var indentation = 0 + var formatted_lines = [] + for line in lines + var stripped = line->substitute('^\s\+', '', '') + if stripped =~ '^}' + indentation -= 2 + endif + formatted_lines->add(repeat(' ', indentation) .. stripped) + if stripped =~ '{$' + indentation += 2 + endif + endfor + return formatted_lines +enddef + +def Popup_show(expr: string) + var formatted = Popup_format(expr) + if evalPopupId != -1 + popup_close(evalPopupId) + endif + # Specifying the line is necessary, as the winbar seems to cause issues + # otherwise. I.e., the popup would be shown one line too high. + evalPopupId = popup_atcursor(formatted, {'line': 'cursor-1'}) +enddef + +def HandleEvaluate(msg: string) + var value = msg + ->substitute('.*value="\(.*\)"', '\1', '') + ->substitute('\\"', '"', 'g') + ->substitute('\\\\', '\\', 'g') + #\ multi-byte characters arrive in octal form, replace everything but NULL values + ->substitute('\\000', NullRepl, 'g') + ->substitute('\\\(\o\o\o\)', (m) => nr2char(str2nr(m[1], 8)), 'g') + #\ Note: GDB docs also mention hex encodings - the translations below work + #\ but we keep them out for performance-reasons until we actually see + #\ those in mi-returns + #\ ->substitute('\\0x00', NullRep, 'g') + #\ ->substitute('\\0x\(\x\x\)', {-> eval('"\x' .. submatch(1) .. '"')}, 'g') + ->substitute(NullRepl, '\\000', 'g') + if evalFromBalloonExpr || evalInPopup + if empty(evalExprResult) + evalExprResult = $'{evalexpr}: {value}' + else + evalExprResult ..= $' = {value}' + endif + else + echomsg $'"{evalexpr}": {value}' + endif + + if evalexpr[0] != '*' && value =~ '^0x' && value != '0x0' && value !~ '"$' + # Looks like a pointer, also display what it points to. + ignoreEvalError = true + SendEval($'*{evalexpr}') + elseif evalFromBalloonExpr + Balloon_show(evalExprResult) + evalFromBalloonExpr = false + elseif evalInPopup + Popup_show(evalExprResult) + evalInPopup = false + endif +enddef + + +# Show a balloon with information of the variable under the mouse pointer, +# if there is any. +def TermDebugBalloonExpr(): string + if v:beval_winid != sourcewin + return '' + endif + if !stopped + # Only evaluate when stopped, otherwise setting a breakpoint using the + # mouse triggers a balloon. + return '' + endif + evalFromBalloonExpr = true + evalExprResult = '' + ignoreEvalError = true + var expr = CleanupExpr(v:beval_text) + SendEval(expr) + return '' +enddef + +# Handle an error. +def HandleError(msg: string) + if ignoreEvalError + # Result of SendEval() failed, ignore. + ignoreEvalError = false + evalFromBalloonExpr = true + return + endif + var msgVal = substitute(msg, '.*msg="\(.*\)"', '\1', '') + Echoerr(substitute(msgVal, '\\"', '"', 'g')) +enddef + +def GotoSourcewinOrCreateIt() + if !win_gotoid(sourcewin) + new + sourcewin = win_getid() + InstallWinbar(false) + endif +enddef + + +def GetDisasmWindow(): bool + # TODO Remove the deprecated features after 1 Jan 2025. + var val: any + if exists('g:termdebug_config') && has_key(g:termdebug_config, 'disasm_window') + val = g:termdebug_config['disasm_window'] + elseif exists('g:termdebug_disasm_window') + val = g:termdebug_disasm_window + else + val = false + endif + return typename(val) == 'number' ? val != 0 : val +enddef + +def GetDisasmWindowHeight(): number + if exists('g:termdebug_config') + return get(g:termdebug_config, 'disasm_window_height', 0) + endif + if exists('g:termdebug_disasm_window') && g:termdebug_disasm_window > 1 + return g:termdebug_disasm_window + endif + return 0 +enddef + +def GotoAsmwinOrCreateIt() + var mdf = '' + if !win_gotoid(asmwin) + if win_gotoid(sourcewin) + # 60 is approx spaceBuffer * 3 + if winwidth(0) > (78 + 60) + mdf = 'vert' + exe $'{mdf} :60new' + else + exe 'rightbelow new' + endif + else + exe 'new' + endif + + asmwin = win_getid() + + setlocal nowrap + setlocal number + setlocal noswapfile + setlocal buftype=nofile + setlocal bufhidden=wipe + setlocal signcolumn=no + setlocal modifiable + + if asmbufnr > 0 && bufexists(asmbufnr) + exe $'buffer {asmbufnr}' + else + exe $"silent file {asmbufname}" + asmbufnr = bufnr(asmbufname) + endif + + if mdf != 'vert' && GetDisasmWindowHeight() > 0 + exe $'resize {GetDisasmWindowHeight()}' + endif + endif + + if asm_addr != '' + var lnum = search($'^{asm_addr}') + if lnum == 0 + if stopped + SendCommand('disassemble $pc') + endif + else + sign_unplace('TermDebug', {id: asm_id}) + sign_place(asm_id, 'TermDebug', 'debugPC', '%', {lnum: lnum}) + endif + endif +enddef + +def GetVariablesWindow(): bool + # TODO Remove the deprecated features after 1 Jan 2025. + var val: any + if exists('g:termdebug_config') && has_key(g:termdebug_config, 'variables_window') + val = g:termdebug_config['variables_window'] + elseif exists('g:termdebug_variables_window') + val = g:termdebug_variables_window + else + val = false + endif + return typename(val) == 'number' ? val != 0 : val +enddef + +def GetVariablesWindowHeight(): number + if exists('g:termdebug_config') + return get(g:termdebug_config, 'variables_window_height', 0) + endif + if exists('g:termdebug_variables_window') && g:termdebug_variables_window > 1 + return g:termdebug_variables_window + endif + return 0 +enddef + + +def GotoVariableswinOrCreateIt() + var mdf = '' + if !win_gotoid(varwin) + if win_gotoid(sourcewin) + # 60 is approx spaceBuffer * 3 + if winwidth(0) > (78 + 60) + mdf = 'vert' + exe $'{mdf} :60new' + else + exe 'rightbelow new' + endif + else + exe 'new' + endif + + varwin = win_getid() + + setlocal nowrap + setlocal noswapfile + setlocal buftype=nofile + setlocal bufhidden=wipe + setlocal signcolumn=no + setlocal modifiable + + # If exists, then open, otherwise create + if varbufnr > 0 && bufexists(varbufnr) + exe $'buffer {varbufnr}' + else + exe $"silent file {varbufname}" + varbufnr = bufnr(varbufname) + endif + + if mdf != 'vert' && GetVariablesWindowHeight() > 0 + exe $'resize {GetVariablesWindowHeight()}' + endif + endif + + if running + SendCommand('-stack-list-variables 2') + endif +enddef + +# Handle stopping and running message from gdb. +# Will update the sign that shows the current position. +def HandleCursor(msg: string) + var wid = win_getid() + + if msg =~ '^\*stopped' + ch_log('program stopped') + stopped = true + if msg =~ '^\*stopped,reason="exited-normally"' + running = false + endif + elseif msg =~ '^\*running' + ch_log('program running') + stopped = false + running = true + endif + + var fname = '' + if msg =~ 'fullname=' + fname = GetLocalFullname(msg) + endif + + if msg =~ 'addr=' + var asm_addr_local = GetAsmAddr(msg) + if asm_addr_local != '' + asm_addr = asm_addr_local + + var curwinid = win_getid() + var lnum = 0 + if win_gotoid(asmwin) + lnum = search($'^{asm_addr}') + if lnum == 0 + SendCommand('disassemble $pc') + else + sign_unplace('TermDebug', {id: asm_id}) + sign_place(asm_id, 'TermDebug', 'debugPC', '%', {lnum: lnum}) + endif + + win_gotoid(curwinid) + endif + endif + endif + + if running && stopped && bufwinnr(varbufname) != -1 + SendCommand('-stack-list-variables 2') + endif + + # Translate to remote file name if needed. + const fremote = Local2RemotePath(fname) + + if msg =~ '^\(\*stopped\|=thread-selected\)' && (fremote != fname || filereadable(fname)) + var lnum = substitute(msg, '.*line="\([^"]*\)".*', '\1', '') + if lnum =~ '^[0-9]*$' + GotoSourcewinOrCreateIt() + if expand('%:p') != fnamemodify(fremote, ':p') + echomsg $"different fname: '{expand('%:p')}' vs '{fnamemodify(fremote, ':p')}'" + augroup Termdebug + # Always open a file read-only instead of showing the ATTENTION + # prompt, since it is unlikely we want to edit the file. + # The file may be changed but not saved, warn for that. + au SwapExists * echohl WarningMsg + | echo 'Warning: file is being edited elsewhere' + | echohl None + | v:swapchoice = 'o' + augroup END + if &modified + # TODO: find existing window + exe $'split {fnameescape(fremote)}' + sourcewin = win_getid() + InstallWinbar(false) + else + exe $'edit {fnameescape(fremote)}' + endif + augroup Termdebug + au! SwapExists + augroup END + endif + exe $":{lnum}" + normal! zv + sign_unplace('TermDebug', {id: pc_id}) + sign_place(pc_id, 'TermDebug', 'debugPC', fremote, + {lnum: str2nr(lnum), priority: 110}) + if !exists('b:save_signcolumn') + b:save_signcolumn = &signcolumn + add(signcolumn_buflist, bufnr()) + endif + setlocal signcolumn=yes + endif + elseif !stopped || fname != '' + sign_unplace('TermDebug', {id: pc_id}) + endif + + win_gotoid(wid) +enddef + +# Create breakpoint sign +def CreateBreakpoint(id: number, subid: number, enabled: string) + var nr = printf('%d.%d', id, subid) + if index(BreakpointSigns, nr) == -1 + add(BreakpointSigns, nr) + var hiName = '' + if enabled == "n" + hiName = "debugBreakpointDisabled" + else + hiName = "debugBreakpoint" + endif + var label = '' + if exists('g:termdebug_config') + if has_key(g:termdebug_config, 'signs') + label = get(g:termdebug_config.signs, id - 1, '') + endif + if label == '' && has_key(g:termdebug_config, 'sign') + label = g:termdebug_config['sign'] + endif + if label == '' && has_key(g:termdebug_config, 'sign_decimal') + label = printf('%02d', id) + if id > 99 + label = '9+' + endif + endif + endif + if label == '' + label = printf('%02X', id) + if id > 255 + label = 'F+' + endif + endif + sign_define($'debugBreakpoint{nr}', + {text: slice(label, 0, 2), + texthl: hiName}) + endif +enddef + +def SplitMsg(str: string): list<string> + return split(str, '{.\{-}}\zs') +enddef + + +# Handle setting a breakpoint +# Will update the sign that shows the breakpoint +def HandleNewBreakpoint(msg: string, modifiedFlag: bool) + var nr = '' + + if msg !~ 'fullname=' + # a watch or a pending breakpoint does not have a file name + if msg =~ 'pending=' + nr = substitute(msg, '.*number=\"\([0-9.]*\)\".*', '\1', '') + var target = substitute(msg, '.*pending=\"\([^"]*\)\".*', '\1', '') + echomsg $'Breakpoint {nr} ({target}) pending.' + endif + return + endif + + for mm in SplitMsg(msg) + var fname = GetLocalFullname(mm) + if empty(fname) + continue + endif + var fremote = Local2RemotePath(fname) + nr = substitute(mm, '.*number="\([0-9.]*\)\".*', '\1', '') + if empty(nr) + return + endif + + # If "nr" is 123 it becomes "123.0" and subid is "0". + # If "nr" is 123.4 it becomes "123.4.0" and subid is "4"; "0" is discarded. + var [id, subid; _] = map(split(nr .. '.0', '\.'), 'str2nr(v:val) + 0') + # var [id, subid; _] = map(split(nr .. '.0', '\.'), 'v:val + 0') + var enabled = substitute(mm, '.*enabled="\([yn]\)".*', '\1', '') + CreateBreakpoint(id, subid, enabled) + + var entries = {} + var entry = {} + if has_key(breakpoints, id) + entries = breakpoints[id] + else + breakpoints[id] = entries + endif + if has_key(entries, subid) + entry = entries[subid] + else + entries[subid] = entry + endif + + var lnum = str2nr(substitute(mm, '.*line="\([^"]*\)".*', '\1', '')) + entry['fname'] = fname + entry['lnum'] = lnum + + var bploc = printf('%s:%d', fname, lnum) + if !has_key(breakpoint_locations, bploc) + breakpoint_locations[bploc] = [] + endif + if breakpoint_locations[bploc]->index(id) == -1 + # Make sure all ids are unique + breakpoint_locations[bploc] += [id] + endif + + var posMsg = '' + if bufloaded(fremote) + PlaceSign(id, subid, entry) + posMsg = $' at line {lnum}.' + else + posMsg = $' in {fremote} at line {lnum}.' + endif + var actionTaken = '' + if !modifiedFlag + actionTaken = 'created' + elseif enabled == 'n' + actionTaken = 'disabled' + else + actionTaken = 'enabled' + endif + echom $'Breakpoint {nr} {actionTaken}{posMsg}' + endfor +enddef + + +def PlaceSign(id: number, subid: number, entry: dict<any>) + var nr = printf('%d.%d', id, subid) + var remote = Local2RemotePath(entry['fname']) + sign_place(Breakpoint2SignNumber(id, subid), 'TermDebug', + $'debugBreakpoint{nr}', remote, + {lnum: entry['lnum'], priority: 110}) + entry['placed'] = 1 +enddef + +# Handle deleting a breakpoint +# Will remove the sign that shows the breakpoint +def HandleBreakpointDelete(msg: string) + var id = substitute(msg, '.*id="\([0-9]*\)\".*', '\1', '') + if empty(id) + return + endif + if has_key(breakpoints, id) + for [subid, entry] in items(breakpoints[id]) + if has_key(entry, 'placed') + sign_unplace('TermDebug', + {id: Breakpoint2SignNumber(str2nr(id), str2nr(subid))}) + remove(entry, 'placed') + endif + endfor + remove(breakpoints, id) + echomsg $'Breakpoint {id} cleared.' + endif +enddef + +# Handle the debugged program starting to run. +# Will store the process ID in pid +def HandleProgramRun(msg: string) + var nr = str2nr(substitute(msg, '.*pid="\([0-9]*\)\".*', '\1', '')) + if nr == 0 + return + endif + pid = nr + ch_log($'Detected process ID: {pid}') +enddef + +# Handle a BufRead autocommand event: place any signs. +def BufRead() + var fname = expand('<afile>:p') + for [id, entries] in items(breakpoints) + for [subid, entry] in items(entries) + if entry['fname'] == fname + PlaceSign(str2nr(id), str2nr(subid), entry) + endif + endfor + endfor +enddef + +# Handle a BufUnloaded autocommand event: unplace any signs. +def BufUnloaded() + var fname = expand('<afile>:p') + for [id, entries] in items(breakpoints) + for [subid, entry] in items(entries) + if entry['fname'] == fname + entry['placed'] = 0 + endif + endfor + endfor +enddef + +InitHighlight() +InitAutocmd() + +# vim: sw=2 sts=2 et |
