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