" Vim completion script
" Language:				Ruby
" Maintainer:			Mark Guzman ( segfault AT hasno DOT info )
" Info:					$Id: rubycomplete.vim,v 1.1 2006/04/15 20:17:41 vimboss Exp $
" URL:					http://vim-ruby.rubyforge.org
" Anon CVS:				See above site
" Release Coordinator:	Doug Kearns <dougkearns@gmail.com>
" ----------------------------------------------------------------------------
"
" Ruby IRB/Complete author: Keiju ISHITSUKA(keiju@ishitsuka.com)
" ----------------------------------------------------------------------------

if !has('ruby')
    echo "Error: Required vim compiled with +ruby"
    finish
endif

if version < 700
    echo "Error: Required vim >= 7.0"
    finish
endif

func! GetRubyVarType(v)
	let stopline = 1
	let vtp = ''
	let pos = getpos('.')
	let [lnum,lcol] = searchpos('^\s*#\s*@var\s*'.a:v.'\>\s\+[^ \t]\+\s*$','nb',stopline)
	if lnum != 0 && lcol != 0
		call setpos('.',pos)
		let str = getline(lnum)
		let vtp = substitute(str,'^\s*#\s*@var\s*'.a:v.'\>\s\+\([^ \t]\+\)\s*$','\1','')
		return vtp
	endif
	call setpos('.',pos)
	let [lnum,lcol] = searchpos(''.a:v.'\>\s*[+\-*/]*=\s*\([^ \t]\+.\(now\|new\|open\|get_instance\)\>\|[\[{"'']\)','nb',stopline)
	if lnum != 0 && lcol != 0
		let str = matchstr(getline(lnum),'=\s*\([^ \t]\+.\(now\|new\|open\|get_instance\)\>\|[\[{"'']\)',lcol)
		let str = substitute(str,'^=\s*','','')
		call setpos('.',pos)
		if str == '"' || str == ''''
			return 'String'
		elseif str == '['
			return 'Array'
		elseif str == '{'
			return 'Hash'
		elseif strlen(str) > 4
            let l = stridx(str,'.')
			return str[0:l-1]
		end
		return ''
	endif
	call setpos('.',pos)
    return ''
endf

function! rubycomplete#Complete(findstart, base)
     "findstart = 1 when we need to get the text length
    if a:findstart
        let line = getline('.')
        let idx = col('.')
        while idx > 0
            let idx -= 1
            let c = line[idx-1]
            if c =~ '\w'
                continue
            elseif ! c =~ '\.'
                idx = -1
                break
            else
                break
            endif
        endwhile

        return idx
    "findstart = 0 when we need to return the list of completions
    else
        execute "ruby get_completions('" . a:base . "')"
        return g:rbcomplete_completions
    endif
endfunction


function! s:DefRuby()
ruby << RUBYEOF
ReservedWords = [
      "BEGIN", "END",
      "alias", "and",
      "begin", "break",
      "case", "class",
      "def", "defined", "do",
      "else", "elsif", "end", "ensure",
      "false", "for",
      "if", "in",
      "module",
      "next", "nil", "not",
      "or",
      "redo", "rescue", "retry", "return",
      "self", "super",
      "then", "true",
      "undef", "unless", "until",
      "when", "while",
      "yield",
    ]

Operators = [ "%", "&", "*", "**", "+",  "-",  "/",
      "<", "<<", "<=", "<=>", "==", "===", "=~", ">", ">=", ">>",
      "[]", "[]=", "^", ]

def identify_type(var)
    @buf = VIM::Buffer.current
    enum = @buf.line_number
    snum = (enum-10).abs
    nums = Range.new( snum, enum )
    regxs = '/.*(%s)\s*=(.*)/' % var
    regx = Regexp.new( regxs )
    nums.each do |x|
        ln = @buf[x]
        #print $~ if regx.match( ln )
    end
end

def load_requires
    @buf = VIM::Buffer.current
    enum = @buf.line_number
    nums = Range.new( 1, enum )
    nums.each do |x|
        ln = @buf[x]
        begin
            eval( "require %s" % $1 ) if /.*require\s*(.*)$/.match( ln )
        rescue Exception
            #ignore?
        end
    end
end

def get_completions(base)
    load_requires
    input = VIM::evaluate('expand("<cWORD>")')
    input += base
    message = nil


    case input
      when /^(\/[^\/]*\/)\.([^.]*)$/
        # Regexp
        receiver = $1
        message = Regexp.quote($2)

        candidates = Regexp.instance_methods(true)
        select_message(receiver, message, candidates)

      when /^([^\]]*\])\.([^.]*)$/
        # Array
        receiver = $1
        message = Regexp.quote($2)

        candidates = Array.instance_methods(true)
        select_message(receiver, message, candidates)

      when /^([^\}]*\})\.([^.]*)$/
        # Proc or Hash
        receiver = $1
        message = Regexp.quote($2)

        candidates = Proc.instance_methods(true) | Hash.instance_methods(true)
        select_message(receiver, message, candidates)

      when /^(:[^:.]*)$/
        # Symbol
        if Symbol.respond_to?(:all_symbols)
          sym = $1
          candidates = Symbol.all_symbols.collect{|s| ":" + s.id2name}
          candidates.grep(/^#{sym}/)
        else
          []
        end

      when /^::([A-Z][^:\.\(]*)$/
        # Absolute Constant or class methods
        receiver = $1
        candidates = Object.constants
        candidates.grep(/^#{receiver}/).collect{|e| "::" + e}

      when /^(((::)?[A-Z][^:.\(]*)+)::?([^:.]*)$/
        # Constant or class methods
        receiver = $1
        message = Regexp.quote($4)
        begin
          candidates = eval("#{receiver}.constants | #{receiver}.methods")
        rescue Exception
          candidates = []
        end
        candidates.grep(/^#{message}/).collect{|e| receiver + "::" + e}

      when /^(:[^:.]+)\.([^.]*)$/
        # Symbol
        receiver = $1
        message = Regexp.quote($2)

        candidates = Symbol.instance_methods(true)
        select_message(receiver, message, candidates)

      when /^([0-9_]+(\.[0-9_]+)?(e[0-9]+)?)\.([^.]*)$/
        # Numeric
        receiver = $1
        message = Regexp.quote($4)

        begin
          candidates = eval(receiver).methods
        rescue Exception
          candidates
        end
        select_message(receiver, message, candidates)

      when /^(\$[^.]*)$/
	      candidates = global_variables.grep(Regexp.new(Regexp.quote($1)))

#      when /^(\$?(\.?[^.]+)+)\.([^.]*)$/
      when /^((\.?[^.]+)+)\.([^.]*)$/
        # variable
        receiver = $1
        message = Regexp.quote($3)

        cv = eval("self.class.constants")

        vartype = VIM::evaluate("GetRubyVarType('%s')" % receiver)
        if vartype != ''
          candidates = eval("#{vartype}.instance_methods")
        elsif (cv).include?(receiver)
          # foo.func and foo is local var.
          candidates = eval("#{receiver}.methods")
        elsif /^[A-Z]/ =~ receiver and /\./ !~ receiver
          # Foo::Bar.func
          begin
            candidates = eval("#{receiver}.methods")
          rescue Exception
            candidates = []
          end
        else
          # func1.func2
          candidates = []
          ObjectSpace.each_object(Module){|m|
            next if m.name != "IRB::Context" and
              /^(IRB|SLex|RubyLex|RubyToken)/ =~ m.name
            candidates.concat m.instance_methods(false)
          }
          candidates.sort!
          candidates.uniq!
        end
        #identify_type( receiver )
        select_message(receiver, message, candidates)

    #when /^((\.?[^.]+)+)\.([^.]*)\(\s*\)*$/
        #function call
        #obj = $1
        #func = $3

      when /^\.([^.]*)$/
	# unknown(maybe String)

        receiver = ""
        message = Regexp.quote($1)

        candidates = String.instance_methods(true)
        select_message(receiver, message, candidates)

    else
      candidates = eval("self.class.constants")

      (candidates|ReservedWords).grep(/^#{Regexp.quote(input)}/)
    end

    #print candidates
    if message != nil && message.length > 0
        rexp = '^%s' % message.downcase
        candidates.delete_if do |c|
            c.downcase.match( rexp )
            $~ == nil
        end
    end

    outp = ""
    #    tags = VIM::evaluate("taglist('^%s$')" %
    (candidates-Object.instance_methods).each { |c| outp += "{'word':'%s','item':'%s'}," % [ c, c ] }
    outp.sub!(/,$/, '')
    VIM::command("let g:rbcomplete_completions = [%s]" % outp)
end


def select_message(receiver, message, candidates)
  candidates.grep(/^#{message}/).collect do |e|
    case e
      when /^[a-zA-Z_]/
        receiver + "." + e
      when /^[0-9]/
      when *Operators
        #receiver + " " + e
    end
  end
  candidates.delete_if { |x| x == nil }
  candidates.uniq!
  candidates.sort!
end
RUBYEOF
endfunction

call s:DefRuby()
" vim: set et ts=4:
