1" Vim completion script
2" Language:		Ruby
3" Maintainer:		Mark Guzman <[email protected]>
4" URL:			https://github.com/vim-ruby/vim-ruby
5" Release Coordinator:	Doug Kearns <[email protected]>
6" Last Change:		2020 Apr 12
7" ----------------------------------------------------------------------------
8"
9" Ruby IRB/Complete author: Keiju ISHITSUKA([email protected])
10" ----------------------------------------------------------------------------
11
12" {{{ requirement checks
13
14function! s:ErrMsg(msg)
15    echohl ErrorMsg
16    echo a:msg
17    echohl None
18endfunction
19
20if !has('ruby')
21    call s:ErrMsg( "Error: Rubycomplete requires vim compiled with +ruby" )
22    call s:ErrMsg( "Error: falling back to syntax completion" )
23    " lets fall back to syntax completion
24    setlocal omnifunc=syntaxcomplete#Complete
25    finish
26endif
27
28if version < 700
29    call s:ErrMsg( "Error: Required vim >= 7.0" )
30    finish
31endif
32" }}} requirement checks
33
34" {{{ configuration failsafe initialization
35if !exists("g:rubycomplete_rails")
36    let g:rubycomplete_rails = 0
37endif
38
39if !exists("g:rubycomplete_classes_in_global")
40    let g:rubycomplete_classes_in_global = 0
41endif
42
43if !exists("g:rubycomplete_buffer_loading")
44    let g:rubycomplete_buffer_loading = 0
45endif
46
47if !exists("g:rubycomplete_include_object")
48    let g:rubycomplete_include_object = 0
49endif
50
51if !exists("g:rubycomplete_include_objectspace")
52    let g:rubycomplete_include_objectspace = 0
53endif
54" }}} configuration failsafe initialization
55
56" {{{ regex patterns
57
58" Regex that defines the start-match for the 'end' keyword.
59let s:end_start_regex =
60      \ '\C\%(^\s*\|[=,*/%+\-|;{]\|<<\|>>\|:\s\)\s*\zs' .
61      \ '\<\%(module\|class\|if\|for\|while\|until\|case\|unless\|begin' .
62      \   '\|\%(\K\k*[!?]\?\s\+\)\=def\):\@!\>' .
63      \ '\|\%(^\|[^.:@$]\)\@<=\<do:\@!\>'
64
65" Regex that defines the middle-match for the 'end' keyword.
66let s:end_middle_regex = '\<\%(ensure\|else\|\%(\%(^\|;\)\s*\)\@<=\<rescue:\@!\>\|when\|elsif\):\@!\>'
67
68" Regex that defines the end-match for the 'end' keyword.
69let s:end_end_regex = '\%(^\|[^.:@$]\)\@<=\<end:\@!\>'
70
71" }}} regex patterns
72
73" {{{ vim-side support functions
74let s:rubycomplete_debug = 0
75
76function! s:dprint(msg)
77    if s:rubycomplete_debug == 1
78        echom a:msg
79    endif
80endfunction
81
82function! s:GetBufferRubyModule(name, ...)
83    if a:0 == 1
84        let [snum,enum] = s:GetBufferRubyEntity(a:name, "module", a:1)
85    else
86        let [snum,enum] = s:GetBufferRubyEntity(a:name, "module")
87    endif
88    return snum . '..' . enum
89endfunction
90
91function! s:GetBufferRubyClass(name, ...)
92    if a:0 >= 1
93        let [snum,enum] = s:GetBufferRubyEntity(a:name, "class", a:1)
94    else
95        let [snum,enum] = s:GetBufferRubyEntity(a:name, "class")
96    endif
97    return snum . '..' . enum
98endfunction
99
100function! s:GetBufferRubySingletonMethods(name)
101endfunction
102
103function! s:GetBufferRubyEntity( name, type, ... )
104    let lastpos = getpos(".")
105    let lastline = lastpos
106    if (a:0 >= 1)
107        let lastline = [ 0, a:1, 0, 0 ]
108        call cursor( a:1, 0 )
109    endif
110
111    let stopline = 1
112
113    let crex = '^\s*\<' . a:type . '\>\s*\<' . escape(a:name, '*') . '\>\s*\(<\s*.*\s*\)\?'
114    let [lnum,lcol] = searchpos( crex, 'w' )
115    "let [lnum,lcol] = searchpairpos( crex . '\zs', '', '\(end\|}\)', 'w' )
116
117    if lnum == 0 && lcol == 0
118        call cursor(lastpos[1], lastpos[2])
119        return [0,0]
120    endif
121
122    let curpos = getpos(".")
123    let [enum,ecol] = searchpairpos( s:end_start_regex, s:end_middle_regex, s:end_end_regex, 'W' )
124    call cursor(lastpos[1], lastpos[2])
125
126    if lnum > enum
127        return [0,0]
128    endif
129    " we found a the class def
130    return [lnum,enum]
131endfunction
132
133function! s:IsInClassDef()
134    return s:IsPosInClassDef( line('.') )
135endfunction
136
137function! s:IsPosInClassDef(pos)
138    let [snum,enum] = s:GetBufferRubyEntity( '.*', "class" )
139    let ret = 'nil'
140
141    if snum < a:pos && a:pos < enum
142        let ret = snum . '..' . enum
143    endif
144
145    return ret
146endfunction
147
148function! s:IsInComment(pos)
149    let stack = synstack(a:pos[0], a:pos[1])
150    if !empty(stack)
151        return synIDattr(stack[0], 'name') =~ 'ruby\%(.*Comment\|Documentation\)'
152    else
153        return 0
154    endif
155endfunction
156
157function! s:GetRubyVarType(v)
158    let stopline = 1
159    let vtp = ''
160    let curpos = getpos('.')
161    let sstr = '^\s*#\s*@var\s*'.escape(a:v, '*').'\>\s\+[^ \t]\+\s*$'
162    let [lnum,lcol] = searchpos(sstr,'nb',stopline)
163    if lnum != 0 && lcol != 0
164        call setpos('.',curpos)
165        let str = getline(lnum)
166        let vtp = substitute(str,sstr,'\1','')
167        return vtp
168    endif
169    call setpos('.',curpos)
170    let ctors = '\(now\|new\|open\|get_instance'
171    if exists('g:rubycomplete_rails') && g:rubycomplete_rails == 1 && s:rubycomplete_rails_loaded == 1
172        let ctors = ctors.'\|find\|create'
173    else
174    endif
175    let ctors = ctors.'\)'
176
177    let fstr = '=\s*\([^ \t]\+.' . ctors .'\>\|[\[{"''/]\|%[xwQqr][(\[{@]\|[A-Za-z0-9@:\-()\.]\+...\?\|lambda\|&\)'
178    let sstr = ''.escape(a:v, '*').'\>\s*[+\-*/]*'.fstr
179    let pos = searchpos(sstr,'bW')
180    while pos != [0,0] && s:IsInComment(pos)
181        let pos = searchpos(sstr,'bW')
182    endwhile
183    if pos != [0,0]
184        let [lnum, col] = pos
185        let str = matchstr(getline(lnum),fstr,col)
186        let str = substitute(str,'^=\s*','','')
187
188        call setpos('.',pos)
189        if str == '"' || str == '''' || stridx(tolower(str), '%q[') != -1
190            return 'String'
191        elseif str == '[' || stridx(str, '%w[') != -1
192            return 'Array'
193        elseif str == '{'
194            return 'Hash'
195        elseif str == '/' || str == '%r{'
196            return 'Regexp'
197        elseif strlen(str) >= 4 && stridx(str,'..') != -1
198            return 'Range'
199        elseif stridx(str, 'lambda') != -1 || str == '&'
200            return 'Proc'
201        elseif strlen(str) > 4
202            let l = stridx(str,'.')
203            return str[0:l-1]
204        end
205        return ''
206    endif
207    call setpos('.',curpos)
208    return ''
209endfunction
210
211"}}} vim-side support functions
212
213"{{{ vim-side completion function
214function! rubycomplete#Init()
215    execute "ruby VimRubyCompletion.preload_rails"
216endfunction
217
218function! rubycomplete#Complete(findstart, base)
219     "findstart = 1 when we need to get the text length
220    if a:findstart
221        let line = getline('.')
222        let idx = col('.')
223        while idx > 0
224            let idx -= 1
225            let c = line[idx-1]
226            if c =~ '\w'
227                continue
228            elseif ! c =~ '\.'
229                let idx = -1
230                break
231            else
232                break
233            endif
234        endwhile
235
236        return idx
237    "findstart = 0 when we need to return the list of completions
238    else
239        let g:rubycomplete_completions = []
240        execute "ruby VimRubyCompletion.get_completions('" . a:base . "')"
241        return g:rubycomplete_completions
242    endif
243endfunction
244"}}} vim-side completion function
245
246"{{{ ruby-side code
247function! s:DefRuby()
248ruby << RUBYEOF
249# {{{ ruby completion
250
251begin
252    require 'rubygems' # let's assume this is safe...?
253rescue Exception
254    #ignore?
255end
256class VimRubyCompletion
257# {{{ constants
258  @@debug = false
259  @@ReservedWords = [
260        "BEGIN", "END",
261        "alias", "and",
262        "begin", "break",
263        "case", "class",
264        "def", "defined", "do",
265        "else", "elsif", "end", "ensure",
266        "false", "for",
267        "if", "in",
268        "module",
269        "next", "nil", "not",
270        "or",
271        "redo", "rescue", "retry", "return",
272        "self", "super",
273        "then", "true",
274        "undef", "unless", "until",
275        "when", "while",
276        "yield",
277      ]
278
279  @@Operators = [ "%", "&", "*", "**", "+",  "-",  "/",
280        "<", "<<", "<=", "<=>", "==", "===", "=~", ">", ">=", ">>",
281        "[]", "[]=", "^", ]
282# }}} constants
283
284# {{{ buffer analysis magic
285  def load_requires
286
287    custom_paths = VIM::evaluate("get(g:, 'rubycomplete_load_paths', [])")
288
289    if !custom_paths.empty?
290      $LOAD_PATH.concat(custom_paths).uniq!
291    end
292
293    buf = VIM::Buffer.current
294    enum = buf.line_number
295    nums = Range.new( 1, enum )
296    nums.each do |x|
297
298      ln = buf[x]
299      begin
300        if /.*require_relative\s*(.*)$/.match( ln )
301          eval( "require %s" % File.expand_path($1) )
302        elsif /.*require\s*(["'].*?["'])/.match( ln )
303          eval( "require %s" % $1 )
304        end
305      rescue Exception => e
306        dprint e.inspect
307      end
308    end
309  end
310
311  def load_gems
312    fpath = VIM::evaluate("get(g:, 'rubycomplete_gemfile_path', 'Gemfile')")
313    return unless File.file?(fpath) && File.readable?(fpath)
314    want_bundler = VIM::evaluate("get(g:, 'rubycomplete_use_bundler')")
315    parse_file = !want_bundler
316    begin
317      require 'bundler'
318      Bundler.setup
319      Bundler.require
320    rescue Exception
321      parse_file = true
322    end
323    if parse_file
324      File.new(fpath).each_line do |line|
325        begin
326          require $1 if /\s*gem\s*['"]([^'"]+)/.match(line)
327        rescue Exception
328        end
329      end
330    end
331  end
332
333  def load_buffer_class(name)
334    dprint "load_buffer_class(%s) START" % name
335    classdef = get_buffer_entity(name, 's:GetBufferRubyClass("%s")')
336    return if classdef == nil
337
338    pare = /^\s*class\s*(.*)\s*<\s*(.*)\s*\n/.match( classdef )
339    load_buffer_class( $2 ) if pare != nil  && $2 != name # load parent class if needed
340
341    mixre = /.*\n\s*(include|prepend)\s*(.*)\s*\n/.match( classdef )
342    load_buffer_module( $2 ) if mixre != nil && $2 != name # load mixins if needed
343
344    begin
345      eval classdef
346    rescue Exception
347      VIM::evaluate( "s:ErrMsg( 'Problem loading class \"%s\", was it already completed?' )" % name )
348    end
349    dprint "load_buffer_class(%s) END" % name
350  end
351
352  def load_buffer_module(name)
353    dprint "load_buffer_module(%s) START" % name
354    classdef = get_buffer_entity(name, 's:GetBufferRubyModule("%s")')
355    return if classdef == nil
356
357    begin
358      eval classdef
359    rescue Exception
360      VIM::evaluate( "s:ErrMsg( 'Problem loading module \"%s\", was it already completed?' )" % name )
361    end
362    dprint "load_buffer_module(%s) END" % name
363  end
364
365  def get_buffer_entity(name, vimfun)
366    loading_allowed = VIM::evaluate("exists('g:rubycomplete_buffer_loading') && g:rubycomplete_buffer_loading")
367    return nil if loading_allowed.to_i.zero?
368    return nil if /(\"|\')+/.match( name )
369    buf = VIM::Buffer.current
370    nums = eval( VIM::evaluate( vimfun % name ) )
371    return nil if nums == nil
372    return nil if nums.min == nums.max && nums.min == 0
373
374    dprint "get_buffer_entity START"
375    visited = []
376    clscnt = 0
377    bufname = VIM::Buffer.current.name
378    classdef = ""
379    cur_line = VIM::Buffer.current.line_number
380    while (nums != nil && !(nums.min == 0 && nums.max == 0) )
381      dprint "visited: %s" % visited.to_s
382      break if visited.index( nums )
383      visited << nums
384
385      nums.each do |x|
386        if x != cur_line
387          next if x == 0
388          ln = buf[x]
389          is_const = false
390          if /^\s*(module|class|def|include)\s+/.match(ln) || is_const = /^\s*?[A-Z]([A-z]|[1-9])*\s*?[|]{0,2}=\s*?.+\s*?/.match(ln)
391            clscnt += 1 if /class|module/.match($1)
392            # We must make sure to load each constant only once to avoid errors
393            if is_const
394                ln.gsub!(/\s*?[|]{0,2}=\s*?/, '||=')
395            end
396            #dprint "\$1$1
397            classdef += "%s\n" % ln
398            classdef += "end\n" if /def\s+/.match(ln)
399            dprint ln
400          end
401        end
402      end
403
404      nm = "%s(::.*)*\", %s, \"" % [ name, nums.last ]
405      nums = eval( VIM::evaluate( vimfun % nm ) )
406      dprint "nm: \"%s\"" % nm
407      dprint "vimfun: %s" % (vimfun % nm)
408      dprint "got nums: %s" % nums.to_s
409    end
410    if classdef.length > 1
411        classdef += "end\n"*clscnt
412        # classdef = "class %s\n%s\nend\n" % [ bufname.gsub( /\/|\\/, "_" ), classdef ]
413    end
414
415    dprint "get_buffer_entity END"
416    dprint "classdef====start"
417    lns = classdef.split( "\n" )
418    lns.each { |x| dprint x }
419    dprint "classdef====end"
420    return classdef
421  end
422
423  def get_var_type( receiver )
424    if /(\"|\')+/.match( receiver )
425      "String"
426    else
427      VIM::evaluate("s:GetRubyVarType('%s')" % receiver)
428    end
429  end
430
431  def dprint( txt )
432    print txt if @@debug
433  end
434
435  def escape_vim_singlequote_string(str)
436    str.to_s.gsub(/'/,"\\'")
437  end
438
439  def get_buffer_entity_list( type )
440    # this will be a little expensive.
441    loading_allowed = VIM::evaluate("exists('g:rubycomplete_buffer_loading') && g:rubycomplete_buffer_loading")
442    allow_aggressive_load = VIM::evaluate("exists('g:rubycomplete_classes_in_global') && g:rubycomplete_classes_in_global")
443    return [] if allow_aggressive_load.to_i.zero? || loading_allowed.to_i.zero?
444
445    buf = VIM::Buffer.current
446    eob = buf.length
447    ret = []
448    rg = 1..eob
449    re = eval( "/^\s*%s\s*([A-Za-z0-9_:-]*)(\s*<\s*([A-Za-z0-9_:-]*))?\s*/" % type )
450
451    rg.each do |x|
452      if re.match( buf[x] )
453        next if type == "def" && eval( VIM::evaluate("s:IsPosInClassDef(%s)" % x) ) != nil
454        ret.push $1
455      end
456    end
457
458    return ret
459  end
460
461  def get_buffer_modules
462    return get_buffer_entity_list( "modules" )
463  end
464
465  def get_buffer_methods
466    return get_buffer_entity_list( "def" )
467  end
468
469  def get_buffer_classes
470    return get_buffer_entity_list( "class" )
471  end
472
473  def load_rails
474    allow_rails = VIM::evaluate("exists('g:rubycomplete_rails') && g:rubycomplete_rails")
475    return if allow_rails.to_i.zero?
476
477    buf_path = VIM::evaluate('expand("%:p")')
478    file_name = VIM::evaluate('expand("%:t")')
479    vim_dir = VIM::evaluate('getcwd()')
480    file_dir = buf_path.gsub( file_name, '' )
481    file_dir.gsub!( /\\/, "/" )
482    vim_dir.gsub!( /\\/, "/" )
483    vim_dir << "/"
484    dirs = [ vim_dir, file_dir ]
485    sdirs = [ "", "./", "../", "../../", "../../../", "../../../../" ]
486    rails_base = nil
487
488    dirs.each do |dir|
489      sdirs.each do |sub|
490        trail = "%s%s" % [ dir, sub ]
491        tcfg = "%sconfig" % trail
492
493        if File.exists?( tcfg )
494          rails_base = trail
495          break
496        end
497      end
498      break if rails_base
499    end
500
501    return if rails_base == nil
502    $:.push rails_base unless $:.index( rails_base )
503
504    bootfile = rails_base + "config/boot.rb"
505    envfile = rails_base + "config/environment.rb"
506    if File.exists?( bootfile ) && File.exists?( envfile )
507      begin
508        require bootfile
509        require envfile
510        begin
511          require 'console_app'
512          require 'console_with_helpers'
513        rescue Exception
514          dprint "Rails 1.1+ Error %s" % $!
515          # assume 1.0
516        end
517        #eval( "Rails::Initializer.run" ) #not necessary?
518        VIM::command('let s:rubycomplete_rails_loaded = 1')
519        dprint "rails loaded"
520      rescue Exception
521        dprint "Rails Error %s" % $!
522        VIM::evaluate( "s:ErrMsg('Error loading rails environment')" )
523      end
524    end
525  end
526
527  def get_rails_helpers
528    allow_rails = VIM::evaluate("exists('g:rubycomplete_rails') && g:rubycomplete_rails")
529    rails_loaded = VIM::evaluate('s:rubycomplete_rails_loaded')
530    return [] if allow_rails.to_i.zero? || rails_loaded.to_i.zero?
531
532    buf_path = VIM::evaluate('expand("%:p")')
533    buf_path.gsub!( /\\/, "/" )
534    path_elm = buf_path.split( "/" )
535    dprint "buf_path: %s" % buf_path
536    types = [ "app", "db", "lib", "test", "components", "script" ]
537
538    i = nil
539    ret = []
540    type = nil
541    types.each do |t|
542      i = path_elm.index( t )
543      break if i
544    end
545    type = path_elm[i]
546    type.downcase!
547
548    dprint "type: %s" % type
549    case type
550      when "app"
551        i += 1
552        subtype = path_elm[i]
553        subtype.downcase!
554
555        dprint "subtype: %s" % subtype
556        case subtype
557          when "views"
558            ret += ActionView::Base.instance_methods
559            ret += ActionView::Base.methods
560          when "controllers"
561            ret += ActionController::Base.instance_methods
562            ret += ActionController::Base.methods
563          when "models"
564            ret += ActiveRecord::Base.instance_methods
565            ret += ActiveRecord::Base.methods
566        end
567
568      when "db"
569        ret += ActiveRecord::ConnectionAdapters::SchemaStatements.instance_methods
570        ret += ActiveRecord::ConnectionAdapters::SchemaStatements.methods
571    end
572
573    return ret
574  end
575
576  def add_rails_columns( cls )
577    allow_rails = VIM::evaluate("exists('g:rubycomplete_rails') && g:rubycomplete_rails")
578    rails_loaded = VIM::evaluate('s:rubycomplete_rails_loaded')
579    return [] if allow_rails.to_i.zero? || rails_loaded.to_i.zero?
580
581    begin
582        eval( "#{cls}.establish_connection" )
583        return [] unless eval( "#{cls}.ancestors.include?(ActiveRecord::Base).to_s" )
584        col = eval( "#{cls}.column_names" )
585        return col if col
586    rescue
587        dprint "add_rails_columns err: (cls: %s) %s" % [ cls, $! ]
588        return []
589    end
590    return []
591  end
592
593  def clean_sel(sel, msg)
594    ret = sel.reject{|x|x.nil?}.uniq
595    ret = ret.grep(/^#{Regexp.quote(msg)}/) if msg != nil
596    ret
597  end
598
599  def get_rails_view_methods
600    allow_rails = VIM::evaluate("exists('g:rubycomplete_rails') && g:rubycomplete_rails")
601    rails_loaded = VIM::evaluate('s:rubycomplete_rails_loaded')
602    return [] if allow_rails.to_i.zero? || rails_loaded.to_i.zero?
603
604    buf_path = VIM::evaluate('expand("%:p")')
605    buf_path.gsub!( /\\/, "/" )
606    pelm = buf_path.split( "/" )
607    idx = pelm.index( "views" )
608
609    return [] unless idx
610    idx += 1
611
612    clspl = pelm[idx].camelize.pluralize
613    cls = clspl.singularize
614
615    ret = []
616    begin
617      ret += eval( "#{cls}.instance_methods" )
618      ret += eval( "#{clspl}Helper.instance_methods" )
619    rescue Exception
620      dprint "Error: Unable to load rails view helpers for %s: %s" % [ cls, $! ]
621    end
622
623    return ret
624  end
625# }}} buffer analysis magic
626
627# {{{ main completion code
628  def self.preload_rails
629    a = VimRubyCompletion.new
630    if VIM::evaluate("has('nvim')") == 0
631      require 'thread'
632      Thread.new(a) do |b|
633        begin
634        b.load_rails
635        rescue
636        end
637      end
638    end
639    a.load_rails
640  rescue
641  end
642
643  def self.get_completions(base)
644    b = VimRubyCompletion.new
645    b.get_completions base
646  end
647
648  def get_completions(base)
649    loading_allowed = VIM::evaluate("exists('g:rubycomplete_buffer_loading') && g:rubycomplete_buffer_loading")
650    if loading_allowed.to_i == 1
651      load_requires
652      load_rails
653    end
654
655    want_gems = VIM::evaluate("get(g:, 'rubycomplete_load_gemfile')")
656    load_gems unless want_gems.to_i.zero?
657
658    input = VIM::Buffer.current.line
659    cpos = VIM::Window.current.cursor[1] - 1
660    input = input[0..cpos]
661    input += base
662    input.sub!(/.*[ \t\n\"\\'`><=;|&{(]/, '') # Readline.basic_word_break_characters
663    input.sub!(/self\./, '')
664    input.sub!(/.*((\.\.[\[(]?)|([\[(]))/, '')
665
666    dprint 'input %s' % input
667    message = nil
668    receiver = nil
669    methods = []
670    variables = []
671    classes = []
672    constants = []
673
674    case input
675      when /^(\/[^\/]*\/)\.([^.]*)$/ # Regexp
676        receiver = $1
677        message = Regexp.quote($2)
678        methods = Regexp.instance_methods(true)
679
680      when /^([^\]]*\])\.([^.]*)$/ # Array
681        receiver = $1
682        message = Regexp.quote($2)
683        methods = Array.instance_methods(true)
684
685      when /^([^\}]*\})\.([^.]*)$/ # Proc or Hash
686        receiver = $1
687        message = Regexp.quote($2)
688        methods = Proc.instance_methods(true) | Hash.instance_methods(true)
689
690      when /^(:[^:.]*)$/ # Symbol
691        dprint "symbol"
692        if Symbol.respond_to?(:all_symbols)
693          receiver = $1
694          message = $1.sub( /:/, '' )
695          methods = Symbol.all_symbols.collect{|s| s.id2name}
696          methods.delete_if { |c| c.match( /'/ ) }
697        end
698
699      when /^::([A-Z][^:\.\(]*)?$/ # Absolute Constant or class methods
700        dprint "const or cls"
701        receiver = $1
702        methods = Object.constants.collect{ |c| c.to_s }.grep(/^#{receiver}/)
703
704      when /^(((::)?[A-Z][^:.\(]*)+?)::?([^:.]*)$/ # Constant or class methods
705        receiver = $1
706        message = Regexp.quote($4)
707        dprint "const or cls 2 [recv: \'%s\', msg: \'%s\']" % [ receiver, message ]
708        load_buffer_class( receiver )
709        load_buffer_module( receiver )
710        begin
711          constants = eval("#{receiver}.constants").collect{ |c| c.to_s }.grep(/^#{message}/)
712          methods = eval("#{receiver}.methods").collect{ |m| m.to_s }.grep(/^#{message}/)
713        rescue Exception
714          dprint "exception: %s" % $!
715          constants = []
716          methods = []
717        end
718
719      when /^(:[^:.]+)\.([^.]*)$/ # Symbol
720        dprint "symbol"
721        receiver = $1
722        message = Regexp.quote($2)
723        methods = Symbol.instance_methods(true)
724
725      when /^([0-9_]+(\.[0-9_]+)?(e[0-9]+)?)\.([^.]*)$/ # Numeric
726        dprint "numeric"
727        receiver = $1
728        message = Regexp.quote($4)
729        begin
730          methods = eval(receiver).methods
731        rescue Exception
732          methods = []
733        end
734
735      when /^(\$[^.]*)$/ #global
736        dprint "global"
737        methods = global_variables.grep(Regexp.new(Regexp.quote($1)))
738
739      when /^((\.?[^.]+)+?)\.([^.]*)$/ # variable
740        dprint "variable"
741        receiver = $1
742        message = Regexp.quote($3)
743        load_buffer_class( receiver )
744
745        cv = eval("self.class.constants")
746        vartype = get_var_type( receiver )
747        dprint "vartype: %s" % vartype
748
749        invalid_vartype = ['', "gets"]
750        if !invalid_vartype.include?(vartype)
751          load_buffer_class( vartype )
752
753          begin
754            methods = eval("#{vartype}.instance_methods")
755            variables = eval("#{vartype}.instance_variables")
756          rescue Exception
757            dprint "load_buffer_class err: %s" % $!
758          end
759        elsif (cv).include?(receiver)
760          # foo.func and foo is local var.
761          methods = eval("#{receiver}.methods")
762          vartype = receiver
763        elsif /^[A-Z]/ =~ receiver and /\./ !~ receiver
764          vartype = receiver
765          # Foo::Bar.func
766          begin
767            methods = eval("#{receiver}.methods")
768          rescue Exception
769          end
770        else
771          # func1.func2
772          ObjectSpace.each_object(Module){|m|
773            next if m.name != "IRB::Context" and
774              /^(IRB|SLex|RubyLex|RubyToken)/ =~ m.name
775            methods.concat m.instance_methods(false)
776          }
777        end
778        variables += add_rails_columns( "#{vartype}" ) if vartype && !invalid_vartype.include?(vartype)
779
780      when /^\(?\s*[A-Za-z0-9:^@.%\/+*\(\)]+\.\.\.?[A-Za-z0-9:^@.%\/+*\(\)]+\s*\)?\.([^.]*)/
781        message = $1
782        methods = Range.instance_methods(true)
783
784      when /^\.([^.]*)$/ # unknown(maybe String)
785        message = Regexp.quote($1)
786        methods = String.instance_methods(true)
787
788    else
789      dprint "default/other"
790      inclass = eval( VIM::evaluate("s:IsInClassDef()") )
791
792      if inclass != nil
793        dprint "inclass"
794        classdef = "%s\n" % VIM::Buffer.current[ inclass.min ]
795        found = /^\s*class\s*([A-Za-z0-9_-]*)(\s*<\s*([A-Za-z0-9_:-]*))?\s*\n$/.match( classdef )
796
797        if found != nil
798          receiver = $1
799          message = input
800          load_buffer_class( receiver )
801          begin
802            methods = eval( "#{receiver}.instance_methods" )
803            variables += add_rails_columns( "#{receiver}" )
804          rescue Exception
805            found = nil
806          end
807        end
808      end
809
810      if inclass == nil || found == nil
811        dprint "inclass == nil"
812        methods = get_buffer_methods
813        methods += get_rails_view_methods
814
815        cls_const = Class.constants
816        constants = cls_const.select { |c| /^[A-Z_-]+$/.match( c ) }
817        classes = eval("self.class.constants") - constants
818        classes += get_buffer_classes
819        classes += get_buffer_modules
820
821        include_objectspace = VIM::evaluate("exists('g:rubycomplete_include_objectspace') && g:rubycomplete_include_objectspace")
822        ObjectSpace.each_object(Class) { |cls| classes << cls.to_s } if include_objectspace == "1"
823        message = receiver = input
824      end
825
826      methods += get_rails_helpers
827      methods += Kernel.public_methods
828    end
829
830    include_object = VIM::evaluate("exists('g:rubycomplete_include_object') && g:rubycomplete_include_object")
831    methods = clean_sel( methods, message )
832    methods = (methods-Object.instance_methods) if include_object == "0"
833    rbcmeth = (VimRubyCompletion.instance_methods-Object.instance_methods) # lets remove those rubycomplete methods
834    methods = (methods-rbcmeth)
835
836    variables = clean_sel( variables, message )
837    classes = clean_sel( classes, message ) - ["VimRubyCompletion"]
838    constants = clean_sel( constants, message )
839
840    valid = []
841    valid += methods.collect { |m| { :name => m.to_s, :type => 'm' } }
842    valid += variables.collect { |v| { :name => v.to_s, :type => 'v' } }
843    valid += classes.collect { |c| { :name => c.to_s, :type => 't' } }
844    valid += constants.collect { |d| { :name => d.to_s, :type => 'd' } }
845    valid.sort! { |x,y| x[:name] <=> y[:name] }
846
847    outp = ""
848
849    rg = 0..valid.length
850    rg.step(150) do |x|
851      stpos = 0+x
852      enpos = 150+x
853      valid[stpos..enpos].each { |c| outp += "{'word':'%s','item':'%s','kind':'%s'}," % [ c[:name], c[:name], c[:type] ].map{|x|escape_vim_singlequote_string(x)} }
854      outp.sub!(/,$/, '')
855
856      VIM::command("call extend(g:rubycomplete_completions, [%s])" % outp)
857      outp = ""
858    end
859  end
860# }}} main completion code
861
862end # VimRubyCompletion
863# }}} ruby completion
864RUBYEOF
865endfunction
866
867let s:rubycomplete_rails_loaded = 0
868
869call s:DefRuby()
870"}}} ruby-side code
871
872" vim:tw=78:sw=4:ts=8:et:fdm=marker:ft=vim:norl:
873