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 let [lnum,lcol] = searchpos(''.a:v.'\>\s*[+\-*/]*=\s*\([^ \t]\+.\(now\|new\|open\|get_instance\)\>\|[\[{"''/]\|%r{\)','nb',stopline) 94 if lnum != 0 && lcol != 0 95 let str = matchstr(getline(lnum),'=\s*\([^ \t]\+.\(now\|new\|open\|get_instance\)\>\|[\[{"''/]\|%r{\)',lcol) 96 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' 104 elseif str == '/' || str == '%r{' 105 return 'Regexp' 106 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 '' 114endfunction 115 116"}}} vim-side support functions 117 118function! 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 139 let g:rubycomplete_completions = [] 140 execute "ruby get_completions('" . a:base . "')" 141 return g:rubycomplete_completions 142 endif 143endfunction 144 145 146function! s:DefRuby() 147ruby << RUBYEOF 148# {{{ ruby completion 149RailsWords = [ 150 "has_many", "has_one", 151 "belongs_to", 152 ] 153 154ReservedWords = [ 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 178 179def load_requires 180 buf = VIM::Buffer.current 181 enum = buf.line_number 182 nums = Range.new( 1, enum ) 183 nums.each do |x| 184 ln = buf[x] 185 begin 186 eval( "require %s" % $1 ) if /.*require\s*(.*)$/.match( ln ) 187 rescue Exception 188 #ignore? 189 end 190 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) 214 buf = VIM::Buffer.current 215 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 223 ln = buf[x] 224 classdef += "%s\n" % ln 225 end 226 end 227 228 return classdef 229end 230 231def 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 250def 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 268 269 return if pok == nil 270 bootfile = pok + "/boot.rb" 271 if File.exists?( bootfile ) 272 require bootfile 273 VIM::evaluate('let g:rubycomplete_rails_loaded = 1') 274 end 275end 276 277def get_rails_helpers 278 allow_rails = VIM::evaluate('g:rubycomplete_rails') 279 rails_loaded = VIM::evaluate('g:rubycomplete_rails_loaded') 280 return [] if allow_rails != '1' || rails_loaded != '1' 281 return RailsWords 282end 283 284def get_completions(base) 285 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 296 297 298 case input 299 when /^(\/[^\/]*\/)\.([^.]*)$/ 300 # Regexp 301 receiver = $1 302 message = Regexp.quote($2) 303 304 candidates = Regexp.instance_methods(true) 305 select_message(receiver, message, candidates) 306 307 when /^([^\]]*\])\.([^.]*)$/ 308 # Array 309 receiver = $1 310 message = Regexp.quote($2) 311 312 candidates = Array.instance_methods(true) 313 select_message(receiver, message, candidates) 314 315 when /^([^\}]*\})\.([^.]*)$/ 316 # Proc or Hash 317 receiver = $1 318 message = Regexp.quote($2) 319 320 candidates = Proc.instance_methods(true) | Hash.instance_methods(true) 321 select_message(receiver, message, candidates) 322 323 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( /'/ ) 331 end 332 candidates.uniq! 333 candidates.sort! 334 else 335 [] 336 end 337 338 when /^::([A-Z][^:\.\(]*)$/ 339 # Absolute Constant or class methods 340 receiver = $1 341 candidates = Object.constants 342 candidates.grep(/^#{receiver}/).collect{|e| "::" + e} 343 344 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} 354 355 when /^(:[^:.]+)\.([^.]*)$/ 356 # Symbol 357 receiver = $1 358 message = Regexp.quote($2) 359 360 candidates = Symbol.instance_methods(true) 361 select_message(receiver, message, candidates) 362 363 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 ) 390 391 begin 392 candidates = eval("#{vartype}.instance_methods") 393 rescue Exception 394 candidates = [] 395 end 396 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) 419 420 #when /^((\.?[^.]+)+)\.([^.]*)\(\s*\)*$/ 421 #function call 422 #obj = $1 423 #func = $3 424 425 when /^\.([^.]*)$/ 426 # unknown(maybe String) 427 428 receiver = "" 429 message = Regexp.quote($1) 430 431 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 ) 445 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 452 end 453 end 454 455 if inclass == nil || found == nil 456 candidates = eval("self.class.constants") 457 candidates += get_buffer_classes 458 candidates.uniq! 459 candidates.sort! 460 (candidates|ReservedWords).grep(/^#{Regexp.quote(input)}/) 461 end 462 end 463 464 #print candidates 465 if message != nil && message.length > 0 466 rexp = '^%s' % message.downcase 467 candidates.delete_if do |c| 468 c.downcase.match( rexp ) 469 $~ == nil 470 end 471 end 472 473 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 ] } 483 outp.sub!(/,$/, '') 484 485 VIM::command("call extend(g:rubycomplete_completions, [%s])" % outp) 486 outp = "" 487 end 488end 489 490 491def select_message(receiver, message, candidates) 492 #tags = VIM::evaluate("taglist('%s')" % receiver) 493 #print tags 494 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 507 508# }}} ruby completion 509RUBYEOF 510endfunction 511 512let g:rubycomplete_rails_loaded = 0 513 514call s:DefRuby() 515" vim:tw=78:sw=4:ts=8:ft=vim:norl: 516