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:		2019 Feb 25
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    rails_config = rails_base + "config/"
505    rails_lib = rails_base + "lib/"
506    $:.push rails_config unless $:.index( rails_config )
507    $:.push rails_lib unless $:.index( rails_lib )
508
509    bootfile = rails_config + "boot.rb"
510    envfile = rails_config + "environment.rb"
511    if File.exists?( bootfile ) && File.exists?( envfile )
512      begin
513        require bootfile
514        require envfile
515        begin
516          require 'console_app'
517          require 'console_with_helpers'
518        rescue Exception
519          dprint "Rails 1.1+ Error %s" % $!
520          # assume 1.0
521        end
522        #eval( "Rails::Initializer.run" ) #not necessary?
523        VIM::command('let s:rubycomplete_rails_loaded = 1')
524        dprint "rails loaded"
525      rescue Exception
526        dprint "Rails Error %s" % $!
527        VIM::evaluate( "s:ErrMsg('Error loading rails environment')" )
528      end
529    end
530  end
531
532  def get_rails_helpers
533    allow_rails = VIM::evaluate("exists('g:rubycomplete_rails') && g:rubycomplete_rails")
534    rails_loaded = VIM::evaluate('s:rubycomplete_rails_loaded')
535    return [] if allow_rails.to_i.zero? || rails_loaded.to_i.zero?
536
537    buf_path = VIM::evaluate('expand("%:p")')
538    buf_path.gsub!( /\\/, "/" )
539    path_elm = buf_path.split( "/" )
540    dprint "buf_path: %s" % buf_path
541    types = [ "app", "db", "lib", "test", "components", "script" ]
542
543    i = nil
544    ret = []
545    type = nil
546    types.each do |t|
547      i = path_elm.index( t )
548      break if i
549    end
550    type = path_elm[i]
551    type.downcase!
552
553    dprint "type: %s" % type
554    case type
555      when "app"
556        i += 1
557        subtype = path_elm[i]
558        subtype.downcase!
559
560        dprint "subtype: %s" % subtype
561        case subtype
562          when "views"
563            ret += ActionView::Base.instance_methods
564            ret += ActionView::Base.methods
565          when "controllers"
566            ret += ActionController::Base.instance_methods
567            ret += ActionController::Base.methods
568          when "models"
569            ret += ActiveRecord::Base.instance_methods
570            ret += ActiveRecord::Base.methods
571        end
572
573      when "db"
574        ret += ActiveRecord::ConnectionAdapters::SchemaStatements.instance_methods
575        ret += ActiveRecord::ConnectionAdapters::SchemaStatements.methods
576    end
577
578    return ret
579  end
580
581  def add_rails_columns( cls )
582    allow_rails = VIM::evaluate("exists('g:rubycomplete_rails') && g:rubycomplete_rails")
583    rails_loaded = VIM::evaluate('s:rubycomplete_rails_loaded')
584    return [] if allow_rails.to_i.zero? || rails_loaded.to_i.zero?
585
586    begin
587        eval( "#{cls}.establish_connection" )
588        return [] unless eval( "#{cls}.ancestors.include?(ActiveRecord::Base).to_s" )
589        col = eval( "#{cls}.column_names" )
590        return col if col
591    rescue
592        dprint "add_rails_columns err: (cls: %s) %s" % [ cls, $! ]
593        return []
594    end
595    return []
596  end
597
598  def clean_sel(sel, msg)
599    ret = sel.reject{|x|x.nil?}.uniq
600    ret = ret.grep(/^#{Regexp.quote(msg)}/) if msg != nil
601    ret
602  end
603
604  def get_rails_view_methods
605    allow_rails = VIM::evaluate("exists('g:rubycomplete_rails') && g:rubycomplete_rails")
606    rails_loaded = VIM::evaluate('s:rubycomplete_rails_loaded')
607    return [] if allow_rails.to_i.zero? || rails_loaded.to_i.zero?
608
609    buf_path = VIM::evaluate('expand("%:p")')
610    buf_path.gsub!( /\\/, "/" )
611    pelm = buf_path.split( "/" )
612    idx = pelm.index( "views" )
613
614    return [] unless idx
615    idx += 1
616
617    clspl = pelm[idx].camelize.pluralize
618    cls = clspl.singularize
619
620    ret = []
621    begin
622      ret += eval( "#{cls}.instance_methods" )
623      ret += eval( "#{clspl}Helper.instance_methods" )
624    rescue Exception
625      dprint "Error: Unable to load rails view helpers for %s: %s" % [ cls, $! ]
626    end
627
628    return ret
629  end
630# }}} buffer analysis magic
631
632# {{{ main completion code
633  def self.preload_rails
634    a = VimRubyCompletion.new
635    if VIM::evaluate("has('nvim')") == 0
636      require 'thread'
637      Thread.new(a) do |b|
638        begin
639        b.load_rails
640        rescue
641        end
642      end
643    end
644    a.load_rails
645  rescue
646  end
647
648  def self.get_completions(base)
649    b = VimRubyCompletion.new
650    b.get_completions base
651  end
652
653  def get_completions(base)
654    loading_allowed = VIM::evaluate("exists('g:rubycomplete_buffer_loading') && g:rubycomplete_buffer_loading")
655    if loading_allowed.to_i == 1
656      load_requires
657      load_rails
658    end
659
660    want_gems = VIM::evaluate("get(g:, 'rubycomplete_load_gemfile')")
661    load_gems unless want_gems.to_i.zero?
662
663    input = VIM::Buffer.current.line
664    cpos = VIM::Window.current.cursor[1] - 1
665    input = input[0..cpos]
666    input += base
667    input.sub!(/.*[ \t\n\"\\'`><=;|&{(]/, '') # Readline.basic_word_break_characters
668    input.sub!(/self\./, '')
669    input.sub!(/.*((\.\.[\[(]?)|([\[(]))/, '')
670
671    dprint 'input %s' % input
672    message = nil
673    receiver = nil
674    methods = []
675    variables = []
676    classes = []
677    constants = []
678
679    case input
680      when /^(\/[^\/]*\/)\.([^.]*)$/ # Regexp
681        receiver = $1
682        message = Regexp.quote($2)
683        methods = Regexp.instance_methods(true)
684
685      when /^([^\]]*\])\.([^.]*)$/ # Array
686        receiver = $1
687        message = Regexp.quote($2)
688        methods = Array.instance_methods(true)
689
690      when /^([^\}]*\})\.([^.]*)$/ # Proc or Hash
691        receiver = $1
692        message = Regexp.quote($2)
693        methods = Proc.instance_methods(true) | Hash.instance_methods(true)
694
695      when /^(:[^:.]*)$/ # Symbol
696        dprint "symbol"
697        if Symbol.respond_to?(:all_symbols)
698          receiver = $1
699          message = $1.sub( /:/, '' )
700          methods = Symbol.all_symbols.collect{|s| s.id2name}
701          methods.delete_if { |c| c.match( /'/ ) }
702        end
703
704      when /^::([A-Z][^:\.\(]*)?$/ # Absolute Constant or class methods
705        dprint "const or cls"
706        receiver = $1
707        methods = Object.constants.collect{ |c| c.to_s }.grep(/^#{receiver}/)
708
709      when /^(((::)?[A-Z][^:.\(]*)+?)::?([^:.]*)$/ # Constant or class methods
710        receiver = $1
711        message = Regexp.quote($4)
712        dprint "const or cls 2 [recv: \'%s\', msg: \'%s\']" % [ receiver, message ]
713        load_buffer_class( receiver )
714        load_buffer_module( receiver )
715        begin
716          constants = eval("#{receiver}.constants").collect{ |c| c.to_s }.grep(/^#{message}/)
717          methods = eval("#{receiver}.methods").collect{ |m| m.to_s }.grep(/^#{message}/)
718        rescue Exception
719          dprint "exception: %s" % $!
720          constants = []
721          methods = []
722        end
723
724      when /^(:[^:.]+)\.([^.]*)$/ # Symbol
725        dprint "symbol"
726        receiver = $1
727        message = Regexp.quote($2)
728        methods = Symbol.instance_methods(true)
729
730      when /^([0-9_]+(\.[0-9_]+)?(e[0-9]+)?)\.([^.]*)$/ # Numeric
731        dprint "numeric"
732        receiver = $1
733        message = Regexp.quote($4)
734        begin
735          methods = eval(receiver).methods
736        rescue Exception
737          methods = []
738        end
739
740      when /^(\$[^.]*)$/ #global
741        dprint "global"
742        methods = global_variables.grep(Regexp.new(Regexp.quote($1)))
743
744      when /^((\.?[^.]+)+?)\.([^.]*)$/ # variable
745        dprint "variable"
746        receiver = $1
747        message = Regexp.quote($3)
748        load_buffer_class( receiver )
749
750        cv = eval("self.class.constants")
751        vartype = get_var_type( receiver )
752        dprint "vartype: %s" % vartype
753
754        invalid_vartype = ['', "gets"]
755        if !invalid_vartype.include?(vartype)
756          load_buffer_class( vartype )
757
758          begin
759            methods = eval("#{vartype}.instance_methods")
760            variables = eval("#{vartype}.instance_variables")
761          rescue Exception
762            dprint "load_buffer_class err: %s" % $!
763          end
764        elsif (cv).include?(receiver)
765          # foo.func and foo is local var.
766          methods = eval("#{receiver}.methods")
767          vartype = receiver
768        elsif /^[A-Z]/ =~ receiver and /\./ !~ receiver
769          vartype = receiver
770          # Foo::Bar.func
771          begin
772            methods = eval("#{receiver}.methods")
773          rescue Exception
774          end
775        else
776          # func1.func2
777          ObjectSpace.each_object(Module){|m|
778            next if m.name != "IRB::Context" and
779              /^(IRB|SLex|RubyLex|RubyToken)/ =~ m.name
780            methods.concat m.instance_methods(false)
781          }
782        end
783        variables += add_rails_columns( "#{vartype}" ) if vartype && !invalid_vartype.include?(vartype)
784
785      when /^\(?\s*[A-Za-z0-9:^@.%\/+*\(\)]+\.\.\.?[A-Za-z0-9:^@.%\/+*\(\)]+\s*\)?\.([^.]*)/
786        message = $1
787        methods = Range.instance_methods(true)
788
789      when /^\.([^.]*)$/ # unknown(maybe String)
790        message = Regexp.quote($1)
791        methods = String.instance_methods(true)
792
793    else
794      dprint "default/other"
795      inclass = eval( VIM::evaluate("s:IsInClassDef()") )
796
797      if inclass != nil
798        dprint "inclass"
799        classdef = "%s\n" % VIM::Buffer.current[ inclass.min ]
800        found = /^\s*class\s*([A-Za-z0-9_-]*)(\s*<\s*([A-Za-z0-9_:-]*))?\s*\n$/.match( classdef )
801
802        if found != nil
803          receiver = $1
804          message = input
805          load_buffer_class( receiver )
806          begin
807            methods = eval( "#{receiver}.instance_methods" )
808            variables += add_rails_columns( "#{receiver}" )
809          rescue Exception
810            found = nil
811          end
812        end
813      end
814
815      if inclass == nil || found == nil
816        dprint "inclass == nil"
817        methods = get_buffer_methods
818        methods += get_rails_view_methods
819
820        cls_const = Class.constants
821        constants = cls_const.select { |c| /^[A-Z_-]+$/.match( c ) }
822        classes = eval("self.class.constants") - constants
823        classes += get_buffer_classes
824        classes += get_buffer_modules
825
826        include_objectspace = VIM::evaluate("exists('g:rubycomplete_include_objectspace') && g:rubycomplete_include_objectspace")
827        ObjectSpace.each_object(Class) { |cls| classes << cls.to_s } if include_objectspace == "1"
828        message = receiver = input
829      end
830
831      methods += get_rails_helpers
832      methods += Kernel.public_methods
833    end
834
835    include_object = VIM::evaluate("exists('g:rubycomplete_include_object') && g:rubycomplete_include_object")
836    methods = clean_sel( methods, message )
837    methods = (methods-Object.instance_methods) if include_object == "0"
838    rbcmeth = (VimRubyCompletion.instance_methods-Object.instance_methods) # lets remove those rubycomplete methods
839    methods = (methods-rbcmeth)
840
841    variables = clean_sel( variables, message )
842    classes = clean_sel( classes, message ) - ["VimRubyCompletion"]
843    constants = clean_sel( constants, message )
844
845    valid = []
846    valid += methods.collect { |m| { :name => m.to_s, :type => 'm' } }
847    valid += variables.collect { |v| { :name => v.to_s, :type => 'v' } }
848    valid += classes.collect { |c| { :name => c.to_s, :type => 't' } }
849    valid += constants.collect { |d| { :name => d.to_s, :type => 'd' } }
850    valid.sort! { |x,y| x[:name] <=> y[:name] }
851
852    outp = ""
853
854    rg = 0..valid.length
855    rg.step(150) do |x|
856      stpos = 0+x
857      enpos = 150+x
858      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)} }
859      outp.sub!(/,$/, '')
860
861      VIM::command("call extend(g:rubycomplete_completions, [%s])" % outp)
862      outp = ""
863    end
864  end
865# }}} main completion code
866
867end # VimRubyCompletion
868# }}} ruby completion
869RUBYEOF
870endfunction
871
872let s:rubycomplete_rails_loaded = 0
873
874call s:DefRuby()
875"}}} ruby-side code
876
877" vim:tw=78:sw=4:ts=8:et:fdm=marker:ft=vim:norl:
878