home *** CD-ROM | disk | FTP | other *** search
/ PC User 2003 January / Disc 3 / Amethyst.iso / live / usr / share / vim / vim60 / plugin / explorer.vim next >
Encoding:
Text File  |  2002-06-19  |  31.8 KB  |  1,240 lines

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