specky/plugin/specky.vim
branchvim-stuff
changeset 2 6b33188f1694
child 3 db3e3abfc647
equal deleted inserted replaced
1:fd505fd00e63 2:6b33188f1694
       
     1 " vim: set noet nosta sw=4 ts=4 fdm=marker :
       
     2 "
       
     3 " Specky!
       
     4 " Mahlon E. Smith <mahlon@martini.nu>
       
     5 " $Id$
       
     6 "
       
     7 
       
     8 " }}}
       
     9 " Hook up the functions to the user supplied key bindings. {{{
       
    10 "
       
    11 if exists( 'g:speckySpecSwitcherKey' )
       
    12 	execute 'map ' . g:speckySpecSwitcherKey . ' :call <SID>SpecSwitcher()<CR>'
       
    13 endif
       
    14 
       
    15 if exists( 'g:speckyQuoteSwitcherKey' )
       
    16 	execute 'map ' . g:speckyQuoteSwitcherKey . ' :call <SID>QuoteSwitcher()<CR>'
       
    17 endif
       
    18 
       
    19 if exists( 'g:speckyRunSpecKey' )
       
    20 	execute 'map ' . g:speckyRunSpecKey . ' :call <SID>RunSpec()<CR>'
       
    21 endif
       
    22 
       
    23 if exists( 'g:speckyRunRdocKey' )
       
    24 	execute 'map ' . g:speckyRunRdocKey . ' :call <SID>RunRdoc()<CR>'
       
    25 endif
       
    26 
       
    27 
       
    28 if exists( 'specky_loaded' )
       
    29 	finish
       
    30 endif
       
    31 let specky_loaded = '$Rev: 92 $'
       
    32 
       
    33 
       
    34 "}}}
       
    35 " Menu configuration {{{
       
    36 "
       
    37 let s:menuloc = '&Plugin.&specky'
       
    38 execute 'menu ' . s:menuloc . '.&Jump\ to\ code/spec :call <SID>SpecSwitcher()<CR>'
       
    39 execute 'menu ' . s:menuloc . '.Run\ &spec :call <SID>RunSpec()<CR>'
       
    40 execute 'menu ' . s:menuloc . '.&RDoc\ lookup :call <SID>RunRdoc()<CR>'
       
    41 execute 'menu ' . s:menuloc . '.Rotate\ &quote\ style :call <SID>QuoteSwitcher()<CR>'
       
    42 
       
    43 
       
    44 " }}}
       
    45 " SpecSwitcher() {{{
       
    46 "
       
    47 " When in ruby code or an rspec BDD file, try and search recursively through
       
    48 " the filesystem (within the current working directory) to find the
       
    49 " respectively matching file.  (code to spec, spec to code.)
       
    50 "
       
    51 " This operates under the assumption that you've used chdir() to put vim into
       
    52 " the top level directory of your project.
       
    53 "
       
    54 function! <SID>SpecSwitcher()
       
    55 
       
    56 	" If we aren't in a ruby file (specs are ruby-mode too) then we probably
       
    57 	" don't care too much about this function.
       
    58 	"
       
    59 	if &ft != 'ruby'
       
    60 		call s:err( "Not currently in ruby-mode." )
       
    61 		return
       
    62 	endif
       
    63 
       
    64 	" Ensure that we can always search recursively for files to open.
       
    65 	"
       
    66 	let l:orig_path = &path
       
    67 	set path=**
       
    68 
       
    69 	" Get the current buffer name, and determine if it is a spec file.
       
    70 	"
       
    71 	" /tmp/something/whatever/rubycode.rb ---> rubycode.rb
       
    72 	" A requisite of the specfiles is that they match to the class/code file,
       
    73 	" this emulates the eigenclass stuff, but doesn't require the same
       
    74 	" directory structures.
       
    75 	"
       
    76 	" rubycode.rb ---> rubycode_spec.rb
       
    77 	" 
       
    78 	let l:filename     = matchstr( bufname('%'), '[0-9A-Za-z_.-]*$' )
       
    79 	let l:is_spec_file = match( l:filename, '_spec.rb$' ) == -1 ? 0 : 1
       
    80 
       
    81 	if l:is_spec_file
       
    82 		let l:other_file = substitute( l:filename, '_spec\.rb$', '\.rb', '' )
       
    83 	else
       
    84 		let l:other_file = substitute( l:filename, '\.rb$', '_spec\.rb', '' )
       
    85 	endif
       
    86 
       
    87 	let l:bufnum = bufnr( l:other_file )
       
    88 	if l:bufnum == -1
       
    89 		" The file isn't currently open, so let's search for it.
       
    90 		execute 'find ' . l:other_file
       
    91 	else
       
    92 		" We've already got an open buffer with this file, just go to it.
       
    93 		execute 'buffer' . l:bufnum
       
    94 	endif
       
    95 
       
    96 	" Restore the original path.
       
    97 	"
       
    98 	execute 'set path=' . l:orig_path
       
    99 endfunction
       
   100 
       
   101 
       
   102 " }}}
       
   103 " QuoteSwitcher() {{{
       
   104 "
       
   105 " Wrap the word under the cursor in quotes.  If in ruby mode,
       
   106 " cycle between quoting styles and symbols.
       
   107 "
       
   108 " variable -> "variable" -> 'variable' -> :variable
       
   109 "
       
   110 function! <SID>QuoteSwitcher()
       
   111 	let l:type = strpart( expand("<cWORD>"), 0, 1 )
       
   112 	let l:word = expand("<cword>")
       
   113 
       
   114 	if l:type == '"'
       
   115 		" Double quote to single
       
   116 		"
       
   117 		execute ":normal viWs'" . l:word . "'"
       
   118 
       
   119 	elseif l:type == "'"
       
   120 		if &ft == "ruby"
       
   121 			" Single quote to symbol
       
   122 			"
       
   123 			execute ':normal viWs:' . l:word
       
   124 		else
       
   125 			" Single quote to double
       
   126 			"
       
   127 			execute ':normal viWs"' . l:word . '"'
       
   128 		end
       
   129 
       
   130 	else
       
   131 		" Whatever to double quote
       
   132 		"
       
   133 		execute ':normal viWs"' . l:word . '"'
       
   134 	endif
       
   135 
       
   136 	" Move the cursor back into the cl:word
       
   137 	"
       
   138 	call cursor( 0, getpos('.')[2] - 1 )
       
   139 endfunction
       
   140 
       
   141 
       
   142 " }}}
       
   143 " RunSpec() {{{
       
   144 "
       
   145 " Run this function while in a spec file to run the specs within vim.
       
   146 "
       
   147 function! <SID>RunSpec()
       
   148 
       
   149 	" If we're in the code instead of the spec, try and switch
       
   150 	" before running tests.
       
   151 	"
       
   152 	let l:filename     = matchstr( bufname('%'), '[0-9A-Za-z_.-]*$' )
       
   153 	let l:is_spec_file = match( l:filename, '_spec.rb$' ) == -1 ? 0 : 1
       
   154 	if ( ! l:is_spec_file )
       
   155 		silent call <SID>SpecSwitcher()
       
   156 	endif
       
   157 
       
   158 	let l:spec   = bufname('%')
       
   159 	let l:buf    = 'specky:specrun'
       
   160 	let l:bufnum = bufnr( l:buf )
       
   161 
       
   162 	" Squash the old buffer, if it exists.
       
   163 	"
       
   164 	if buflisted( l:buf )
       
   165 		execute 'bd! ' . l:buf
       
   166 	endif
       
   167 
       
   168 	execute ( exists('g:speckyVertSplit') ? 'vert new ' : 'new ') . l:buf
       
   169 	setlocal buftype=nofile bufhidden=delete noswapfile filetype=specrun
       
   170 	set foldtext='--'.getline(v:foldstart).v:folddashes
       
   171 
       
   172 	" Set up some convenient keybindings.
       
   173 	"
       
   174 	nnoremap <silent> <buffer> q :close<CR>
       
   175 	nnoremap <silent> <buffer> e :call <SID>FindSpecError(1)<CR>
       
   176 	nnoremap <silent> <buffer> r :call <SID>FindSpecError(-1)<CR>
       
   177 	nnoremap <silent> <buffer> E :call <SID>FindSpecError(0)<CR>
       
   178 	nnoremap <silent> <buffer> <C-e> :let b:err_line=1<CR>
       
   179 
       
   180 	" Default cmd for spec
       
   181 	"
       
   182 	if !exists( 'g:speckyRunSpecCmd' )
       
   183 		let g:speckyRunSpecCmd = 'spec -fs'
       
   184 	endif
       
   185 
       
   186 	" Call spec and gather up the output
       
   187 	"
       
   188 	let l:cmd    =  g:speckyRunSpecCmd . ' ' . l:spec
       
   189 	let l:output = system( l:cmd )
       
   190 	call append( 0, split( l:output, "\n" ) )
       
   191 	call append( 0, '' )
       
   192 	call append( 0, 'Output of: ' . l:cmd  )
       
   193 	normal gg
       
   194 
       
   195 	" Lockdown the buffer
       
   196 	"
       
   197 	setlocal nomodifiable
       
   198 endfunction
       
   199 
       
   200 
       
   201 " }}}
       
   202 " RunRdoc() {{{
       
   203 "
       
   204 " Get documentation for the word under the cursor.
       
   205 "
       
   206 function! <SID>RunRdoc()
       
   207 
       
   208 	" If we aren't in a ruby file (specs are ruby-mode too) then we probably
       
   209 	" don't care too much about this function.
       
   210 	"
       
   211 	if ( &ft != 'ruby' && &ft != 'rdoc' )
       
   212 		call s:err( "Not currently in ruby-mode." )
       
   213 		return
       
   214 	endif
       
   215 
       
   216 	" Set defaults
       
   217 	"
       
   218 	if !exists( 'g:speckyRunRdocCmd' )
       
   219 		let g:speckyRunRdocCmd = 'ri'
       
   220 	endif
       
   221 
       
   222 	let l:buf     = 'specky:rdoc'
       
   223 	let l:bufname = bufname('%')
       
   224 
       
   225 	if ( match( l:bufname, l:buf ) != -1 )
       
   226 		" Already in the rdoc buffer.  This allows us to lookup
       
   227 		" something like Kernel#require.
       
   228 		"
       
   229 		let l:word = expand('<cWORD>')
       
   230 	else
       
   231 		" Not in the rdoc buffer.  This allows us to lookup
       
   232 		" something like 'each' in some_hash.each { ... }
       
   233 		"
       
   234 		let l:word = expand('<cword>')
       
   235 	endif
       
   236 
       
   237 	" Squash the old buffer, if it exists.
       
   238 	"
       
   239 	if buflisted( l:buf )
       
   240 		execute 'bd! ' . l:buf
       
   241 	endif
       
   242 
       
   243 	" With multiple matches, strip the comams from the cWORD.
       
   244 	"
       
   245 	let l:word = substitute( l:word, ',', '', 'eg' )
       
   246 
       
   247 	execute ( exists('g:speckyVertSplit') ? 'vert new ' : 'new ') . l:buf
       
   248 	setlocal buftype=nofile bufhidden=delete noswapfile filetype=rdoc
       
   249 	nnoremap <silent> <buffer> q :close<CR>
       
   250 
       
   251 	" Call the documentation and gather up the output
       
   252 	"
       
   253 	let l:cmd    = g:speckyRunRdocCmd . ' ' . l:word
       
   254 	let l:output = system( l:cmd )
       
   255 	call append( 0, split( l:output, "\n" ) )
       
   256 	execute 'normal gg'
       
   257 
       
   258 	" Lockdown the buffer
       
   259 	"
       
   260 	execute 'setlocal nomodifiable'
       
   261 endfunction
       
   262 
       
   263 
       
   264 " }}}
       
   265 " FindSpecError( detail ) {{{
       
   266 "
       
   267 " Convenience searches for jumping to spec failures.
       
   268 "
       
   269 function! <SID>FindSpecError( detail )
       
   270 
       
   271 	let l:err_str = '(FAILED\|ERROR - \d\+)$'
       
   272 
       
   273 	if ( a:detail == 0 )
       
   274 		" Find the detailed failure text for the current failure line,
       
   275 		" and unfold it.
       
   276 		"
       
   277 		let l:orig_so = &so
       
   278 		set so=100
       
   279 		call search('^' . matchstr(getline('.'),'\d\+)$') )
       
   280 		if has('folding')
       
   281 			silent! normal za
       
   282 		endif
       
   283 		execute 'set so=' . l:orig_so
       
   284 
       
   285 	else
       
   286 		" Find the 'regular' failure line
       
   287 		"
       
   288 		if exists( 'b:err_line' )
       
   289 			call cursor( b:err_line, a:detail == -1 ? 1 : strlen(getline(b:err_line)) )
       
   290 		endif
       
   291 		call search( l:err_str, a:detail == -1 ? 'b' : '' )
       
   292 		let b:err_line = line('.')
       
   293 		nohl
       
   294 
       
   295 	endif
       
   296 endfunction
       
   297 
       
   298 
       
   299 " }}}
       
   300 " s:err( msg ) "{{{
       
   301 " Notify of problems in a consistent fashion.
       
   302 "
       
   303 function! s:err( msg )
       
   304 	echohl WarningMsg|echomsg 'specky: ' . a:msg|echohl None
       
   305 endfunction " }}}
       
   306