comparison runtime/plugin/explorer.vim @ 7:3fc0f57ecb91 v7.0001

updated for version 7.0001
author vimboss
date Sun, 13 Jun 2004 20:20:40 +0000
parents
children 4e2284e71352
comparison
equal deleted inserted replaced
6:c2daee826b8f 7:3fc0f57ecb91
1 "=============================================================================
2 " File: explorer.vim
3 " Author: M A Aziz Ahmed (aziz@acorn-networks.com - doesn't work)
4 " Last Change: 2004 May 13
5 " Version: 2.5 + changes
6 " Additions by Mark Waggoner (waggoner@aracnet.com) et al.
7 "-----------------------------------------------------------------------------
8 " This file implements a file explorer.
9 "-----------------------------------------------------------------------------
10 " Normally, this file will reside in the plugins directory and be
11 " automatically sourced. If not, you must manually source this file
12 " using :source explorer.vim
13 "
14 " To use it, just edit a directory (vi dirname) or type :Explore to
15 " launch the file explorer in the current window, or :Sexplore to split
16 " the current window and launch explorer there.
17 "
18 " If the current buffer is modified, the window is always split.
19 "
20 " It is also possible to delete files and rename files within explorer.
21 " See :help file-explorer for more details
22 "
23 "-----------------------------------------------------------------------------
24 " Update history removed, it's not very interesting.
25 " Contributors were: Doug Potts, Bram Moolenaar, Thomas Köhler
26 "=============================================================================
27
28 " Has this already been loaded?
29 if exists("loaded_explorer")
30 finish
31 endif
32 let loaded_explorer=1
33
34 " Line continuation used here
35 let s:cpo_save = &cpo
36 set cpo&vim
37
38 "---
39 " Default settings for global configuration variables
40
41 " Split vertically instead of horizontally?
42 if !exists("g:explVertical")
43 let g:explVertical=0
44 endif
45
46 " How big to make the window? Set to "" to avoid resizing
47 if !exists("g:explWinSize")
48 let g:explWinSize=15
49 endif
50
51 " When opening a new file/directory, split below current window (or
52 " above)? 1 = below, 0 = to above
53 if !exists("g:explSplitBelow")
54 let g:explSplitBelow = &splitbelow
55 endif
56
57 " Split to right of current window (or to left)?
58 " 1 = to right, 0 = to left
59 if !exists("g:explSplitRight")
60 let g:explSplitRight = &splitright
61 endif
62
63 " Start the first explorer window...
64 " Defaults to be the same as explSplitBelow
65 if !exists("g:explStartBelow")
66 let g:explStartBelow = g:explSplitBelow
67 endif
68
69 " Start the first explorer window...
70 " Defaults to be the same as explSplitRight
71 if !exists("g:explStartRight")
72 let g:explStartRight = g:explSplitRight
73 endif
74
75 " Show detailed help?
76 if !exists("g:explDetailedHelp")
77 let g:explDetailedHelp=0
78 endif
79
80 " Show file size and dates?
81 if !exists("g:explDetailedList")
82 let g:explDetailedList=0
83 endif
84
85 " Format for the date
86 if !exists("g:explDateFormat")
87 let g:explDateFormat="%d %b %Y %H:%M"
88 endif
89
90 " Files to hide
91 if !exists("g:explHideFiles")
92 let g:explHideFiles=''
93 endif
94
95 " Field to sort by
96 if !exists("g:explSortBy")
97 let g:explSortBy='name'
98 endif
99
100 " Segregate directories? 1, 0, or -1
101 if !exists("g:explDirsFirst")
102 let g:explDirsFirst=1
103 endif
104
105 " Segregate items in suffixes option? 1, 0, or -1
106 if !exists("g:explSuffixesLast")
107 let g:explSuffixesLast=1
108 endif
109
110 " Include separator lines between directories, files, and suffixes?
111 if !exists("g:explUseSeparators")
112 let g:explUseSeparators=0
113 endif
114
115 " Execute file handler
116 if !exists("g:explFileHandler")
117 if has("win32")
118 " for Win32 use rundll32
119 function! s:explFileHandlerWin32(fn)
120 exec 'silent !start rundll32 url.dll,FileProtocolHandler "'
121 \ . escape(a:fn, '%#') . '"'
122 endfunction
123 let g:explFileHandler = "<SID>explFileHandlerWin32"
124
125 elseif has("unix")
126 " for KDE use kfmclient, for GNUME use gnome-open
127 if executable("kfmclient")
128 let g:explFileHandlerCmd = "kfmclient exec"
129 elseif executable("gnome-open")
130 let g:explFileHandlerCmd = "gnome-open"
131 else
132 let g:explFileHandlerCmd = ""
133 endif
134 if g:explFileHandlerCmd != ""
135 function! s:explFileHandlerUnix(fn)
136 if &shellredir =~ "%s"
137 let redir = substitute(&shellredir, "%s", "/dev/null", "")
138 else
139 let redir = &shellredir . "/dev/null"
140 endif
141 " Need to escape % and # but not spaces.
142 exec "silent !" . g:explFileHandlerCmd . " '" . escape(a:fn, '%#') . "'" . redir
143 endfunction
144 let g:explFileHandler = "<SID>explFileHandlerUnix"
145 endif
146 endif
147 endif
148
149 "---
150 " script variables - these are the same across all
151 " explorer windows
152
153 " characters that must be escaped for a regular expression
154 let s:escregexp = '/*^$.~\'
155
156 " characters that must be escaped for filenames
157 if has("dos16") || has("dos32") || has("win16") || has("win32") || has("os2")
158 let s:escfilename = ' %#'
159 else
160 let s:escfilename = ' \%#[]'
161 endif
162
163
164 " A line to use for separating sections
165 let s:separator='"---------------------------------------------------'
166
167 "---
168 " Create commands
169
170 if !exists(':Explore')
171 command -n=? -complete=dir Explore :call s:StartExplorer(0, '<a>')
172 endif
173 if !exists(':Sexplore')
174 command -n=? -complete=dir Sexplore :call s:StartExplorer(1, '<a>')
175 endif
176
177 "---
178 " Start the explorer using the preferences from the global variables
179 "
180 function! s:StartExplorer(split, start_dir)
181 let startcmd = "edit"
182 if a:start_dir != ""
183 let fname=a:start_dir
184 else
185 let fname = expand("%:p:h")
186 endif
187 if fname == ""
188 let fname = getcwd()
189 endif
190
191 " Create a variable to use if splitting vertically
192 let splitMode = ""
193 if g:explVertical == 1
194 let splitMode = "vertical"
195 endif
196
197 " Save the user's settings for splitbelow and splitright
198 let savesplitbelow = &splitbelow
199 let savesplitright = &splitright
200
201 if a:split || &modified
202 let startcmd = splitMode . " " . g:explWinSize . "new " . fname
203 let &splitbelow = g:explStartBelow
204 let &splitright = g:explStartRight
205 else
206 let startcmd = "edit " . fname
207 endif
208 silent execute startcmd
209 let &splitbelow = savesplitbelow
210 let &splitright = savesplitright
211 endfunction
212
213 "---
214 " This is the main entry for 'editing' a directory
215 "
216 function! s:EditDir()
217 " Get out of here right away if this isn't a directory!
218 let name = expand("%")
219 if name == ""
220 let name = expand("%:p")
221 endif
222 if !isdirectory(name)
223 return
224 endif
225
226 " Turn off the swapfile, set the buffer type so that it won't get
227 " written, and so that it will get deleted when it gets hidden.
228 setlocal noreadonly modifiable
229 setlocal noswapfile
230 setlocal buftype=nowrite
231 setlocal bufhidden=delete
232 " Don't wrap around long lines
233 setlocal nowrap
234
235 " No need for any insertmode abbreviations, since we don't allow
236 " insertions anyway!
237 iabc <buffer>
238
239 " Long or short listing? Use the global variable the first time
240 " explorer is called, after that use the script variable as set by
241 " the interactive user.
242 if exists("s:longlist")
243 let w:longlist = s:longlist
244 else
245 let w:longlist = g:explDetailedList
246 endif
247
248 " Show keyboard shortcuts?
249 if exists("s:longhelp")
250 let w:longhelp = s:longhelp
251 else
252 let w:longhelp = g:explDetailedHelp
253 endif
254
255 " Set the sort based on the global variables the first time. If you
256 " later change the sort order, it will be retained in the s:sortby
257 " variable for the next time you open explorer
258 let w:sortdirection=1
259 let w:sortdirlabel = ""
260 let w:sorttype = ""
261 if exists("s:sortby")
262 let sortby=s:sortby
263 else
264 let sortby=g:explSortBy
265 endif
266 if sortby =~ "reverse"
267 let w:sortdirection=-1
268 let w:sortdirlabel = "reverse "
269 endif
270 if sortby =~ "date"
271 let w:sorttype = "date"
272 elseif sortby =~ "size"
273 let w:sorttype = "size"
274 else
275 let w:sorttype = "name"
276 endif
277 call s:SetSuffixesLast()
278
279 " If directory is already loaded, don't open it again!
280 if line('$') > 1
281 setlocal readonly nomodifiable
282 return
283 endif
284
285 " Get the complete path to the directory to look at with a slash at
286 " the end. This also removes "/../" and "/./" things.
287 let b:completePath = s:Path(expand("%:p"))
288
289 " Add a slash at the end
290 if b:completePath !~ '/$'
291 let b:completePath = b:completePath . '/'
292 endif
293
294 " escape special characters for exec commands
295 let b:completePathEsc = escape(b:completePath, s:escfilename)
296 let b:parentDirEsc = substitute(b:completePathEsc, '/[^/]*/$', '/', 'g')
297
298 " Set up syntax highlighting
299 " Something wrong with the evaluation of the conditional though...
300 if has("syntax") && exists("g:syntax_on") && !has("syntax_items")
301 syn match browseSynopsis "^\"[ -].*"
302 syn match browseDirectory "[^\"].*/ "
303 syn match browseDirectory "[^\"].*/$"
304 syn match browseCurDir "^\"= .*$"
305 syn match browseSortBy "^\" Sorted by .*$" contains=browseSuffixInfo
306 syn match browseSuffixInfo "(.*)$" contained
307 syn match browseFilter "^\" Not Showing:.*$"
308 syn match browseFiletime "««\d\+$"
309 exec('syn match browseSuffixes "' . b:suffixesHighlight . '"')
310
311 "hi def link browseSynopsis PreProc
312 hi def link browseSynopsis Special
313 hi def link browseDirectory Directory
314 hi def link browseCurDir Statement
315 hi def link browseSortBy String
316 hi def link browseSuffixInfo Type
317 hi def link browseFilter String
318 hi def link browseFiletime Ignore
319 hi def link browseSuffixes Type
320 endif
321
322 " Set filter for hiding files
323 let b:filterFormula=substitute(g:explHideFiles, '\([^\\]\),', '\1\\|', 'g')
324 if b:filterFormula != ''
325 let b:filtering="\nNot showing: " . b:filterFormula
326 else
327 let b:filtering=""
328 endif
329
330 " Show the files
331 call s:ShowDirectory()
332
333 " Set up mappings for this buffer
334 let cpo_save = &cpo
335 set cpo&vim
336 nnoremap <buffer> <cr> :call <SID>EditEntry("","edit")<cr>
337 nnoremap <buffer> - :exec ("silent e " . b:parentDirEsc)<cr>
338 if exists("g:explFileHandler")
339 nnoremap <buffer> x :call <SID>ExecuteEntry()<cr>
340 endif
341 nnoremap <buffer> o :call <SID>OpenEntry()<cr>
342 nnoremap <buffer> O :call <SID>OpenEntryPrevWindow()<cr>
343 nnoremap <buffer> p :call <SID>EditEntry("","pedit")<cr>
344 nnoremap <buffer> ? :call <SID>ToggleHelp()<cr>
345 nnoremap <buffer> a :call <SID>ShowAllFiles()<cr>
346 nnoremap <buffer> R :call <SID>RenameFile()<cr>
347 nnoremap <buffer> D :. call <SID>DeleteFile()<cr>
348 vnoremap <buffer> D :call <SID>DeleteFile()<cr>
349 nnoremap <buffer> i :call <SID>ToggleLongList()<cr>
350 nnoremap <buffer> s :call <SID>SortSelect()<cr>
351 nnoremap <buffer> r :call <SID>SortReverse()<cr>
352 nnoremap <buffer> c :exec "cd ".b:completePathEsc<cr>
353 nnoremap <buffer> <2-leftmouse> :call <SID>DoubleClick()<cr>
354 if exists("*ExplorerCustomMap")
355 call ExplorerCustomMap()
356 endif
357 let &cpo = cpo_save
358
359 " prevent the buffer from being modified
360 setlocal readonly nomodifiable
361 endfunction
362
363 "---
364 " Determine the number of windows open to this buffer number.
365 " Care of Yegappan Lakshman. Thanks!
366 fun! s:BufInWindows(bnum)
367 let cnt = 0
368 let winnum = 1
369 while 1
370 let bufnum = winbufnr(winnum)
371 if bufnum < 0
372 break
373 endif
374 if bufnum == a:bnum
375 let cnt = cnt + 1
376 endif
377 let winnum = winnum + 1
378 endwhile
379
380 return cnt
381 endfunction
382
383 " If this is the only window, open file in a new window
384 " Otherwise, open file in the most recently visited window
385 "
386 function! s:OpenEntryPrevWindow()
387 " Figure out if there are any other windows
388 let n = winnr()
389 wincmd p
390 " No other window? Then open a new one
391 if n == winnr()
392 call s:OpenEntry()
393 " Other windows exist
394 else
395 " Check if the previous buffer is modified - ask if they want to save!
396 " Was it modified, and is it the only window open to this file
397 if &modified && s:BufInWindows(winbufnr(winnr())) < 2
398 let bufname = bufname(winbufnr(winnr()))
399
400 let action=confirm("Save Changes in " . bufname . "?","&Yes\n&No\n&Cancel")
401 " Yes - try to save - if there is an error, cancel
402 if action == 1
403 let v:errmsg = ""
404 silent w
405 if v:errmsg != ""
406 echoerr "Unable to write buffer!"
407 wincmd p
408 return
409 endif
410 " No, abandon changes
411 elseif action == 2
412 set nomodified
413 echomsg "Warning, abandoning changes in " . bufname
414 " Cancel (or any other result), don't do the open
415 else
416 wincmd p
417 return
418 endif
419 endif
420 wincmd p
421 call s:EditEntry("wincmd p","edit")
422 endif
423 endfunction
424
425
426 "---
427 " Open a file or directory in a new window.
428 " Use g:explSplitBelow and g:explSplitRight to decide where to put the
429 " split window, and resize the original explorer window if it is
430 " larger than g:explWinSize
431 "
432 function! s:OpenEntry()
433 " Are we on a line with a file name?
434 let l = getline(".")
435 if l =~ '^"'
436 return
437 endif
438
439 " Copy window settings to script settings
440 let s:sortby=w:sortdirlabel . w:sorttype
441 let s:longhelp = w:longhelp
442 let s:longlist = w:longlist
443
444 " Get the window number of the explorer window
445 let n = winnr()
446
447 " Save the user's settings for splitbelow and splitright
448 let savesplitbelow=&splitbelow
449 let savesplitright=&splitright
450
451 " Figure out how to do the split based on the user's preferences.
452 " We want to split to the (left,right,top,bottom) of the explorer
453 " window, but we want to extract the screen real-estate from the
454 " window next to the explorer if possible.
455 "
456 " 'there' will be set to a command to move from the split window
457 " back to the explorer window
458 "
459 " 'back' will be set to a command to move from the explorer window
460 " back to the newly split window
461 "
462 " 'right' and 'below' will be set to the settings needed for
463 " splitbelow and splitright IF the explorer is the only window.
464 "
465 if g:explVertical
466 if g:explSplitRight
467 let there="wincmd h"
468 let back ="wincmd l"
469 let right=1
470 let below=0
471 else
472 let there="wincmd l"
473 let back ="wincmd h"
474 let right=0
475 let below=0
476 endif
477 else
478 if g:explSplitBelow
479 let there="wincmd k"
480 let back ="wincmd j"
481 let right=0
482 let below=1
483 else
484 let there="wincmd j"
485 let back ="wincmd k"
486 let right=0
487 let below=0
488 endif
489 endif
490
491 " Get the file name
492 let fn=s:GetFullFileName()
493
494 " Attempt to go to adjacent window
495 exec(back)
496 " If no adjacent window, set splitright and splitbelow appropriately
497 if n == winnr()
498 let &splitright=right
499 let &splitbelow=below
500 else
501 " found adjacent window - invert split direction
502 let &splitright=!right
503 let &splitbelow=!below
504 endif
505
506 " Create a variable to use if splitting vertically
507 let splitMode = ""
508 if g:explVertical == 1
509 let splitMode = "vertical"
510 endif
511
512 " Is it a directory? If so, get a real path to it instead of
513 " relative path. This also removes "/../" and "/./" things.
514 if isdirectory(fn)
515 let fn = fnamemodify(fn, ":p")
516 endif
517
518 " Open the new window
519 exec("silent " . splitMode." sp " . escape(fn,s:escfilename))
520
521 " resize the explorer window if it is larger than the requested size
522 exec(there)
523 if g:explWinSize =~ '[0-9]\+' && winheight("") > g:explWinSize
524 exec("silent ".splitMode." resize ".g:explWinSize)
525 endif
526 exec(back)
527
528 " Restore splitmode settings
529 let &splitbelow=savesplitbelow
530 let &splitright=savesplitright
531
532 endfunction
533
534 function! s:ExecuteEntry()
535 " Are we on a line with a file name?
536 let l = getline(".")
537 if l =~ '^"'
538 return
539 endif
540
541 " Get the file name
542 let fn = s:GetFullFileName()
543 if has("win32") && fn =~ '^//'
544 let fn = substitute(fn, '/', '\\', 'g')
545 endif
546 exec "call " . g:explFileHandler . "(fn)"
547 endfunction
548
549 "---
550 " Double click with the mouse
551 "
552 function s:DoubleClick()
553 if expand("<cfile>") =~ '[\\/]$'
554 call s:EditEntry("","edit") " directory: open in this window
555 else
556 call s:OpenEntryPrevWindow() " file: open in another window
557 endif
558 endfun
559
560 "---
561 " Open file or directory in the same window as the explorer is
562 " currently in
563 "
564 function! s:EditEntry(movefirst,editcmd)
565 " Are we on a line with a file name?
566 let l = getline(".")
567 if l =~ '^"'
568 return
569 endif
570
571 " Copy window settings to script settings
572 let s:sortby=w:sortdirlabel . w:sorttype
573 let s:longhelp = w:longhelp
574 let s:longlist = w:longlist
575
576 " Get the file name
577 let fn = s:GetFullFileName()
578 if isdirectory(fn)
579 " This removes "/../" and "/./" things.
580 let fn = fnamemodify(fn, ":p")
581 endif
582
583 " Move to desired window if needed
584 exec(a:movefirst)
585 " Edit the file/dir
586 exec(a:editcmd . " " . escape(fn,s:escfilename))
587 endfunction
588
589
590 "---
591 " Create a regular expression out of the suffixes option for sorting
592 " and set a string to indicate whether we are sorting with the
593 " suffixes at the end (or the beginning)
594 "
595 function! s:SetSuffixesLast()
596 let b:suffixesRegexp = '\(' . substitute(escape(&suffixes,s:escregexp),',','\\|','g') . '\)$'
597 let b:suffixesHighlight = '^[^"].*\(' . substitute(escape(&suffixes,s:escregexp),',','\\|','g') . '\)\( \|$\)'
598 if has("fname_case")
599 let b:suffixesRegexp = '\C' . b:suffixesRegexp
600 let b:suffixesHighlight = '\C' . b:suffixesHighlight
601 else
602 let b:suffixesRegexp = '\c' . b:suffixesRegexp
603 let b:suffixesHighlight = '\c' . b:suffixesHighlight
604 endif
605 if g:explSuffixesLast > 0 && &suffixes != ""
606 let b:suffixeslast=" (" . &suffixes . " at end of list)"
607 elseif g:explSuffixesLast < 0 && &suffixes != ""
608 let b:suffixeslast=" (" . &suffixes . " at start of list)"
609 else
610 let b:suffixeslast=" ('suffixes' mixed with files)"
611 endif
612 endfunction
613
614 "---
615 " Show the header and contents of the directory
616 "
617 function! s:ShowDirectory()
618 "Delete all lines
619 1,$d _
620 " Prevent a report of our actions from showing up
621 let oldRep=&report
622 let save_sc = &sc
623 set report=10000 nosc
624
625 " Add the header
626 call s:AddHeader()
627 $d _
628
629 " Display the files
630
631 " Get a list of all the files
632 let files = s:Path(glob(b:completePathEsc . "*"))
633 if files != "" && files !~ "\n$"
634 let files = files . "\n"
635 endif
636
637 " Add the dot files now, making sure "." is not included!
638 let files = files . substitute(s:Path(glob(b:completePathEsc . ".*")), "[^\n]*/./\\=\n", '' , '')
639 if files != "" && files !~ "\n$"
640 let files = files . "\n"
641 endif
642
643 " Are there any files left after filtering?
644 if files != ""
645 normal! mt
646 put =files
647 let b:maxFileLen = 0
648 0
649 /^"=/+1,$g/^/call s:MarkDirs()
650 normal! `t
651 call s:AddFileInfo()
652 endif
653
654 normal! zz
655
656 " Move to first directory in the listing
657 0
658 /^"=/+1
659
660 " Do the sort
661 call s:SortListing("Loaded contents of ".b:completePath.". ")
662
663 " Move to first directory in the listing
664 0
665 /^"=/+1
666
667 let &report=oldRep
668 let &sc = save_sc
669
670 endfunction
671
672 "---
673 " Mark which items are directories - called once for each file name
674 " must be used only when size/date is not displayed
675 "
676 function! s:MarkDirs()
677 let oldRep=&report
678 set report=1000
679 "Remove slashes if added
680 s;/$;;e
681 "Removes all the leading slashes and adds slashes at the end of directories
682 s;^.*\\\([^\\]*\)$;\1;e
683 s;^.*/\([^/]*\)$;\1;e
684 "normal! ^
685 let currLine=getline(".")
686 if isdirectory(b:completePath . currLine)
687 s;$;/;
688 let fileLen=strlen(currLine)+1
689 else
690 let fileLen=strlen(currLine)
691 if (b:filterFormula!="") && (currLine =~ b:filterFormula)
692 " Don't show the file if it is to be filtered.
693 d _
694 endif
695 endif
696 if fileLen > b:maxFileLen
697 let b:maxFileLen=fileLen
698 endif
699 let &report=oldRep
700 endfunction
701
702 "---
703 " Make sure a path has proper form
704 "
705 function! s:Path(p)
706 if has("dos16") || has("dos32") || has("win16") || has("win32") || has("os2")
707 return substitute(a:p,'\\','/','g')
708 else
709 return a:p
710 endif
711 endfunction
712
713 "---
714 " Extract the file name from a line in several different forms
715 "
716 function! s:GetFullFileNameEsc()
717 return s:EscapeFilename(s:GetFullFileName())
718 endfunction
719
720 function! s:GetFileNameEsc()
721 return s:EscapeFilename(s:GetFileName())
722 endfunction
723
724 function! s:EscapeFilename(name)
725 return escape(a:name,s:escfilename)
726 endfunction
727
728
729 function! s:GetFullFileName()
730 return s:ExtractFullFileName(getline("."))
731 endfunction
732
733 function! s:GetFileName()
734 return s:ExtractFileName(getline("."))
735 endfunction
736
737 function! s:ExtractFullFileName(line)
738 let fn=s:ExtractFileName(a:line)
739 if fn == '/'
740 return b:completePath
741 else
742 return b:completePath . s:ExtractFileName(a:line)
743 endif
744 endfunction
745
746 function! s:ExtractFileName(line)
747 return substitute(strpart(a:line,0,b:maxFileLen),'\s\+$','','')
748 endfunction
749
750 "---
751 " Get the size of the file
752 function! s:ExtractFileSize(line)
753 if (w:longlist==0)
754 return getfsize(s:ExtractFileName(a:line))
755 else
756 return strpart(a:line,b:maxFileLen+2,b:maxFileSizeLen)
757 endif
758 endfunction
759
760 "---
761 " Get the date of the file as a number
762 function! s:ExtractFileDate(line)
763 if w:longlist==0
764 return getftime(s:ExtractFileName(a:line))
765 else
766 return strpart(matchstr(strpart(a:line,b:maxFileLen+b:maxFileSizeLen+4),"««.*"),2) + 0
767 endif
768 endfunction
769
770
771 "---
772 " Add the header with help information
773 "
774 function! s:AddHeader()
775 let save_f=@f
776 1
777 if w:longhelp==1
778 let @f="\" <enter> : open file or directory\n"
779 \."\" o : open new window for file/directory\n"
780 \."\" O : open file in previously visited window\n"
781 \."\" p : preview the file\n"
782 if exists("g:explFileHandler")
783 let @f=@f."\" x : execute file or directory\n"
784 endif
785 let @f=@f
786 \."\" i : toggle size/date listing\n"
787 \."\" s : select sort field r : reverse sort\n"
788 \."\" - : go up one level c : cd to this dir\n"
789 \."\" R : rename file D : delete file\n"
790 \."\" :help file-explorer for detailed help\n"
791 else
792 let @f="\" Press ? for keyboard shortcuts\n"
793 endif
794 let @f=@f."\" Sorted by ".w:sortdirlabel.w:sorttype.b:suffixeslast.b:filtering."\n"
795 let @f=@f."\"= ".b:completePath."\n"
796 put! f
797 let @f=save_f
798 endfunction
799
800
801 "---
802 " Show the size and date for each file
803 "
804 function! s:AddFileInfo()
805 let save_sc = &sc
806 set nosc
807
808 " Mark our starting point
809 normal! mt
810
811 call s:RemoveSeparators()
812
813 " Remove all info
814 0
815 /^"=/+1,$g/^/call setline(line("."),s:GetFileName())
816
817 " Add info if requested
818 if w:longlist==1
819 " Add file size and calculate maximum length of file size field
820 let b:maxFileSizeLen = 0
821 0
822 /^"=/+1,$g/^/let fn=s:GetFullFileName() |
823 \let fileSize=getfsize(fn) |
824 \let fileSizeLen=strlen(fileSize) |
825 \if fileSizeLen > b:maxFileSizeLen |
826 \ let b:maxFileSizeLen = fileSizeLen |
827 \endif |
828 \exec "normal! ".(b:maxFileLen-strlen(getline("."))+2)."A \<esc>" |
829 \exec 's/$/'.fileSize.'/'
830
831 " Right justify the file sizes and
832 " add file modification date
833 0
834 /^"=/+1,$g/^/let fn=s:GetFullFileName() |
835 \exec "normal! A \<esc>$b".(b:maxFileLen+b:maxFileSizeLen-strlen(getline("."))+3)."i \<esc>\"_x" |
836 \exec 's/$/ '.escape(s:FileModDate(fn), '/').'/'
837 setlocal nomodified
838 endif
839
840 call s:AddSeparators()
841
842 " return to start
843 normal! `t
844
845 let &sc = save_sc
846 endfunction
847
848
849 "----
850 " Get the modification time for a file
851 "
852 function! s:FileModDate(name)
853 let filetime=getftime(a:name)
854 if filetime > 0
855 return strftime(g:explDateFormat,filetime) . " ««" . filetime
856 else
857 return ""
858 endif
859 endfunction
860
861 "---
862 " Delete a file or files
863 "
864 function! s:DeleteFile() range
865 let oldRep = &report
866 let &report = 1000
867
868 let filesDeleted = 0
869 let stopDel = 0
870 let delAll = 0
871 let currLine = a:firstline
872 let lastLine = a:lastline
873 setlocal noreadonly modifiable
874
875 while ((currLine <= lastLine) && (stopDel==0))
876 exec(currLine)
877 let fileName=s:GetFullFileName()
878 if isdirectory(fileName)
879 echo fileName." : Directory deletion not supported yet"
880 let currLine = currLine + 1
881 else
882 if delAll == 0
883 let sure=input("Delete ".fileName." (y/n/a/q)? ")
884 if sure=="a"
885 let delAll = 1
886 endif
887 endif
888 if (sure=="y") || (sure=="a")
889 let success=delete(fileName)
890 if success!=0
891 exec (" ")
892 echo "\nCannot delete ".fileName
893 let currLine = currLine + 1
894 else
895 d _
896 let filesDeleted = filesDeleted + 1
897 let lastLine = lastLine - 1
898 endif
899 elseif sure=="q"
900 let stopDel = 1
901 elseif sure=="n"
902 let currLine = currLine + 1
903 endif
904 endif
905 endwhile
906 echo "\n".filesDeleted." files deleted"
907 let &report = oldRep
908 setlocal nomodified
909 setlocal readonly nomodifiable
910 endfunction
911
912 "---
913 " Rename a file
914 "
915 function! s:RenameFile()
916 let fileName=s:GetFullFileName()
917 setlocal noreadonly modifiable
918 if isdirectory(fileName)
919 echo "Directory renaming not supported yet"
920 elseif filereadable(fileName)
921 let altName=input("Rename ".fileName." to : ")
922 echo " "
923 if altName==""
924 setlocal readonly nomodifiable
925 return
926 endif
927 let success=rename(fileName, b:completePath.altName)
928 if success!=0
929 echo "Cannot rename ".fileName. " to ".altName
930 else
931 echo "Renamed ".fileName." to ".altName
932 let oldRep=&report
933 set report=1000
934 e!
935 let &report=oldRep
936 endif
937 endif
938 setlocal nomodified
939 setlocal readonly nomodifiable
940 endfunction
941
942 "---
943 " Toggle between short and long help
944 "
945 function! s:ToggleHelp()
946 if exists("w:longhelp") && w:longhelp==0
947 let w:longhelp=1
948 let s:longhelp=1
949 else
950 let w:longhelp=0
951 let s:longhelp=0
952 endif
953 " Allow modification
954 setlocal noreadonly modifiable
955 call s:UpdateHeader()
956 " Disallow modification
957 setlocal readonly nomodifiable
958 endfunction
959
960 "---
961 " Update the header
962 "
963 function! s:UpdateHeader()
964 let oldRep=&report
965 set report=10000
966 " Save position
967 normal! mt
968 " Remove old header
969 0
970 1,/^"=/ d _
971 " Add new header
972 call s:AddHeader()
973 " Go back where we came from if possible
974 0
975 if line("'t") != 0
976 normal! `t
977 endif
978
979 let &report=oldRep
980 setlocal nomodified
981 endfunction
982
983 "---
984 " Toggle long vs. short listing
985 "
986 function! s:ToggleLongList()
987 setlocal noreadonly modifiable
988 if exists("w:longlist") && w:longlist==1
989 let w:longlist=0
990 let s:longlist=0
991 else
992 let w:longlist=1
993 let s:longlist=1
994 endif
995 call s:AddFileInfo()
996 setlocal readonly nomodifiable
997 endfunction
998
999 "---
1000 " Show all files - remove filtering
1001 "
1002 function! s:ShowAllFiles()
1003 setlocal noreadonly modifiable
1004 let b:filterFormula=""
1005 let b:filtering=""
1006 call s:ShowDirectory()
1007 setlocal readonly nomodifiable
1008 endfunction
1009
1010 "---
1011 " Figure out what section we are in
1012 "
1013 function! s:GetSection()
1014 let fn=s:GetFileName()
1015 let section="file"
1016 if fn =~ '/$'
1017 let section="directory"
1018 elseif fn =~ b:suffixesRegexp
1019 let section="suffixes"
1020 endif
1021 return section
1022 endfunction
1023
1024 "---
1025 " Remove section separators
1026 "
1027 function! s:RemoveSeparators()
1028 if !g:explUseSeparators
1029 return
1030 endif
1031 0
1032 silent! exec '/^"=/+1,$g/^' . s:separator . "/d _"
1033 endfunction
1034
1035 "---
1036 " Add section separators
1037 " between directories and files if they are separated
1038 " between files and 'suffixes' files if they are separated
1039 function! s:AddSeparators()
1040 if !g:explUseSeparators
1041 return
1042 endif
1043 0
1044 /^"=/+1
1045 let lastsec=s:GetSection()
1046 +1
1047 .,$g/^/let sec=s:GetSection() |
1048 \if g:explDirsFirst != 0 && sec != lastsec &&
1049 \ (lastsec == "directory" || sec == "directory") |
1050 \ exec "normal! I" . s:separator . "\n\<esc>" |
1051 \elseif g:explSuffixesLast != 0 && sec != lastsec &&
1052 \ (lastsec == "suffixes" || sec == "suffixes") |
1053 \ exec "normal! I" . s:separator . "\n\<esc>" |
1054 \endif |
1055 \let lastsec=sec
1056 endfunction
1057
1058 "---
1059 " General string comparison function
1060 "
1061 function! s:StrCmp(line1, line2, direction)
1062 if a:line1 < a:line2
1063 return -a:direction
1064 elseif a:line1 > a:line2
1065 return a:direction
1066 else
1067 return 0
1068 endif
1069 endfunction
1070
1071 "---
1072 " Function for use with Sort(), to compare the file names
1073 " Default sort is to put in alphabetical order, but with all directory
1074 " names before all file names
1075 "
1076 function! s:FileNameCmp(line1, line2, direction)
1077 let f1=s:ExtractFileName(a:line1)
1078 let f2=s:ExtractFileName(a:line2)
1079
1080 " Put directory names before file names
1081 if (g:explDirsFirst != 0) && (f1 =~ '\/$') && (f2 !~ '\/$')
1082 return -g:explDirsFirst
1083 elseif (g:explDirsFirst != 0) && (f1 !~ '\/$') && (f2 =~ '\/$')
1084 return g:explDirsFirst
1085 elseif (g:explSuffixesLast != 0) && (f1 =~ b:suffixesRegexp) && (f2 !~ b:suffixesRegexp)
1086 return g:explSuffixesLast
1087 elseif (g:explSuffixesLast != 0) && (f1 !~ b:suffixesRegexp) && (f2 =~ b:suffixesRegexp)
1088 return -g:explSuffixesLast
1089 else
1090 return s:StrCmp(substitute(f1, "/$", "", ""), substitute(f2, "/$", "", ""), a:direction)
1091 endif
1092
1093 endfunction
1094
1095 "---
1096 " Function for use with Sort(), to compare the file modification dates
1097 " Default sort is to put NEWEST files first. Reverse will put oldest
1098 " files first
1099 "
1100 function! s:FileDateCmp(line1, line2, direction)
1101 let f1=s:ExtractFileName(a:line1)
1102 let f2=s:ExtractFileName(a:line2)
1103 let t1=s:ExtractFileDate(a:line1)
1104 let t2=s:ExtractFileDate(a:line2)
1105
1106 " Put directory names before file names
1107 if (g:explDirsFirst != 0) && (f1 =~ '\/$') && (f2 !~ '\/$')
1108 return -g:explDirsFirst
1109 elseif (g:explDirsFirst != 0) && (f1 !~ '\/$') && (f2 =~ '\/$')
1110 return g:explDirsFirst
1111 elseif (g:explSuffixesLast != 0) && (f1 =~ b:suffixesRegexp) && (f2 !~ b:suffixesRegexp)
1112 return g:explSuffixesLast
1113 elseif (g:explSuffixesLast != 0) && (f1 !~ b:suffixesRegexp) && (f2 =~ b:suffixesRegexp)
1114 return -g:explSuffixesLast
1115 elseif t1 > t2
1116 return -a:direction
1117 elseif t1 < t2
1118 return a:direction
1119 else
1120 return s:StrCmp(substitute(f1, "/$", "", ""), substitute(f2, "/$", "", ""), 1)
1121 endif
1122 endfunction
1123
1124 "---
1125 " Function for use with Sort(), to compare the file sizes
1126 " Default sort is to put largest files first. Reverse will put
1127 " smallest files first
1128 "
1129 function! s:FileSizeCmp(line1, line2, direction)
1130 let f1=s:ExtractFileName(a:line1)
1131 let f2=s:ExtractFileName(a:line2)
1132 let s1=s:ExtractFileSize(a:line1)
1133 let s2=s:ExtractFileSize(a:line2)
1134
1135 if (g:explDirsFirst != 0) && (f1 =~ '\/$') && (f2 !~ '\/$')
1136 return -g:explDirsFirst
1137 elseif (g:explDirsFirst != 0) && (f1 !~ '\/$') && (f2 =~ '\/$')
1138 return g:explDirsFirst
1139 elseif (g:explSuffixesLast != 0) && (f1 =~ b:suffixesRegexp) && (f2 !~ b:suffixesRegexp)
1140 return g:explSuffixesLast
1141 elseif (g:explSuffixesLast != 0) && (f1 !~ b:suffixesRegexp) && (f2 =~ b:suffixesRegexp)
1142 return -g:explSuffixesLast
1143 elseif s1 > s2
1144 return -a:direction
1145 elseif s1 < s2
1146 return a:direction
1147 else
1148 return s:StrCmp(substitute(f1, "/$", "", ""), substitute(f2, "/$", "", ""), 1)
1149 endif
1150 endfunction
1151
1152 "---
1153 " Sort lines. SortR() is called recursively.
1154 "
1155 function! s:SortR(start, end, cmp, direction)
1156
1157 " Bottom of the recursion if start reaches end
1158 if a:start >= a:end
1159 return
1160 endif
1161 "
1162 let partition = a:start - 1
1163 let middle = partition
1164 let partStr = getline((a:start + a:end) / 2)
1165 let i = a:start
1166 while (i <= a:end)
1167 let str = getline(i)
1168 exec "let result = " . a:cmp . "(str, partStr, " . a:direction . ")"
1169 if result <= 0
1170 " Need to put it before the partition. Swap lines i and partition.
1171 let partition = partition + 1
1172 if result == 0
1173 let middle = partition
1174 endif
1175 if i != partition
1176 let str2 = getline(partition)
1177 call setline(i, str2)
1178 call setline(partition, str)
1179 endif
1180 endif
1181 let i = i + 1
1182 endwhile
1183
1184 " Now we have a pointer to the "middle" element, as far as partitioning
1185 " goes, which could be anywhere before the partition. Make sure it is at
1186 " the end of the partition.
1187 if middle != partition
1188 let str = getline(middle)
1189 let str2 = getline(partition)
1190 call setline(middle, str2)
1191 call setline(partition, str)
1192 endif
1193 call s:SortR(a:start, partition - 1, a:cmp,a:direction)
1194 call s:SortR(partition + 1, a:end, a:cmp,a:direction)
1195 endfunction
1196
1197 "---
1198 " To Sort a range of lines, pass the range to Sort() along with the name of a
1199 " function that will compare two lines.
1200 "
1201 function! s:Sort(cmp,direction) range
1202 call s:SortR(a:firstline, a:lastline, a:cmp, a:direction)
1203 endfunction
1204
1205 "---
1206 " Reverse the current sort order
1207 "
1208 function! s:SortReverse()
1209 if exists("w:sortdirection") && w:sortdirection == -1
1210 let w:sortdirection = 1
1211 let w:sortdirlabel = ""
1212 else
1213 let w:sortdirection = -1
1214 let w:sortdirlabel = "reverse "
1215 endif
1216 let s:sortby=w:sortdirlabel . w:sorttype
1217 call s:SortListing("")
1218 endfunction
1219
1220 "---
1221 " Toggle through the different sort orders
1222 "
1223 function! s:SortSelect()
1224 " Select the next sort option
1225 if !exists("w:sorttype")
1226 let w:sorttype="name"
1227 elseif w:sorttype == "name"
1228 let w:sorttype="size"
1229 elseif w:sorttype == "size"
1230 let w:sorttype="date"
1231 else
1232 let w:sorttype="name"
1233 endif
1234 let s:sortby=w:sortdirlabel . w:sorttype
1235 call s:SortListing("")
1236 endfunction
1237
1238 "---
1239 " Sort the file listing
1240 "
1241 function! s:SortListing(msg)
1242 " Save the line we start on so we can go back there when done
1243 " sorting
1244 let startline = getline(".")
1245 let col=col(".")
1246 let lin=line(".")
1247
1248 " Allow modification
1249 setlocal noreadonly modifiable
1250
1251 " Send a message about what we're doing
1252 " Don't really need this - it can cause hit return prompts
1253 " echo a:msg . "Sorting by" . w:sortdirlabel . w:sorttype
1254
1255 " Create a regular expression out of the suffixes option in case
1256 " we need it.
1257 call s:SetSuffixesLast()
1258
1259 " Remove section separators
1260 call s:RemoveSeparators()
1261
1262 " Do the sort
1263 0
1264 if w:sorttype == "size"
1265 /^"=/+1,$call s:Sort("s:FileSizeCmp",w:sortdirection)
1266 elseif w:sorttype == "date"
1267 /^"=/+1,$call s:Sort("s:FileDateCmp",w:sortdirection)
1268 else
1269 /^"=/+1,$call s:Sort("s:FileNameCmp",w:sortdirection)
1270 endif
1271
1272 " Replace the header with updated information
1273 call s:UpdateHeader()
1274
1275 " Restore section separators
1276 call s:AddSeparators()
1277
1278 " Return to the position we started on
1279 0
1280 if search('\m^'.escape(startline,s:escregexp),'W') <= 0
1281 execute lin
1282 endif
1283 execute "normal!" col . "|"
1284
1285 " Disallow modification
1286 setlocal nomodified
1287 setlocal readonly nomodifiable
1288
1289 endfunction
1290
1291 "---
1292 " Setup for editing directories after starting up by going to each window.
1293 " Required for "vim -o filename dirname"
1294 "
1295 function! s:EditAll()
1296 if winbufnr(2) == -1
1297 return
1298 endif
1299 let cmd = winrestcmd()
1300 let curwin = winnr()
1301 while 1
1302 wincmd w
1303 if winnr() == curwin
1304 break
1305 endif
1306 call s:EditDir()
1307 endwhile
1308 exe cmd
1309 endfunction
1310
1311 "---
1312 " Set up the autocommand to allow directories to be edited
1313 "
1314 augroup fileExplorer
1315 au!
1316 " Fill the window when entering the buffer; ":edit dir".
1317 au BufEnter * call s:EditDir()
1318 " Set the window variables after a split; ":split".
1319 au WinEnter * if !exists("w:sortdirection") | call s:EditDir() | endif
1320 " Fill the windows after Vim has started up.
1321 au VimEnter * call s:EditAll()
1322 augroup end
1323
1324 " restore 'cpo'
1325 let &cpo = s:cpo_save
1326 unlet s:cpo_save