neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

rubycomplete.vim (25801B)


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