From dfd66bbd226fb83f09152c619f0bc6753f06ec63 Mon Sep 17 00:00:00 2001 From: yan Date: Wed, 7 Dec 2011 01:40:44 -0800 Subject: [PATCH] modularized: gitgrep, lustyjuggler, greplace --- .gitmodules | 9 + README.md | 6 +- vim/bundle/sjbach-lusty | 1 + vim/bundle/skwp-greplace | 1 + vim/bundle/tjennings-git-grep-vim | 1 + vim/plugin/color_sample_pack.vim | 166 -- vim/plugin/git-grep.vim | 42 - vim/plugin/lusty-explorer.vim | 2377 ----------------------------- vim/plugin/lusty-juggler.vim | 1143 -------------- vimrc | 17 +- 10 files changed, 21 insertions(+), 3742 deletions(-) create mode 160000 vim/bundle/sjbach-lusty create mode 160000 vim/bundle/skwp-greplace create mode 160000 vim/bundle/tjennings-git-grep-vim delete mode 100644 vim/plugin/color_sample_pack.vim delete mode 100644 vim/plugin/git-grep.vim delete mode 100755 vim/plugin/lusty-explorer.vim delete mode 100644 vim/plugin/lusty-juggler.vim diff --git a/.gitmodules b/.gitmodules index bd7d52e..9f3947f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -55,3 +55,12 @@ [submodule "vim/bundle/vim-scripts-bufexplorer.zip"] path = vim/bundle/vim-scripts-bufexplorer.zip url = https://github.com/vim-scripts/bufexplorer.zip.git +[submodule "vim/bundle/sjbach-lusty"] + path = vim/bundle/sjbach-lusty + url = https://github.com/sjbach/lusty.git +[submodule "vim/bundle/tjennings-git-grep-vim"] + path = vim/bundle/tjennings-git-grep-vim + url = https://github.com/tjennings/git-grep-vim +[submodule "vim/bundle/skwp-greplace"] + path = vim/bundle/skwp-greplace + url = https://github.com/skwp/greplace.vim diff --git a/README.md b/README.md index 2eac928..d57f15a 100644 --- a/README.md +++ b/README.md @@ -141,9 +141,8 @@ Included vim plugins * NERDTree - everyone's favorite tree browser * NERDTree-tabs - makes NERDTree play nice with MacVim tabs so that it's on every tab * ShowMarks - creates a visual gutter to the left of the number column showing you your marks (saved locations). use \mt to toggle it, \mm to place the next available mark, \mh to delete, \ma to clear all. Use standard vim mark navigation ('X) for mark named X. - * EasyMotion - hit ,, (forward) or z,, (back) and watch the magic happen. just type the letters and jump directly to your target - * BufExplorer - access with 'B' - a big buffer window, good for when you want to search for a buffer - * LustyJuggler - access with 'S' - a small buffer window at the bottom, each buffer is assigned to a home row key in order + * EasyMotion - hit ,, (forward) or z,, (back) and watch the magic happen. just type the letters and jump directly to your target - in the provided vimrc the keys are optimized for home and upper row, no pinkies + * LustyJuggler/Explorer - hit B, type buf name to match a buffer, or type S and use the home row keys to select a buffer Git @@ -163,6 +162,7 @@ Included vim plugins Utils + * greplace - use :Gsearch to find across many files, replace inside the changes, then :Greplace to do a replace across all matches * ConqueTerm - embedded fully colorful shell inside your vim * vim-ruby-conque - helpers to run ruby,rspec,rake within ConqueTerm - use \rr (ruby), \ss (rspec), \ll (rspec line), \RR (rake) * vim-markdown-preview - :Mm to view your README.md as html diff --git a/vim/bundle/sjbach-lusty b/vim/bundle/sjbach-lusty new file mode 160000 index 0000000..6b59f0d --- /dev/null +++ b/vim/bundle/sjbach-lusty @@ -0,0 +1 @@ +Subproject commit 6b59f0db14e76d14ce979648f7592abc7d7e0cb4 diff --git a/vim/bundle/skwp-greplace b/vim/bundle/skwp-greplace new file mode 160000 index 0000000..e7fb26e --- /dev/null +++ b/vim/bundle/skwp-greplace @@ -0,0 +1 @@ +Subproject commit e7fb26ec95ecf97bfe4b99683b95cb37c654460a diff --git a/vim/bundle/tjennings-git-grep-vim b/vim/bundle/tjennings-git-grep-vim new file mode 160000 index 0000000..f340ec9 --- /dev/null +++ b/vim/bundle/tjennings-git-grep-vim @@ -0,0 +1 @@ +Subproject commit f340ec93f5071238823db6dcd2431c924fee4418 diff --git a/vim/plugin/color_sample_pack.vim b/vim/plugin/color_sample_pack.vim deleted file mode 100644 index 767af7c..0000000 --- a/vim/plugin/color_sample_pack.vim +++ /dev/null @@ -1,166 +0,0 @@ -" Vim default schemes -amenu T&hemes.D&efault.Blue :colo blue -amenu T&hemes.D&efault.DarkBlue :colo darkblue -amenu T&hemes.D&efault.Default :colo default -amenu T&hemes.D&efault.Delek :colo delek -amenu T&hemes.D&efault.Desert :colo desert -amenu T&hemes.D&efault.ElfLord :colo elflord -amenu T&hemes.D&efault.Evening :colo evening -amenu T&hemes.D&efault.Koehler :colo koehler -amenu T&hemes.D&efault.Morning :colo morning -amenu T&hemes.D&efault.Murphy :colo murphy -amenu T&hemes.D&efault.Pablo :colo pablo -amenu T&hemes.D&efault.PeachPuff :colo peachpuff -amenu T&hemes.D&efault.Ron :colo ron -amenu T&hemes.D&efault.Shine :colo shine -amenu T&hemes.D&efault.Torte :colo torte - -" Recommended Themes -amenu T&hemes.&Recommendations.InkPot :colo inkpot -amenu T&hemes.&Recommendations.MetaCosm :colo metacosm -amenu T&hemes.&Recommendations.MidNight2 :colo midnight2 -amenu T&hemes.&Recommendations.PS_Warm :let psc_style='warm':colo ps_color - -amenu T&hemes.-s1- : - -"submenu black -amenu T&hemes.black.brookstream :colo brookstream -amenu T&hemes.black.candy :colo candy -amenu T&hemes.black.colorer :colo colorer -amenu T&hemes.black.dante :colo dante -amenu T&hemes.black.darkocean :colo darkocean -amenu T&hemes.black.dw_blue :colo dw_blue -amenu T&hemes.black.dw_cyan :colo dw_cyan -amenu T&hemes.black.dw_green :colo dw_green -amenu T&hemes.black.dw_orange :colo dw_orange -amenu T&hemes.black.dw_purple :colo dw_purple -amenu T&hemes.black.dw_red :colo dw_red -amenu T&hemes.black.dw_yellow :colo dw_yellow -amenu T&hemes.black.elflord :colo elflord -amenu T&hemes.black.fnaqevan :colo fnaqevan -amenu T&hemes.black.golden :colo golden -amenu T&hemes.black.*inkpot :colo inkpot -amenu T&hemes.black.koehler :colo koehler -amenu T&hemes.black.less :colo less -amenu T&hemes.black.manxome :colo manxome -amenu T&hemes.black.matrix :colo matrix -amenu T&hemes.black.metacosm :colo metacosm -amenu T&hemes.black.*motus :colo motus -amenu T&hemes.black.murphy :colo murphy -amenu T&hemes.black.neverness :colo neverness -amenu T&hemes.black.nightwish :colo nightwish -amenu T&hemes.black.oceanblack :colo oceanblack -amenu T&hemes.black.pablo :colo pablo -amenu T&hemes.black.*ps_color :colo ps_color -amenu T&hemes.black.relaxedgreen :colo relaxedgreen -amenu T&hemes.black.ron :colo ron -amenu T&hemes.black.tango :colo tango -amenu T&hemes.black.*torte :colo torte -amenu T&hemes.black.vibrantink :colo vibrantink -amenu T&hemes.black.*vividchalk :colo vividchalk - -"submenu blue -amenu T&hemes.blue.astronaut :colo astronaut -amenu T&hemes.blue.asu1dark :colo asu1dark -amenu T&hemes.blue.blue :colo blue -amenu T&hemes.blue.bluegreen :colo bluegreen -amenu T&hemes.blue.borland :colo borland -amenu T&hemes.blue.breeze :colo breeze -amenu T&hemes.blue.darkblue :colo darkblue -amenu T&hemes.blue.darkblue2 :colo darkblue2 -amenu T&hemes.blue.denim :colo denim -amenu T&hemes.blue.desertedocean :colo desertedocean -amenu T&hemes.blue.dusk :colo dusk -amenu T&hemes.blue.hhazure :colo hhazure -amenu T&hemes.blue.midnight :colo midnight -amenu T&hemes.blue.midnight2 :colo midnight2 -amenu T&hemes.blue.navajo-night :colo navajo-night -amenu T&hemes.blue.oceandeep :colo oceandeep -amenu T&hemes.blue.sea :colo sea -amenu T&hemes.blue.softblue :colo softblue - -"submenu cyan -amenu T&hemes.cyan.darkslategray :colo darkslategray -amenu T&hemes.cyan.marklar :colo marklar - -"submenu darkgrey -amenu T&hemes.darkgrey.candycode :colo candycode -amenu T&hemes.darkgrey.darktango :colo darktango -amenu T&hemes.darkgrey.hhspring :colo hhspring -amenu T&hemes.darkgrey.hhteal :colo hhteal -amenu T&hemes.darkgrey.hhviolet :colo hhviolet -amenu T&hemes.darkgrey.lettuce :colo lettuce -amenu T&hemes.darkgrey.night :colo night -amenu T&hemes.darkgrey.rdark :colo rdark - -"submenu green -amenu T&hemes.green.*tabula :colo tabula - -"submenu grey -amenu T&hemes.grey.biogoo :colo biogoo -amenu T&hemes.grey.camo :colo camo -amenu T&hemes.grey.*dawn :colo dawn -amenu T&hemes.grey.desert :colo desert -amenu T&hemes.grey.desertEx :colo desertEx -amenu T&hemes.grey.evening :colo evening -amenu T&hemes.grey.fog :colo fog -amenu T&hemes.grey.freya :colo freya -amenu T&hemes.grey.neon :colo neon -amenu T&hemes.grey.slate :colo slate -amenu T&hemes.grey.umber-green :colo umber-green -amenu T&hemes.grey.xemacs :colo xemacs -amenu T&hemes.grey.*zenburn :colo zenburn - -"submenu magenta -amenu T&hemes.magenta.lilac :colo lilac - -"submenu offwhite -amenu T&hemes.offwhite.autumn :colo autumn -amenu T&hemes.offwhite.autumnleaf :colo autumnleaf -amenu T&hemes.offwhite.*baycomb :colo baycomb -amenu T&hemes.offwhite.fine_blue :colo fine_blue -amenu T&hemes.offwhite.fruit :colo fruit -amenu T&hemes.offwhite.habiLight :colo habiLight -amenu T&hemes.offwhite.ironman :colo ironman -amenu T&hemes.offwhite.morning :colo morning -amenu T&hemes.offwhite.nuvola :colo nuvola -amenu T&hemes.offwhite.oceanlight :colo oceanlight -amenu T&hemes.offwhite.pyte :colo pyte -amenu T&hemes.offwhite.simpleandfriendly :colo simpleandfriendly - -"submenu red -amenu T&hemes.red.ChocolateLiquor :colo ChocolateLiquor -amenu T&hemes.red.hhpink :colo hhpink -amenu T&hemes.red.*navajo :colo navajo -amenu T&hemes.red.peachpuff :colo peachpuff -amenu T&hemes.red.tomatosoup :colo tomatosoup - -"submenu unknown -amenu T&hemes.unknown.bw :colo bw -amenu T&hemes.unknown.calmar256-light :colo calmar256-light -amenu T&hemes.unknown.chela_light :colo chela_light -amenu T&hemes.unknown.colorscheme_template :colo colorscheme_template -amenu T&hemes.unknown.default :colo default -amenu T&hemes.unknown.desert256 :colo desert256 -amenu T&hemes.unknown.impact :colo impact -amenu T&hemes.unknown.psql :colo psql -amenu T&hemes.unknown.vc :colo vc - -"submenu white -amenu T&hemes.white.delek :colo delek -amenu T&hemes.white.eclipse :colo eclipse -amenu T&hemes.white.martin_krischik :colo martin_krischik -amenu T&hemes.white.*moria :colo moria -amenu T&hemes.white.print_bw :colo print_bw -amenu T&hemes.white.shine :colo shine -amenu T&hemes.white.*sienna :colo sienna -amenu T&hemes.white.tolerable :colo tolerable -amenu T&hemes.white.zellner :colo zellner - -"submenu yellow -amenu T&hemes.yellow.PapayaWhip :colo PapayaWhip -amenu T&hemes.yellow.buttercream :colo buttercream -amenu T&hemes.yellow.hhorange :colo hhorange -amenu T&hemes.yellow.olive :colo olive -amenu T&hemes.yellow.professional :colo professional -amenu T&hemes.yellow.robinhood :colo robinhood diff --git a/vim/plugin/git-grep.vim b/vim/plugin/git-grep.vim deleted file mode 100644 index 2db3482..0000000 --- a/vim/plugin/git-grep.vim +++ /dev/null @@ -1,42 +0,0 @@ -let g:gitgrepprg="git\\ grep\\ -n" - -function! GitGrep(args) - let grepprg_bak=&grepprg - exec "set grepprg=" . g:gitgrepprg - execute "silent! grep " . a:args - botright copen - let &grepprg=grepprg_bak - exec "redraw!" -endfunction - -function! GitGrepAdd(args) - let grepprg_bak=&grepprg - exec "set grepprg=" . g:gitgrepprg - execute "silent! grepadd " . a:args - botright copen - let &grepprg=grepprg_bak - exec "redraw!" -endfunction - -function! LGitGrep(args) - let grepprg_bak=&grepprg - exec "set grepprg=" . g:gitgrepprg - execute "silent! lgrep " . a:args - botright lopen - let &grepprg=grepprg_bak - exec "redraw!" -endfunction - -function! LGitGrepAdd(args) - let grepprg_bak=&grepprg - exec "set grepprg=" . g:gitgrepprg - execute "silent! lgrepadd " . a:args - botright lopen - let &grepprg=grepprg_bak - exec "redraw!" -endfunction - -command! -nargs=* -complete=file GitGrep call GitGrep() -command! -nargs=* -complete=file GitGrepAdd call GitGrepAdd() -command! -nargs=* -complete=file LGitGrep call LGitGrep() -command! -nargs=* -complete=file LGitGrepAdd call LGitGrepAdd() diff --git a/vim/plugin/lusty-explorer.vim b/vim/plugin/lusty-explorer.vim deleted file mode 100755 index 92c4376..0000000 --- a/vim/plugin/lusty-explorer.vim +++ /dev/null @@ -1,2377 +0,0 @@ -" Copyright: Copyright (C) 2007-2011 Stephen Bach -" Permission is hereby granted to use and distribute this code, -" with or without modifications, provided that this copyright -" notice is copied with it. Like anything else that's free, -" lusty-explorer.vim is provided *as is* and comes with no -" warranty of any kind, either expressed or implied. In no -" event will the copyright holder be liable for any damages -" resulting from the use of this software. -" -" Name Of File: lusty-explorer.vim -" Description: Dynamic Filesystem and Buffer Explorer Vim Plugin -" Maintainers: Stephen Bach -" Matt Tolton -" Contributors: Raimon Grau, Sergey Popov, Yuichi Tateno, Bernhard Walle, -" Rajendra Badapanda, cho45, Simo Salminen, Sami Samhuri, -" Matt Tolton, Björn Winckler, sowill, David Brown -" Brett DiFrischia, Ali Asad Lotia, Kenneth Love, Ben Boeckel, -" robquant, lilydjwg, Martin Wache, Johannes Holzfuß -" Donald Curtis, Jan Zwiener, Giuseppe Rota, Toby O'Connell -" -" Release Date: April 29, 2011 -" Version: 4.1 -" -" Usage: -" lf - Opens the filesystem explorer. -" lr - Opens the filesystem explorer from the -" directory of the current file. -" lb - Opens the buffer explorer. -" lg - Opens the buffer grep, for searching through -" all loaded buffers -" -" You can also use the commands: -" -" ":LustyFilesystemExplorer [optional-path]" -" ":LustyFilesystemExplorerFromHere" -" ":LustyBufferExplorer" -" ":LustyBufferGrep" -" -" (Personally, I map these to ,f ,r ,b and ,g) -" -" When launched, a new window appears at bottom presenting a -" table of files/dirs or buffers, and in the status bar a -" prompt: -" -" >> -" -" As you type, the table updates for possible matches using a -" fuzzy matching algorithm (or regex matching, in the case of -" grep). Special keys include: -" -" open selected match -" open selected match -" cancel -" cancel -" cancel -" -" open selected match in a new [t]ab -" open selected match in a new h[o]rizontal split -" open selected match in a new [v]ertical split -" -" select [n]ext match -" select [p]revious match -" select [f]orward one column -" select [b]ack one column -" -" clear prompt -" -" Additional shortcuts for the filesystem explorer: -" -" ascend one directory at prompt -" [r]efresh directory contents -" open [a]ll files in current table -" create new buffer with the given name and path -" -" Filesystem Explorer: -" -" - Directory contents are memoized. ( to refresh.) -" - You can recurse into and out of directories by typing the directory name -" and a slash, e.g. "stuff/" or "../". -" - Variable expansion, e.g. "$D" -> "/long/dir/path/". -" - Tilde (~) expansion, e.g. "~/" -> "/home/steve/". -" - Dotfiles are hidden by default, but are shown if the current search term -" begins with a '.'. To show these file at all times, set this option: -" -" let g:LustyExplorerAlwaysShowDotFiles = 1 -" -" You can prevent certain files from appearing in the table with the -" following variable: -" -" set wildignore=*.o,*.fasl,CVS -" -" The above will mask all object files, compiled lisp files, and -" files/directories named CVS from appearing in the table. Note that they -" can still be opened by being named explicitly. -" -" See :help 'wildignore' for more information. -" -" Buffer Explorer: -" -" - Buffers are sorted first by fuzzy match and then by most-recently used. -" - The currently active buffer is highlighted. -" -" Buffer Grep: -" -" - Searches all loaded buffers. -" - Uses Ruby-style regexes instead of Vim style. This means: -" -" - \b instead of \< or \> for beginning/end of word. -" - (foo|bar) instead of \(foo\|bar\) -" - {2,5} instead of \{2,5} -" - + instead of \+ -" - Generally, fewer backslashes. :-) -" -" - For now, searches are always case-insensitive. -" - Matches from the previous grep are remembered upon relaunch; clear with -" . -" -" -" Install Details: -" -" Copy this file into $HOME/.vim/plugin directory so that it will be sourced -" on startup automatically. -" -" Note! This plugin requires Vim be compiled with Ruby interpretation. If you -" don't know if your build of Vim has this functionality, you can check by -" running "vim --version" from the command line and looking for "+ruby". -" Alternatively, just try sourcing this script. -" -" If your version of Vim does not have "+ruby" but you would still like to -" use this plugin, you can fix it. See the "Check for Ruby functionality" -" comment below for instructions. -" -" If you are using the same Vim configuration and plugins for multiple -" machines, some of which have Ruby and some of which don't, you may want to -" turn off the "Sorry, LustyExplorer requires ruby" warning. You can do so -" like this (in .vimrc): -" -" let g:LustyExplorerSuppressRubyWarning = 1 -" -" -" Contributing: -" -" Patches and suggestions welcome. Note: lusty-explorer.vim is a generated -" file; if you'd like to submit a patch, check out the Github development -" repository: -" -" http://github.com/sjbach/lusty -" -" -" GetLatestVimScripts: 1890 1 :AutoInstall: lusty-explorer.vim -" -" TODO: -" - when an edited file is in nowrap mode and the explorer is called while the -" current window is scrolled to the right, name truncation occurs. -" - enable VimSwaps stuff -" - set callback when pipe is ready for read and force refresh() -" - uppercase character should make matching case-sensitive -" - FilesystemGrep -" - C-jhkl navigation to highlight a file? - -" Exit quickly when already loaded. -if exists("g:loaded_lustyexplorer") - finish -endif - -if &compatible - echohl ErrorMsg - echo "LustyExplorer is not designed to run in &compatible mode;" - echo "To use this plugin, first disable vi-compatible mode like so:\n" - - echo " :set nocompatible\n" - - echo "Or even better, just create an empty .vimrc file." - echohl none - finish -endif - -if exists("g:FuzzyFinderMode.TextMate") - echohl WarningMsg - echo "Warning: LustyExplorer detects the presence of fuzzyfinder_textmate;" - echo "that plugin often interacts poorly with other Ruby plugins." - echo "If LustyExplorer gives you an error, you can probably fix it by" - echo "renaming fuzzyfinder_textmate.vim to zzfuzzyfinder_textmate.vim so" - echo "that it is last in the load order." - echohl none -endif - -" Check for Ruby functionality. -if !has("ruby") || version < 700 - if !exists("g:LustyExplorerSuppressRubyWarning") || - \ g:LustyExplorerSuppressRubyWarning == "0" - if !exists("g:LustyJugglerSuppressRubyWarning") || - \ g:LustyJugglerSuppressRubyWarning == "0" - echohl ErrorMsg - echon "Sorry, LustyExplorer requires ruby. " - echon "Here are some tips for adding it:\n" - - echo "Debian / Ubuntu:" - echo " # apt-get install vim-ruby\n" - - echo "Fedora:" - echo " # yum install vim-enhanced\n" - - echo "Gentoo:" - echo " # USE=\"ruby\" emerge vim\n" - - echo "FreeBSD:" - echo " # pkg_add -r vim+ruby\n" - - echo "Windows:" - echo " 1. Download and install Ruby from here:" - echo " http://www.ruby-lang.org/" - echo " 2. Install a Vim binary with Ruby support:" - echo " http://segfault.hasno.info/vim/gvim72.zip\n" - - echo "Manually (including Cygwin):" - echo " 1. Install Ruby." - echo " 2. Download the Vim source package (say, vim-7.0.tar.bz2)" - echo " 3. Build and install:" - echo " # tar -xvjf vim-7.0.tar.bz2" - echo " # ./configure --enable-rubyinterp" - echo " # make && make install" - - echo "(If you just wish to stifle this message, set the following option:" - echo " let g:LustyExplorerSuppressRubyWarning = 1)" - echohl none - endif - endif - finish -endif - -if ! &hidden - echohl WarningMsg - echo "You are running with 'hidden' mode off. LustyExplorer may" - echo "sometimes emit error messages in this mode -- you should turn" - echo "it on, like so:\n" - - echo " :set hidden\n" - - echo "Even better, put this in your .vimrc file." - echohl none -endif - -let g:loaded_lustyexplorer = "yep" - -" Commands. -command LustyBufferExplorer :call LustyBufferExplorerStart() -command -nargs=? LustyFilesystemExplorer :call LustyFilesystemExplorerStart("") -command LustyFilesystemExplorerFromHere :call LustyFilesystemExplorerStart(expand("%:p:h")) -command LustyBufferGrep :call LustyBufferGrepStart() - -" Deprecated command names. -command BufferExplorer :call - \ deprecated('BufferExplorer', 'LustyBufferExplorer') -command FilesystemExplorer :call - \ deprecated('FilesystemExplorer', 'LustyFilesystemExplorer') -command FilesystemExplorerFromHere :call - \ deprecated('FilesystemExplorerFromHere', - \ 'LustyFilesystemExplorerFromHere') - -function! s:deprecated(old, new) - echohl WarningMsg - echo ":" . a:old . " is deprecated; use :" . a:new . " instead." - echohl none -endfunction - - -" Default mappings. -nmap lf :LustyFilesystemExplorer -nmap lr :LustyFilesystemExplorerFromHere -nmap lb :LustyBufferExplorer -nmap lg :LustyBufferGrep - -" Vim-to-ruby function calls. -function! s:LustyFilesystemExplorerStart(path) - exec "ruby LustyE::profile() { $lusty_filesystem_explorer.run_from_path('".a:path."') }" -endfunction - -function! s:LustyBufferExplorerStart() - ruby LustyE::profile() { $lusty_buffer_explorer.run } -endfunction - -function! s:LustyBufferGrepStart() - ruby LustyE::profile() { $lusty_buffer_grep.run } -endfunction - -function! s:LustyFilesystemExplorerCancel() - ruby LustyE::profile() { $lusty_filesystem_explorer.cancel } -endfunction - -function! s:LustyBufferExplorerCancel() - ruby LustyE::profile() { $lusty_buffer_explorer.cancel } -endfunction - -function! s:LustyBufferGrepCancel() - ruby LustyE::profile() { $lusty_buffer_grep.cancel } -endfunction - -function! s:LustyFilesystemExplorerKeyPressed(code_arg) - ruby LustyE::profile() { $lusty_filesystem_explorer.key_pressed } -endfunction - -function! s:LustyBufferExplorerKeyPressed(code_arg) - ruby LustyE::profile() { $lusty_buffer_explorer.key_pressed } -endfunction - -function! s:LustyBufferGrepKeyPressed(code_arg) - ruby LustyE::profile() { $lusty_buffer_grep.key_pressed } -endfunction - -" Setup the autocommands that handle buffer MRU ordering. -augroup LustyExplorer - autocmd! - autocmd BufEnter * ruby LustyE::profile() { $le_buffer_stack.push } - autocmd BufDelete * ruby LustyE::profile() { $le_buffer_stack.pop } - autocmd BufWipeout * ruby LustyE::profile() { $le_buffer_stack.pop } -augroup End - -ruby << EOF - -require 'pathname' -# For IO#ready -- but Cygwin doesn't have io/wait. -require 'io/wait' unless RUBY_PLATFORM =~ /cygwin/ -# Needed for String#each_char in Ruby 1.8 on some platforms. -require 'jcode' unless "".respond_to? :each_char -# Needed for Array#each_slice in Ruby 1.8 on some platforms. -require 'enumerator' unless [].respond_to? :each_slice - -$LUSTY_PROFILING = false - -if $LUSTY_PROFILING - require 'rubygems' - require 'ruby-prof' -end - - -module VIM - - unless const_defined? "MOST_POSITIVE_INTEGER" - MOST_POSITIVE_INTEGER = 2**(32 - 1) - 2 # Vim ints are signed 32-bit. - end - - def self.zero?(var) - # In Vim 7.2 and older, VIM::evaluate returns Strings for boolean - # expressions; in later versions, Fixnums. - case var - when String - var == "0" - when Fixnum - var == 0 - else - LustyE::assert(false, "unexpected type: #{var.class}") - end - end - - def self.nonzero?(var) - not zero?(var) - end - - def self.evaluate_bool(var) - nonzero? evaluate(var) - end - - def self.exists?(s) - nonzero? evaluate("exists('#{s}')") - end - - def self.has_syntax? - nonzero? evaluate('has("syntax")') - end - - def self.has_ext_maparg? - # The 'dict' parameter to mapargs() was introduced in Vim 7.3.32 - nonzero? evaluate('v:version > 703 || (v:version == 703 && has("patch32"))') - end - - def self.columns - evaluate("&columns").to_i - end - - def self.lines - evaluate("&lines").to_i - end - - def self.getcwd - evaluate("getcwd()") - end - - def self.bufname(i) - if evaluate_bool("empty(bufname(#{i}))") - "" - else - evaluate("bufname(#{i})") - end - end - - def self.single_quote_escape(s) - # Everything in a Vim single-quoted string is literal, except single - # quotes. Single quotes are escaped by doubling them. - s.gsub("'", "''") - end - - def self.filename_escape(s) - # Escape slashes, open square braces, spaces, sharps, double quotes and - # percent signs. - s.gsub(/\\/, '\\\\\\').gsub(/[\[ #"%]/, '\\\\\0') - end - - def self.regex_escape(s) - s.gsub(/[\]\[.~"^$\\*]/,'\\\\\0') - end - - class Buffer - def modified? - VIM::nonzero? VIM::evaluate("getbufvar(#{number()}, '&modified')") - end - - def listed? - VIM::nonzero? VIM::evaluate("getbufvar(#{number()}, '&buflisted')") - end - - def self.obj_for_bufnr(n) - # There's gotta be a better way to do this... - (0..VIM::Buffer.count-1).each do |i| - obj = VIM::Buffer[i] - return obj if obj.number == n - end - - return nil - end - end - - # Print with colours - def self.pretty_msg(*rest) - return if rest.length == 0 - return if rest.length % 2 != 0 - - command "redraw" # see :help echo-redraw - i = 0 - while i < rest.length do - command "echohl #{rest[i]}" - command "echon '#{rest[i+1]}'" - i += 2 - end - - command 'echohl None' - end -end - -# Hack for wide CJK characters. -if VIM::exists?("*strwidth") - module VIM - def self.strwidth(s) - # strwidth() is defined in Vim 7.3. - evaluate("strwidth('#{single_quote_escape(s)}')").to_i - end - end -else - module VIM - def self.strwidth(s) - s.length - end - end -end - - -# Utility functions. -module LustyE - - unless const_defined? "MOST_POSITIVE_FIXNUM" - MOST_POSITIVE_FIXNUM = 2**(0.size * 8 -2) -1 - end - - def self.simplify_path(s) - s = s.gsub(/\/+/, '/') # Remove redundant '/' characters - begin - if s[0] == ?~ - # Tilde expansion - First expand the ~ part (e.g. '~' or '~steve') - # and then append the rest of the path. We can't just call - # expand_path() or it'll throw on bad paths. - s = File.expand_path(s.sub(/\/.*/,'')) + \ - s.sub(/^[^\/]+/,'') - end - - if s == '/' - # Special-case root so we don't add superfluous '/' characters, - # as this can make Cygwin choke. - s - elsif ends_with?(s, File::SEPARATOR) - File.expand_path(s) + File::SEPARATOR - else - dirname_expanded = File.expand_path(File.dirname(s)) - if dirname_expanded == '/' - dirname_expanded + File.basename(s) - else - dirname_expanded + File::SEPARATOR + File.basename(s) - end - end - rescue ArgumentError - s - end - end - - def self.longest_common_prefix(paths) - prefix = paths[0] - paths.each do |path| - for i in 0...prefix.length - if path.length <= i or prefix[i] != path[i] - prefix = prefix[0...i] - prefix = prefix[0..(prefix.rindex('/') or -1)] - break - end - end - end - - prefix - end - - def self.ready_for_read?(io) - if io.respond_to? :ready? - ready? - else - result = IO.select([io], nil, nil, 0) - result && (result.first.first == io) - end - end - - def self.ends_with?(s1, s2) - tail = s1[-s2.length, s2.length] - tail == s2 - end - - def self.starts_with?(s1, s2) - head = s1[0, s2.length] - head == s2 - end - - def self.option_set?(opt_name) - opt_name = "g:LustyExplorer" + opt_name - VIM::evaluate_bool("exists('#{opt_name}') && #{opt_name} != '0'") - end - - def self.profile - # Profile (if enabled) and provide better - # backtraces when there's an error. - - if $LUSTY_PROFILING - if not RubyProf.running? - RubyProf.measure_mode = RubyProf::WALL_TIME - RubyProf.start - else - RubyProf.resume - end - end - - begin - yield - rescue Exception => e - puts e - puts e.backtrace - end - - if $LUSTY_PROFILING and RubyProf.running? - RubyProf.pause - end - end - - class AssertionError < StandardError ; end - - def self.assert(condition, message = 'assertion failure') - raise AssertionError.new(message) unless condition - end - - def self.d(s) - # (Debug print) - $stderr.puts s - end -end - - -# Mercury fuzzy matching algorithm, written by Matt Tolton. -# based on the Quicksilver and LiquidMetal fuzzy matching algorithms -class Mercury - public - def self.score(string, abbrev) - return self.new(string, abbrev).score() - end - - def score() - return @@SCORE_TRAILING if @abbrev.empty? - return @@SCORE_NO_MATCH if @abbrev.length > @string.length - - raw_score = raw_score(0, 0, 0, false) - return raw_score / @string.length - end - - def initialize(string, abbrev) - @string = string - @lower_string = string.downcase() - @abbrev = abbrev.downcase() - @level = 0 - @branches = 0 - end - - private - @@SCORE_NO_MATCH = 0.0 # do not change, this is assumed to be 0.0 - @@SCORE_EXACT_MATCH = 1.0 - @@SCORE_MATCH = 0.9 - @@SCORE_TRAILING = 0.7 - @@SCORE_TRAILING_BUT_STARTED = 0.80 - @@SCORE_BUFFER = 0.70 - @@SCORE_BUFFER_BUT_STARTED = 0.80 - - @@BRANCH_LIMIT = 100 - - #def raw_score(a, b, c, d) - # @level += 1 - # puts "#{' ' * @level}#{a}, #{b}, #{c}, #{d}" - # ret = recurse_and_score(a, b, c, d) - # puts "#{' ' * @level}#{a}, #{b}, #{c}, #{d} -> #{ret}" - # @level -= 1 - # return ret - #end - - def raw_score(abbrev_idx, match_idx, score_idx, first_char_matched) - index = @lower_string.index(@abbrev[abbrev_idx], match_idx) - return 0.0 if index.nil? - - # TODO Instead of having two scores, should there be a sliding "match" - # score based on the distance of the matched character to the beginning - # of the string? - if abbrev_idx == index - score = @@SCORE_EXACT_MATCH - else - score = @@SCORE_MATCH - end - - started = (index == 0 or first_char_matched) - - # If matching on a word boundary, score the characters since the last match - if index > score_idx - buffer_score = started ? @@SCORE_BUFFER_BUT_STARTED : @@SCORE_BUFFER - if " \t/._-".include?(@string[index - 1]) - score += @@SCORE_MATCH - score += buffer_score * ((index - 1) - score_idx) - elsif @string[index] >= "A"[0] and @string[index] <= "Z"[0] - score += buffer_score * (index - score_idx) - end - end - - if abbrev_idx + 1 == @abbrev.length - trailing_score = started ? @@SCORE_TRAILING_BUT_STARTED : @@SCORE_TRAILING - # We just matched the last character in the pattern - score += trailing_score * (@string.length - (index + 1)) - else - tail_score = raw_score(abbrev_idx + 1, index + 1, index + 1, started) - return 0.0 if tail_score == 0.0 - score += tail_score - end - - if @branches < @@BRANCH_LIMIT - @branches += 1 - alternate = raw_score(abbrev_idx, - index + 1, - score_idx, - first_char_matched) - #puts "#{' ' * @level}#{score}, #{alternate}" - score = [score, alternate].max - end - - return score - end -end - - -module LustyE - -# Abstract base class. -class Entry - attr_accessor :full_name, :short_name, :label - def initialize(full_name, short_name, label) - @full_name = full_name - @short_name = short_name - @label = label - end - - # NOTE: very similar to BufferStack::shorten_paths() - def self.compute_buffer_entries() - buffer_entries = [] - - $le_buffer_stack.numbers.each do |n| - o = VIM::Buffer.obj_for_bufnr(n) - next if (o.nil? or not o.listed?) - buffer_entries << self.new(o, n) - end - - # Put the current buffer at the end of the list. - buffer_entries << buffer_entries.shift - - # Shorten each buffer name by removing all path elements which are not - # needed to differentiate a given name from other names. This usually - # results in only the basename shown, but if several buffers of the - # same basename are opened, there will be more. - - # Group the buffers by common basename - common_base = Hash.new { |hash, k| hash[k] = [] } - buffer_entries.each do |entry| - if entry.full_name - basename = Pathname.new(entry.full_name).basename.to_s - common_base[basename] << entry - end - end - - # Determine the longest common prefix for each basename group. - basename_to_prefix = {} - common_base.each do |base, entries| - if entries.length > 1 - full_names = entries.map { |e| e.full_name } - basename_to_prefix[base] = LustyE::longest_common_prefix(full_names) - end - end - - # Compute shortened buffer names by removing prefix, if possible. - buffer_entries.each do |entry| - full_name = entry.full_name - - short_name = if full_name.nil? - '[No Name]' - elsif LustyE::starts_with?(full_name, "scp://") - full_name - else - base = Pathname.new(full_name).basename.to_s - prefix = basename_to_prefix[base] - - prefix ? full_name[prefix.length..-1] \ - : base - end - - entry.short_name = short_name - end - - buffer_entries - end -end - -# Used in FilesystemExplorer -class FilesystemEntry < Entry - attr_accessor :current_score - def initialize(label) - super("::UNSET::", "::UNSET::", label) - @current_score = 0.0 - end -end - -# Used in BufferExplorer -class BufferEntry < Entry - attr_accessor :vim_buffer, :mru_placement, :current_score - def initialize(vim_buffer, mru_placement) - super(vim_buffer.name, "::UNSET::", "::UNSET::") - @vim_buffer = vim_buffer - @mru_placement = mru_placement - @current_score = 0.0 - end -end - -# Used in BufferGrep -class GrepEntry < Entry - attr_accessor :vim_buffer, :mru_placement, :line_number - def initialize(vim_buffer, mru_placement) - super(vim_buffer.name, "::UNSET::", "::UNSET::") - @vim_buffer = vim_buffer - @mru_placement = mru_placement - @line_number = 0 - end -end - -end - - -# Abstract base class; extended as BufferExplorer, FilesystemExplorer -module LustyE -class Explorer - public - def initialize - @settings = SavedSettings.new - @display = Display.new title() - @prompt = nil - @current_sorted_matches = [] - @running = false - end - - def run - return if @running - - @settings.save - @running = true - @calling_window = $curwin - @saved_alternate_bufnum = if VIM::evaluate_bool("expand('#') == ''") - nil - else - VIM::evaluate("bufnr(expand('#'))") - end - create_explorer_window() - refresh(:full) - end - - def key_pressed() - # Grab argument from the Vim function. - i = VIM::evaluate("a:code_arg").to_i - refresh_mode = :full - - case i - when 32..126 # Printable characters - c = i.chr - @prompt.add! c - @selected_index = 0 - when 8 # Backspace/Del/C-h - @prompt.backspace! - @selected_index = 0 - when 9, 13 # Tab and Enter - choose(:current_tab) - when 23 # C-w (delete 1 dir backward) - @prompt.up_one_dir! - @selected_index = 0 - when 14 # C-n (select next) - @selected_index = \ - if @current_sorted_matches.size.zero? - 0 - else - (@selected_index + 1) % @current_sorted_matches.size - end - refresh_mode = :no_recompute - when 16 # C-p (select previous) - @selected_index = \ - if @current_sorted_matches.size.zero? - 0 - else - (@selected_index - 1) % @current_sorted_matches.size - end - refresh_mode = :no_recompute - when 6 # C-f (select right) - @selected_index = \ - if @row_count.nil? || @row_count.zero? - 0 - else - columns = \ - (@current_sorted_matches.size.to_f / @row_count.to_f).ceil - cur_column = @selected_index / @row_count - cur_row = @selected_index % @row_count - new_column = (cur_column + 1) % columns - if (new_column + 1) * (cur_row + 1) > @current_sorted_matches.size - new_column = 0 - end - new_column * @row_count + cur_row - end - refresh_mode = :no_recompute - when 2 # C-b (select left) - @selected_index = \ - if @row_count.nil? || @row_count.zero? - 0 - else - columns = \ - (@current_sorted_matches.size.to_f / @row_count.to_f).ceil - cur_column = @selected_index / @row_count - cur_row = @selected_index % @row_count - new_column = (cur_column - 1) % columns - if (new_column + 1) * (cur_row + 1) > @current_sorted_matches.size - new_column = columns - 2 - end - new_column * @row_count + cur_row - end - refresh_mode = :no_recompute - when 15 # C-o choose in new horizontal split - choose(:new_split) - when 20 # C-t choose in new tab - choose(:new_tab) - when 21 # C-u clear prompt - @prompt.clear! - @selected_index = 0 - when 22 # C-v choose in new vertical split - choose(:new_vsplit) - end - - refresh(refresh_mode) - end - - def cancel - if @running - cleanup() - # fix alternate file - if @saved_alternate_bufnum - cur = $curbuf - VIM::command "silent b #{@saved_alternate_bufnum}" - VIM::command "silent b #{cur.number}" - end - - if $LUSTY_PROFILING - outfile = File.new('lusty-explorer-rbprof.html', 'a') - #RubyProf::CallTreePrinter.new(RubyProf.stop).print(outfile) - RubyProf::GraphHtmlPrinter.new(RubyProf.stop).print(outfile) - end - end - end - - private - def refresh(mode) - return if not @running - - if mode == :full - @current_sorted_matches = compute_sorted_matches() - end - - on_refresh() - highlight_selected_index() if VIM::has_syntax? - @row_count = @display.print @current_sorted_matches.map { |x| x.label } - @prompt.print Display.max_width - end - - def create_explorer_window - # Trim out the "::" in "LustyE::FooExplorer" - key_binding_prefix = 'Lusty' + self.class.to_s.sub(/.*::/,'') - - @display.create(key_binding_prefix) - set_syntax_matching() - end - - def highlight_selected_index - # Note: overridden by BufferGrep - VIM::command 'syn clear LustySelected' - - entry = @current_sorted_matches[@selected_index] - return if entry.nil? - - escaped = VIM::regex_escape(entry.label) - label_match_string = Display.entry_syntaxify(escaped, false) - VIM::command "syn match LustySelected \"#{label_match_string}\" " \ - 'contains=LustyGrepMatch' - end - - def choose(open_mode) - entry = @current_sorted_matches[@selected_index] - return if entry.nil? - open_entry(entry, open_mode) - end - - def cleanup - @display.close - Window.select @calling_window - @settings.restore - @running = false - VIM::message "" - LustyE::assert(@calling_window == $curwin) - end - - # Pure virtual methods - # - set_syntax_matching - # - on_refresh - # - open_entry - # - compute_sorted_matches - -end -end - - -module LustyE -class BufferExplorer < Explorer - public - def initialize - super - @prompt = Prompt.new - @buffer_entries = [] - end - - def run - unless @running - @prompt.clear! - @curbuf_at_start = VIM::Buffer.current - @buffer_entries = BufferEntry::compute_buffer_entries() - @buffer_entries.each do |e| - # Show modification indicator - e.label = e.short_name - e.label << " [+]" if e.vim_buffer.modified? - # Disabled: show buffer number next to name - #e.label << " #{buffer.number.to_s}" - end - - @selected_index = 0 - super - end - end - - private - def title - '[LustyExplorer-Buffers]' - end - - def set_syntax_matching - # Base highlighting -- more is set on refresh. - if VIM::has_syntax? - VIM::command 'syn match LustySlash "/" contained' - VIM::command 'syn match LustyDir "\%(\S\+ \)*\S\+/" ' \ - 'contains=LustySlash' - VIM::command 'syn match LustyModified " \[+\]"' - end - end - - def curbuf_match_string - curbuf = @buffer_entries.find { |x| x.vim_buffer == @curbuf_at_start } - if curbuf - escaped = VIM::regex_escape(curbuf.label) - Display.entry_syntaxify(escaped, @prompt.insensitive?) - else - "" - end - end - - def on_refresh - # Highlighting for the current buffer name. - if VIM::has_syntax? - VIM::command 'syn clear LustyCurrentBuffer' - VIM::command 'syn match LustyCurrentBuffer ' \ - "\"#{curbuf_match_string()}\" " \ - 'contains=LustyModified' - end - end - - def current_abbreviation - @prompt.input - end - - def compute_sorted_matches - abbrev = current_abbreviation() - - if abbrev.length == 0 - # Take (current) MRU order if we have no abbreviation. - @buffer_entries - else - matching_entries = \ - @buffer_entries.select { |x| - x.current_score = Mercury.score(x.short_name, abbrev) - x.current_score != 0.0 - } - - # Sort by score. - matching_entries.sort! { |x, y| - if x.current_score == y.current_score - x.mru_placement <=> y.mru_placement - else - y.current_score <=> x.current_score - end - } - end - end - - def open_entry(entry, open_mode) - cleanup() - LustyE::assert($curwin == @calling_window) - - number = entry.vim_buffer.number - LustyE::assert(number) - - cmd = case open_mode - when :current_tab - "b" - when :new_tab - # For some reason just using tabe or e gives an error when - # the alternate-file isn't set. - "tab split | b" - when :new_split - "sp | b" - when :new_vsplit - "vs | b" - else - LustyE::assert(false, "bad open mode") - end - - VIM::command "silent #{cmd} #{number}" - end -end -end - - -module LustyE -class FilesystemExplorer < Explorer - public - def initialize - super - @prompt = FilesystemPrompt.new - @memoized_dir_contents = {} - end - - def run - return if @running - - FileMasks.create_glob_masks() - @vim_swaps = VimSwaps.new - @selected_index = 0 - super - end - - def run_from_path(path) - return if @running - if path.empty? - path = VIM::getcwd() - end - if path.respond_to?(:force_encoding) - path = path.force_encoding(VIM::evaluate('&enc')) - end - @prompt.set!(path + File::SEPARATOR) - run() - end - - def key_pressed() - i = VIM::evaluate("a:code_arg").to_i - - case i - when 1, 10 # , - cleanup() - # Open all non-directories currently in view. - @current_sorted_matches.each do |e| - path_str = \ - if @prompt.at_dir? - @prompt.input + e.label - else - dir = @prompt.dirname - if dir == '/' - dir + e.label - else - dir + File::SEPARATOR + e.label - end - end - - load_file(path_str, :current_tab) unless File.directory?(path_str) - end - when 5 # edit file, create it if necessary - if not @prompt.at_dir? - cleanup() - # Force a reread of this directory so that the new file will - # show up (as long as it is saved before the next run). - @memoized_dir_contents.delete(view_path()) - load_file(@prompt.input, :current_tab) - end - when 18 # refresh - @memoized_dir_contents.delete(view_path()) - refresh(:full) - else - super - end - end - - private - def title - '[LustyExplorer-Files]' - end - - def set_syntax_matching - # Base highlighting -- more is set on refresh. - if VIM::has_syntax? - VIM::command 'syn match LustySlash "/" contained' - VIM::command 'syn match LustyDir "\%(\S\+ \)*\S\+/" ' \ - 'contains=LustySlash' - end - end - - def on_refresh - if VIM::has_syntax? - VIM::command 'syn clear LustyFileWithSwap' - - view = view_path() - @vim_swaps.file_names.each do |file_with_swap| - if file_with_swap.dirname == view - base = file_with_swap.basename - escaped = VIM::regex_escape(base.to_s) - match_str = Display.entry_syntaxify(escaped, false) - VIM::command "syn match LustyFileWithSwap \"#{match_str}\"" - end - end - end - - # TODO: restore highlighting for open buffers? - end - - def current_abbreviation - if @prompt.at_dir? - "" - else - File.basename(@prompt.input) - end - end - - def view_path - input = @prompt.input - - path = \ - if @prompt.at_dir? and \ - input.length > 1 # Not root - # The last element in the path is a directory + '/' and we want to - # see what's in it instead of what's in its parent directory. - - Pathname.new(input[0..-2]) # Canonicalize by removing trailing '/' - else - Pathname.new(input).dirname - end - - return path - end - - def all_files_at_view - view = view_path() - - unless @memoized_dir_contents.has_key?(view) - - if not view.directory? - return [] - elsif not view.readable? - # TODO: show "-- PERMISSION DENIED --" - return [] - end - - # Generate an array of the files - entries = [] - view_str = view.to_s - unless LustyE::ends_with?(view_str, File::SEPARATOR) - # Don't double-up on '/' -- makes Cygwin sad. - view_str << File::SEPARATOR - end - - Dir.foreach(view_str) do |name| - next if name == "." # Skip pwd - next if name == ".." and LustyE::option_set?("AlwaysShowDotFiles") - - # Hide masked files. - next if FileMasks.masked?(name) - - if FileTest.directory?(view_str + name) - name << File::SEPARATOR - end - entries << FilesystemEntry.new(name) - end - @memoized_dir_contents[view] = entries - end - - all = @memoized_dir_contents[view] - - if LustyE::option_set?("AlwaysShowDotFiles") or \ - current_abbreviation()[0] == ?. - all - else - # Filter out dotfiles if the current abbreviation doesn't start with - # '.'. - all.select { |x| x.label[0] != ?. } - end - end - - def compute_sorted_matches - abbrev = current_abbreviation() - - unsorted = all_files_at_view() - - if abbrev.length == 0 - # Sort alphabetically if we have no abbreviation. - unsorted.sort { |x, y| x.label <=> y.label } - else - matches = \ - unsorted.select { |x| - x.current_score = Mercury.score(x.label, abbrev) - x.current_score != 0.0 - } - - if abbrev == '.' - # Sort alphabetically, otherwise it just looks weird. - matches.sort! { |x, y| x.label <=> y.label } - else - # Sort by score. - matches.sort! { |x, y| y.current_score <=> x.current_score } - end - end - end - - def open_entry(entry, open_mode) - path = view_path() + entry.label - - if File.directory?(path) - # Recurse into the directory instead of opening it. - @prompt.set!(path.to_s) - @selected_index = 0 - elsif entry.label.include?(File::SEPARATOR) - # Don't open a fake file/buffer with "/" in its name. - return - else - cleanup() - load_file(path.to_s, open_mode) - end - end - - def load_file(path_str, open_mode) - LustyE::assert($curwin == @calling_window) - # Escape for Vim and remove leading ./ for files in pwd. - filename_escaped = VIM::filename_escape(path_str).sub(/^\.\//,"") - single_quote_escaped = VIM::single_quote_escape(filename_escaped) - sanitized = VIM::evaluate "fnamemodify('#{single_quote_escaped}', ':.')" - cmd = case open_mode - when :current_tab - "e" - when :new_tab - "tabe" - when :new_split - "sp" - when :new_vsplit - "vs" - else - LustyE::assert(false, "bad open mode") - end - - VIM::command "silent #{cmd} #{sanitized}" - end -end -end - - -# TODO: -# - some way for user to indicate case-sensitive regex -# - add slash highlighting back to file name? - -module LustyE -class BufferGrep < Explorer - public - def initialize - super - @display.single_column_mode = true - @prompt = Prompt.new - @buffer_entries = [] - @matched_strings = [] - - # State from previous run, so you don't have to retype - # your search each time to get the previous entries. - @previous_input = '' - @previous_grep_entries = [] - @previous_matched_strings = [] - @previous_selected_index = 0 - end - - def run - return if @running - - @prompt.set! @previous_input - @buffer_entries = GrepEntry::compute_buffer_entries() - - @selected_index = @previous_selected_index - super - end - - private - def title - '[LustyExplorer-BufferGrep]' - end - - def set_syntax_matching - VIM::command 'syn clear LustyGrepFileName' - VIM::command 'syn clear LustyGrepLineNumber' - VIM::command 'syn clear LustyGrepContext' - - # Base syntax matching -- others are set on refresh. - - VIM::command \ - 'syn match LustyGrepFileName "^\zs.\{-}\ze:\d\+:" ' \ - 'contains=NONE ' \ - 'nextgroup=LustyGrepLineNumber' - - VIM::command \ - 'syn match LustyGrepLineNumber ":\d\+:" ' \ - 'contained ' \ - 'contains=NONE ' \ - 'nextgroup=LustyGrepContext' - - VIM::command \ - 'syn match LustyGrepContext ".*" ' \ - 'transparent ' \ - 'contained ' \ - 'contains=LustyGrepMatch' - end - - def on_refresh - if VIM::has_syntax? - - VIM::command 'syn clear LustyGrepMatch' - - if not @matched_strings.empty? - sub_regexes = @matched_strings.map { |s| VIM::regex_escape(s) } - syntax_regex = '\%(' + sub_regexes.join('\|') + '\)' - VIM::command "syn match LustyGrepMatch \"#{syntax_regex}\" " \ - "contained " \ - "contains=NONE" - end - end - end - - def highlight_selected_index - VIM::command 'syn clear LustySelected' - - entry = @current_sorted_matches[@selected_index] - return if entry.nil? - - match_string = "#{entry.short_name}:#{entry.line_number}:" - escaped = VIM::regex_escape(match_string) - VIM::command "syn match LustySelected \"^#{match_string}\" " \ - 'contains=NONE ' \ - 'nextgroup=LustyGrepContext' - end - - def current_abbreviation - @prompt.input - end - - def compute_sorted_matches - abbrev = current_abbreviation() - - grep_entries = @previous_grep_entries - @matched_strings = @previous_matched_strings - - @previous_input = '' - @previous_grep_entries = [] - @previous_matched_strings = [] - @previous_selected_index = 0 - - if not grep_entries.empty? - return grep_entries - elsif abbrev == '' - @buffer_entries.each do |e| - e.label = e.short_name - end - return @buffer_entries - end - - begin - regex = Regexp.compile(abbrev, Regexp::IGNORECASE) - rescue RegexpError => e - return [] - end - - max_visible_entries = Display.max_height - - # Used to avoid duplicating match strings, which slows down refresh. - highlight_hash = {} - - # Search through every line of every open buffer for the - # given expression. - @buffer_entries.each do |entry| - vim_buffer = entry.vim_buffer - line_count = vim_buffer.count - (1..line_count). each do |i| - line = vim_buffer[i] - match = regex.match(line) - if match - matched_str = match.to_s - - grep_entry = entry.clone() - grep_entry.line_number = i - grep_entry.label = "#{grep_entry.short_name}:#{i}:#{line}" - grep_entries << grep_entry - - # Keep track of all matched strings - unless highlight_hash[matched_str] - @matched_strings << matched_str - highlight_hash[matched_str] = true - end - - if grep_entries.length > max_visible_entries - return grep_entries - end - end - end - end - - return grep_entries - end - - def open_entry(entry, open_mode) - cleanup() - LustyE::assert($curwin == @calling_window) - - number = entry.vim_buffer.number - LustyE::assert(number) - - cmd = case open_mode - when :current_tab - "b" - when :new_tab - # For some reason just using tabe or e gives an error when - # the alternate-file isn't set. - "tab split | b" - when :new_split - "sp | b" - when :new_vsplit - "vs | b" - else - LustyE::assert(false, "bad open mode") - end - - # Open buffer and go to the line number. - VIM::command "silent #{cmd} #{number}" - VIM::command "#{entry.line_number}" - end - - def cleanup - @previous_input = @prompt.input - @previous_grep_entries = @current_sorted_matches - @previous_matched_strings = @matched_strings - @previous_selected_index = @selected_index - super - end -end -end - - -module LustyE - -# Used in BufferExplorer -class Prompt - private - @@PROMPT = ">> " - - public - def initialize - clear! - end - - def clear! - @input = "" - end - - def print(max_width = 0) - text = @input - # may need some extra characters for "..." and spacing - max_width -= 5 - if max_width > 0 && text.length > max_width - text = "..." + text[(text.length - max_width + 3 ) .. -1] - end - - VIM::pretty_msg("Comment", @@PROMPT, - "None", VIM::single_quote_escape(text), - "Underlined", " ") - end - - def set!(s) - @input = s - end - - def input - @input - end - - def insensitive? - @input == @input.downcase - end - - def ends_with?(c) - LustyE::ends_with?(@input, c) - end - - def add!(s) - @input << s - end - - def backspace! - @input.chop! - end - - def up_one_dir! - @input.chop! - while !@input.empty? and @input[-1] != ?/ - @input.chop! - end - end -end - -# Used in FilesystemExplorer -class FilesystemPrompt < Prompt - - def initialize - super - @memoized = nil - @dirty = true - end - - def clear! - super - @dirty = true - end - - def set!(s) - # On Windows, Vim will return paths with a '\' separator, but - # we want to use '/'. - super(s.gsub('\\', '/')) - @dirty = true - end - - def backspace! - super - @dirty = true - end - - def up_one_dir! - super - @dirty = true - end - - def at_dir? - # We have not typed anything yet or have just typed the final '/' on a - # directory name in pwd. This check is interspersed throughout - # FilesystemExplorer because of the conventions of basename and dirname. - input().empty? or input()[-1] == File::SEPARATOR[0] - # Don't think the File.directory? call is necessary, but leaving this - # here as a reminder. - #(File.directory?(input()) and input().ends_with?(File::SEPARATOR)) - end - - def insensitive? - at_dir? or (basename() == basename().downcase) - end - - def add!(s) - # Assumption: add!() will only receive enough chars at a time to complete - # a single directory level, e.g. foo/, not foo/bar/ - - @input << s - @dirty = true - end - - def input - if @dirty - @memoized = LustyE::simplify_path(variable_expansion(@input)) - @dirty = false - end - - @memoized - end - - def basename - File.basename input() - end - - def dirname - File.dirname input() - end - - private - def variable_expansion (input_str) - strings = input_str.split('$', -1) - return "" if strings.nil? or strings.length == 0 - - first = strings.shift - - # Try to expand each instance of $. - strings.inject(first) { |str, s| - if s =~ /^(\w+)/ and ENV[$1] - str + s.sub($1, ENV[$1]) - else - str + "$" + s - end - } - end -end - -end - - -# Simplify switching between windows. -module LustyE -class Window - def self.select(window) - return true if window == $curwin - - start = $curwin - - # Try to select the given window. - begin - VIM::command "wincmd w" - end while ($curwin != window) and ($curwin != start) - - if $curwin == window - return true - else - # Failed -- re-select the starting window. - VIM::command("wincmd w") while $curwin != start - VIM::pretty_msg("ErrorMsg", "Cannot find the correct window!") - return false - end - end -end -end - - -# Save and restore settings when creating the explorer buffer. -module LustyE -class SavedSettings - def initialize - save() - end - - def save - @timeoutlen = VIM::evaluate("&timeoutlen") - - @splitbelow = VIM::evaluate_bool("&splitbelow") - @insertmode = VIM::evaluate_bool("&insertmode") - @showcmd = VIM::evaluate_bool("&showcmd") - @list = VIM::evaluate_bool("&list") - - @report = VIM::evaluate("&report") - @sidescroll = VIM::evaluate("&sidescroll") - @sidescrolloff = VIM::evaluate("&sidescrolloff") - - VIM::command "let s:win_size_restore = winrestcmd()" - end - - def restore - VIM::set_option "timeoutlen=#{@timeoutlen}" - - if @splitbelow - VIM::set_option "splitbelow" - else - VIM::set_option "nosplitbelow" - end - - if @insertmode - VIM::set_option "insertmode" - else - VIM::set_option "noinsertmode" - end - - if @showcmd - VIM::set_option "showcmd" - else - VIM::set_option "noshowcmd" - end - - if @list - VIM::set_option "list" - else - VIM::set_option "nolist" - end - - VIM::command "set report=#{@report}" - VIM::command "set sidescroll=#{@sidescroll}" - VIM::command "set sidescrolloff=#{@sidescrolloff}" - - VIM::command "exe s:win_size_restore" - end -end -end - - -# Manage the explorer buffer. -module LustyE - -class Display - private - @@COLUMN_SEPARATOR = " " - @@NO_MATCHES_STRING = "-- NO MATCHES --" - @@TRUNCATED_STRING = "-- TRUNCATED --" - - public - ENTRY_START_VIM_REGEX = '\%(^\|' + @@COLUMN_SEPARATOR + '\)' - ENTRY_END_VIM_REGEX = '\%(\s*$\|' + @@COLUMN_SEPARATOR + '\)' - - def self.entry_syntaxify(s, case_insensitive) - # Create a match regex string for the given s. This is for a Vim regex, - # not for a Ruby regex. - - str = "#{ENTRY_START_VIM_REGEX}\\zs#{s}\\ze#{ENTRY_END_VIM_REGEX}" - - str << '\c' if case_insensitive - - return str - end - - attr_writer :single_column_mode - def initialize(title) - @title = title - @window = nil - @buffer = nil - @single_column_mode = false - end - - def create(prefix) - - # Make a window for the display and move there. - # Start at size 1 to mitigate flashing effect when - # we resize the window later. - VIM::command "silent! botright 1split #{@title}" - - @window = $curwin - @buffer = $curbuf - - # - # Display buffer is special -- set options. - # - - # Buffer-local. - VIM::command "setlocal bufhidden=delete" - VIM::command "setlocal buftype=nofile" - VIM::command "setlocal nomodifiable" - VIM::command "setlocal noswapfile" - VIM::command "setlocal nowrap" - VIM::command "setlocal nonumber" - VIM::command "setlocal foldcolumn=0" - VIM::command "setlocal nocursorline" - VIM::command "setlocal nospell" - VIM::command "setlocal nobuflisted" - VIM::command "setlocal textwidth=0" - VIM::command "setlocal noreadonly" - - # Non-buffer-local (Vim is annoying). - # (Update SavedSettings if adding to below.) - VIM::set_option "timeoutlen=0" - VIM::set_option "noinsertmode" - VIM::set_option "noshowcmd" - VIM::set_option "nolist" - VIM::set_option "report=9999" - VIM::set_option "sidescroll=0" - VIM::set_option "sidescrolloff=0" - - # TODO -- cpoptions? - - # - # Syntax highlighting. - # - - if VIM::has_syntax? - # General syntax matching. - VIM::command 'syn match LustyNoEntries "\%^\s*' \ - "#{@@NO_MATCHES_STRING}" \ - '\s*\%$"' - VIM::command 'syn match LustyTruncated "^\s*' \ - "#{@@TRUNCATED_STRING}" \ - '\s*$"' - - # Colour highlighting. - VIM::command 'highlight link LustyDir Directory' - VIM::command 'highlight link LustySlash Function' - VIM::command 'highlight link LustySelected Type' - VIM::command 'highlight link LustyModified Special' - VIM::command 'highlight link LustyCurrentBuffer Constant' - VIM::command 'highlight link LustyGrepMatch IncSearch' - VIM::command 'highlight link LustyGrepLineNumber Directory' - VIM::command 'highlight link LustyGrepFileName Comment' - VIM::command 'highlight link LustyGrepContext None' # transparent - VIM::command 'highlight link LustyOpenedFile PreProc' - VIM::command 'highlight link LustyFileWithSwap WarningMsg' - VIM::command 'highlight link LustyNoEntries ErrorMsg' - VIM::command 'highlight link LustyTruncated Visual' - - if VIM::exists? '*clearmatches' - VIM::evaluate 'clearmatches()' - end - end - - # - # Key mappings - we need to reroute user input. - # - - # Non-special printable characters. - printables = '/!"#$%&\'()*+,-.0123456789:<=>?#@"' \ - 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' \ - '[]^_`abcdefghijklmnopqrstuvwxyz{}~' - - map = "noremap " - - printables.each_byte do |b| - VIM::command "#{map} :call #{prefix}KeyPressed(#{b})" - end - - # Special characters - VIM::command "#{map} :call #{prefix}KeyPressed(9)" - VIM::command "#{map} :call #{prefix}KeyPressed(92)" - VIM::command "#{map} :call #{prefix}KeyPressed(32)" - VIM::command "#{map} \026| :call #{prefix}KeyPressed(124)" - - VIM::command "#{map} :call #{prefix}KeyPressed(8)" - VIM::command "#{map} :call #{prefix}KeyPressed(8)" - VIM::command "#{map} :call #{prefix}KeyPressed(8)" - - VIM::command "#{map} :call #{prefix}KeyPressed(13)" - VIM::command "#{map} :call #{prefix}KeyPressed(10)" - VIM::command "#{map} :call #{prefix}KeyPressed(1)" - - VIM::command "#{map} :call #{prefix}Cancel()" - VIM::command "#{map} :call #{prefix}Cancel()" - VIM::command "#{map} :call #{prefix}Cancel()" - - VIM::command "#{map} :call #{prefix}KeyPressed(23)" - VIM::command "#{map} :call #{prefix}KeyPressed(14)" - VIM::command "#{map} :call #{prefix}KeyPressed(16)" - VIM::command "#{map} :call #{prefix}KeyPressed(6)" - VIM::command "#{map} :call #{prefix}KeyPressed(2)" - VIM::command "#{map} :call #{prefix}KeyPressed(15)" - VIM::command "#{map} :call #{prefix}KeyPressed(20)" - VIM::command "#{map} :call #{prefix}KeyPressed(22)" - VIM::command "#{map} :call #{prefix}KeyPressed(5)" - VIM::command "#{map} :call #{prefix}KeyPressed(18)" - VIM::command "#{map} :call #{prefix}KeyPressed(21)" - VIM::command "#{map} OD :call #{prefix}KeyPressed(2)" - VIM::command "#{map} OC :call #{prefix}KeyPressed(6)" - VIM::command "#{map} OA :call #{prefix}KeyPressed(16)" - VIM::command "#{map} OB :call #{prefix}KeyPressed(14)" - VIM::command "#{map} :call #{prefix}KeyPressed(2)" - VIM::command "#{map} :call #{prefix}KeyPressed(6)" - VIM::command "#{map} :call #{prefix}KeyPressed(16)" - VIM::command "#{map} :call #{prefix}KeyPressed(14)" - end - - def print(strings) - Window.select(@window) || return - - if strings.empty? - print_no_entries() - return - end - - row_count, col_count, col_widths, truncated = \ - compute_optimal_layout(strings) - - # Slice the strings into rows. - rows = Array.new(row_count){[]} - col_index = 0 - strings.each_slice(row_count) do |column| - column_width = col_widths[col_index] - column.each_index do |i| - string = column[i] - - rows[i] << string - - if col_index < col_count - 1 - # Add spacer to the width of the column - rows[i] << (" " * (column_width - VIM::strwidth(string))) - rows[i] << @@COLUMN_SEPARATOR - end - end - - col_index += 1 - break if col_index >= col_count - end - - print_rows(rows, truncated) - row_count - end - - def close - # Only wipe the buffer if we're *sure* it's the explorer. - if Window.select @window and \ - $curbuf == @buffer and \ - $curbuf.name =~ /#{Regexp.escape(@title)}$/ - VIM::command "bwipeout!" - @window = nil - @buffer = nil - end - end - - def self.max_height - stored_height = $curwin.height - $curwin.height = VIM::MOST_POSITIVE_INTEGER - highest_allowable = $curwin.height - $curwin.height = stored_height - highest_allowable - end - - def self.max_width - VIM::columns() - end - - private - - def compute_optimal_layout(strings) - # Compute optimal row count and corresponding column count. - # The display attempts to fit `strings' on as few rows as - # possible. - - max_width = Display.max_width() - max_height = Display.max_height() - displayable_string_upper_bound = compute_displayable_upper_bound(strings) - - # Determine optimal row count. - optimal_row_count, truncated = \ - if @single_column_mode - if strings.length <= max_height - [strings.length, false] - else - [max_height - 1, true] - end - elsif strings.length > displayable_string_upper_bound - # Use all available rows and truncate results. - # The -1 is for the truncation indicator. - [Display.max_height - 1, true] - else - single_row_width = \ - strings.inject(0) { |len, s| - len + @@COLUMN_SEPARATOR.length + s.length - } - if single_row_width <= max_width or \ - strings.length == 1 - # All fits on a single row - [1, false] - else - compute_optimal_row_count(strings) - end - end - - # Compute column_count and column_widths. - column_count = 0 - column_widths = [] - total_width = 0 - strings.each_slice(optimal_row_count) do |column| - longest = column.max { |a, b| VIM::strwidth(a) <=> VIM::strwidth(b) } - column_width = VIM::strwidth(longest) - total_width += column_width - - break if total_width > max_width - - column_count += 1 - column_widths << column_width - total_width += @@COLUMN_SEPARATOR.length - end - - [optimal_row_count, column_count, column_widths, truncated] - end - - def print_rows(rows, truncated) - unlock_and_clear() - - # Grow/shrink the window as needed - $curwin.height = rows.length + (truncated ? 1 : 0) - - # Print the rows. - rows.each_index do |i| - $curwin.cursor = [i+1, 1] - $curbuf.append(i, rows[i].join('')) - end - - # Print a TRUNCATED indicator, if needed. - if truncated - $curbuf.append($curbuf.count - 1, \ - @@TRUNCATED_STRING.center($curwin.width, " ")) - end - - # Stretch the last line to the length of the window with whitespace so - # that we can "hide" the cursor in the corner. - last_line = $curbuf[$curbuf.count - 1] - last_line << (" " * [$curwin.width - last_line.length,0].max) - $curbuf[$curbuf.count - 1] = last_line - - # There's a blank line at the end of the buffer because of how - # VIM::Buffer.append works. - $curbuf.delete $curbuf.count - lock() - end - - def print_no_entries - unlock_and_clear() - $curwin.height = 1 - $curbuf[1] = @@NO_MATCHES_STRING.center($curwin.width, " ") - lock() - end - - def unlock_and_clear - VIM::command "setlocal modifiable" - - # Clear the explorer (black hole register) - VIM::command "silent %d _" - end - - def lock - VIM::command "setlocal nomodifiable" - - # Hide the cursor - VIM::command "normal! Gg$" - end - - def compute_displayable_upper_bound(strings) - # Compute an upper-bound on the number of displayable matches. - # Basically: find the length of the longest string, then keep - # adding shortest strings until we pass the width of the Vim - # window. This is the maximum possible column-count assuming - # all strings can fit. Then multiply by the number of rows. - - sorted_by_shortest = strings.sort { |x, y| x.length <=> y.length } - longest_length = sorted_by_shortest.pop.length - - row_width = longest_length + @@COLUMN_SEPARATOR.length - - max_width = Display.max_width() - column_count = 1 - - sorted_by_shortest.each do |str| - row_width += str.length - if row_width > max_width - break - end - - column_count += 1 - row_width += @@COLUMN_SEPARATOR.length - end - - column_count * Display.max_height() - end - - def compute_optimal_row_count(strings) - max_width = Display.max_width - max_height = Display.max_height - - # Hashes by range, e.g. 0..2, representing the width - # of the column bounded by that range. - col_range_widths = {} - - # Binary search; find the lowest number of rows at which we - # can fit all the strings. - - # We've already failed for a single row, so start at two. - lower = 1 # (1 = 2 - 1) - upper = max_height + 1 - while lower + 1 != upper - row_count = (lower + upper) / 2 # Mid-point - - col_start_index = 0 - col_end_index = row_count - 1 - total_width = 0 - - while col_end_index < strings.length - total_width += \ - compute_column_width(col_start_index..col_end_index, - strings, col_range_widths) - - if total_width > max_width - # Early exit. - total_width = LustyE::MOST_POSITIVE_FIXNUM - break - end - - total_width += @@COLUMN_SEPARATOR.length - - col_start_index += row_count - col_end_index += row_count - - if col_end_index >= strings.length and \ - col_start_index < strings.length - # Remainder; last iteration will not be a full column. - col_end_index = strings.length - 1 - end - end - - # The final column doesn't need a separator. - total_width -= @@COLUMN_SEPARATOR.length - - if total_width <= max_width - # This row count fits. - upper = row_count - else - # This row count doesn't fit. - lower = row_count - end - end - - if upper > max_height - # No row count can accomodate all strings; have to truncate. - # (-1 for the truncate indicator) - [max_height - 1, true] - else - [upper, false] - end - end - - def compute_column_width(range, strings, col_range_widths) - - if (range.first == range.last) - return strings[range.first].length - end - - width = col_range_widths[range] - - if width.nil? - # Recurse for each half of the range. - split_point = range.first + ((range.last - range.first) >> 1) - - first_half = compute_column_width(range.first..split_point, - strings, col_range_widths) - second_half = compute_column_width(split_point+1..range.last, - strings, col_range_widths) - - width = [first_half, second_half].max - col_range_widths[range] = width - end - - width - end -end -end - - -module LustyE -class FileMasks - private - @@glob_masks = [] - - public - def FileMasks.create_glob_masks - @@glob_masks = \ - if VIM::exists? "g:LustyExplorerFileMasks" - # Note: this variable deprecated. - VIM::evaluate("g:LustyExplorerFileMasks").split(',') - elsif VIM::exists? "&wildignore" - VIM::evaluate("&wildignore").split(',') - else - [] - end - end - - def FileMasks.masked?(str) - @@glob_masks.each do |mask| - return true if File.fnmatch(mask, str) - end - - return false - end -end -end - - -module LustyE -class VimSwaps - def initialize - if VIM::has_syntax? -# FIXME: vvv disabled -# @vim_r = IO.popen("vim -r --noplugin -i NONE 2>&1") -# @files_with_swaps = nil - @files_with_swaps = [] - else - @files_with_swaps = [] - end - end - - def file_names - if @files_with_swaps.nil? - if LustyE::ready_for_read?(@vim_r) - @files_with_swaps = [] - @vim_r.each_line do |line| - if line =~ /^ +file name: (.*)$/ - file = $1.chomp - @files_with_swaps << Pathname.new(LustyE::simplify_path(file)) - end - end - else - return [] - end - end - - @files_with_swaps - end -end -end - - -# Maintain MRU ordering. -module LustyE -class BufferStack - public - def initialize - @stack = [] - - (0..VIM::Buffer.count-1).each do |i| - @stack << VIM::Buffer[i].number - end - end - - # Switch to the previous buffer (the one you were using before the - # current one). This is basically a smarter replacement for :b#, - # accounting for the situation where your previous buffer no longer - # exists. - def juggle_previous - buf = num_at_pos(2) - VIM::command "b #{buf}" - end - - def names(n = :all) - # Get the last n buffer names by MRU. Show only as much of - # the name as necessary to differentiate between buffers of - # the same name. - cull! - names = @stack.collect { |i| VIM::bufname(i) }.reverse - if n != :all - names = names[0,n] - end - shorten_paths(names) - end - - def numbers(n = :all) - # Get the last n buffer numbers by MRU. - cull! - numbers = @stack.reverse - if n == :all - numbers - else - numbers[0,n] - end - end - - def num_at_pos(i) - cull! - return @stack[-i] ? @stack[-i] : @stack.first - end - - def length - cull! - return @stack.length - end - - def push - @stack.delete $curbuf.number - @stack << $curbuf.number - end - - def pop - number = VIM::evaluate('bufnr(expand(""))') - @stack.delete number - end - - private - def cull! - # Remove empty and unlisted buffers. - @stack.delete_if { |x| - not (VIM::evaluate_bool("bufexists(#{x})") and - VIM::evaluate_bool("getbufvar(#{x}, '&buflisted')")) - } - end - - # NOTE: very similar to Entry::compute_buffer_entries() - def shorten_paths(buffer_names) - # Shorten each buffer name by removing all path elements which are not - # needed to differentiate a given name from other names. This usually - # results in only the basename shown, but if several buffers of the - # same basename are opened, there will be more. - - # Group the buffers by common basename - common_base = Hash.new { |hash, k| hash[k] = [] } - buffer_names.each do |name| - basename = Pathname.new(name).basename.to_s - common_base[basename] << name - end - - # Determine the longest common prefix for each basename group. - basename_to_prefix = {} - common_base.each do |k, names| - if names.length > 1 - basename_to_prefix[k] = LustyE::longest_common_prefix(names) - end - end - - # Shorten each buffer_name by removing the prefix. - buffer_names.map { |name| - base = Pathname.new(name).basename.to_s - prefix = basename_to_prefix[base] - prefix ? name[prefix.length..-1] \ - : base - } - end -end - -end - - - -$lusty_buffer_explorer = LustyE::BufferExplorer.new -$lusty_filesystem_explorer = LustyE::FilesystemExplorer.new -$lusty_buffer_grep = LustyE::BufferGrep.new -$le_buffer_stack = LustyE::BufferStack.new - -EOF - -" vim: set sts=2 sw=2: diff --git a/vim/plugin/lusty-juggler.vim b/vim/plugin/lusty-juggler.vim deleted file mode 100644 index 7aa096d..0000000 --- a/vim/plugin/lusty-juggler.vim +++ /dev/null @@ -1,1143 +0,0 @@ -" Copyright: Copyright (C) 2008-2011 Stephen Bach -" Permission is hereby granted to use and distribute this code, -" with or without modifications, provided that this copyright -" notice is copied with it. Like anything else that's free, -" lusty-juggler.vim is provided *as is* and comes with no -" warranty of any kind, either expressed or implied. In no -" event will the copyright holder be liable for any damages -" resulting from the use of this software. -" -" Name Of File: lusty-juggler.vim -" Description: Dynamic Buffer Switcher Vim Plugin -" Maintainer: Stephen Bach -" Contributors: Juan Frias, Bartosz Leper, Marco Barberis, Vincent Driessen, -" Martin Wache, Johannes Holzfuß, Adam Rutkowski, Carlo Teubner, -" lilydjwg, Leonid Shevtsov, Giuseppe Rota -" -" Release Date: April 29, 2011 -" Version: 1.3 -" -" Usage: -" lj - Opens the buffer juggler. -" -" You can also use this command: -" -" ":LustyJuggler" -" -" (Personally, I map this to ,j) -" -" When launched, the command bar at bottom is replaced with a -" new bar showing the names of currently-opened buffers in -" most-recently-used order. -" -" The buffers are mapped to these keys: -" -" 1st|2nd|3rd|4th|5th|6th|7th|8th|9th|10th -" ---------------------------------------- -" a s d f g h j k l ; -" 1 2 3 4 5 6 7 8 9 0 -" -" So if you type "f" or "4", the fourth buffer name will be -" highlighted and the bar will shift to center it as necessary -" (and show more of the buffer names on the right). -" -" If you want to switch to that buffer, press "f" or "4" again -" or press "". Alternatively, press one of the other -" mapped keys to highlight another buffer. -" -" To display the key with the name of the buffer, add one of -" the following lines to your .vimrc: -" -" let g:LustyJugglerShowKeys = 'a' (for alpha characters) -" let g:LustyJugglerShowKeys = 1 (for digits) -" -" To cancel the juggler, press any of "q", "", "", "", or "". -" -" LustyJuggler can act very much like window switching. -" To enable this mode, add the following line to your .vimrc: -" -" let g:LustyJugglerAltTabMode = 1 -" -" Then, given the following mapping: -" -" noremap :LustyJuggler -" -" Pressing "" will launch the LustyJuggler with the -" previous buffer highlighted. Typing "" again will cycle -" to the next buffer (in most-recently used order), and -" "" will open the highlighted buffer. For example, the -" sequence "" will open the previous buffer, and -" "" will open the buffer used just before the -" previous buffer, and so on. -" -" Bonus: This plugin also includes the following command, which will -" immediately switch to your previously used buffer: -" -" ":LustyJugglePrevious" -" -" This is similar to the ":b#" command, but accounts for the -" common situation where the previously used buffer (#) has -" been killed and is thus inaccessible. In that case, it will -" instead switch to the buffer used before that one (and on down -" the line if that buffer has been killed too). -" -" -" Install Details: -" -" Copy this file into $HOME/.vim/plugin directory so that it will be sourced -" on startup automatically. -" -" Note! This plugin requires Vim be compiled with Ruby interpretation. If you -" don't know if your build of Vim has this functionality, you can check by -" running "vim --version" from the command line and looking for "+ruby". -" Alternatively, just try sourcing this script. -" -" If your version of Vim does not have "+ruby" but you would still like to -" use this plugin, you can fix it. See the "Check for Ruby functionality" -" comment below for instructions. -" -" If you are using the same Vim configuration and plugins for multiple -" machines, some of which have Ruby and some of which don't, you may want to -" turn off the "Sorry, LustyJuggler requires ruby" warning. You can do so -" like this (in .vimrc): -" -" let g:LustyJugglerSuppressRubyWarning = 1 -" -" -" Contributing: -" -" Patches and suggestions welcome. Note: lusty-juggler.vim is a generated -" file; if you'd like to submit a patch, check out the Github development -" repository: -" -" http://github.com/sjbach/lusty -" -" -" GetLatestVimScripts: 2050 1 :AutoInstall: lusty-juggler.vim -" -" TODO: -" - Add TAB recognition back. -" - Add option to open buffer immediately when mapping is pressed (but not -" release the juggler until the confirmation press). -" - Have the delimiter character settable. -" - have colours settable? - -" Exit quickly when already loaded. -if exists("g:loaded_lustyjuggler") - finish -endif - -if &compatible - echohl ErrorMsg - echo "LustyJuggler is not designed to run in &compatible mode;" - echo "To use this plugin, first disable vi-compatible mode like so:\n" - - echo " :set nocompatible\n" - - echo "Or even better, just create an empty .vimrc file." - echohl none - finish -endif - -if exists("g:FuzzyFinderMode.TextMate") - echohl WarningMsg - echo "Warning: LustyJuggler detects the presence of fuzzyfinder_textmate;" - echo "that plugin often interacts poorly with other Ruby plugins." - echo "If LustyJuggler gives you an error, you can probably fix it by" - echo "renaming fuzzyfinder_textmate.vim to zzfuzzyfinder_textmate.vim so" - echo "that it is last in the load order." - echohl none -endif - -" Check for Ruby functionality. -if !has("ruby") - if !exists("g:LustyExplorerSuppressRubyWarning") || - \ g:LustyExplorerSuppressRubyWarning == "0" - if !exists("g:LustyJugglerSuppressRubyWarning") || - \ g:LustyJugglerSuppressRubyWarning == "0" - echohl ErrorMsg - echon "Sorry, LustyJuggler requires ruby. " - echon "Here are some tips for adding it:\n" - - echo "Debian / Ubuntu:" - echo " # apt-get install vim-ruby\n" - - echo "Fedora:" - echo " # yum install vim-enhanced\n" - - echo "Gentoo:" - echo " # USE=\"ruby\" emerge vim\n" - - echo "FreeBSD:" - echo " # pkg_add -r vim+ruby\n" - - echo "Windows:" - echo " 1. Download and install Ruby from here:" - echo " http://www.ruby-lang.org/" - echo " 2. Install a Vim binary with Ruby support:" - echo " http://segfault.hasno.info/vim/gvim72.zip\n" - - echo "Manually (including Cygwin):" - echo " 1. Install Ruby." - echo " 2. Download the Vim source package (say, vim-7.0.tar.bz2)" - echo " 3. Build and install:" - echo " # tar -xvjf vim-7.0.tar.bz2" - echo " # ./configure --enable-rubyinterp" - echo " # make && make install\n" - - echo "(If you just wish to stifle this message, set the following option:" - echo " let g:LustyJugglerSuppressRubyWarning = 1)" - echohl none - endif - endif - finish -endif - -let g:loaded_lustyjuggler = "yep" - -" Commands. -command LustyJuggler :call LustyJugglerStart() -command LustyJugglePrevious :call LustyJugglePreviousRun() - -" Deprecated command names. -command JugglePrevious :call - \ deprecated('JugglePrevious', 'LustyJugglePrevious') - -function! s:deprecated(old, new) - echohl WarningMsg - echo ":" . a:old . " is deprecated; use :" . a:new . " instead." - echohl none -endfunction - - -" Default mappings. -nmap lj :LustyJuggler - -" Vim-to-ruby function calls. -function! s:LustyJugglerStart() - ruby LustyJ::profile() { $lusty_juggler.run } -endfunction - -function! s:LustyJugglerKeyPressed(code_arg) - ruby LustyJ::profile() { $lusty_juggler.key_pressed } -endfunction - -function! s:LustyJugglerCancel() - ruby LustyJ::profile() { $lusty_juggler.cleanup } -endfunction - -function! s:LustyJugglePreviousRun() - ruby LustyJ::profile() { $lj_buffer_stack.juggle_previous } -endfunction - -" Setup the autocommands that handle buffer MRU ordering. -augroup LustyJuggler - autocmd! - autocmd BufEnter * ruby LustyJ::profile() { $lj_buffer_stack.push } - autocmd BufDelete * ruby LustyJ::profile() { $lj_buffer_stack.pop } - autocmd BufWipeout * ruby LustyJ::profile() { $lj_buffer_stack.pop } -augroup End - -" Used to work around a flaw in Vim's ruby bindings. -let s:maparg_holder = 0 -let s:maparg_dict_holder = { } - -ruby << EOF - -require 'pathname' - -$LUSTY_PROFILING = false - -if $LUSTY_PROFILING - require 'rubygems' - require 'ruby-prof' -end - - -module VIM - - unless const_defined? "MOST_POSITIVE_INTEGER" - MOST_POSITIVE_INTEGER = 2**(32 - 1) - 2 # Vim ints are signed 32-bit. - end - - def self.zero?(var) - # In Vim 7.2 and older, VIM::evaluate returns Strings for boolean - # expressions; in later versions, Fixnums. - case var - when String - var == "0" - when Fixnum - var == 0 - else - LustyJ::assert(false, "unexpected type: #{var.class}") - end - end - - def self.nonzero?(var) - not zero?(var) - end - - def self.evaluate_bool(var) - nonzero? evaluate(var) - end - - def self.exists?(s) - nonzero? evaluate("exists('#{s}')") - end - - def self.has_syntax? - nonzero? evaluate('has("syntax")') - end - - def self.has_ext_maparg? - # The 'dict' parameter to mapargs() was introduced in Vim 7.3.32 - nonzero? evaluate('v:version > 703 || (v:version == 703 && has("patch32"))') - end - - def self.columns - evaluate("&columns").to_i - end - - def self.lines - evaluate("&lines").to_i - end - - def self.getcwd - evaluate("getcwd()") - end - - def self.bufname(i) - if evaluate_bool("empty(bufname(#{i}))") - "" - else - evaluate("bufname(#{i})") - end - end - - def self.single_quote_escape(s) - # Everything in a Vim single-quoted string is literal, except single - # quotes. Single quotes are escaped by doubling them. - s.gsub("'", "''") - end - - def self.filename_escape(s) - # Escape slashes, open square braces, spaces, sharps, double quotes and - # percent signs. - s.gsub(/\\/, '\\\\\\').gsub(/[\[ #"%]/, '\\\\\0') - end - - def self.regex_escape(s) - s.gsub(/[\]\[.~"^$\\*]/,'\\\\\0') - end - - class Buffer - def modified? - VIM::nonzero? VIM::evaluate("getbufvar(#{number()}, '&modified')") - end - - def listed? - VIM::nonzero? VIM::evaluate("getbufvar(#{number()}, '&buflisted')") - end - - def self.obj_for_bufnr(n) - # There's gotta be a better way to do this... - (0..VIM::Buffer.count-1).each do |i| - obj = VIM::Buffer[i] - return obj if obj.number == n - end - - return nil - end - end - - # Print with colours - def self.pretty_msg(*rest) - return if rest.length == 0 - return if rest.length % 2 != 0 - - command "redraw" # see :help echo-redraw - i = 0 - while i < rest.length do - command "echohl #{rest[i]}" - command "echon '#{rest[i+1]}'" - i += 2 - end - - command 'echohl None' - end -end - -# Hack for wide CJK characters. -if VIM::exists?("*strwidth") - module VIM - def self.strwidth(s) - # strwidth() is defined in Vim 7.3. - evaluate("strwidth('#{single_quote_escape(s)}')").to_i - end - end -else - module VIM - def self.strwidth(s) - s.length - end - end -end - - -# Utility functions. -module LustyJ - - unless const_defined? "MOST_POSITIVE_FIXNUM" - MOST_POSITIVE_FIXNUM = 2**(0.size * 8 -2) -1 - end - - def self.simplify_path(s) - s = s.gsub(/\/+/, '/') # Remove redundant '/' characters - begin - if s[0] == ?~ - # Tilde expansion - First expand the ~ part (e.g. '~' or '~steve') - # and then append the rest of the path. We can't just call - # expand_path() or it'll throw on bad paths. - s = File.expand_path(s.sub(/\/.*/,'')) + \ - s.sub(/^[^\/]+/,'') - end - - if s == '/' - # Special-case root so we don't add superfluous '/' characters, - # as this can make Cygwin choke. - s - elsif ends_with?(s, File::SEPARATOR) - File.expand_path(s) + File::SEPARATOR - else - dirname_expanded = File.expand_path(File.dirname(s)) - if dirname_expanded == '/' - dirname_expanded + File.basename(s) - else - dirname_expanded + File::SEPARATOR + File.basename(s) - end - end - rescue ArgumentError - s - end - end - - def self.longest_common_prefix(paths) - prefix = paths[0] - paths.each do |path| - for i in 0...prefix.length - if path.length <= i or prefix[i] != path[i] - prefix = prefix[0...i] - prefix = prefix[0..(prefix.rindex('/') or -1)] - break - end - end - end - - prefix - end - - def self.ready_for_read?(io) - if io.respond_to? :ready? - ready? - else - result = IO.select([io], nil, nil, 0) - result && (result.first.first == io) - end - end - - def self.ends_with?(s1, s2) - tail = s1[-s2.length, s2.length] - tail == s2 - end - - def self.starts_with?(s1, s2) - head = s1[0, s2.length] - head == s2 - end - - def self.option_set?(opt_name) - opt_name = "g:LustyExplorer" + opt_name - VIM::evaluate_bool("exists('#{opt_name}') && #{opt_name} != '0'") - end - - def self.profile - # Profile (if enabled) and provide better - # backtraces when there's an error. - - if $LUSTY_PROFILING - if not RubyProf.running? - RubyProf.measure_mode = RubyProf::WALL_TIME - RubyProf.start - else - RubyProf.resume - end - end - - begin - yield - rescue Exception => e - puts e - puts e.backtrace - end - - if $LUSTY_PROFILING and RubyProf.running? - RubyProf.pause - end - end - - class AssertionError < StandardError ; end - - def self.assert(condition, message = 'assertion failure') - raise AssertionError.new(message) unless condition - end - - def self.d(s) - # (Debug print) - $stderr.puts s - end -end - - -module LustyJ -class LustyJuggler - private - @@KEYS = { "a" => 1, - "s" => 2, - "d" => 3, - "f" => 4, - "g" => 5, - "h" => 6, - "j" => 7, - "k" => 8, - "l" => 9, - ";" => 10, - "1" => 1, - "2" => 2, - "3" => 3, - "4" => 4, - "5" => 5, - "6" => 6, - "7" => 7, - "8" => 8, - "9" => 9, - "0" => 10 } - - public - def initialize - @running = false - @last_pressed = nil - @name_bar = NameBar.new - end - - def run - if $lj_buffer_stack.length <= 1 - VIM::pretty_msg("PreProc", "No other buffers") - return - end - - # If already running, highlight next buffer - if @running and LustyJuggler::alt_tab_mode_active? - @last_pressed = (@last_pressed % $lj_buffer_stack.length) + 1; - print_buffer_list(@last_pressed) - return - end - - return if @running - @running = true - - # Need to zero the timeout length or pressing 'g' will hang. - @ruler = VIM::evaluate_bool("&ruler") - @showcmd = VIM::evaluate_bool("&showcmd") - @showmode = VIM::evaluate_bool("&showmode") - @timeoutlen = VIM::evaluate("&timeoutlen") - VIM::set_option 'timeoutlen=0' - VIM::set_option 'noruler' - VIM::set_option 'noshowcmd' - VIM::set_option 'noshowmode' - - @key_mappings_map = Hash.new { |hash, k| hash[k] = [] } - - # Selection keys. - @@KEYS.keys.each do |c| - map_key(c, ":call LustyJugglerKeyPressed('#{c}')") - end - # Can't use '' as an argument to :call func for some reason. - map_key("", ":call LustyJugglerKeyPressed('ENTER')") - map_key("", ":call LustyJugglerKeyPressed('TAB')") - - # Split opener keys - map_key("v", ":call LustyJugglerKeyPressed('v')") - map_key("b", ":call LustyJugglerKeyPressed('b')") - - # Left and Right keys - map_key("OD", ":call LustyJugglerKeyPressed('Left')") - map_key("OC", ":call LustyJugglerKeyPressed('Right')") - map_key("", ":call LustyJugglerKeyPressed('Left')") - map_key("", ":call LustyJugglerKeyPressed('Right')") - - # Cancel keys. - map_key("i", ":call LustyJugglerCancel()") - map_key("q", ":call LustyJugglerCancel()") - map_key("", ":call LustyJugglerCancel()") - map_key("", ":call LustyJugglerCancel()") - map_key("", ":call LustyJugglerCancel()") - map_key("", ":call LustyJugglerCancel()") - map_key("", ":call LustyJugglerCancel()") - - @last_pressed = 2 if LustyJuggler::alt_tab_mode_active? - print_buffer_list(@last_pressed) - end - - def key_pressed() - c = VIM::evaluate("a:code_arg") - - if @last_pressed.nil? and c == 'ENTER' - cleanup() - elsif @last_pressed and (@@KEYS[c] == @last_pressed or c == 'ENTER') - choose(@last_pressed) - cleanup() - elsif @last_pressed and %w(v b).include?(c) - c=='v' ? vsplit(@last_pressed) : hsplit(@last_pressed) - cleanup() - elsif c == 'Left' - @last_pressed = (@last_pressed.nil?) ? 0 : (@last_pressed) - @last_pressed = (@last_pressed - 1) < 1 ? $lj_buffer_stack.length : (@last_pressed - 1) - print_buffer_list(@last_pressed) - elsif c == 'Right' - @last_pressed = (@last_pressed.nil?) ? 0 : (@last_pressed) - @last_pressed = (@last_pressed + 1) > $lj_buffer_stack.length ? 1 : (@last_pressed + 1) - print_buffer_list(@last_pressed) - else - @last_pressed = @@KEYS[c] - print_buffer_list(@last_pressed) - end - end - - # Restore settings, mostly. - def cleanup - @last_pressed = nil - - VIM::set_option "timeoutlen=#{@timeoutlen}" - VIM::set_option "ruler" if @ruler - VIM::set_option "showcmd" if @showcmd - VIM::set_option "showmode" if @showmode - - @@KEYS.keys.each do |c| - unmap_key(c) - end - unmap_key("") - unmap_key("") - - unmap_key("v") - unmap_key("b") - - unmap_key("i") - unmap_key("q") - unmap_key("") - unmap_key("") - unmap_key("") - unmap_key("") - unmap_key("") - unmap_key("OC") - unmap_key("OD") - unmap_key("") - unmap_key("") - - @running = false - VIM::message '' - VIM::command 'redraw' # Prevents "Press ENTER to continue" message. - end - - private - def self.alt_tab_mode_active? - return (VIM::exists?("g:LustyJugglerAltTabMode") and - VIM::evaluate("g:LustyJugglerAltTabMode").to_i != 0) - end - - def print_buffer_list(highlighted_entry = nil) - # If the user pressed a key higher than the number of open buffers, - # highlight the highest (see also BufferStack.num_at_pos()). - - @name_bar.selected_buffer = \ - if highlighted_entry - # Correct for zero-based array. - [highlighted_entry, $lj_buffer_stack.length].min - 1 - else - nil - end - - @name_bar.print - end - - def choose(i) - buf = $lj_buffer_stack.num_at_pos(i) - VIM::command "b #{buf}" - end - - def vsplit(i) - buf = $lj_buffer_stack.num_at_pos(i) - VIM::command "vert sb #{buf}" - end - - def hsplit(i) - buf = $lj_buffer_stack.num_at_pos(i) - VIM::command "sb #{buf}" - end - - def map_key(key, action) - ['n','s','x','o','i','c','l'].each do |mode| - VIM::command "let s:maparg_holder = maparg('#{key}', '#{mode}')" - if VIM::evaluate_bool("s:maparg_holder != ''") - orig_rhs = VIM::evaluate("s:maparg_holder") - if VIM::has_ext_maparg? - VIM::command "let s:maparg_dict_holder = maparg('#{key}', '#{mode}', 0, 1)" - nore = VIM::evaluate_bool("s:maparg_dict_holder['noremap']") ? 'nore' : '' - silent = VIM::evaluate_bool("s:maparg_dict_holder['silent']") ? ' ' : '' - expr = VIM::evaluate_bool("s:maparg_dict_holder['expr']") ? ' ' : '' - buffer = VIM::evaluate_bool("s:maparg_dict_holder['buffer']") ? ' ' : '' - restore_cmd = "#{mode}#{nore}map#{silent}#{expr}#{buffer} #{key} #{orig_rhs}" - else - nore = LustyJ::starts_with?(orig_rhs, '') ? '' : 'nore' - restore_cmd = "#{mode}#{nore}map #{key} #{orig_rhs}" - end - @key_mappings_map[key] << [ mode, restore_cmd ] - end - VIM::command "#{mode}noremap #{key} #{action}" - end - end - - def unmap_key(key) - #first, unmap lusty_juggler's maps - ['n','s','x','o','i','c','l'].each do |mode| - VIM::command "#{mode}unmap #{key}" - end - - if @key_mappings_map.has_key?(key) - @key_mappings_map[key].each do |a| - mode, restore_cmd = *a - # for mappings that have on the rhs \|, the \ is somehow stripped - restore_cmd.gsub!("|", "\\|") - VIM::command restore_cmd - end - end - end -end -end - - -# An item (delimiter/separator or buffer name) on the NameBar. -module LustyJ -class BarItem - def initialize(str, color) - @str = str - @color = color - end - - def length - @str.length - end - - def pretty_print_input - [@color, @str] - end - - def [](*rest) - return BarItem.new(@str[*rest], @color) - end - - def self.full_length(array) - if array - array.inject(0) { |sum, el| sum + el.length } - else - 0 - end - end -end - -class BufferItem < BarItem - def initialize(str, highlighted) - @str = str - @highlighted = highlighted - destructure() - end - - def [](*rest) - return BufferItem.new(@str[*rest], @highlighted) - end - - def pretty_print_input - @array - end - - private - @@BUFFER_COLOR = "PreProc" - #@@BUFFER_COLOR = "None" - @@DIR_COLOR = "Directory" - @@SLASH_COLOR = "Function" - @@HIGHLIGHTED_COLOR = "Question" - - # Breakdown the string to colourize each part. - def destructure - if @highlighted - buf_color = @@HIGHLIGHTED_COLOR - dir_color = @@HIGHLIGHTED_COLOR - slash_color = @@HIGHLIGHTED_COLOR - else - buf_color = @@BUFFER_COLOR - dir_color = @@DIR_COLOR - slash_color = @@SLASH_COLOR - end - - pieces = @str.split(File::SEPARATOR, -1) - - @array = [] - @array << dir_color - @array << pieces.shift - pieces.each { |piece| - @array << slash_color - @array << File::SEPARATOR - @array << dir_color - @array << piece - } - - # Last piece is the actual name. - @array[-2] = buf_color - end -end - -class SeparatorItem < BarItem - public - def initialize - super(@@TEXT, @@COLOR) - end - - private - @@TEXT = "|" - #@@COLOR = "NonText" - @@COLOR = "None" -end - -class LeftContinuerItem < BarItem - public - def initialize - super(@@TEXT, @@COLOR) - end - - def self.length - @@TEXT.length - end - - private - @@TEXT = "<" - @@COLOR = "NonText" -end - -class RightContinuerItem < BarItem - public - def initialize - super(@@TEXT, @@COLOR) - end - - def self.length - @@TEXT.length - end - - private - @@TEXT = ">" - @@COLOR = "NonText" -end - -end - - -# A one-line display of the open buffers, appearing in the command display. -module LustyJ -class NameBar - public - def initialize - @selected_buffer = nil - end - - attr_writer :selected_buffer - - def print - items = create_items() - - selected_item = \ - if @selected_buffer - # Account for the separators we've added. - [@selected_buffer * 2, (items.length - 1)].min - end - - clipped = clip(items, selected_item) - NameBar.do_pretty_print(clipped) - end - - private - @@LETTERS = ["a", "s", "d", "f", "g", "h", "j", "k", "l", ";"] - - - def create_items - names = $lj_buffer_stack.names(10) - - items = names.inject([]) { |array, name| - key = if VIM::exists?("g:LustyJugglerShowKeys") - case VIM::evaluate("g:LustyJugglerShowKeys").to_s - when /[[:alpha:]]/ - @@LETTERS[array.size / 2] + ":" - when /[[:digit:]]/ - "#{((array.size / 2) + 1) % 10}:" - else - "" - end - else - "" - end - - array << BufferItem.new("#{key}#{name}", - (@selected_buffer and \ - name == names[@selected_buffer])) - array << SeparatorItem.new - } - items.pop # Remove last separator. - - return items - end - - # Clip the given array of items to the available display width. - def clip(items, selected) - # This function is pretty hard to follow... - - # Note: Vim gives the annoying "Press ENTER to continue" message if we - # use the full width. - columns = VIM::columns() - 1 - - if BarItem.full_length(items) <= columns - return items - end - - selected = 0 if selected.nil? - half_displayable_len = columns / 2 - - # The selected buffer is excluded since it's basically split between - # the sides. - left_len = BarItem.full_length items[0, selected - 1] - right_len = BarItem.full_length items[selected + 1, items.length - 1] - - right_justify = (left_len > half_displayable_len) and \ - (right_len < half_displayable_len) - - selected_str_half_len = (items[selected].length / 2) + \ - (items[selected].length % 2 == 0 ? 0 : 1) - - if right_justify - # Right justify the bar. - first_layout = self.method :layout_right - second_layout = self.method :layout_left - first_adjustment = selected_str_half_len - second_adjustment = -selected_str_half_len - else - # Left justify (sort-of more likely). - first_layout = self.method :layout_left - second_layout = self.method :layout_right - first_adjustment = -selected_str_half_len - second_adjustment = selected_str_half_len - end - - # Layout the first side. - allocation = half_displayable_len + first_adjustment - first_side, remainder = first_layout.call(items, - selected, - allocation) - - # Then layout the second side, also grabbing any unused space. - allocation = half_displayable_len + \ - second_adjustment + \ - remainder - second_side, remainder = second_layout.call(items, - selected, - allocation) - - if right_justify - second_side + first_side - else - first_side + second_side - end - end - - # Clip the given array of items to the given space, counting downwards. - def layout_left(items, selected, space) - trimmed = [] - - i = selected - 1 - while i >= 0 - m = items[i] - if space > m.length - trimmed << m - space -= m.length - elsif space > 0 - trimmed << m[m.length - (space - LeftContinuerItem.length), \ - space - LeftContinuerItem.length] - trimmed << LeftContinuerItem.new - space = 0 - else - break - end - i -= 1 - end - - return trimmed.reverse, space - end - - # Clip the given array of items to the given space, counting upwards. - def layout_right(items, selected, space) - trimmed = [] - - i = selected - while i < items.length - m = items[i] - if space > m.length - trimmed << m - space -= m.length - elsif space > 0 - trimmed << m[0, space - RightContinuerItem.length] - trimmed << RightContinuerItem.new - space = 0 - else - break - end - i += 1 - end - - return trimmed, space - end - - def NameBar.do_pretty_print(items) - args = items.inject([]) { |array, item| - array = array + item.pretty_print_input - } - - VIM::pretty_msg *args - end -end - -end - - - -# Maintain MRU ordering. -module LustyJ -class BufferStack - public - def initialize - @stack = [] - - (0..VIM::Buffer.count-1).each do |i| - @stack << VIM::Buffer[i].number - end - end - - # Switch to the previous buffer (the one you were using before the - # current one). This is basically a smarter replacement for :b#, - # accounting for the situation where your previous buffer no longer - # exists. - def juggle_previous - buf = num_at_pos(2) - VIM::command "b #{buf}" - end - - def names(n = :all) - # Get the last n buffer names by MRU. Show only as much of - # the name as necessary to differentiate between buffers of - # the same name. - cull! - names = @stack.collect { |i| VIM::bufname(i) }.reverse - if n != :all - names = names[0,n] - end - shorten_paths(names) - end - - def numbers(n = :all) - # Get the last n buffer numbers by MRU. - cull! - numbers = @stack.reverse - if n == :all - numbers - else - numbers[0,n] - end - end - - def num_at_pos(i) - cull! - return @stack[-i] ? @stack[-i] : @stack.first - end - - def length - cull! - return @stack.length - end - - def push - @stack.delete $curbuf.number - @stack << $curbuf.number - end - - def pop - number = VIM::evaluate('bufnr(expand(""))') - @stack.delete number - end - - private - def cull! - # Remove empty and unlisted buffers. - @stack.delete_if { |x| - not (VIM::evaluate_bool("bufexists(#{x})") and - VIM::evaluate_bool("getbufvar(#{x}, '&buflisted')")) - } - end - - # NOTE: very similar to Entry::compute_buffer_entries() - def shorten_paths(buffer_names) - # Shorten each buffer name by removing all path elements which are not - # needed to differentiate a given name from other names. This usually - # results in only the basename shown, but if several buffers of the - # same basename are opened, there will be more. - - # Group the buffers by common basename - common_base = Hash.new { |hash, k| hash[k] = [] } - buffer_names.each do |name| - basename = Pathname.new(name).basename.to_s - common_base[basename] << name - end - - # Determine the longest common prefix for each basename group. - basename_to_prefix = {} - common_base.each do |k, names| - if names.length > 1 - basename_to_prefix[k] = LustyJ::longest_common_prefix(names) - end - end - - # Shorten each buffer_name by removing the prefix. - buffer_names.map { |name| - base = Pathname.new(name).basename.to_s - prefix = basename_to_prefix[base] - prefix ? name[prefix.length..-1] \ - : base - } - end -end - -end - - - -$lusty_juggler = LustyJ::LustyJuggler.new -$lj_buffer_stack = LustyJ::BufferStack.new - -EOF - -" vim: set sts=2 sw=2: diff --git a/vimrc b/vimrc index b5f841a..43608f6 100644 --- a/vimrc +++ b/vimrc @@ -230,11 +230,6 @@ set viminfo='100,f1 " this uses ctags. the standard way to get this is Ctrl-] nnoremap F -"open buf explorer with B -map B bv -let g:bufExplorerShowRelativePath=1 " Show relative paths. -let g:bufExplorerDefaultHelp=0 - "toggle between last two buffers with Z (normally ctrl-shift-6) nnoremap Z @@ -368,11 +363,11 @@ nmap gcp p " LustyJuggler " ======================================== -" User LustyJuggler buffer switcher by hitting S -" and then using the homerow keys to select the file -" double tap the home row key to go to the file or hit -" once to just select it in the juggler -nmap S \lj +" better triggers for buffer switching +" B to use the juggler, S to search the buffers +nmap B \lj +nmap S \lb + let g:LustyJugglerSuppressRubyWarning = 1 let g:LustyJugglerAltTabMode = 1 " Colors to make LustyJuggler more usable @@ -398,7 +393,7 @@ hi! EasyMotionTarget guifg=yellow " but it'll all be isolated to one area of the keyboard call EasyMotion#InitOptions({ \ 'leader_key' : '' -\ , 'keys' : 'asdfwejkliogh' +\ , 'keys' : 'fjdkslewio' \ , 'do_shade' : 1 \ , 'do_mapping' : 1 \ , 'grouping' : 1