home *** CD-ROM | disk | FTP | other *** search
/ PC World 2005 June / PCWorld_2005-06_cd.bin / software / vyzkuste / firewally / firewally.exe / framework-2.3.exe / explorer.vim < prev    next >
Text File  |  2003-08-12  |  35KB  |  1,331 lines

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