mirror of
				https://github.com/vim/vim.git
				synced 2025-10-31 09:57:14 -04:00 
			
		
		
		
	
		
			
				
	
	
		
			491 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			VimL
		
	
	
	
	
	
			
		
		
	
	
			491 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			VimL
		
	
	
	
	
	
| " Vim completion script
 | |
| " Language:             Ruby
 | |
| " Maintainer:           Mark Guzman <segfault@hasno.info>
 | |
| " Info:                 $Id$
 | |
| " 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)
 | |
| " ----------------------------------------------------------------------------
 | |
| 
 | |
| " {{{ requirement checks
 | |
| if !has('ruby')
 | |
|     echohl ErrorMsg
 | |
|     echo "Error: Required vim compiled with +ruby"
 | |
|     echohl None
 | |
|     finish
 | |
| endif
 | |
| 
 | |
| if version < 700
 | |
|     echohl ErrorMsg
 | |
|     echo "Error: Required vim >= 7.0"
 | |
|     echohl None
 | |
|     finish
 | |
| endif
 | |
| " }}} requirement checks
 | |
| 
 | |
| if !exists("g:rubycomplete_rails")
 | |
|     let g:rubycomplete_rails = 0
 | |
| endif
 | |
| 
 | |
| if !exists("g:rubycomplete_classes_in_global")
 | |
|     let g:rubycomplete_classes_in_global = 0
 | |
| endif
 | |
| 
 | |
| " {{{ vim-side support functions
 | |
| function! GetBufferRubyModule(name)
 | |
|     let [snum,enum] = GetBufferRubyEntity(a:name, "module")
 | |
|     return snum . '..' . enum
 | |
| endfunction
 | |
| 
 | |
| function! GetBufferRubyClass(name)
 | |
|     let [snum,enum] = GetBufferRubyEntity(a:name, "class")
 | |
|     return snum . '..' . enum
 | |
| endfunction
 | |
| 
 | |
| function! GetBufferRubySingletonMethods(name)
 | |
| endfunction
 | |
| 
 | |
| function! GetBufferRubyEntity( name, type )
 | |
|     let stopline = 1
 | |
|     let crex = '^\s*' . a:type . '\s*' . a:name . '\s*\(<\s*.*\s*\)\?\n*\(\(\s\|#\).*\n*\)*\n*\s*end$'
 | |
|     let [lnum,lcol] = searchpos( crex, 'nbw')
 | |
|     if lnum == 0 && lcol == 0
 | |
|         return [0,0]
 | |
|     endif
 | |
| 
 | |
|     let [enum,ecol] = searchpos( crex, 'nebw')
 | |
|     if lnum > enum
 | |
|         let realdef = getline( lnum )
 | |
|         let crexb = '^' . realdef . '\n*\(\(\s\|#\).*\n*\)*\n*\s*end$'
 | |
|         let [enum,ecol] = searchpos( crexb, 'necw' )
 | |
|     endif
 | |
|     " we found a the class def
 | |
|     return [lnum,enum]
 | |
| endfunction
 | |
| 
 | |
| function! IsInClassDef()
 | |
|     let [snum,enum] = GetBufferRubyEntity( '.*', "class" )
 | |
|     let ret = 'nil'
 | |
|     let pos = line('.')
 | |
| 
 | |
|     if snum < pos && pos < enum
 | |
|         let ret = snum . '..' . enum
 | |
|     endif
 | |
| 
 | |
|     return ret
 | |
| endfunction
 | |
| 
 | |
| function! 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)
 | |
|     if g:rubycomplete_rails == 1 && g:rubycomplete_rails_loaded == 1
 | |
|         let ctors = '\(now\|new\|open\|get_instance\|find\|create\)'
 | |
|     else
 | |
|         let ctors = '\(now\|new\|open\|get_instance\)'
 | |
|     endif
 | |
| 
 | |
|     let [lnum,lcol] = searchpos(''.a:v.'\>\s*[+\-*/]*=\s*\([^ \t]\+.' . ctors .'\>\|[\[{"''/]\|%r{\|[A-Za-z0-9@:\-()]\+...\?\)','nb',stopline)
 | |
|     if lnum != 0 && lcol != 0
 | |
|         let str = matchstr(getline(lnum),'=\s*\([^ \t]\+.' . ctors . '\>\|[\[{"''/]\|%r{\|[A-Za-z0-9@:\-()]\+...\?\)',lcol)
 | |
|         let str = substitute(str,'^=\s*','','')
 | |
|         call setpos('.',pos)
 | |
|         if str == '"' || str == ''''
 | |
|             return 'String'
 | |
|         elseif str == '['
 | |
|             return 'Array'
 | |
|         elseif str == '{'
 | |
|             return 'Hash'
 | |
|         elseif str == '/' || str == '%r{'
 | |
|             return 'Regexp'
 | |
|         elseif strlen(str) >= 4 && stridx(str,'..') != -1
 | |
|             return 'Range'
 | |
|         elseif strlen(str) > 4
 | |
|             let l = stridx(str,'.')
 | |
|             return str[0:l-1]
 | |
|         end
 | |
|         return ''
 | |
|     endif
 | |
|     call setpos('.',pos)
 | |
|     return ''
 | |
| endfunction
 | |
| 
 | |
| "}}} vim-side support functions
 | |
| 
 | |
| 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
 | |
|         let g:rubycomplete_completions = []
 | |
|         execute "ruby get_completions('" . a:base . "')"
 | |
|         return g:rubycomplete_completions
 | |
|     endif
 | |
| endfunction
 | |
| 
 | |
| 
 | |
| function! s:DefRuby()
 | |
| ruby << RUBYEOF
 | |
| # {{{ ruby completion
 | |
| RailsWords = [
 | |
|       "has_many", "has_one",
 | |
|       "belongs_to",
 | |
|     ]
 | |
| 
 | |
| 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 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 load_buffer_class(name)
 | |
|   classdef = get_buffer_entity(name, 'GetBufferRubyClass("%s")')
 | |
|   return if classdef == nil
 | |
| 
 | |
|   pare = /^\s*class\s*(.*)\s*<\s*(.*)\s*\n/.match( classdef )
 | |
|   load_buffer_class( $2 ) if pare != nil
 | |
| 
 | |
|   mixre = /.*\n\s*include\s*(.*)\s*\n/.match( classdef )
 | |
|   load_buffer_module( $2 ) if mixre != nil
 | |
| 
 | |
|   eval classdef
 | |
| end
 | |
| 
 | |
| def load_buffer_module(name)
 | |
|   classdef = get_buffer_entity(name, 'GetBufferRubyModule("%s")')
 | |
|   return if classdef == nil
 | |
| 
 | |
|   eval classdef
 | |
| end
 | |
| 
 | |
| def get_buffer_entity(name, vimfun)
 | |
|   return nil if /(\"|\')+/.match( name )
 | |
|   buf = VIM::Buffer.current
 | |
|   nums = eval( VIM::evaluate( vimfun % name ) )
 | |
|   return nil if nums == nil
 | |
|   return nil if nums.min == nums.max && nums.min == 0
 | |
| 
 | |
|   cur_line = VIM::Buffer.current.line_number
 | |
|   classdef = ""
 | |
|   nums.each do |x|
 | |
|     if x != cur_line
 | |
|       ln = buf[x]
 | |
|       classdef += "%s\n" % ln
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   return classdef
 | |
| end
 | |
| 
 | |
| def get_var_type( receiver )
 | |
|   if /(\"|\')+/.match( receiver )
 | |
|     "String"
 | |
|   else
 | |
|     VIM::evaluate("GetRubyVarType('%s')" % receiver)
 | |
|   end
 | |
| end
 | |
| 
 | |
| def get_buffer_classes()
 | |
|   # this will be a little expensive.
 | |
|   allow_aggressive_load = VIM::evaluate('g:rubycomplete_classes_in_global')
 | |
|   return [] if allow_aggressive_load != '1'
 | |
| 
 | |
|   buf = VIM::Buffer.current
 | |
|   eob = buf.length
 | |
|   ret = []
 | |
|   rg = 1..eob
 | |
| 
 | |
|   rg.each do |x|
 | |
|     if /^\s*class\s*([A-Za-z0-9_-]*)(\s*<\s*([A-Za-z0-9_:-]*))?\s*/.match( buf[x] )
 | |
|       ret.push $1
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   return ret
 | |
| end
 | |
| 
 | |
| def load_rails()
 | |
|   allow_rails = VIM::evaluate('g:rubycomplete_rails')
 | |
|   return if allow_rails != '1'
 | |
| 
 | |
|   buf_path = VIM::evaluate('expand("%:p")')
 | |
|   file_name = VIM::evaluate('expand("%:t")')
 | |
|   path = buf_path.gsub( file_name, '' )
 | |
|   path.gsub!( /\\/, "/" )
 | |
|   pup = [ "./", "../", "../../", "../../../", "../../../../" ]
 | |
|   pok = nil
 | |
| 
 | |
|   pup.each do |sup|
 | |
|     tpok = "%s%sconfig" % [ path, sup ]
 | |
|     if File.exists?( tpok )
 | |
|       pok = tpok
 | |
|       break
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   return if pok == nil
 | |
| 
 | |
|   bootfile = pok + "/boot.rb"
 | |
|   envfile = pok + "/environment.rb"
 | |
|   if File.exists?( bootfile ) && File.exists?( envfile )
 | |
|     begin
 | |
|       require bootfile
 | |
|       require envfile
 | |
|       require 'console_app'
 | |
|       require 'console_with_helpers'
 | |
|       VIM::command('let g:rubycomplete_rails_loaded = 1')
 | |
|     rescue
 | |
|       print "Error loading rails environment"
 | |
|     end
 | |
|   end
 | |
| end
 | |
| 
 | |
| def get_rails_helpers
 | |
|   allow_rails = VIM::evaluate('g:rubycomplete_rails')
 | |
|   rails_loaded = VIM::evaluate('g:rubycomplete_rails_loaded')
 | |
|   return [] if allow_rails != '1' || rails_loaded != '1'
 | |
|   return RailsWords
 | |
| end
 | |
| 
 | |
| def get_completions(base)
 | |
|   load_requires
 | |
|   load_rails
 | |
| 
 | |
|   input = VIM::Buffer.current.line
 | |
|   cpos = VIM::Window.current.cursor[1] - 1
 | |
|   input = input[0..cpos] if cpos != 0
 | |
|   input += base
 | |
| 
 | |
|   rip = input.rindex(/\s/,cpos)
 | |
|   if rip
 | |
|     input = input[rip..input.length]
 | |
|   end
 | |
| 
 | |
|   asn = /^.*(\+|\-|\*|=|\(|\[)=?(\s*[A-Za-z0-9_:@.-]*)(\s*(\{|\+|\-|\*|\%|\/)?\s*).*/
 | |
|   if asn.match(input)
 | |
|     input = $2
 | |
|   end
 | |
| 
 | |
|   input.strip!
 | |
|   message = nil
 | |
|   receiver = nil
 | |
|   candidates = []
 | |
| 
 | |
|   case input
 | |
|     when /^(\/[^\/]*\/)\.([^.]*)$/ # Regexp
 | |
|       receiver = $1
 | |
|       message = Regexp.quote($2)
 | |
|       candidates = Regexp.instance_methods(true)
 | |
| 
 | |
|     when /^([^\]]*\])\.([^.]*)$/ # Array
 | |
|       receiver = $1
 | |
|       message = Regexp.quote($2)
 | |
|       candidates = Array.instance_methods(true)
 | |
| 
 | |
|     when /^([^\}]*\})\.([^.]*)$/ # Proc or Hash
 | |
|       receiver = $1
 | |
|       message = Regexp.quote($2)
 | |
|       candidates = Proc.instance_methods(true) | Hash.instance_methods(true)
 | |
| 
 | |
|     when /^(:[^:.]*)$/ # Symbol
 | |
|       if Symbol.respond_to?(:all_symbols)
 | |
|         receiver = $1
 | |
|         candidates = Symbol.all_symbols.collect{|s| s.id2name}
 | |
|         candidates.delete_if { |c| c.match( /'/ ) }
 | |
|       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)
 | |
| 
 | |
|     when /^([0-9_]+(\.[0-9_]+)?(e[0-9]+)?)\.([^.]*)$/ # Numeric
 | |
|       receiver = $1
 | |
|       message = Regexp.quote($4)
 | |
|       begin
 | |
|         candidates = eval(receiver).methods
 | |
|       rescue Exception
 | |
|         candidates
 | |
|       end
 | |
| 
 | |
|     when /^(\$[^.]*)$/ #global
 | |
|       candidates = global_variables.grep(Regexp.new(Regexp.quote($1)))
 | |
| 
 | |
|     when /^((\.?[^.]+)+)\.([^.]*)$/ # variable
 | |
|       receiver = $1
 | |
|       message = Regexp.quote($3)
 | |
|       load_buffer_class( receiver )
 | |
| 
 | |
|       cv = eval("self.class.constants")
 | |
|       vartype = get_var_type( receiver )
 | |
|       if vartype != ''
 | |
|         load_buffer_class( vartype )
 | |
| 
 | |
|         begin
 | |
|           candidates = eval("#{vartype}.instance_methods")
 | |
|         rescue Exception
 | |
|           candidates = []
 | |
|         end
 | |
|       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)
 | |
|         }
 | |
|       end
 | |
| 
 | |
|     when /^\(?\s*[A-Za-z0-9:^@.%\/+*\(\)]+\.\.\.?[A-Za-z0-9:^@.%\/+*\(\)]+\s*\)?\.([^.]*)/
 | |
|       message = $1
 | |
|       candidates = Range.instance_methods(true)
 | |
| 
 | |
|     when /^\[(\s*[A-Za-z0-9:^@.%\/+*\(\)\[\]\{\}.\'\"],?)*\].([^.]*)/
 | |
|       message = $2
 | |
|       candidates = Array.instance_methods(true)
 | |
| 
 | |
|     when /^\.([^.]*)$/ # unknown(maybe String)
 | |
|       message = Regexp.quote($1)
 | |
|       candidates = String.instance_methods(true)
 | |
| 
 | |
|   else
 | |
|     inclass = eval( VIM::evaluate("IsInClassDef()") )
 | |
| 
 | |
|     if inclass != nil
 | |
|       classdef = "%s\n" % VIM::Buffer.current[ inclass.min ]
 | |
|       found = /^\s*class\s*([A-Za-z0-9_-]*)(\s*<\s*([A-Za-z0-9_:-]*))?\s*\n$/.match( classdef )
 | |
| 
 | |
|       if found != nil
 | |
|         receiver = $1
 | |
|         message = input
 | |
|         load_buffer_class( receiver )
 | |
|         begin
 | |
|           candidates = eval( "#{receiver}.instance_methods" )
 | |
|           candidates += get_rails_helpers
 | |
|         rescue Exception
 | |
|           found = nil
 | |
|         end
 | |
|       end
 | |
|     end
 | |
| 
 | |
|     if inclass == nil || found == nil
 | |
|       candidates = eval("self.class.constants")
 | |
|       candidates += get_buffer_classes
 | |
|       message = receiver = input
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   candidates.delete_if { |x| x == nil }
 | |
|   candidates.uniq!
 | |
|   candidates.sort!
 | |
|   candidates = candidates.grep(/^#{Regexp.quote(message)}/) if message != nil
 | |
| 
 | |
|   outp = ""
 | |
|   valid = (candidates-Object.instance_methods)
 | |
| 
 | |
|   rg = 0..valid.length
 | |
|   rg.step(150) do |x|
 | |
|     stpos = 0+x
 | |
|     enpos = 150+x
 | |
|     valid[stpos..enpos].each { |c| outp += "{'word':'%s','item':'%s'}," % [ c, c ] }
 | |
|     outp.sub!(/,$/, '')
 | |
| 
 | |
|     VIM::command("call extend(g:rubycomplete_completions, [%s])" % outp)
 | |
|     outp = ""
 | |
|   end
 | |
| end
 | |
| 
 | |
| # }}} ruby completion
 | |
| RUBYEOF
 | |
| endfunction
 | |
| 
 | |
| let g:rubycomplete_rails_loaded = 0
 | |
| 
 | |
| call s:DefRuby()
 | |
| " vim:tw=78:sw=4:ts=8:et:ft=vim:norl:
 |