" vimcommander - (hopefully) vim + totalcommander-like file explorer for vim " vim: fdm=marker foldmarker=fu!,endf " " Author: Leandro Penz " Date: 2003/11/01 " Email: lpenz AT terra DOT com DOT br " Version: $Id: vimcommander.vim,v 1.35 2003/11/09 17:14:14 lpenz Exp $ " " Shameless using opsplorer.vim by Patrick Schiel. " fu! CommanderMappings() "opsplorer legacy: noremap :cal OnClick() noremap <2-LeftMouse> :cal OnDoubleClick(-1) noremap :cal OnDoubleClick(0) noremap :cal OnDoubleClick(1) noremap :cal GotoNextEntry() noremap :cal GotoPrevEntry() noremap :cal GotoNextNode() noremap :cal GotoPrevNode() noremap :cal BuildParentTree() "total-cmd keys: noremap :cal SwitchBuffer() noremap :cal FileView() noremap :cal FileEdit() noremap :cal NewFileEdit() noremap :cal DirCreate() noremap :cal GetOrPutDir('l') noremap :cal GetOrPutDir('r') noremap :cal GetOrPutDir('l') noremap :cal GetOrPutDir('r') noremap :cal PutDir() noremap :cal FileCopy() noremap :cal FileMove() noremap :cal FileDelete() noremap :cal FileDelete() noremap :cal ExchangeDirs() noremap :cal RefreshDisplays() noremap :cal VimCommanderToggle() noremap :cal VimCommanderToggle() noremap :cal Select() noremap :cal SelectPattern('*') noremap :cal DeSelectPattern('*') noremap :cal SelectPatternAsk() noremap :cal DeSelectPatternAsk() noremap :cal SetMatchPattern() noremap :cal VimCommanderToggle() endf fu! VimCommanderToggle() if exists("g:vimcommander_loaded") if(g:vimcommander_loaded==1) " its on screen - close cal Close() el " its loaded, but not on screen cal VimCommanderShow() end el cal First() en endf fu!First() cal InitOptions() let s:path_left=getcwd() let s:path_right=getcwd() let s:line_right=2 let s:line_left=2 let g:lastside="VimCommanderLeft" cal VimCommanderShow() endf fu! VimCommanderShow() if exists("g:vimcommander_loaded") && g:vimcommander_loaded==1 " on screen return end "close all windows let s:orig_buffer=bufname("") let v:errmsg='' while v:errmsg=='' silent! close endwhile "reset aucmd autocmd! BufEnter VimCommanderLeft autocmd! BufEnter VimCommanderRight autocmd! BufWinLeave VimCommanderLeft autocmd! BufWinLeave VimCommanderRight " create new window let winsize=&lines exe winsize." split VimCommanderRight" let s:bufnr_right=winbufnr(0) " setup mappings, apply options, colors and draw tree cal InitCommanderOptions() cal CommanderMappings() cal InitCommanderColors() cal BuildTree(s:path_right) exe s:line_right exe "vs VimCommanderLeft" let s:bufnr_left=winbufnr(0) cal InitCommanderOptions() cal CommanderMappings() cal InitCommanderColors() cal BuildTree(s:path_left) exe s:line_left let g:vimcommander_loaded=1 "Goto vimcommander window winc j hide let winnum = bufwinnr(g:lastside) if winnum != -1 " Jump to the existing window if winnr() != winnum exe winnum . 'wincmd w' endif endif norm z- autocmd BufEnter VimCommanderLeft let g:lastside="VimCommanderLeft" autocmd BufEnter VimCommanderRight let g:lastside="VimCommanderRight" autocmd BufWinLeave VimCommanderLeft cal VimCommanderToggle() autocmd BufWinLeave VimCommanderRight cal VimCommanderToggle() endf fu! Close() autocmd! BufEnter VimCommanderLeft autocmd! BufEnter VimCommanderRight autocmd! BufWinLeave VimCommanderLeft autocmd! BufWinLeave VimCommanderRight let winnum = bufwinnr("VimCommanderLeft") if winnum != -1 " Jump to the existing window if winnr() != winnum exe winnum . 'wincmd w' endif endif let s:line_left=line('.') silent! close let winnum = bufwinnr("VimCommanderRight") if winnum != -1 " Jump to the existing window if winnr() != winnum exe winnum . 'wincmd w' endif endif let s:line_right=line('.') silent! close let g:vimcommander_loaded=0 if bufwinnr("VimCommanderRight")!=-1 exe "new +buffer ".s:orig_buffer exe 'wincmd w' close end endf fu! InitCommanderOptions() silent! setlocal noscrollbind silent! setlocal nowrap silent! setlocal nonu silent! setlocal buftype=nofile silent! setlocal bufhidden=delete silent! setlocal noswapfile silent! setlocal nobuflisted silent! setlocal nonumber silent! setlocal incsearch let b:vimcommander_selected="" endf fu! InitCommanderColors() sy clear if s:use_colors syntax match VimCommanderSelectedFile '^\s*<.*>$' syntax match VimCommanderSelectedDir '<\w.*>$' contained syntax match VimCommanderPath "^/.*" syntax match VimCommanderDirLine "^[+-].*" transparent contains=VimCommanderSelectedDir,VimCommanderNode syntax match VimCommanderNode "^[+-]" contained syntax match VimCommanderFileLine "^\s*\w\w*.*$" transparent contains=ALL syntax match VimCommanderFile "\w.*" contained syntax match VimCommanderSource "^\s*\w\w*.*\.c$" contained syntax match VimCommanderHeader "^\s*\w\w*.*\.h$" contained syntax match VimCommanderSpecial "^\s*\(Makefile\|config.mk\)$" contained hi link VimCommanderPath Label hi link VimCommanderNode Comment "hi link OpsFile Question hi link VimCommanderFile Comment hi link VimCommanderSource Question hi link VimCommanderHeader Include hi link VimCommanderSpecial Function hi link VimCommanderSelectedFile Visual hi link VimCommanderSelectedDir Visual en endf fu! SwitchBuffer() if winbufnr(0) == s:bufnr_left winc l else winc h end endf fu! GetPathName(xpos,ypos) let xpos=a:xpos let ypos=a:ypos " check for directory.. let line=getline(ypos) " check selected if line=~"\s*<.*>$" let xpos=xpos+1 end if getline(ypos)[xpos]=~"[+-]" let path=strpart(getline(ypos),xpos+1,col('$')) el " otherwise filename let path=strpart(getline(ypos),xpos,col('$')) let xpos=xpos-1 en if line=~"\s*<.*>$" let path=strpart(path, 0, strlen(path)-1) end let path='/'.path " walk up tree and append subpaths " add base path " not needed, if in root if getline(1)!='/' let path=getline(1).path en retu path endf fu! PathUnderCursor() let xpos=col('.')-1 let ypos=line('.') if ypos>1 "not on line 1 norm 1|g^ let xpos=col('.')-1 let rv=GetPathName(xpos,ypos) return rv end return "" endf fu! ProvideBuffer() "winc j new endf fu! FileView() let path=PathUnderCursor() if(isdirectory(path)) return end cal ProvideBuffer() exe "edit ".path setl noma setl ro cal Close() endf fu! FileEdit() let path=PathUnderCursor() if(isdirectory(path)) return end cal ProvideBuffer() exe "edit ".path setl ma setl noro cal Close() endf fu! NewFileEdit() let path=MyPath() let newfile=PathUnderCursor() if(isdirectory(newfile)) let newfile=path end let newfile=input("File to edit: ", newfile) if newfile=="" return end if(isdirectory(newfile)) echo "Unable to edit file: directory with same name exists" return end cal ProvideBuffer() exe "edit ".newfile setlocal ma setlocal noro cal Close() endf fu! MyPath() if winbufnr(0) == s:bufnr_left return s:path_left."/" else return s:path_right."/" en endf fu! BuildTree(path) let path=a:path let b:vimcommander_selected="" " clean up setl ma norm ggVGxo " check if no unneeded trailing / is there if strlen(path)>1&&path[strlen(path)-1]=="/" let path=strpart(path,0,strlen(path)-1) en if(winbufnr(0)==s:bufnr_right) let s:path_right=path else let s:path_left=path end cal setline(1,path) setl noma nomod " pass -1 as xpos to start at column 0 cal TreeExpand(-1,1,path) " move to first entry norm ggj1|g^ endf fu! RefreshDisplays() let line=line('.') cal BuildTree(MyPath()) exec line cal SwitchBuffer() let line=line('.') cal BuildTree(MyPath()) exec line cal SwitchBuffer() endf fu! DirCreate() norm 1|g^ let newdir="" let newdir=input("New directory name: ","") if filereadable(newdir) echo "File with that name exists." return end if isdirectory(newdir) echo "Directory already exists." return end let i=system("mkdir ".MyPath().newdir) cal RefreshDisplays() norm gg1j cal search("^+".newdir."$") endf fu! OtherPath() if winbufnr(0) == s:bufnr_left return s:path_right."/" else return s:path_left."/" en endf fu! GetName(xpos,ypos) let xpos=a:xpos let ypos=a:ypos " check for directory.. if getline(ypos)[xpos]=~"[+-]" let path=strpart(getline(ypos),xpos+1,col('$')) el " otherwise filename let path=strpart(getline(ypos),xpos,col('$')) let xpos=xpos-1 en retu path endf fu! FilenameUnderCursor() norm 1|g^ let path=GetName(col('.')-1,line('.')) if path=~"^<.*>$" let path=strpart(path, 1, strlen(path)-2) end return path endf fu! NameUnderCursor() norm 1|g^ return GetName(col('.')-1,line('.')) endf fu! FileCopy() let i=0 if strlen(b:vimcommander_selected)>0 let name=SelectedNum(b:vimcommander_selected, i) let filename=MyPath().name let otherfilename=OtherPath().name let i=i+1 else let name=" " let filename=PathUnderCursor() let otherfilename=OtherPath().FilenameUnderCursor() end let opt="y" while strlen(name)>0 if filereadable(filename) || isdirectory(filename) if strlen(b:vimcommander_selected)==0 let newfilename=input("Copy ".filename." to: ",otherfilename) else let newfilename=otherfilename end if filereadable(filename) && isdirectory(newfilename) echo "Can't overwrite directory ".newfilename." with file" return end if isdirectory(filename) && filereadable(newfilename) echo "Can't overwrite file ".newfilename." with directory" return end if filereadable(newfilename) if opt!~"^[AakK]$" let opt=input("File ".newfilename." exists, overwrite? [nkya] ","y") if opt=="" return end end if opt=~"^[yYAa]$" " copy file cal system('cp -Rf "'.filename.'" "'.newfilename.'"') en el " copy file cal system('cp -Rf "'.filename.'" "'.newfilename.'"') en en if strlen(b:vimcommander_selected)>0 let name=SelectedNum(b:vimcommander_selected, i) let filename=MyPath().name let otherfilename=OtherPath().name let i=i+1 else let name="" end endwhile let b:vimcommander_selected="" cal RefreshDisplays() endf fu! FileMove() let i=0 if strlen(b:vimcommander_selected)>0 let name=SelectedNum(b:vimcommander_selected, i) let filename=MyPath().name let otherfilename=OtherPath().name let i=i+1 else let name=" " let filename=PathUnderCursor() let otherfilename=OtherPath().FilenameUnderCursor() end let opt='y' while strlen(name)>0 if filereadable(filename) || isdirectory(filename) if strlen(b:vimcommander_selected)==0 let newfilename=input("Move ".filename." to: ",otherfilename) else let newfilename=otherfilename end if filereadable(filename) && isdirectory(newfilename) echo "Can't overwrite directory with file" return end if isdirectory(filename) && filereadable(newfilename) echo "Can't overwrite file with directory" return end if isdirectory(filename) && isdirectory(newfilename) echo "Can't overwrite directory with directory" return end if filereadable(newfilename) if opt!~"^[AakK]$" let opt=input("File ".newfilename." exists, overwrite? [nkya] ","y") if opt=="" return end end if opt=~"^[yYAa]$" " move file system('mv -f "'.filename.'" "'.newfilename.'"') en el " move file system('mv "'.filename.'" "'.newfilename.'"') en en if strlen(b:vimcommander_selected)>0 let name=SelectedNum(b:vimcommander_selected, i) let filename=MyPath().name let otherfilename=OtherPath().name let i=i+1 else let name="" end endwhile let b:vimcommander_selected="" cal RefreshDisplays() endf fu! FileDelete() let i=0 if strlen(b:vimcommander_selected)>0 let name=SelectedNum(b:vimcommander_selected, i) let filename=MyPath().name let i=i+1 else let name=" " let filename=PathUnderCursor() end let opt="" while strlen(name)>0 if filereadable(filename) || isdirectory(filename) if opt!~"^[AakK]$" let opt=input("OK to delete ".fnamemodify(filename,":t")."? [nkya] ","y") if opt=="" return end end if opt=~"^[yYAa]$" cal system('rm -rf "'.filename.'"') en en if strlen(b:vimcommander_selected)>0 let name=SelectedNum(b:vimcommander_selected, i) let filename=MyPath().name let i=i+1 else let name="" end endwhile let b:vimcommander_selected="" cal RefreshDisplays() endf fu! PutDir() let mypath=MyPath() cal SwitchBuffer() cal BuildTree(mypath) cal SwitchBuffer() cal RefreshDisplays() endf fu! GetOrPutDir(dir) if a:dir=='l' && winbufnr(0)==s:bufnr_left " left and left - getdir cal BuildTree(OtherPath()) return end if a:dir=='r' && winbufnr(0)==s:bufnr_right " right and right - getdir cal BuildTree(OtherPath()) return end " Crossed - putdir let path=PathUnderCursor() if !isdirectory(path) return end cal SwitchBuffer() cal BuildTree(path) cal SwitchBuffer() cal RefreshDisplays() endf fu! ExchangeDirs() let pathtmp=s:path_left let s:path_left=s:path_right let s:path_right=pathtmp let myline=line('.') cal BuildTree(MyPath()) cal SwitchBuffer() cal BuildTree(MyPath()) exec myline cal RefreshDisplays() endf fu! SelectedNum(str,idx) let mystr=a:str let i=0 wh i") let mystr=strpart(mystr, 1, pos-1) return mystr endf fu! Select() let name=NameUnderCursor() if name =~ "^+*<\w*.*>$" " deselected let name=strpart(name, 1, strlen(name)-2) let tmp="" let found=SelectedNum(b:vimcommander_selected, 0) let i=1 while found!="" if found!=name if tmp!="" let tmp=tmp.' ' end let tmp=tmp.'<'.found.'>' end let found=SelectedNum(b:vimcommander_selected, i) let i=i+1 endwhile let b:vimcommander_selected=tmp setl ma norm 1|g^ if getline(line('.'))[0]=='+' norm l end norm x norm $x norm 1|g^ setl noma norm j else " select if b:vimcommander_selected=="" let b:vimcommander_selected='<'.NameUnderCursor().'>' else let b:vimcommander_selected=b:vimcommander_selected." <".NameUnderCursor().">" end setl ma norm ^ if getline(line('.'))[0]=='+' norm l end norm i< norm A> setl noma norm 1|g^ norm j end endf fu! SelectPattern(pattern) let origdirlist='' let path=MyPath() if s:show_hidden_files let dirlistorig=glob(path.'/.*'.a:pattern)."\n" en let origdirlist=origdirlist.globpath(path, a:pattern)."\n" let myline=line('.') norm G let lastline=line('.') norm gg norm j while line('.')0 " get next line let entry=GetNextLine(dirlist) let dirlist=CutFirstLine(dirlist) " only files if entry!="." && entry!=".." && entry!="" "echo "cursor in ".PathUnderCursor()." entry ".entry." len ".strlen(dirlist) if entry==PathUnderCursor() cal Select() norm k let dirlist="" continue end en endw norm j endwhile exe myline endf fu! DeSelectPattern(pattern) let origdirlist='' let path=MyPath() if s:show_hidden_files let dirlistorig=glob(path.'/.*'.a:pattern)."\n" en let origdirlist=origdirlist.globpath(path, a:pattern)."\n" let myline=line('.') norm G let lastline=line('.') norm gg norm j while line('.')NameUnderCursor() if path=~"^<.*>$" let path=MyPath().strpart(path,1,strlen(path)-2) wh strlen(dirlist)>0 " get next line let entry=GetNextLine(dirlist) let dirlist=CutFirstLine(dirlist) " only files if entry!="." && entry!=".." && entry!="" " echo "cursor in ".path." entry ".entry." len ".strlen(dirlist) if entry==path cal Select() norm k let dirlist="" continue end end endw end norm j endwhile exe myline endf fu! SelectPatternAsk() let pattern=input("Select with pattern: ",'*') cal SelectPattern(pattern) echo "" endf fu! DeSelectPatternAsk() let pattern=input("Deselect with pattern: ",'*') cal DeSelectPattern(pattern) echo "" endf "== From Opsplorer: ========================================================== fu! InitOptions() let s:single_click_to_edit=0 let s:file_match_pattern="*" "let s:file_match_pattern="\"`ls -d * | egrep -v \"(\.d$|\.o$|^tags$)\";ls config.mk`\"" let s:show_hidden_files=0 let s:split_vertical=1 let s:split_width=20 let s:split_minwidth=1 let s:use_colors=1 let s:close_explorer_after_open=0 endf fu! InsertFilename() norm 1|g^ let filename=GetPathName(col('.')-1,line('.')) winc p exe "norm a".filename endf fu! InsertFileContent() norm 1|g^ let filename=GetPathName(col('.')-1,line('.')) if filereadable(filename) winc p exe "r ".filename en endf fu! FileSee() norm 1|g^ let filename=GetPathName(col('.')-1,line('.')) if filereadable(filename) let i=system("see ".filename."&") en endf fu! BuildParentTree() norm gg$F/ let mydir=getline(line('.')) let mypos="^+".strpart(mydir, strridx(mydir,'/')+1)."$" cal OnDoubleClick(0) call search(mypos) endf fu! GotoNextNode() " in line 1 like next entry if line('.')==1 cal GotoNextEntry() el norm j1|g^ wh getline('.')[col('.')-1] !~ "[+-]" && line('.')GotoPrevNode() " entering base path section? if line('.')<3 cal GotoPrevEntry() el norm k1|g^ wh getline('.')[col('.')-1] !~ "[+-]" && line('.')>1 norm k1|g^ endw en endf fu! GotoNextEntry() let xpos=col('.') " different movement in line 1 if line('.')==1 " if over slash, move one to right if getline('.')[xpos-1]=='/' norm l " only root path there, move down if col('.')==1 norm j1|g^ en el " otherwise after next slash norm f/l " if already last path, move down if col('.')==xpos norm j1|g^ en en el " next line, first nonblank norm j1|g^ en endf fu! GotoPrevEntry() " different movement in line 1 if line('.')==1 " move after prev slash norm hF/l el " enter line 1 at the end if line('.')==2 norm k$F/l el " prev line, first nonblank norm k1|g^ en en endf fu! OnClick() let xpos=col('.')-1 let ypos=line('.') if IsTreeNode(xpos,ypos) cal TreeNodeAction(xpos,ypos) elsei s:single_click_to_edit cal OnDoubleClick() en endf fu! OnDoubleClick(close_explorer) let s:close_explorer=a:close_explorer if s:close_explorer==-1 let s:close_explorer=s:close_explorer_after_open en let xpos=col('.')-1 let ypos=line('.') " clicked on node "if IsTreeNode(xpos,ypos) " cal TreeNodeAction(xpos,ypos) "el " go to first non-blank when line>1 if ypos>1 norm 1|g^ let xpos=col('.')-1 " check, if it's a directory let path=GetPathName(xpos,ypos) if isdirectory(path) " build new root structure cal BuildTree(path) "exe "cd ".getline(1) el " try to resolve filename " and open in other window let path=GetPathName(xpos,ypos) if filereadable(path) " go to last accessed buffer winc j " append sequence for opening file "exe "cd ".fnamemodify(path,":h") exe "e ".path if s:close_explorer==2 "eh view setl noma setl ro else setl ma setl noro end en if s:close_explorer==1 cal VimCommanderToggle() en en el " we're on line 1 here! getting new base path now... " advance to next slash if getline(1)[xpos]!="/" norm f/ " no next slash -> current directory, just rebuild if col('.')-1==xpos cal BuildTree(getline(1)) "exe "cd ".getline(1) retu en en " cut ending slash norm h " rebuild tree with new path cal BuildTree(strpart(getline(1),0,col('.'))) en "en endf fu! TreeExpand(xpos,ypos,path) let path=a:path setl ma " first get all subdirectories let dirlist="" " extra globbing for hidden files if s:show_hidden_files let dirlist=glob(path.'/.*')."\n" en " add norm entries let dirlist=dirlist.glob(path.'/*')."\n" " remember where to append let row=a:ypos wh strlen(dirlist)>0 " get next line let entry=GetNextLine(dirlist) let dirlist=CutFirstLine(dirlist) " add to tree if directory if isdirectory(entry) let entry=substitute(entry,".*/",'','') if entry!="." && entry!=".." " indent, mark as node and append let entry=SpaceString(a:xpos+1)."+".entry cal append(row,entry) let row=row+1 en en endw " now get files let dirlist="" " extra globbing for hidden files if s:show_hidden_files let dirlist=glob(path.'/.*'.s:file_match_pattern)."\n" en let dirlist=dirlist.globpath(path, s:file_match_pattern)."\n" wh strlen(dirlist)>0 " get next line let entry=GetNextLine(dirlist) let dirlist=CutFirstLine(dirlist) " only files if entry!="." && entry!=".." && entry!="" if !isdirectory(entry)&&filereadable(entry) let entry=substitute(entry,".*/",'','') " indent and append let entry=SpaceString(a:xpos+2).entry cal append(row,entry) let row=row+1 en en endw setl noma nomod endf fu! TreeCollapse(xpos,ypos) setl ma " turn - into +, go to next line norm r+j " delete lines til next line with same indent wh (getline('.')[a:xpos+1] =~ '[ +-]') && (line('$') != line('.')) norm dd endw " go up again norm k setl noma nomod endf fu! TreeNodeAction(xpos,ypos) if getline(a:ypos)[a:xpos] == '+' cal TreeExpand(a:xpos,a:ypos,GetPathName(a:xpos,a:ypos)) elsei getline(a:ypos)[a:xpos] == '-' cal TreeCollapse(a:xpos,a:ypos) en endf fu! IsTreeNode(xpos,ypos) if getline(a:ypos)[a:xpos] =~ '[+-]' " is it a directory or file starting with +/- ? if isdirectory(GetPathName(a:xpos,a:ypos)) retu 1 el retu 0 en el retu 0 en endf fu! ToggleShowHidden() let s:show_hidden_files = 1-s:show_hidden_files cal BuildTree(getline(1)) endf fu! SetMatchPattern() let s:file_match_pattern=input("Match pattern: ",s:file_match_pattern) cal BuildTree(getline(1)) endf fu! GetNextLine(text) let pos=match(a:text,"\n") retu strpart(a:text,0,pos) endf fu! CutFirstLine(text) let pos=match(a:text,"\n") retu strpart(a:text,pos+1,strlen(a:text)) endf fu! SpaceString(width) let spacer="" let width=a:width wh width>0 let spacer=spacer." " let width=width-1 endw retu spacer endf