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