blob: ea184702326c6fac3cc1a85078e8cc6686aa9304 [file] [log] [blame]
Bram Moolenaarc6249bb2006-04-15 20:25:09 +00001" Vim completion script
Bram Moolenaard09091d2019-01-17 16:07:22 +01002" Language: Ruby
3" Maintainer: Mark Guzman <segfault@hasno.info>
4" URL: https://github.com/vim-ruby/vim-ruby
5" Release Coordinator: Doug Kearns <dougkearns@gmail.com>
6" Last Change: 2019 Jan 06
Bram Moolenaarc6249bb2006-04-15 20:25:09 +00007" ----------------------------------------------------------------------------
8"
9" Ruby IRB/Complete author: Keiju ISHITSUKA(keiju@ishitsuka.com)
10" ----------------------------------------------------------------------------
11
Bram Moolenaareb3593b2006-04-22 22:33:57 +000012" {{{ requirement checks
Bram Moolenaarec7944a2013-06-12 21:29:15 +020013
14function! s:ErrMsg(msg)
15 echohl ErrorMsg
16 echo a:msg
17 echohl None
18endfunction
19
Bram Moolenaarc6249bb2006-04-15 20:25:09 +000020if !has('ruby')
Bram Moolenaarec7944a2013-06-12 21:29:15 +020021 call s:ErrMsg( "Error: Rubycomplete requires vim compiled with +ruby" )
22 call s:ErrMsg( "Error: falling back to syntax completion" )
Bram Moolenaar9964e462007-05-05 17:54:07 +000023 " lets fall back to syntax completion
24 setlocal omnifunc=syntaxcomplete#Complete
Bram Moolenaarc6249bb2006-04-15 20:25:09 +000025 finish
26endif
27
28if version < 700
Bram Moolenaarec7944a2013-06-12 21:29:15 +020029 call s:ErrMsg( "Error: Required vim >= 7.0" )
Bram Moolenaarc6249bb2006-04-15 20:25:09 +000030 finish
31endif
Bram Moolenaareb3593b2006-04-22 22:33:57 +000032" }}} requirement checks
Bram Moolenaarc6249bb2006-04-15 20:25:09 +000033
Bram Moolenaar9964e462007-05-05 17:54:07 +000034" {{{ configuration failsafe initialization
Bram Moolenaareb3593b2006-04-22 22:33:57 +000035if !exists("g:rubycomplete_rails")
36 let g:rubycomplete_rails = 0
37endif
Bram Moolenaarfc1421e2006-04-20 22:17:20 +000038
Bram Moolenaareb3593b2006-04-22 22:33:57 +000039if !exists("g:rubycomplete_classes_in_global")
40 let g:rubycomplete_classes_in_global = 0
41endif
42
Bram Moolenaar9964e462007-05-05 17:54:07 +000043if !exists("g:rubycomplete_buffer_loading")
Bram Moolenaara7241f52008-06-24 20:39:31 +000044 let g:rubycomplete_buffer_loading = 0
Bram Moolenaar9964e462007-05-05 17:54:07 +000045endif
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
Bram Moolenaareb3593b2006-04-22 22:33:57 +000056" {{{ vim-side support functions
Bram Moolenaar9964e462007-05-05 17:54:07 +000057let s:rubycomplete_debug = 0
58
Bram Moolenaar9964e462007-05-05 17:54:07 +000059function! 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
Bram Moolenaarfc1421e2006-04-20 22:17:20 +000071 return snum . '..' . enum
72endfunction
73
Bram Moolenaar9964e462007-05-05 17:54:07 +000074function! 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
Bram Moolenaarfc1421e2006-04-20 22:17:20 +000080 return snum . '..' . enum
81endfunction
82
Bram Moolenaar9964e462007-05-05 17:54:07 +000083function! s:GetBufferRubySingletonMethods(name)
Bram Moolenaarfc1421e2006-04-20 22:17:20 +000084endfunction
85
Bram Moolenaar9964e462007-05-05 17:54:07 +000086function! 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
Bram Moolenaarfc1421e2006-04-20 22:17:20 +000094 let stopline = 1
Bram Moolenaar9964e462007-05-05 17:54:07 +000095
Bram Moolenaar89bcfda2016-08-30 23:26:57 +020096 let crex = '^\s*\<' . a:type . '\>\s*\<' . escape(a:name, '*') . '\>\s*\(<\s*.*\s*\)\?'
Bram Moolenaar9964e462007-05-05 17:54:07 +000097 let [lnum,lcol] = searchpos( crex, 'w' )
98 "let [lnum,lcol] = searchpairpos( crex . '\zs', '', '\(end\|}\)', 'w' )
99
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000100 if lnum == 0 && lcol == 0
Bram Moolenaar9964e462007-05-05 17:54:07 +0000101 call cursor(lastpos[1], lastpos[2])
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000102 return [0,0]
103 endif
104
Bram Moolenaar9964e462007-05-05 17:54:07 +0000105 let curpos = getpos(".")
Bram Moolenaard09091d2019-01-17 16:07:22 +0100106 let [enum,ecol] = searchpairpos( crex, '', '\(end\|}\)', 'W' )
Bram Moolenaar9964e462007-05-05 17:54:07 +0000107 call cursor(lastpos[1], lastpos[2])
108
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000109 if lnum > enum
Bram Moolenaar9964e462007-05-05 17:54:07 +0000110 return [0,0]
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000111 endif
112 " we found a the class def
113 return [lnum,enum]
114endfunction
115
Bram Moolenaar9964e462007-05-05 17:54:07 +0000116function! s:IsInClassDef()
117 return s:IsPosInClassDef( line('.') )
118endfunction
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000119
Bram Moolenaar9964e462007-05-05 17:54:07 +0000120function! s:IsPosInClassDef(pos)
121 let [snum,enum] = s:GetBufferRubyEntity( '.*', "class" )
122 let ret = 'nil'
123
124 if snum < a:pos && a:pos < enum
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000125 let ret = snum . '..' . enum
126 endif
127
128 return ret
129endfunction
130
Bram Moolenaar9964e462007-05-05 17:54:07 +0000131function! s:GetRubyVarType(v)
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000132 let stopline = 1
133 let vtp = ''
134 let pos = getpos('.')
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200135 let sstr = '^\s*#\s*@var\s*'.escape(a:v, '*').'\>\s\+[^ \t]\+\s*$'
Bram Moolenaar9964e462007-05-05 17:54:07 +0000136 let [lnum,lcol] = searchpos(sstr,'nb',stopline)
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000137 if lnum != 0 && lcol != 0
138 call setpos('.',pos)
139 let str = getline(lnum)
Bram Moolenaar9964e462007-05-05 17:54:07 +0000140 let vtp = substitute(str,sstr,'\1','')
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000141 return vtp
142 endif
143 call setpos('.',pos)
Bram Moolenaar9964e462007-05-05 17:54:07 +0000144 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'
Bram Moolenaar551dbcc2006-04-25 22:13:59 +0000147 else
Bram Moolenaar551dbcc2006-04-25 22:13:59 +0000148 endif
Bram Moolenaar9964e462007-05-05 17:54:07 +0000149 let ctors = ctors.'\)'
Bram Moolenaar551dbcc2006-04-25 22:13:59 +0000150
Bram Moolenaar9964e462007-05-05 17:54:07 +0000151 let fstr = '=\s*\([^ \t]\+.' . ctors .'\>\|[\[{"''/]\|%[xwQqr][(\[{@]\|[A-Za-z0-9@:\-()\.]\+...\?\|lambda\|&\)'
Bram Moolenaar89bcfda2016-08-30 23:26:57 +0200152 let sstr = ''.escape(a:v, '*').'\>\s*[+\-*/]*'.fstr
Bram Moolenaar9964e462007-05-05 17:54:07 +0000153 let [lnum,lcol] = searchpos(sstr,'nb',stopline)
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000154 if lnum != 0 && lcol != 0
Bram Moolenaar9964e462007-05-05 17:54:07 +0000155 let str = matchstr(getline(lnum),fstr,lcol)
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000156 let str = substitute(str,'^=\s*','','')
Bram Moolenaar9964e462007-05-05 17:54:07 +0000157
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000158 call setpos('.',pos)
Bram Moolenaar9964e462007-05-05 17:54:07 +0000159 if str == '"' || str == '''' || stridx(tolower(str), '%q[') != -1
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000160 return 'String'
Bram Moolenaar9964e462007-05-05 17:54:07 +0000161 elseif str == '[' || stridx(str, '%w[') != -1
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000162 return 'Array'
163 elseif str == '{'
164 return 'Hash'
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000165 elseif str == '/' || str == '%r{'
166 return 'Regexp'
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000167 elseif strlen(str) >= 4 && stridx(str,'..') != -1
168 return 'Range'
Bram Moolenaar9964e462007-05-05 17:54:07 +0000169 elseif stridx(str, 'lambda') != -1 || str == '&'
170 return 'Proc'
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000171 elseif strlen(str) > 4
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000172 let l = stridx(str,'.')
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000173 return str[0:l-1]
174 end
175 return ''
176 endif
177 call setpos('.',pos)
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000178 return ''
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000179endfunction
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000180
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000181"}}} vim-side support functions
182
Bram Moolenaar9964e462007-05-05 17:54:07 +0000183"{{{ vim-side completion function
184function! rubycomplete#Init()
185 execute "ruby VimRubyCompletion.preload_rails"
186endfunction
187
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000188function! 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 =~ '\.'
Bram Moolenaar45758762016-10-12 23:08:06 +0200199 let idx = -1
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000200 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
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000209 let g:rubycomplete_completions = []
Bram Moolenaar9964e462007-05-05 17:54:07 +0000210 execute "ruby VimRubyCompletion.get_completions('" . a:base . "')"
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000211 return g:rubycomplete_completions
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000212 endif
213endfunction
Bram Moolenaar9964e462007-05-05 17:54:07 +0000214"}}} vim-side completion function
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000215
Bram Moolenaar9964e462007-05-05 17:54:07 +0000216"{{{ ruby-side code
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000217function! s:DefRuby()
218ruby << RUBYEOF
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000219# {{{ ruby completion
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000220
Bram Moolenaar9964e462007-05-05 17:54:07 +0000221begin
222 require 'rubygems' # let's assume this is safe...?
223rescue Exception
224 #ignore?
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000225end
Bram Moolenaar9964e462007-05-05 17:54:07 +0000226class 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 ]
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000248
Bram Moolenaar9964e462007-05-05 17:54:07 +0000249 @@Operators = [ "%", "&", "*", "**", "+", "-", "/",
250 "<", "<<", "<=", "<=>", "==", "===", "=~", ">", ">=", ">>",
251 "[]", "[]=", "^", ]
252# }}} constants
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000253
Bram Moolenaar9964e462007-05-05 17:54:07 +0000254# {{{ buffer analysis magic
255 def load_requires
Bram Moolenaard09091d2019-01-17 16:07:22 +0100256
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
Bram Moolenaar9964e462007-05-05 17:54:07 +0000263 buf = VIM::Buffer.current
264 enum = buf.line_number
265 nums = Range.new( 1, enum )
266 nums.each do |x|
Bram Moolenaard09091d2019-01-17 16:07:22 +0100267
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000268 ln = buf[x]
Bram Moolenaar9964e462007-05-05 17:54:07 +0000269 begin
Bram Moolenaard09091d2019-01-17 16:07:22 +0100270 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
Bram Moolenaar9964e462007-05-05 17:54:07 +0000277 end
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000278 end
279 end
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000280
Bram Moolenaar89bcfda2016-08-30 23:26:57 +0200281 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
Bram Moolenaar9964e462007-05-05 17:54:07 +0000303 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
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000307
Bram Moolenaar9964e462007-05-05 17:54:07 +0000308 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
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000310
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200311 mixre = /.*\n\s*(include|prepend)\s*(.*)\s*\n/.match( classdef )
Bram Moolenaar9964e462007-05-05 17:54:07 +0000312 load_buffer_module( $2 ) if mixre != nil && $2 != name # load mixins if needed
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000313
Bram Moolenaar551dbcc2006-04-25 22:13:59 +0000314 begin
Bram Moolenaar9964e462007-05-05 17:54:07 +0000315 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")
Bram Moolenaarc236c162008-07-13 17:41:49 +0000337 return nil if loading_allowed.to_i.zero?
Bram Moolenaar9964e462007-05-05 17:54:07 +0000338 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]
Bram Moolenaard09091d2019-01-17 16:07:22 +0100359 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
Bram Moolenaar1d689522010-05-28 20:54:39 +0200366 #dprint "\$1$1
Bram Moolenaar9964e462007-05-05 17:54:07 +0000367 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)
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000398 end
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000399 end
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000400
Bram Moolenaar9964e462007-05-05 17:54:07 +0000401 def dprint( txt )
402 print txt if @@debug
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000403 end
404
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200405 def escape_vim_singlequote_string(str)
406 str.to_s.gsub(/'/,"\\'")
407 end
408
Bram Moolenaar9964e462007-05-05 17:54:07 +0000409 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")
Bram Moolenaarc236c162008-07-13 17:41:49 +0000413 return [] if allow_aggressive_load.to_i.zero? || loading_allowed.to_i.zero?
Bram Moolenaar9964e462007-05-05 17:54:07 +0000414
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
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000429 end
430
Bram Moolenaar9964e462007-05-05 17:54:07 +0000431 def get_buffer_modules
432 return get_buffer_entity_list( "modules" )
433 end
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000434
Bram Moolenaar9964e462007-05-05 17:54:07 +0000435 def get_buffer_methods
436 return get_buffer_entity_list( "def" )
437 end
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000438
Bram Moolenaar9964e462007-05-05 17:54:07 +0000439 def get_buffer_classes
440 return get_buffer_entity_list( "class" )
441 end
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000442
Bram Moolenaar9964e462007-05-05 17:54:07 +0000443 def load_rails
444 allow_rails = VIM::evaluate("exists('g:rubycomplete_rails') && g:rubycomplete_rails")
Bram Moolenaarc236c162008-07-13 17:41:49 +0000445 return if allow_rails.to_i.zero?
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000446
Bram Moolenaar9964e462007-05-05 17:54:07 +0000447 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
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000457
Bram Moolenaar9964e462007-05-05 17:54:07 +0000458 dirs.each do |dir|
459 sdirs.each do |sub|
460 trail = "%s%s" % [ dir, sub ]
461 tcfg = "%sconfig" % trail
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000462
Bram Moolenaar9964e462007-05-05 17:54:07 +0000463 if File.exists?( tcfg )
464 rails_base = trail
465 break
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000466 end
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000467 end
Bram Moolenaar9964e462007-05-05 17:54:07 +0000468 break if rails_base
469 end
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000470
Bram Moolenaar9964e462007-05-05 17:54:07 +0000471 return if rails_base == nil
472 $:.push rails_base unless $:.index( rails_base )
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000473
Bram Moolenaar9964e462007-05-05 17:54:07 +0000474 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 )
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000478
Bram Moolenaar9964e462007-05-05 17:54:07 +0000479 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
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000501
Bram Moolenaar9964e462007-05-05 17:54:07 +0000502 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')
Bram Moolenaarc236c162008-07-13 17:41:49 +0000505 return [] if allow_rails.to_i.zero? || rails_loaded.to_i.zero?
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000506
Bram Moolenaar9964e462007-05-05 17:54:07 +0000507 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" ]
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000512
Bram Moolenaar9964e462007-05-05 17:54:07 +0000513 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
Bram Moolenaar9964e462007-05-05 17:54:07 +0000548 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')
Bram Moolenaarc236c162008-07-13 17:41:49 +0000554 return [] if allow_rails.to_i.zero? || rails_loaded.to_i.zero?
Bram Moolenaar9964e462007-05-05 17:54:07 +0000555
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)
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200569 ret = sel.reject{|x|x.nil?}.uniq
570 ret = ret.grep(/^#{Regexp.quote(msg)}/) if msg != nil
571 ret
Bram Moolenaar9964e462007-05-05 17:54:07 +0000572 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')
Bram Moolenaarc236c162008-07-13 17:41:49 +0000577 return [] if allow_rails.to_i.zero? || rails_loaded.to_i.zero?
Bram Moolenaar9964e462007-05-05 17:54:07 +0000578
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
Bram Moolenaard09091d2019-01-17 16:07:22 +0100605 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
Bram Moolenaar9964e462007-05-05 17:54:07 +0000612 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")
Bram Moolenaarc236c162008-07-13 17:41:49 +0000625 if loading_allowed.to_i == 1
Bram Moolenaar9964e462007-05-05 17:54:07 +0000626 load_requires
627 load_rails
628 end
629
Bram Moolenaar89bcfda2016-08-30 23:26:57 +0200630 want_gems = VIM::evaluate("get(g:, 'rubycomplete_load_gemfile')")
631 load_gems unless want_gems.to_i.zero?
Bram Moolenaar89bcfda2016-08-30 23:26:57 +0200632
Bram Moolenaar9964e462007-05-05 17:54:07 +0000633 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
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000651 receiver = $1
Bram Moolenaar9964e462007-05-05 17:54:07 +0000652 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
Bram Moolenaar1d689522010-05-28 20:54:39 +0200680 when /^(((::)?[A-Z][^:.\(]*)+?)::?([^:.]*)$/ # Constant or class methods
Bram Moolenaar9964e462007-05-05 17:54:07 +0000681 receiver = $1
682 message = Regexp.quote($4)
683 dprint "const or cls 2 [recv: \'%s\', msg: \'%s\']" % [ receiver, message ]
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000684 load_buffer_class( receiver )
Bram Moolenaard09091d2019-01-17 16:07:22 +0100685 load_buffer_module( receiver )
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000686 begin
Bram Moolenaar9964e462007-05-05 17:54:07 +0000687 classes = eval("#{receiver}.constants")
688 #methods = eval("#{receiver}.methods")
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000689 rescue Exception
Bram Moolenaar9964e462007-05-05 17:54:07 +0000690 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
Bram Moolenaar1d689522010-05-28 20:54:39 +0200715 when /^((\.?[^.]+)+?)\.([^.]*)$/ # variable
Bram Moolenaar9964e462007-05-05 17:54:07 +0000716 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
Bram Moolenaar89bcfda2016-08-30 23:26:57 +0200724
725 invalid_vartype = ['', "gets"]
726 if !invalid_vartype.include?(vartype)
Bram Moolenaar9964e462007-05-05 17:54:07 +0000727 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
Bram Moolenaar89bcfda2016-08-30 23:26:57 +0200754 variables += add_rails_columns( "#{vartype}" ) if vartype && !invalid_vartype.include?(vartype)
Bram Moolenaar9964e462007-05-05 17:54:07 +0000755
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
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000783 end
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000784 end
Bram Moolenaar9964e462007-05-05 17:54:07 +0000785
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
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000804 end
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000805
Bram Moolenaar9964e462007-05-05 17:54:07 +0000806 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)
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000811
Bram Moolenaar9964e462007-05-05 17:54:07 +0000812 variables = clean_sel( variables, message )
813 classes = clean_sel( classes, message ) - ["VimRubyCompletion"]
814 constants = clean_sel( constants, message )
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000815
Bram Moolenaar9964e462007-05-05 17:54:07 +0000816 valid = []
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200817 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' } }
Bram Moolenaar9964e462007-05-05 17:54:07 +0000821 valid.sort! { |x,y| x[:name] <=> y[:name] }
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000822
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000823 outp = ""
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000824
Bram Moolenaar9964e462007-05-05 17:54:07 +0000825 rg = 0..valid.length
826 rg.step(150) do |x|
827 stpos = 0+x
828 enpos = 150+x
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200829 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)} }
Bram Moolenaar9964e462007-05-05 17:54:07 +0000830 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
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000839# }}} ruby completion
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000840RUBYEOF
841endfunction
842
Bram Moolenaar9964e462007-05-05 17:54:07 +0000843let s:rubycomplete_rails_loaded = 0
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000844
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000845call s:DefRuby()
Bram Moolenaar9964e462007-05-05 17:54:07 +0000846"}}} ruby-side code
847
Bram Moolenaar9964e462007-05-05 17:54:07 +0000848" vim:tw=78:sw=4:ts=8:et:fdm=marker:ft=vim:norl: