831
|
1 " Vim completion script
|
|
2 " Language: Ruby
|
838
|
3 " Maintainer: Mark Guzman <segfault@hasno.info>
|
831
|
4 " Info: $Id$
|
|
5 " URL: http://vim-ruby.rubyforge.org
|
|
6 " Anon CVS: See above site
|
|
7 " Release Coordinator: Doug Kearns <dougkearns@gmail.com>
|
|
8 " ----------------------------------------------------------------------------
|
|
9 "
|
|
10 " Ruby IRB/Complete author: Keiju ISHITSUKA(keiju@ishitsuka.com)
|
|
11 " ----------------------------------------------------------------------------
|
|
12
|
840
|
13 " {{{ requirement checks
|
831
|
14 if !has('ruby')
|
838
|
15 echohl ErrorMsg
|
831
|
16 echo "Error: Required vim compiled with +ruby"
|
838
|
17 echohl None
|
831
|
18 finish
|
|
19 endif
|
|
20
|
|
21 if version < 700
|
838
|
22 echohl ErrorMsg
|
831
|
23 echo "Error: Required vim >= 7.0"
|
838
|
24 echohl None
|
831
|
25 finish
|
|
26 endif
|
840
|
27 " }}} requirement checks
|
831
|
28
|
840
|
29 if !exists("g:rubycomplete_rails")
|
|
30 let g:rubycomplete_rails = 0
|
|
31 endif
|
838
|
32
|
840
|
33 if !exists("g:rubycomplete_classes_in_global")
|
|
34 let g:rubycomplete_classes_in_global = 0
|
|
35 endif
|
|
36
|
|
37 " {{{ vim-side support functions
|
838
|
38 function! GetBufferRubyModule(name)
|
|
39 let [snum,enum] = GetBufferRubyEntity(a:name, "module")
|
|
40 return snum . '..' . enum
|
|
41 endfunction
|
|
42
|
|
43 function! GetBufferRubyClass(name)
|
|
44 let [snum,enum] = GetBufferRubyEntity(a:name, "class")
|
|
45 return snum . '..' . enum
|
|
46 endfunction
|
|
47
|
|
48 function! GetBufferRubySingletonMethods(name)
|
|
49 endfunction
|
|
50
|
|
51 function! GetBufferRubyEntity( name, type )
|
|
52 let stopline = 1
|
|
53 let crex = '^\s*' . a:type . '\s*' . a:name . '\s*\(<\s*.*\s*\)\?\n*\(\(\s\|#\).*\n*\)*\n*\s*end$'
|
|
54 let [lnum,lcol] = searchpos( crex, 'nbw')
|
|
55 if lnum == 0 && lcol == 0
|
|
56 return [0,0]
|
|
57 endif
|
|
58
|
|
59 let [enum,ecol] = searchpos( crex, 'nebw')
|
|
60 if lnum > enum
|
|
61 let realdef = getline( lnum )
|
|
62 let crexb = '^' . realdef . '\n*\(\(\s\|#\).*\n*\)*\n*\s*end$'
|
|
63 let [enum,ecol] = searchpos( crexb, 'necw' )
|
|
64 endif
|
|
65 " we found a the class def
|
|
66 return [lnum,enum]
|
|
67 endfunction
|
|
68
|
|
69 function! IsInClassDef()
|
|
70 let [snum,enum] = GetBufferRubyEntity( '.*', "class" )
|
|
71 let ret = 'nil'
|
|
72 let pos = line('.')
|
|
73
|
|
74 if snum < pos && pos < enum
|
|
75 let ret = snum . '..' . enum
|
|
76 endif
|
|
77
|
|
78 return ret
|
|
79 endfunction
|
|
80
|
|
81 function! GetRubyVarType(v)
|
831
|
82 let stopline = 1
|
|
83 let vtp = ''
|
|
84 let pos = getpos('.')
|
|
85 let [lnum,lcol] = searchpos('^\s*#\s*@var\s*'.a:v.'\>\s\+[^ \t]\+\s*$','nb',stopline)
|
|
86 if lnum != 0 && lcol != 0
|
|
87 call setpos('.',pos)
|
|
88 let str = getline(lnum)
|
|
89 let vtp = substitute(str,'^\s*#\s*@var\s*'.a:v.'\>\s\+\([^ \t]\+\)\s*$','\1','')
|
|
90 return vtp
|
|
91 endif
|
|
92 call setpos('.',pos)
|
843
|
93 if g:rubycomplete_rails == 1 && g:rubycomplete_rails_loaded == 1
|
|
94 let ctors = '\(now\|new\|open\|get_instance\|find\|create\)'
|
|
95 else
|
|
96 let ctors = '\(now\|new\|open\|get_instance\)'
|
|
97 endif
|
|
98
|
|
99 let [lnum,lcol] = searchpos(''.a:v.'\>\s*[+\-*/]*=\s*\([^ \t]\+.' . ctors .'\>\|[\[{"''/]\|%r{\)','nb',stopline)
|
831
|
100 if lnum != 0 && lcol != 0
|
843
|
101 let str = matchstr(getline(lnum),'=\s*\([^ \t]\+.' . ctors . '\>\|[\[{"''/]\|%r{\)',lcol)
|
831
|
102 let str = substitute(str,'^=\s*','','')
|
|
103 call setpos('.',pos)
|
|
104 if str == '"' || str == ''''
|
|
105 return 'String'
|
|
106 elseif str == '['
|
|
107 return 'Array'
|
|
108 elseif str == '{'
|
|
109 return 'Hash'
|
838
|
110 elseif str == '/' || str == '%r{'
|
|
111 return 'Regexp'
|
831
|
112 elseif strlen(str) > 4
|
|
113 let l = stridx(str,'.')
|
|
114 return str[0:l-1]
|
|
115 end
|
|
116 return ''
|
|
117 endif
|
|
118 call setpos('.',pos)
|
|
119 return ''
|
838
|
120 endfunction
|
831
|
121
|
840
|
122 "}}} vim-side support functions
|
|
123
|
831
|
124 function! rubycomplete#Complete(findstart, base)
|
|
125 "findstart = 1 when we need to get the text length
|
|
126 if a:findstart
|
|
127 let line = getline('.')
|
|
128 let idx = col('.')
|
|
129 while idx > 0
|
|
130 let idx -= 1
|
|
131 let c = line[idx-1]
|
|
132 if c =~ '\w'
|
|
133 continue
|
|
134 elseif ! c =~ '\.'
|
|
135 idx = -1
|
|
136 break
|
|
137 else
|
|
138 break
|
|
139 endif
|
|
140 endwhile
|
|
141
|
|
142 return idx
|
|
143 "findstart = 0 when we need to return the list of completions
|
|
144 else
|
838
|
145 let g:rubycomplete_completions = []
|
831
|
146 execute "ruby get_completions('" . a:base . "')"
|
838
|
147 return g:rubycomplete_completions
|
831
|
148 endif
|
|
149 endfunction
|
|
150
|
|
151
|
|
152 function! s:DefRuby()
|
|
153 ruby << RUBYEOF
|
840
|
154 # {{{ ruby completion
|
838
|
155 RailsWords = [
|
|
156 "has_many", "has_one",
|
|
157 "belongs_to",
|
|
158 ]
|
|
159
|
831
|
160 ReservedWords = [
|
|
161 "BEGIN", "END",
|
|
162 "alias", "and",
|
|
163 "begin", "break",
|
|
164 "case", "class",
|
|
165 "def", "defined", "do",
|
|
166 "else", "elsif", "end", "ensure",
|
|
167 "false", "for",
|
|
168 "if", "in",
|
|
169 "module",
|
|
170 "next", "nil", "not",
|
|
171 "or",
|
|
172 "redo", "rescue", "retry", "return",
|
|
173 "self", "super",
|
|
174 "then", "true",
|
|
175 "undef", "unless", "until",
|
|
176 "when", "while",
|
|
177 "yield",
|
|
178 ]
|
|
179
|
|
180 Operators = [ "%", "&", "*", "**", "+", "-", "/",
|
|
181 "<", "<<", "<=", "<=>", "==", "===", "=~", ">", ">=", ">>",
|
|
182 "[]", "[]=", "^", ]
|
|
183
|
838
|
184
|
|
185 def load_requires
|
840
|
186 buf = VIM::Buffer.current
|
|
187 enum = buf.line_number
|
838
|
188 nums = Range.new( 1, enum )
|
|
189 nums.each do |x|
|
840
|
190 ln = buf[x]
|
838
|
191 begin
|
|
192 eval( "require %s" % $1 ) if /.*require\s*(.*)$/.match( ln )
|
|
193 rescue Exception
|
|
194 #ignore?
|
831
|
195 end
|
838
|
196 end
|
|
197 end
|
|
198
|
|
199 def load_buffer_class(name)
|
|
200 classdef = get_buffer_entity(name, 'GetBufferRubyClass("%s")')
|
|
201 return if classdef == nil
|
|
202
|
|
203 pare = /^\s*class\s*(.*)\s*<\s*(.*)\s*\n/.match( classdef )
|
|
204 load_buffer_class( $2 ) if pare != nil
|
|
205
|
|
206 mixre = /.*\n\s*include\s*(.*)\s*\n/.match( classdef )
|
|
207 load_buffer_module( $2 ) if mixre != nil
|
|
208
|
|
209 eval classdef
|
|
210 end
|
|
211
|
|
212 def load_buffer_module(name)
|
|
213 classdef = get_buffer_entity(name, 'GetBufferRubyModule("%s")')
|
|
214 return if classdef == nil
|
|
215
|
|
216 eval classdef
|
831
|
217 end
|
|
218
|
838
|
219 def get_buffer_entity(name, vimfun)
|
840
|
220 buf = VIM::Buffer.current
|
838
|
221 nums = eval( VIM::evaluate( vimfun % name ) )
|
|
222 return nil if nums == nil
|
|
223 return nil if nums.min == nums.max && nums.min == 0
|
|
224
|
|
225 cur_line = VIM::Buffer.current.line_number
|
|
226 classdef = ""
|
|
227 nums.each do |x|
|
|
228 if x != cur_line
|
840
|
229 ln = buf[x]
|
838
|
230 classdef += "%s\n" % ln
|
831
|
231 end
|
838
|
232 end
|
|
233
|
|
234 return classdef
|
|
235 end
|
|
236
|
840
|
237 def get_buffer_classes()
|
|
238 # this will be a little expensive.
|
|
239 allow_aggressive_load = VIM::evaluate('g:rubycomplete_classes_in_global')
|
|
240 return [] if allow_aggressive_load != '1'
|
|
241
|
|
242 buf = VIM::Buffer.current
|
|
243 eob = buf.length
|
|
244 ret = []
|
|
245 rg = 1..eob
|
|
246
|
|
247 rg.each do |x|
|
|
248 if /^\s*class\s*([A-Za-z0-9_-]*)(\s*<\s*([A-Za-z0-9_:-]*))?\s*/.match( buf[x] )
|
|
249 ret.push $1
|
|
250 end
|
|
251 end
|
|
252
|
|
253 return ret
|
|
254 end
|
|
255
|
838
|
256 def load_rails()
|
|
257 allow_rails = VIM::evaluate('g:rubycomplete_rails')
|
|
258 return if allow_rails != '1'
|
|
259
|
|
260 buf_path = VIM::evaluate('expand("%:p")')
|
|
261 file_name = VIM::evaluate('expand("%:t")')
|
|
262 path = buf_path.gsub( file_name, '' )
|
|
263 path.gsub!( /\\/, "/" )
|
843
|
264 pup = [ "./", "../", "../../", "../../../", "../../../../" ]
|
838
|
265 pok = nil
|
|
266
|
|
267 pup.each do |sup|
|
|
268 tpok = "%s%sconfig" % [ path, sup ]
|
|
269 if File.exists?( tpok )
|
|
270 pok = tpok
|
|
271 break
|
|
272 end
|
|
273 end
|
840
|
274
|
|
275 return if pok == nil
|
843
|
276
|
838
|
277 bootfile = pok + "/boot.rb"
|
843
|
278 envfile = pok + "/environment.rb"
|
|
279 if File.exists?( bootfile ) && File.exists?( envfile )
|
|
280 begin
|
|
281 require bootfile
|
|
282 require envfile
|
|
283 require 'console_app'
|
|
284 require 'console_with_helpers'
|
|
285 VIM::command('let g:rubycomplete_rails_loaded = 1')
|
|
286 rescue
|
|
287 print "Error loading rails environment"
|
|
288 end
|
840
|
289 end
|
838
|
290 end
|
|
291
|
|
292 def get_rails_helpers
|
|
293 allow_rails = VIM::evaluate('g:rubycomplete_rails')
|
840
|
294 rails_loaded = VIM::evaluate('g:rubycomplete_rails_loaded')
|
|
295 return [] if allow_rails != '1' || rails_loaded != '1'
|
838
|
296 return RailsWords
|
831
|
297 end
|
|
298
|
|
299 def get_completions(base)
|
838
|
300 load_requires
|
|
301 load_rails
|
|
302
|
|
303 input = VIM::evaluate('expand("<cWORD>")')
|
|
304 input += base
|
|
305 input.lstrip!
|
|
306 if input.length == 0
|
|
307 input = VIM::Buffer.current.line
|
|
308 input.strip!
|
|
309 end
|
|
310 message = nil
|
831
|
311
|
|
312
|
838
|
313 case input
|
|
314 when /^(\/[^\/]*\/)\.([^.]*)$/
|
|
315 # Regexp
|
|
316 receiver = $1
|
|
317 message = Regexp.quote($2)
|
|
318
|
|
319 candidates = Regexp.instance_methods(true)
|
|
320 select_message(receiver, message, candidates)
|
831
|
321
|
838
|
322 when /^([^\]]*\])\.([^.]*)$/
|
|
323 # Array
|
|
324 receiver = $1
|
|
325 message = Regexp.quote($2)
|
|
326
|
|
327 candidates = Array.instance_methods(true)
|
|
328 select_message(receiver, message, candidates)
|
|
329
|
|
330 when /^([^\}]*\})\.([^.]*)$/
|
|
331 # Proc or Hash
|
|
332 receiver = $1
|
|
333 message = Regexp.quote($2)
|
831
|
334
|
838
|
335 candidates = Proc.instance_methods(true) | Hash.instance_methods(true)
|
|
336 select_message(receiver, message, candidates)
|
831
|
337
|
838
|
338 when /^(:[^:.]*)$/
|
|
339 # Symbol
|
|
340 if Symbol.respond_to?(:all_symbols)
|
|
341 sym = $1
|
|
342 candidates = Symbol.all_symbols.collect{|s| ":" + s.id2name}
|
|
343 candidates.grep(/^#{sym}/)
|
|
344 candidates.delete_if do |c|
|
|
345 c.match( /'/ )
|
|
346 end
|
|
347 candidates.uniq!
|
|
348 candidates.sort!
|
|
349 else
|
|
350 []
|
|
351 end
|
831
|
352
|
838
|
353 when /^::([A-Z][^:\.\(]*)$/
|
|
354 # Absolute Constant or class methods
|
|
355 receiver = $1
|
|
356 candidates = Object.constants
|
|
357 candidates.grep(/^#{receiver}/).collect{|e| "::" + e}
|
831
|
358
|
838
|
359 when /^(((::)?[A-Z][^:.\(]*)+)::?([^:.]*)$/
|
|
360 # Constant or class methods
|
|
361 receiver = $1
|
|
362 message = Regexp.quote($4)
|
|
363 begin
|
|
364 candidates = eval("#{receiver}.constants | #{receiver}.methods")
|
|
365 rescue Exception
|
|
366 candidates = []
|
|
367 end
|
|
368 candidates.grep(/^#{message}/).collect{|e| receiver + "::" + e}
|
831
|
369
|
838
|
370 when /^(:[^:.]+)\.([^.]*)$/
|
|
371 # Symbol
|
|
372 receiver = $1
|
|
373 message = Regexp.quote($2)
|
|
374
|
|
375 candidates = Symbol.instance_methods(true)
|
|
376 select_message(receiver, message, candidates)
|
|
377
|
|
378 when /^([0-9_]+(\.[0-9_]+)?(e[0-9]+)?)\.([^.]*)$/
|
|
379 # Numeric
|
|
380 receiver = $1
|
|
381 message = Regexp.quote($4)
|
831
|
382
|
838
|
383 begin
|
|
384 candidates = eval(receiver).methods
|
|
385 rescue Exception
|
|
386 candidates
|
|
387 end
|
|
388 select_message(receiver, message, candidates)
|
|
389
|
|
390 when /^(\$[^.]*)$/
|
|
391 candidates = global_variables.grep(Regexp.new(Regexp.quote($1)))
|
831
|
392
|
838
|
393 # when /^(\$?(\.?[^.]+)+)\.([^.]*)$/
|
|
394 when /^((\.?[^.]+)+)\.([^.]*)$/
|
|
395 # variable
|
|
396 receiver = $1
|
|
397 message = Regexp.quote($3)
|
|
398 load_buffer_class( receiver )
|
|
399
|
|
400 cv = eval("self.class.constants")
|
|
401
|
|
402 vartype = VIM::evaluate("GetRubyVarType('%s')" % receiver)
|
|
403 if vartype != ''
|
|
404 load_buffer_class( vartype )
|
|
405
|
831
|
406 begin
|
838
|
407 candidates = eval("#{vartype}.instance_methods")
|
831
|
408 rescue Exception
|
|
409 candidates = []
|
|
410 end
|
838
|
411 elsif (cv).include?(receiver)
|
|
412 # foo.func and foo is local var.
|
|
413 candidates = eval("#{receiver}.methods")
|
|
414 elsif /^[A-Z]/ =~ receiver and /\./ !~ receiver
|
|
415 # Foo::Bar.func
|
831
|
416 begin
|
838
|
417 candidates = eval("#{receiver}.methods")
|
831
|
418 rescue Exception
|
838
|
419 candidates = []
|
831
|
420 end
|
838
|
421 else
|
|
422 # func1.func2
|
|
423 candidates = []
|
|
424 ObjectSpace.each_object(Module){|m|
|
|
425 next if m.name != "IRB::Context" and
|
|
426 /^(IRB|SLex|RubyLex|RubyToken)/ =~ m.name
|
|
427 candidates.concat m.instance_methods(false)
|
|
428 }
|
|
429 candidates.sort!
|
|
430 candidates.uniq!
|
|
431 end
|
|
432 #identify_type( receiver )
|
|
433 select_message(receiver, message, candidates)
|
831
|
434
|
|
435 #when /^((\.?[^.]+)+)\.([^.]*)\(\s*\)*$/
|
|
436 #function call
|
|
437 #obj = $1
|
|
438 #func = $3
|
|
439
|
838
|
440 when /^\.([^.]*)$/
|
831
|
441 # unknown(maybe String)
|
|
442
|
838
|
443 receiver = ""
|
|
444 message = Regexp.quote($1)
|
|
445
|
|
446 candidates = String.instance_methods(true)
|
|
447 select_message(receiver, message, candidates)
|
|
448
|
|
449 else
|
|
450 inclass = eval( VIM::evaluate("IsInClassDef()") )
|
831
|
451
|
838
|
452 if inclass != nil
|
|
453 classdef = "%s\n" % VIM::Buffer.current[ inclass.min ]
|
|
454 found = /^\s*class\s*([A-Za-z0-9_-]*)(\s*<\s*([A-Za-z0-9_:-]*))?\s*\n$/.match( classdef )
|
|
455
|
|
456 if found != nil
|
|
457 receiver = $1
|
|
458 message = input
|
|
459 load_buffer_class( receiver )
|
840
|
460 begin
|
|
461 candidates = eval( "#{receiver}.instance_methods" )
|
|
462 candidates += get_rails_helpers
|
|
463 select_message(receiver, message, candidates)
|
|
464 rescue Exception
|
|
465 found = nil
|
|
466 end
|
838
|
467 end
|
|
468 end
|
|
469
|
|
470 if inclass == nil || found == nil
|
831
|
471 candidates = eval("self.class.constants")
|
840
|
472 candidates += get_buffer_classes
|
|
473 candidates.uniq!
|
|
474 candidates.sort!
|
831
|
475 (candidates|ReservedWords).grep(/^#{Regexp.quote(input)}/)
|
|
476 end
|
838
|
477 end
|
831
|
478
|
|
479 #print candidates
|
838
|
480 if message != nil && message.length > 0
|
|
481 rexp = '^%s' % message.downcase
|
|
482 candidates.delete_if do |c|
|
|
483 c.downcase.match( rexp )
|
|
484 $~ == nil
|
831
|
485 end
|
838
|
486 end
|
831
|
487
|
838
|
488 outp = ""
|
|
489
|
|
490 # tags = VIM::evaluate("taglist('^%s$')" %
|
|
491 valid = (candidates-Object.instance_methods)
|
|
492
|
|
493 rg = 0..valid.length
|
|
494 rg.step(150) do |x|
|
|
495 stpos = 0+x
|
|
496 enpos = 150+x
|
|
497 valid[stpos..enpos].each { |c| outp += "{'word':'%s','item':'%s'}," % [ c, c ] }
|
|
498 outp.sub!(/,$/, '')
|
|
499
|
|
500 VIM::command("call extend(g:rubycomplete_completions, [%s])" % outp)
|
831
|
501 outp = ""
|
838
|
502 end
|
831
|
503 end
|
|
504
|
|
505
|
|
506 def select_message(receiver, message, candidates)
|
838
|
507 #tags = VIM::evaluate("taglist('%s')" % receiver)
|
|
508 #print tags
|
831
|
509 candidates.grep(/^#{message}/).collect do |e|
|
|
510 case e
|
|
511 when /^[a-zA-Z_]/
|
|
512 receiver + "." + e
|
|
513 when /^[0-9]/
|
|
514 when *Operators
|
|
515 #receiver + " " + e
|
|
516 end
|
|
517 end
|
|
518 candidates.delete_if { |x| x == nil }
|
|
519 candidates.uniq!
|
|
520 candidates.sort!
|
|
521 end
|
840
|
522
|
|
523 # }}} ruby completion
|
831
|
524 RUBYEOF
|
|
525 endfunction
|
|
526
|
840
|
527 let g:rubycomplete_rails_loaded = 0
|
838
|
528
|
831
|
529 call s:DefRuby()
|
840
|
530 " vim:tw=78:sw=4:ts=8:ft=vim:norl:
|