blob: d1c8a26538977b7ef7ed7c47cb27153cd467ccad [file] [log] [blame]
Bram Moolenaarc6249bb2006-04-15 20:25:09 +00001" Vim completion script
2" Language: Ruby
Bram Moolenaarfc1421e2006-04-20 22:17:20 +00003" Maintainer: Mark Guzman <segfault@hasno.info>
Bram Moolenaarc6249bb2006-04-15 20:25:09 +00004" 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
Bram Moolenaareb3593b2006-04-22 22:33:57 +000013" {{{ requirement checks
Bram Moolenaarc6249bb2006-04-15 20:25:09 +000014if !has('ruby')
Bram Moolenaarfc1421e2006-04-20 22:17:20 +000015 echohl ErrorMsg
Bram Moolenaarc6249bb2006-04-15 20:25:09 +000016 echo "Error: Required vim compiled with +ruby"
Bram Moolenaarfc1421e2006-04-20 22:17:20 +000017 echohl None
Bram Moolenaarc6249bb2006-04-15 20:25:09 +000018 finish
19endif
20
21if version < 700
Bram Moolenaarfc1421e2006-04-20 22:17:20 +000022 echohl ErrorMsg
Bram Moolenaarc6249bb2006-04-15 20:25:09 +000023 echo "Error: Required vim >= 7.0"
Bram Moolenaarfc1421e2006-04-20 22:17:20 +000024 echohl None
Bram Moolenaarc6249bb2006-04-15 20:25:09 +000025 finish
26endif
Bram Moolenaareb3593b2006-04-22 22:33:57 +000027" }}} requirement checks
Bram Moolenaarc6249bb2006-04-15 20:25:09 +000028
Bram Moolenaareb3593b2006-04-22 22:33:57 +000029if !exists("g:rubycomplete_rails")
30 let g:rubycomplete_rails = 0
31endif
Bram Moolenaarfc1421e2006-04-20 22:17:20 +000032
Bram Moolenaareb3593b2006-04-22 22:33:57 +000033if !exists("g:rubycomplete_classes_in_global")
34 let g:rubycomplete_classes_in_global = 0
35endif
36
37" {{{ vim-side support functions
Bram Moolenaarfc1421e2006-04-20 22:17:20 +000038function! GetBufferRubyModule(name)
39 let [snum,enum] = GetBufferRubyEntity(a:name, "module")
40 return snum . '..' . enum
41endfunction
42
43function! GetBufferRubyClass(name)
44 let [snum,enum] = GetBufferRubyEntity(a:name, "class")
45 return snum . '..' . enum
46endfunction
47
48function! GetBufferRubySingletonMethods(name)
49endfunction
50
51function! 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]
67endfunction
68
69function! 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
79endfunction
80
81function! GetRubyVarType(v)
Bram Moolenaarc6249bb2006-04-15 20:25:09 +000082 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)
Bram Moolenaarfc1421e2006-04-20 22:17:20 +000093 let [lnum,lcol] = searchpos(''.a:v.'\>\s*[+\-*/]*=\s*\([^ \t]\+.\(now\|new\|open\|get_instance\)\>\|[\[{"''/]\|%r{\)','nb',stopline)
Bram Moolenaarc6249bb2006-04-15 20:25:09 +000094 if lnum != 0 && lcol != 0
Bram Moolenaarfc1421e2006-04-20 22:17:20 +000095 let str = matchstr(getline(lnum),'=\s*\([^ \t]\+.\(now\|new\|open\|get_instance\)\>\|[\[{"''/]\|%r{\)',lcol)
Bram Moolenaarc6249bb2006-04-15 20:25:09 +000096 let str = substitute(str,'^=\s*','','')
97 call setpos('.',pos)
98 if str == '"' || str == ''''
99 return 'String'
100 elseif str == '['
101 return 'Array'
102 elseif str == '{'
103 return 'Hash'
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000104 elseif str == '/' || str == '%r{'
105 return 'Regexp'
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000106 elseif strlen(str) > 4
107 let l = stridx(str,'.')
108 return str[0:l-1]
109 end
110 return ''
111 endif
112 call setpos('.',pos)
113 return ''
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000114endfunction
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000115
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000116"}}} vim-side support functions
117
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000118function! rubycomplete#Complete(findstart, base)
119 "findstart = 1 when we need to get the text length
120 if a:findstart
121 let line = getline('.')
122 let idx = col('.')
123 while idx > 0
124 let idx -= 1
125 let c = line[idx-1]
126 if c =~ '\w'
127 continue
128 elseif ! c =~ '\.'
129 idx = -1
130 break
131 else
132 break
133 endif
134 endwhile
135
136 return idx
137 "findstart = 0 when we need to return the list of completions
138 else
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000139 let g:rubycomplete_completions = []
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000140 execute "ruby get_completions('" . a:base . "')"
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000141 return g:rubycomplete_completions
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000142 endif
143endfunction
144
145
146function! s:DefRuby()
147ruby << RUBYEOF
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000148# {{{ ruby completion
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000149RailsWords = [
150 "has_many", "has_one",
151 "belongs_to",
152 ]
153
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000154ReservedWords = [
155 "BEGIN", "END",
156 "alias", "and",
157 "begin", "break",
158 "case", "class",
159 "def", "defined", "do",
160 "else", "elsif", "end", "ensure",
161 "false", "for",
162 "if", "in",
163 "module",
164 "next", "nil", "not",
165 "or",
166 "redo", "rescue", "retry", "return",
167 "self", "super",
168 "then", "true",
169 "undef", "unless", "until",
170 "when", "while",
171 "yield",
172 ]
173
174Operators = [ "%", "&", "*", "**", "+", "-", "/",
175 "<", "<<", "<=", "<=>", "==", "===", "=~", ">", ">=", ">>",
176 "[]", "[]=", "^", ]
177
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000178
179def load_requires
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000180 buf = VIM::Buffer.current
181 enum = buf.line_number
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000182 nums = Range.new( 1, enum )
183 nums.each do |x|
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000184 ln = buf[x]
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000185 begin
186 eval( "require %s" % $1 ) if /.*require\s*(.*)$/.match( ln )
187 rescue Exception
188 #ignore?
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000189 end
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000190 end
191end
192
193def load_buffer_class(name)
194 classdef = get_buffer_entity(name, 'GetBufferRubyClass("%s")')
195 return if classdef == nil
196
197 pare = /^\s*class\s*(.*)\s*<\s*(.*)\s*\n/.match( classdef )
198 load_buffer_class( $2 ) if pare != nil
199
200 mixre = /.*\n\s*include\s*(.*)\s*\n/.match( classdef )
201 load_buffer_module( $2 ) if mixre != nil
202
203 eval classdef
204end
205
206def load_buffer_module(name)
207 classdef = get_buffer_entity(name, 'GetBufferRubyModule("%s")')
208 return if classdef == nil
209
210 eval classdef
211end
212
213def get_buffer_entity(name, vimfun)
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000214 buf = VIM::Buffer.current
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000215 nums = eval( VIM::evaluate( vimfun % name ) )
216 return nil if nums == nil
217 return nil if nums.min == nums.max && nums.min == 0
218
219 cur_line = VIM::Buffer.current.line_number
220 classdef = ""
221 nums.each do |x|
222 if x != cur_line
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000223 ln = buf[x]
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000224 classdef += "%s\n" % ln
225 end
226 end
227
228 return classdef
229end
230
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000231def get_buffer_classes()
232 # this will be a little expensive.
233 allow_aggressive_load = VIM::evaluate('g:rubycomplete_classes_in_global')
234 return [] if allow_aggressive_load != '1'
235
236 buf = VIM::Buffer.current
237 eob = buf.length
238 ret = []
239 rg = 1..eob
240
241 rg.each do |x|
242 if /^\s*class\s*([A-Za-z0-9_-]*)(\s*<\s*([A-Za-z0-9_:-]*))?\s*/.match( buf[x] )
243 ret.push $1
244 end
245 end
246
247 return ret
248end
249
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000250def load_rails()
251 allow_rails = VIM::evaluate('g:rubycomplete_rails')
252 return if allow_rails != '1'
253
254 buf_path = VIM::evaluate('expand("%:p")')
255 file_name = VIM::evaluate('expand("%:t")')
256 path = buf_path.gsub( file_name, '' )
257 path.gsub!( /\\/, "/" )
258 pup = [ "../", "../../", "../../../", "../../../../" ]
259 pok = nil
260
261 pup.each do |sup|
262 tpok = "%s%sconfig" % [ path, sup ]
263 if File.exists?( tpok )
264 pok = tpok
265 break
266 end
267 end
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000268
269 return if pok == nil
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000270 bootfile = pok + "/boot.rb"
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000271 if File.exists?( bootfile )
272 require bootfile
273 VIM::evaluate('let g:rubycomplete_rails_loaded = 1')
274 end
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000275end
276
277def get_rails_helpers
278 allow_rails = VIM::evaluate('g:rubycomplete_rails')
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000279 rails_loaded = VIM::evaluate('g:rubycomplete_rails_loaded')
280 return [] if allow_rails != '1' || rails_loaded != '1'
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000281 return RailsWords
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000282end
283
284def get_completions(base)
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000285 load_requires
286 load_rails
287
288 input = VIM::evaluate('expand("<cWORD>")')
289 input += base
290 input.lstrip!
291 if input.length == 0
292 input = VIM::Buffer.current.line
293 input.strip!
294 end
295 message = nil
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000296
297
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000298 case input
299 when /^(\/[^\/]*\/)\.([^.]*)$/
300 # Regexp
301 receiver = $1
302 message = Regexp.quote($2)
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000303
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000304 candidates = Regexp.instance_methods(true)
305 select_message(receiver, message, candidates)
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000306
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000307 when /^([^\]]*\])\.([^.]*)$/
308 # Array
309 receiver = $1
310 message = Regexp.quote($2)
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000311
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000312 candidates = Array.instance_methods(true)
313 select_message(receiver, message, candidates)
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000314
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000315 when /^([^\}]*\})\.([^.]*)$/
316 # Proc or Hash
317 receiver = $1
318 message = Regexp.quote($2)
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000319
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000320 candidates = Proc.instance_methods(true) | Hash.instance_methods(true)
321 select_message(receiver, message, candidates)
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000322
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000323 when /^(:[^:.]*)$/
324 # Symbol
325 if Symbol.respond_to?(:all_symbols)
326 sym = $1
327 candidates = Symbol.all_symbols.collect{|s| ":" + s.id2name}
328 candidates.grep(/^#{sym}/)
329 candidates.delete_if do |c|
330 c.match( /'/ )
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000331 end
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000332 candidates.uniq!
333 candidates.sort!
334 else
335 []
336 end
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000337
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000338 when /^::([A-Z][^:\.\(]*)$/
339 # Absolute Constant or class methods
340 receiver = $1
341 candidates = Object.constants
342 candidates.grep(/^#{receiver}/).collect{|e| "::" + e}
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000343
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000344 when /^(((::)?[A-Z][^:.\(]*)+)::?([^:.]*)$/
345 # Constant or class methods
346 receiver = $1
347 message = Regexp.quote($4)
348 begin
349 candidates = eval("#{receiver}.constants | #{receiver}.methods")
350 rescue Exception
351 candidates = []
352 end
353 candidates.grep(/^#{message}/).collect{|e| receiver + "::" + e}
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000354
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000355 when /^(:[^:.]+)\.([^.]*)$/
356 # Symbol
357 receiver = $1
358 message = Regexp.quote($2)
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000359
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000360 candidates = Symbol.instance_methods(true)
361 select_message(receiver, message, candidates)
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000362
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000363 when /^([0-9_]+(\.[0-9_]+)?(e[0-9]+)?)\.([^.]*)$/
364 # Numeric
365 receiver = $1
366 message = Regexp.quote($4)
367
368 begin
369 candidates = eval(receiver).methods
370 rescue Exception
371 candidates
372 end
373 select_message(receiver, message, candidates)
374
375 when /^(\$[^.]*)$/
376 candidates = global_variables.grep(Regexp.new(Regexp.quote($1)))
377
378# when /^(\$?(\.?[^.]+)+)\.([^.]*)$/
379 when /^((\.?[^.]+)+)\.([^.]*)$/
380 # variable
381 receiver = $1
382 message = Regexp.quote($3)
383 load_buffer_class( receiver )
384
385 cv = eval("self.class.constants")
386
387 vartype = VIM::evaluate("GetRubyVarType('%s')" % receiver)
388 if vartype != ''
389 load_buffer_class( vartype )
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000390
391 begin
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000392 candidates = eval("#{vartype}.instance_methods")
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000393 rescue Exception
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000394 candidates = []
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000395 end
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000396 elsif (cv).include?(receiver)
397 # foo.func and foo is local var.
398 candidates = eval("#{receiver}.methods")
399 elsif /^[A-Z]/ =~ receiver and /\./ !~ receiver
400 # Foo::Bar.func
401 begin
402 candidates = eval("#{receiver}.methods")
403 rescue Exception
404 candidates = []
405 end
406 else
407 # func1.func2
408 candidates = []
409 ObjectSpace.each_object(Module){|m|
410 next if m.name != "IRB::Context" and
411 /^(IRB|SLex|RubyLex|RubyToken)/ =~ m.name
412 candidates.concat m.instance_methods(false)
413 }
414 candidates.sort!
415 candidates.uniq!
416 end
417 #identify_type( receiver )
418 select_message(receiver, message, candidates)
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000419
420 #when /^((\.?[^.]+)+)\.([^.]*)\(\s*\)*$/
421 #function call
422 #obj = $1
423 #func = $3
424
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000425 when /^\.([^.]*)$/
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000426 # unknown(maybe String)
427
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000428 receiver = ""
429 message = Regexp.quote($1)
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000430
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000431 candidates = String.instance_methods(true)
432 select_message(receiver, message, candidates)
433
434 else
435 inclass = eval( VIM::evaluate("IsInClassDef()") )
436
437 if inclass != nil
438 classdef = "%s\n" % VIM::Buffer.current[ inclass.min ]
439 found = /^\s*class\s*([A-Za-z0-9_-]*)(\s*<\s*([A-Za-z0-9_:-]*))?\s*\n$/.match( classdef )
440
441 if found != nil
442 receiver = $1
443 message = input
444 load_buffer_class( receiver )
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000445 begin
446 candidates = eval( "#{receiver}.instance_methods" )
447 candidates += get_rails_helpers
448 select_message(receiver, message, candidates)
449 rescue Exception
450 found = nil
451 end
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000452 end
453 end
454
455 if inclass == nil || found == nil
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000456 candidates = eval("self.class.constants")
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000457 candidates += get_buffer_classes
458 candidates.uniq!
459 candidates.sort!
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000460 (candidates|ReservedWords).grep(/^#{Regexp.quote(input)}/)
461 end
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000462 end
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000463
464 #print candidates
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000465 if message != nil && message.length > 0
466 rexp = '^%s' % message.downcase
467 candidates.delete_if do |c|
468 c.downcase.match( rexp )
469 $~ == nil
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000470 end
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000471 end
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000472
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000473 outp = ""
474
475 # tags = VIM::evaluate("taglist('^%s$')" %
476 valid = (candidates-Object.instance_methods)
477
478 rg = 0..valid.length
479 rg.step(150) do |x|
480 stpos = 0+x
481 enpos = 150+x
482 valid[stpos..enpos].each { |c| outp += "{'word':'%s','item':'%s'}," % [ c, c ] }
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000483 outp.sub!(/,$/, '')
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000484
485 VIM::command("call extend(g:rubycomplete_completions, [%s])" % outp)
486 outp = ""
487 end
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000488end
489
490
491def select_message(receiver, message, candidates)
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000492 #tags = VIM::evaluate("taglist('%s')" % receiver)
493 #print tags
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000494 candidates.grep(/^#{message}/).collect do |e|
495 case e
496 when /^[a-zA-Z_]/
497 receiver + "." + e
498 when /^[0-9]/
499 when *Operators
500 #receiver + " " + e
501 end
502 end
503 candidates.delete_if { |x| x == nil }
504 candidates.uniq!
505 candidates.sort!
506end
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000507
508# }}} ruby completion
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000509RUBYEOF
510endfunction
511
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000512let g:rubycomplete_rails_loaded = 0
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000513
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000514call s:DefRuby()
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000515" vim:tw=78:sw=4:ts=8:ft=vim:norl: