1" Vim completion script 2" Language: Ruby 3" Maintainer: Mark Guzman <[email protected]> 4" Info: $Id$ 5" URL: http://vim-ruby.rubyforge.org 6" Anon CVS: See above site 7" Release Coordinator: Doug Kearns <[email protected]> 8" ---------------------------------------------------------------------------- 9" 10" Ruby IRB/Complete author: Keiju ISHITSUKA([email protected]) 11" ---------------------------------------------------------------------------- 12 13" {{{ requirement checks 14if !has('ruby') 15 echohl ErrorMsg 16 echo "Error: Required vim compiled with +ruby" 17 echohl None 18 finish 19endif 20 21if version < 700 22 echohl ErrorMsg 23 echo "Error: Required vim >= 7.0" 24 echohl None 25 finish 26endif 27" }}} requirement checks 28 29if !exists("g:rubycomplete_rails") 30 let g:rubycomplete_rails = 0 31endif 32 33if !exists("g:rubycomplete_classes_in_global") 34 let g:rubycomplete_classes_in_global = 0 35endif 36 37" {{{ vim-side support functions 38function! 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) 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) 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) 100 if lnum != 0 && lcol != 0 101 let str = matchstr(getline(lnum),'=\s*\([^ \t]\+.' . ctors . '\>\|[\[{"''/]\|%r{\)',lcol) 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' 110 elseif str == '/' || str == '%r{' 111 return 'Regexp' 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 '' 120endfunction 121 122"}}} vim-side support functions 123 124function! 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 145 let g:rubycomplete_completions = [] 146 execute "ruby get_completions('" . a:base . "')" 147 return g:rubycomplete_completions 148 endif 149endfunction 150 151 152function! s:DefRuby() 153ruby << RUBYEOF 154# {{{ ruby completion 155RailsWords = [ 156 "has_many", "has_one", 157 "belongs_to", 158 ] 159 160ReservedWords = [ 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 180Operators = [ "%", "&", "*", "**", "+", "-", "/", 181 "<", "<<", "<=", "<=>", "==", "===", "=~", ">", ">=", ">>", 182 "[]", "[]=", "^", ] 183 184 185def load_requires 186 buf = VIM::Buffer.current 187 enum = buf.line_number 188 nums = Range.new( 1, enum ) 189 nums.each do |x| 190 ln = buf[x] 191 begin 192 eval( "require %s" % $1 ) if /.*require\s*(.*)$/.match( ln ) 193 rescue Exception 194 #ignore? 195 end 196 end 197end 198 199def 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 210end 211 212def load_buffer_module(name) 213 classdef = get_buffer_entity(name, 'GetBufferRubyModule("%s")') 214 return if classdef == nil 215 216 eval classdef 217end 218 219def get_buffer_entity(name, vimfun) 220 buf = VIM::Buffer.current 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 229 ln = buf[x] 230 classdef += "%s\n" % ln 231 end 232 end 233 234 return classdef 235end 236 237def 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 254end 255 256def 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!( /\\/, "/" ) 264 pup = [ "./", "../", "../../", "../../../", "../../../../" ] 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 274 275 return if pok == nil 276 277 bootfile = pok + "/boot.rb" 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 289 end 290end 291 292def get_rails_helpers 293 allow_rails = VIM::evaluate('g:rubycomplete_rails') 294 rails_loaded = VIM::evaluate('g:rubycomplete_rails_loaded') 295 return [] if allow_rails != '1' || rails_loaded != '1' 296 return RailsWords 297end 298 299def get_completions(base) 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 311 312 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) 321 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) 334 335 candidates = Proc.instance_methods(true) | Hash.instance_methods(true) 336 select_message(receiver, message, candidates) 337 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 352 353 when /^::([A-Z][^:\.\(]*)$/ 354 # Absolute Constant or class methods 355 receiver = $1 356 candidates = Object.constants 357 candidates.grep(/^#{receiver}/).collect{|e| "::" + e} 358 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} 369 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) 382 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))) 392 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 406 begin 407 candidates = eval("#{vartype}.instance_methods") 408 rescue Exception 409 candidates = [] 410 end 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 416 begin 417 candidates = eval("#{receiver}.methods") 418 rescue Exception 419 candidates = [] 420 end 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) 434 435 #when /^((\.?[^.]+)+)\.([^.]*)\(\s*\)*$/ 436 #function call 437 #obj = $1 438 #func = $3 439 440 when /^\.([^.]*)$/ 441 # unknown(maybe String) 442 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()") ) 451 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 ) 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 467 end 468 end 469 470 if inclass == nil || found == nil 471 candidates = eval("self.class.constants") 472 candidates += get_buffer_classes 473 candidates.uniq! 474 candidates.sort! 475 (candidates|ReservedWords).grep(/^#{Regexp.quote(input)}/) 476 end 477 end 478 479 #print candidates 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 485 end 486 end 487 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) 501 outp = "" 502 end 503end 504 505 506def select_message(receiver, message, candidates) 507 #tags = VIM::evaluate("taglist('%s')" % receiver) 508 #print tags 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! 521end 522 523# }}} ruby completion 524RUBYEOF 525endfunction 526 527let g:rubycomplete_rails_loaded = 0 528 529call s:DefRuby() 530" vim:tw=78:sw=4:ts=8:ft=vim:norl: 531