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