blob: e8a1879668979818ae313b7d8ac6ac2e3b286b91 [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>
Bram Moolenaar2ed639a2019-12-09 23:11:18 +01006" Last Change: 2019 Feb 25
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 Moolenaar2ed639a2019-12-09 23:11:18 +010056" {{{ regex patterns
57
58" Regex that defines the start-match for the 'end' keyword.
59let s:end_start_regex =
60 \ '\C\%(^\s*\|[=,*/%+\-|;{]\|<<\|>>\|:\s\)\s*\zs' .
61 \ '\<\%(module\|class\|if\|for\|while\|until\|case\|unless\|begin' .
62 \ '\|\%(\K\k*[!?]\?\s\+\)\=def\):\@!\>' .
63 \ '\|\%(^\|[^.:@$]\)\@<=\<do:\@!\>'
64
65" Regex that defines the middle-match for the 'end' keyword.
66let s:end_middle_regex = '\<\%(ensure\|else\|\%(\%(^\|;\)\s*\)\@<=\<rescue:\@!\>\|when\|elsif\):\@!\>'
67
68" Regex that defines the end-match for the 'end' keyword.
69let s:end_end_regex = '\%(^\|[^.:@$]\)\@<=\<end:\@!\>'
70
71" }}} regex patterns
72
Bram Moolenaareb3593b2006-04-22 22:33:57 +000073" {{{ vim-side support functions
Bram Moolenaar9964e462007-05-05 17:54:07 +000074let s:rubycomplete_debug = 0
75
Bram Moolenaar9964e462007-05-05 17:54:07 +000076function! s:dprint(msg)
77 if s:rubycomplete_debug == 1
78 echom a:msg
79 endif
80endfunction
81
82function! s:GetBufferRubyModule(name, ...)
83 if a:0 == 1
84 let [snum,enum] = s:GetBufferRubyEntity(a:name, "module", a:1)
85 else
86 let [snum,enum] = s:GetBufferRubyEntity(a:name, "module")
87 endif
Bram Moolenaarfc1421e2006-04-20 22:17:20 +000088 return snum . '..' . enum
89endfunction
90
Bram Moolenaar9964e462007-05-05 17:54:07 +000091function! s:GetBufferRubyClass(name, ...)
92 if a:0 >= 1
93 let [snum,enum] = s:GetBufferRubyEntity(a:name, "class", a:1)
94 else
95 let [snum,enum] = s:GetBufferRubyEntity(a:name, "class")
96 endif
Bram Moolenaarfc1421e2006-04-20 22:17:20 +000097 return snum . '..' . enum
98endfunction
99
Bram Moolenaar9964e462007-05-05 17:54:07 +0000100function! s:GetBufferRubySingletonMethods(name)
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000101endfunction
102
Bram Moolenaar9964e462007-05-05 17:54:07 +0000103function! s:GetBufferRubyEntity( name, type, ... )
104 let lastpos = getpos(".")
105 let lastline = lastpos
106 if (a:0 >= 1)
107 let lastline = [ 0, a:1, 0, 0 ]
108 call cursor( a:1, 0 )
109 endif
110
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000111 let stopline = 1
Bram Moolenaar9964e462007-05-05 17:54:07 +0000112
Bram Moolenaar89bcfda2016-08-30 23:26:57 +0200113 let crex = '^\s*\<' . a:type . '\>\s*\<' . escape(a:name, '*') . '\>\s*\(<\s*.*\s*\)\?'
Bram Moolenaar9964e462007-05-05 17:54:07 +0000114 let [lnum,lcol] = searchpos( crex, 'w' )
115 "let [lnum,lcol] = searchpairpos( crex . '\zs', '', '\(end\|}\)', 'w' )
116
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000117 if lnum == 0 && lcol == 0
Bram Moolenaar9964e462007-05-05 17:54:07 +0000118 call cursor(lastpos[1], lastpos[2])
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000119 return [0,0]
120 endif
121
Bram Moolenaar9964e462007-05-05 17:54:07 +0000122 let curpos = getpos(".")
Bram Moolenaar2ed639a2019-12-09 23:11:18 +0100123 let [enum,ecol] = searchpairpos( s:end_start_regex, s:end_middle_regex, s:end_end_regex, 'W' )
Bram Moolenaar9964e462007-05-05 17:54:07 +0000124 call cursor(lastpos[1], lastpos[2])
125
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000126 if lnum > enum
Bram Moolenaar9964e462007-05-05 17:54:07 +0000127 return [0,0]
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000128 endif
129 " we found a the class def
130 return [lnum,enum]
131endfunction
132
Bram Moolenaar9964e462007-05-05 17:54:07 +0000133function! s:IsInClassDef()
134 return s:IsPosInClassDef( line('.') )
135endfunction
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000136
Bram Moolenaar9964e462007-05-05 17:54:07 +0000137function! s:IsPosInClassDef(pos)
138 let [snum,enum] = s:GetBufferRubyEntity( '.*', "class" )
139 let ret = 'nil'
140
141 if snum < a:pos && a:pos < enum
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000142 let ret = snum . '..' . enum
143 endif
144
145 return ret
146endfunction
147
Bram Moolenaar2ed639a2019-12-09 23:11:18 +0100148function! s:IsInComment(pos)
149 let stack = synstack(a:pos[0], a:pos[1])
150 if !empty(stack)
151 return synIDattr(stack[0], 'name') =~ 'ruby\%(.*Comment\|Documentation\)'
152 else
153 return 0
154 endif
155endfunction
156
Bram Moolenaar9964e462007-05-05 17:54:07 +0000157function! s:GetRubyVarType(v)
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000158 let stopline = 1
159 let vtp = ''
Bram Moolenaar2ed639a2019-12-09 23:11:18 +0100160 let curpos = getpos('.')
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200161 let sstr = '^\s*#\s*@var\s*'.escape(a:v, '*').'\>\s\+[^ \t]\+\s*$'
Bram Moolenaar9964e462007-05-05 17:54:07 +0000162 let [lnum,lcol] = searchpos(sstr,'nb',stopline)
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000163 if lnum != 0 && lcol != 0
Bram Moolenaar2ed639a2019-12-09 23:11:18 +0100164 call setpos('.',curpos)
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000165 let str = getline(lnum)
Bram Moolenaar9964e462007-05-05 17:54:07 +0000166 let vtp = substitute(str,sstr,'\1','')
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000167 return vtp
168 endif
Bram Moolenaar2ed639a2019-12-09 23:11:18 +0100169 call setpos('.',curpos)
Bram Moolenaar9964e462007-05-05 17:54:07 +0000170 let ctors = '\(now\|new\|open\|get_instance'
171 if exists('g:rubycomplete_rails') && g:rubycomplete_rails == 1 && s:rubycomplete_rails_loaded == 1
172 let ctors = ctors.'\|find\|create'
Bram Moolenaar551dbcc2006-04-25 22:13:59 +0000173 else
Bram Moolenaar551dbcc2006-04-25 22:13:59 +0000174 endif
Bram Moolenaar9964e462007-05-05 17:54:07 +0000175 let ctors = ctors.'\)'
Bram Moolenaar551dbcc2006-04-25 22:13:59 +0000176
Bram Moolenaar9964e462007-05-05 17:54:07 +0000177 let fstr = '=\s*\([^ \t]\+.' . ctors .'\>\|[\[{"''/]\|%[xwQqr][(\[{@]\|[A-Za-z0-9@:\-()\.]\+...\?\|lambda\|&\)'
Bram Moolenaar89bcfda2016-08-30 23:26:57 +0200178 let sstr = ''.escape(a:v, '*').'\>\s*[+\-*/]*'.fstr
Bram Moolenaar2ed639a2019-12-09 23:11:18 +0100179 let pos = searchpos(sstr,'bW')
180 while pos != [0,0] && s:IsInComment(pos)
181 let pos = searchpos(sstr,'bW')
182 endwhile
183 if pos != [0,0]
184 let [lnum, col] = pos
185 let str = matchstr(getline(lnum),fstr,col)
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000186 let str = substitute(str,'^=\s*','','')
Bram Moolenaar9964e462007-05-05 17:54:07 +0000187
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000188 call setpos('.',pos)
Bram Moolenaar9964e462007-05-05 17:54:07 +0000189 if str == '"' || str == '''' || stridx(tolower(str), '%q[') != -1
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000190 return 'String'
Bram Moolenaar9964e462007-05-05 17:54:07 +0000191 elseif str == '[' || stridx(str, '%w[') != -1
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000192 return 'Array'
193 elseif str == '{'
194 return 'Hash'
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000195 elseif str == '/' || str == '%r{'
196 return 'Regexp'
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000197 elseif strlen(str) >= 4 && stridx(str,'..') != -1
198 return 'Range'
Bram Moolenaar9964e462007-05-05 17:54:07 +0000199 elseif stridx(str, 'lambda') != -1 || str == '&'
200 return 'Proc'
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000201 elseif strlen(str) > 4
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000202 let l = stridx(str,'.')
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000203 return str[0:l-1]
204 end
205 return ''
206 endif
Bram Moolenaar2ed639a2019-12-09 23:11:18 +0100207 call setpos('.',curpos)
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000208 return ''
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000209endfunction
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000210
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000211"}}} vim-side support functions
212
Bram Moolenaar9964e462007-05-05 17:54:07 +0000213"{{{ vim-side completion function
214function! rubycomplete#Init()
215 execute "ruby VimRubyCompletion.preload_rails"
216endfunction
217
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000218function! rubycomplete#Complete(findstart, base)
219 "findstart = 1 when we need to get the text length
220 if a:findstart
221 let line = getline('.')
222 let idx = col('.')
223 while idx > 0
224 let idx -= 1
225 let c = line[idx-1]
226 if c =~ '\w'
227 continue
228 elseif ! c =~ '\.'
Bram Moolenaar45758762016-10-12 23:08:06 +0200229 let idx = -1
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000230 break
231 else
232 break
233 endif
234 endwhile
235
236 return idx
237 "findstart = 0 when we need to return the list of completions
238 else
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000239 let g:rubycomplete_completions = []
Bram Moolenaar9964e462007-05-05 17:54:07 +0000240 execute "ruby VimRubyCompletion.get_completions('" . a:base . "')"
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000241 return g:rubycomplete_completions
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000242 endif
243endfunction
Bram Moolenaar9964e462007-05-05 17:54:07 +0000244"}}} vim-side completion function
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000245
Bram Moolenaar9964e462007-05-05 17:54:07 +0000246"{{{ ruby-side code
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000247function! s:DefRuby()
248ruby << RUBYEOF
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000249# {{{ ruby completion
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000250
Bram Moolenaar9964e462007-05-05 17:54:07 +0000251begin
252 require 'rubygems' # let's assume this is safe...?
253rescue Exception
254 #ignore?
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000255end
Bram Moolenaar9964e462007-05-05 17:54:07 +0000256class VimRubyCompletion
257# {{{ constants
258 @@debug = false
259 @@ReservedWords = [
260 "BEGIN", "END",
261 "alias", "and",
262 "begin", "break",
263 "case", "class",
264 "def", "defined", "do",
265 "else", "elsif", "end", "ensure",
266 "false", "for",
267 "if", "in",
268 "module",
269 "next", "nil", "not",
270 "or",
271 "redo", "rescue", "retry", "return",
272 "self", "super",
273 "then", "true",
274 "undef", "unless", "until",
275 "when", "while",
276 "yield",
277 ]
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000278
Bram Moolenaar9964e462007-05-05 17:54:07 +0000279 @@Operators = [ "%", "&", "*", "**", "+", "-", "/",
280 "<", "<<", "<=", "<=>", "==", "===", "=~", ">", ">=", ">>",
281 "[]", "[]=", "^", ]
282# }}} constants
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000283
Bram Moolenaar9964e462007-05-05 17:54:07 +0000284# {{{ buffer analysis magic
285 def load_requires
Bram Moolenaard09091d2019-01-17 16:07:22 +0100286
287 custom_paths = VIM::evaluate("get(g:, 'rubycomplete_load_paths', [])")
288
289 if !custom_paths.empty?
290 $LOAD_PATH.concat(custom_paths).uniq!
291 end
292
Bram Moolenaar9964e462007-05-05 17:54:07 +0000293 buf = VIM::Buffer.current
294 enum = buf.line_number
295 nums = Range.new( 1, enum )
296 nums.each do |x|
Bram Moolenaard09091d2019-01-17 16:07:22 +0100297
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000298 ln = buf[x]
Bram Moolenaar9964e462007-05-05 17:54:07 +0000299 begin
Bram Moolenaard09091d2019-01-17 16:07:22 +0100300 if /.*require_relative\s*(.*)$/.match( ln )
301 eval( "require %s" % File.expand_path($1) )
302 elsif /.*require\s*(["'].*?["'])/.match( ln )
303 eval( "require %s" % $1 )
304 end
305 rescue Exception => e
306 dprint e.inspect
Bram Moolenaar9964e462007-05-05 17:54:07 +0000307 end
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000308 end
309 end
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000310
Bram Moolenaar89bcfda2016-08-30 23:26:57 +0200311 def load_gems
312 fpath = VIM::evaluate("get(g:, 'rubycomplete_gemfile_path', 'Gemfile')")
313 return unless File.file?(fpath) && File.readable?(fpath)
314 want_bundler = VIM::evaluate("get(g:, 'rubycomplete_use_bundler')")
315 parse_file = !want_bundler
316 begin
317 require 'bundler'
318 Bundler.setup
319 Bundler.require
320 rescue Exception
321 parse_file = true
322 end
323 if parse_file
324 File.new(fpath).each_line do |line|
325 begin
326 require $1 if /\s*gem\s*['"]([^'"]+)/.match(line)
327 rescue Exception
328 end
329 end
330 end
331 end
332
Bram Moolenaar9964e462007-05-05 17:54:07 +0000333 def load_buffer_class(name)
334 dprint "load_buffer_class(%s) START" % name
335 classdef = get_buffer_entity(name, 's:GetBufferRubyClass("%s")')
336 return if classdef == nil
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000337
Bram Moolenaar9964e462007-05-05 17:54:07 +0000338 pare = /^\s*class\s*(.*)\s*<\s*(.*)\s*\n/.match( classdef )
339 load_buffer_class( $2 ) if pare != nil && $2 != name # load parent class if needed
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000340
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200341 mixre = /.*\n\s*(include|prepend)\s*(.*)\s*\n/.match( classdef )
Bram Moolenaar9964e462007-05-05 17:54:07 +0000342 load_buffer_module( $2 ) if mixre != nil && $2 != name # load mixins if needed
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000343
Bram Moolenaar551dbcc2006-04-25 22:13:59 +0000344 begin
Bram Moolenaar9964e462007-05-05 17:54:07 +0000345 eval classdef
346 rescue Exception
347 VIM::evaluate( "s:ErrMsg( 'Problem loading class \"%s\", was it already completed?' )" % name )
348 end
349 dprint "load_buffer_class(%s) END" % name
350 end
351
352 def load_buffer_module(name)
353 dprint "load_buffer_module(%s) START" % name
354 classdef = get_buffer_entity(name, 's:GetBufferRubyModule("%s")')
355 return if classdef == nil
356
357 begin
358 eval classdef
359 rescue Exception
360 VIM::evaluate( "s:ErrMsg( 'Problem loading module \"%s\", was it already completed?' )" % name )
361 end
362 dprint "load_buffer_module(%s) END" % name
363 end
364
365 def get_buffer_entity(name, vimfun)
366 loading_allowed = VIM::evaluate("exists('g:rubycomplete_buffer_loading') && g:rubycomplete_buffer_loading")
Bram Moolenaarc236c162008-07-13 17:41:49 +0000367 return nil if loading_allowed.to_i.zero?
Bram Moolenaar9964e462007-05-05 17:54:07 +0000368 return nil if /(\"|\')+/.match( name )
369 buf = VIM::Buffer.current
370 nums = eval( VIM::evaluate( vimfun % name ) )
371 return nil if nums == nil
372 return nil if nums.min == nums.max && nums.min == 0
373
374 dprint "get_buffer_entity START"
375 visited = []
376 clscnt = 0
377 bufname = VIM::Buffer.current.name
378 classdef = ""
379 cur_line = VIM::Buffer.current.line_number
380 while (nums != nil && !(nums.min == 0 && nums.max == 0) )
381 dprint "visited: %s" % visited.to_s
382 break if visited.index( nums )
383 visited << nums
384
385 nums.each do |x|
386 if x != cur_line
387 next if x == 0
388 ln = buf[x]
Bram Moolenaard09091d2019-01-17 16:07:22 +0100389 is_const = false
390 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)
391 clscnt += 1 if /class|module/.match($1)
392 # We must make sure to load each constant only once to avoid errors
393 if is_const
394 ln.gsub!(/\s*?[|]{0,2}=\s*?/, '||=')
395 end
Bram Moolenaar1d689522010-05-28 20:54:39 +0200396 #dprint "\$1$1
Bram Moolenaar9964e462007-05-05 17:54:07 +0000397 classdef += "%s\n" % ln
398 classdef += "end\n" if /def\s+/.match(ln)
399 dprint ln
400 end
401 end
402 end
403
404 nm = "%s(::.*)*\", %s, \"" % [ name, nums.last ]
405 nums = eval( VIM::evaluate( vimfun % nm ) )
406 dprint "nm: \"%s\"" % nm
407 dprint "vimfun: %s" % (vimfun % nm)
408 dprint "got nums: %s" % nums.to_s
409 end
410 if classdef.length > 1
411 classdef += "end\n"*clscnt
412 # classdef = "class %s\n%s\nend\n" % [ bufname.gsub( /\/|\\/, "_" ), classdef ]
413 end
414
415 dprint "get_buffer_entity END"
416 dprint "classdef====start"
417 lns = classdef.split( "\n" )
418 lns.each { |x| dprint x }
419 dprint "classdef====end"
420 return classdef
421 end
422
423 def get_var_type( receiver )
424 if /(\"|\')+/.match( receiver )
425 "String"
426 else
427 VIM::evaluate("s:GetRubyVarType('%s')" % receiver)
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000428 end
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000429 end
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000430
Bram Moolenaar9964e462007-05-05 17:54:07 +0000431 def dprint( txt )
432 print txt if @@debug
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000433 end
434
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200435 def escape_vim_singlequote_string(str)
436 str.to_s.gsub(/'/,"\\'")
437 end
438
Bram Moolenaar9964e462007-05-05 17:54:07 +0000439 def get_buffer_entity_list( type )
440 # this will be a little expensive.
441 loading_allowed = VIM::evaluate("exists('g:rubycomplete_buffer_loading') && g:rubycomplete_buffer_loading")
442 allow_aggressive_load = VIM::evaluate("exists('g:rubycomplete_classes_in_global') && g:rubycomplete_classes_in_global")
Bram Moolenaarc236c162008-07-13 17:41:49 +0000443 return [] if allow_aggressive_load.to_i.zero? || loading_allowed.to_i.zero?
Bram Moolenaar9964e462007-05-05 17:54:07 +0000444
445 buf = VIM::Buffer.current
446 eob = buf.length
447 ret = []
448 rg = 1..eob
449 re = eval( "/^\s*%s\s*([A-Za-z0-9_:-]*)(\s*<\s*([A-Za-z0-9_:-]*))?\s*/" % type )
450
451 rg.each do |x|
452 if re.match( buf[x] )
453 next if type == "def" && eval( VIM::evaluate("s:IsPosInClassDef(%s)" % x) ) != nil
454 ret.push $1
455 end
456 end
457
458 return ret
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000459 end
460
Bram Moolenaar9964e462007-05-05 17:54:07 +0000461 def get_buffer_modules
462 return get_buffer_entity_list( "modules" )
463 end
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000464
Bram Moolenaar9964e462007-05-05 17:54:07 +0000465 def get_buffer_methods
466 return get_buffer_entity_list( "def" )
467 end
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000468
Bram Moolenaar9964e462007-05-05 17:54:07 +0000469 def get_buffer_classes
470 return get_buffer_entity_list( "class" )
471 end
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000472
Bram Moolenaar9964e462007-05-05 17:54:07 +0000473 def load_rails
474 allow_rails = VIM::evaluate("exists('g:rubycomplete_rails') && g:rubycomplete_rails")
Bram Moolenaarc236c162008-07-13 17:41:49 +0000475 return if allow_rails.to_i.zero?
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000476
Bram Moolenaar9964e462007-05-05 17:54:07 +0000477 buf_path = VIM::evaluate('expand("%:p")')
478 file_name = VIM::evaluate('expand("%:t")')
479 vim_dir = VIM::evaluate('getcwd()')
480 file_dir = buf_path.gsub( file_name, '' )
481 file_dir.gsub!( /\\/, "/" )
482 vim_dir.gsub!( /\\/, "/" )
483 vim_dir << "/"
484 dirs = [ vim_dir, file_dir ]
485 sdirs = [ "", "./", "../", "../../", "../../../", "../../../../" ]
486 rails_base = nil
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000487
Bram Moolenaar9964e462007-05-05 17:54:07 +0000488 dirs.each do |dir|
489 sdirs.each do |sub|
490 trail = "%s%s" % [ dir, sub ]
491 tcfg = "%sconfig" % trail
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000492
Bram Moolenaar9964e462007-05-05 17:54:07 +0000493 if File.exists?( tcfg )
494 rails_base = trail
495 break
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000496 end
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000497 end
Bram Moolenaar9964e462007-05-05 17:54:07 +0000498 break if rails_base
499 end
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000500
Bram Moolenaar9964e462007-05-05 17:54:07 +0000501 return if rails_base == nil
502 $:.push rails_base unless $:.index( rails_base )
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000503
Bram Moolenaar9964e462007-05-05 17:54:07 +0000504 rails_config = rails_base + "config/"
505 rails_lib = rails_base + "lib/"
506 $:.push rails_config unless $:.index( rails_config )
507 $:.push rails_lib unless $:.index( rails_lib )
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000508
Bram Moolenaar9964e462007-05-05 17:54:07 +0000509 bootfile = rails_config + "boot.rb"
510 envfile = rails_config + "environment.rb"
511 if File.exists?( bootfile ) && File.exists?( envfile )
512 begin
513 require bootfile
514 require envfile
515 begin
516 require 'console_app'
517 require 'console_with_helpers'
518 rescue Exception
519 dprint "Rails 1.1+ Error %s" % $!
520 # assume 1.0
521 end
522 #eval( "Rails::Initializer.run" ) #not necessary?
523 VIM::command('let s:rubycomplete_rails_loaded = 1')
524 dprint "rails loaded"
525 rescue Exception
526 dprint "Rails Error %s" % $!
527 VIM::evaluate( "s:ErrMsg('Error loading rails environment')" )
528 end
529 end
530 end
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000531
Bram Moolenaar9964e462007-05-05 17:54:07 +0000532 def get_rails_helpers
533 allow_rails = VIM::evaluate("exists('g:rubycomplete_rails') && g:rubycomplete_rails")
534 rails_loaded = VIM::evaluate('s:rubycomplete_rails_loaded')
Bram Moolenaarc236c162008-07-13 17:41:49 +0000535 return [] if allow_rails.to_i.zero? || rails_loaded.to_i.zero?
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000536
Bram Moolenaar9964e462007-05-05 17:54:07 +0000537 buf_path = VIM::evaluate('expand("%:p")')
538 buf_path.gsub!( /\\/, "/" )
539 path_elm = buf_path.split( "/" )
540 dprint "buf_path: %s" % buf_path
541 types = [ "app", "db", "lib", "test", "components", "script" ]
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000542
Bram Moolenaar9964e462007-05-05 17:54:07 +0000543 i = nil
544 ret = []
545 type = nil
546 types.each do |t|
547 i = path_elm.index( t )
548 break if i
549 end
550 type = path_elm[i]
551 type.downcase!
552
553 dprint "type: %s" % type
554 case type
555 when "app"
556 i += 1
557 subtype = path_elm[i]
558 subtype.downcase!
559
560 dprint "subtype: %s" % subtype
561 case subtype
562 when "views"
563 ret += ActionView::Base.instance_methods
564 ret += ActionView::Base.methods
565 when "controllers"
566 ret += ActionController::Base.instance_methods
567 ret += ActionController::Base.methods
568 when "models"
569 ret += ActiveRecord::Base.instance_methods
570 ret += ActiveRecord::Base.methods
571 end
572
573 when "db"
574 ret += ActiveRecord::ConnectionAdapters::SchemaStatements.instance_methods
575 ret += ActiveRecord::ConnectionAdapters::SchemaStatements.methods
576 end
577
Bram Moolenaar9964e462007-05-05 17:54:07 +0000578 return ret
579 end
580
581 def add_rails_columns( cls )
582 allow_rails = VIM::evaluate("exists('g:rubycomplete_rails') && g:rubycomplete_rails")
583 rails_loaded = VIM::evaluate('s:rubycomplete_rails_loaded')
Bram Moolenaarc236c162008-07-13 17:41:49 +0000584 return [] if allow_rails.to_i.zero? || rails_loaded.to_i.zero?
Bram Moolenaar9964e462007-05-05 17:54:07 +0000585
586 begin
587 eval( "#{cls}.establish_connection" )
588 return [] unless eval( "#{cls}.ancestors.include?(ActiveRecord::Base).to_s" )
589 col = eval( "#{cls}.column_names" )
590 return col if col
591 rescue
592 dprint "add_rails_columns err: (cls: %s) %s" % [ cls, $! ]
593 return []
594 end
595 return []
596 end
597
598 def clean_sel(sel, msg)
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200599 ret = sel.reject{|x|x.nil?}.uniq
600 ret = ret.grep(/^#{Regexp.quote(msg)}/) if msg != nil
601 ret
Bram Moolenaar9964e462007-05-05 17:54:07 +0000602 end
603
604 def get_rails_view_methods
605 allow_rails = VIM::evaluate("exists('g:rubycomplete_rails') && g:rubycomplete_rails")
606 rails_loaded = VIM::evaluate('s:rubycomplete_rails_loaded')
Bram Moolenaarc236c162008-07-13 17:41:49 +0000607 return [] if allow_rails.to_i.zero? || rails_loaded.to_i.zero?
Bram Moolenaar9964e462007-05-05 17:54:07 +0000608
609 buf_path = VIM::evaluate('expand("%:p")')
610 buf_path.gsub!( /\\/, "/" )
611 pelm = buf_path.split( "/" )
612 idx = pelm.index( "views" )
613
614 return [] unless idx
615 idx += 1
616
617 clspl = pelm[idx].camelize.pluralize
618 cls = clspl.singularize
619
620 ret = []
621 begin
622 ret += eval( "#{cls}.instance_methods" )
623 ret += eval( "#{clspl}Helper.instance_methods" )
624 rescue Exception
625 dprint "Error: Unable to load rails view helpers for %s: %s" % [ cls, $! ]
626 end
627
628 return ret
629 end
630# }}} buffer analysis magic
631
632# {{{ main completion code
633 def self.preload_rails
634 a = VimRubyCompletion.new
Bram Moolenaard09091d2019-01-17 16:07:22 +0100635 if VIM::evaluate("has('nvim')") == 0
636 require 'thread'
637 Thread.new(a) do |b|
638 begin
639 b.load_rails
640 rescue
641 end
Bram Moolenaar9964e462007-05-05 17:54:07 +0000642 end
643 end
644 a.load_rails
645 rescue
646 end
647
648 def self.get_completions(base)
649 b = VimRubyCompletion.new
650 b.get_completions base
651 end
652
653 def get_completions(base)
654 loading_allowed = VIM::evaluate("exists('g:rubycomplete_buffer_loading') && g:rubycomplete_buffer_loading")
Bram Moolenaarc236c162008-07-13 17:41:49 +0000655 if loading_allowed.to_i == 1
Bram Moolenaar9964e462007-05-05 17:54:07 +0000656 load_requires
657 load_rails
658 end
659
Bram Moolenaar89bcfda2016-08-30 23:26:57 +0200660 want_gems = VIM::evaluate("get(g:, 'rubycomplete_load_gemfile')")
661 load_gems unless want_gems.to_i.zero?
Bram Moolenaar89bcfda2016-08-30 23:26:57 +0200662
Bram Moolenaar9964e462007-05-05 17:54:07 +0000663 input = VIM::Buffer.current.line
664 cpos = VIM::Window.current.cursor[1] - 1
665 input = input[0..cpos]
666 input += base
667 input.sub!(/.*[ \t\n\"\\'`><=;|&{(]/, '') # Readline.basic_word_break_characters
668 input.sub!(/self\./, '')
669 input.sub!(/.*((\.\.[\[(]?)|([\[(]))/, '')
670
671 dprint 'input %s' % input
672 message = nil
673 receiver = nil
674 methods = []
675 variables = []
676 classes = []
677 constants = []
678
679 case input
680 when /^(\/[^\/]*\/)\.([^.]*)$/ # Regexp
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000681 receiver = $1
Bram Moolenaar9964e462007-05-05 17:54:07 +0000682 message = Regexp.quote($2)
683 methods = Regexp.instance_methods(true)
684
685 when /^([^\]]*\])\.([^.]*)$/ # Array
686 receiver = $1
687 message = Regexp.quote($2)
688 methods = Array.instance_methods(true)
689
690 when /^([^\}]*\})\.([^.]*)$/ # Proc or Hash
691 receiver = $1
692 message = Regexp.quote($2)
693 methods = Proc.instance_methods(true) | Hash.instance_methods(true)
694
695 when /^(:[^:.]*)$/ # Symbol
696 dprint "symbol"
697 if Symbol.respond_to?(:all_symbols)
698 receiver = $1
699 message = $1.sub( /:/, '' )
700 methods = Symbol.all_symbols.collect{|s| s.id2name}
701 methods.delete_if { |c| c.match( /'/ ) }
702 end
703
Bram Moolenaar2ed639a2019-12-09 23:11:18 +0100704 when /^::([A-Z][^:\.\(]*)?$/ # Absolute Constant or class methods
Bram Moolenaar9964e462007-05-05 17:54:07 +0000705 dprint "const or cls"
706 receiver = $1
Bram Moolenaar2ed639a2019-12-09 23:11:18 +0100707 methods = Object.constants.collect{ |c| c.to_s }.grep(/^#{receiver}/)
Bram Moolenaar9964e462007-05-05 17:54:07 +0000708
Bram Moolenaar1d689522010-05-28 20:54:39 +0200709 when /^(((::)?[A-Z][^:.\(]*)+?)::?([^:.]*)$/ # Constant or class methods
Bram Moolenaar9964e462007-05-05 17:54:07 +0000710 receiver = $1
711 message = Regexp.quote($4)
712 dprint "const or cls 2 [recv: \'%s\', msg: \'%s\']" % [ receiver, message ]
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000713 load_buffer_class( receiver )
Bram Moolenaard09091d2019-01-17 16:07:22 +0100714 load_buffer_module( receiver )
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000715 begin
Bram Moolenaar2ed639a2019-12-09 23:11:18 +0100716 constants = eval("#{receiver}.constants").collect{ |c| c.to_s }.grep(/^#{message}/)
717 methods = eval("#{receiver}.methods").collect{ |m| m.to_s }.grep(/^#{message}/)
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000718 rescue Exception
Bram Moolenaar9964e462007-05-05 17:54:07 +0000719 dprint "exception: %s" % $!
Bram Moolenaar2ed639a2019-12-09 23:11:18 +0100720 constants = []
Bram Moolenaar9964e462007-05-05 17:54:07 +0000721 methods = []
722 end
Bram Moolenaar9964e462007-05-05 17:54:07 +0000723
724 when /^(:[^:.]+)\.([^.]*)$/ # Symbol
725 dprint "symbol"
726 receiver = $1
727 message = Regexp.quote($2)
728 methods = Symbol.instance_methods(true)
729
730 when /^([0-9_]+(\.[0-9_]+)?(e[0-9]+)?)\.([^.]*)$/ # Numeric
731 dprint "numeric"
732 receiver = $1
733 message = Regexp.quote($4)
734 begin
735 methods = eval(receiver).methods
736 rescue Exception
737 methods = []
738 end
739
740 when /^(\$[^.]*)$/ #global
741 dprint "global"
742 methods = global_variables.grep(Regexp.new(Regexp.quote($1)))
743
Bram Moolenaar1d689522010-05-28 20:54:39 +0200744 when /^((\.?[^.]+)+?)\.([^.]*)$/ # variable
Bram Moolenaar9964e462007-05-05 17:54:07 +0000745 dprint "variable"
746 receiver = $1
747 message = Regexp.quote($3)
748 load_buffer_class( receiver )
749
750 cv = eval("self.class.constants")
751 vartype = get_var_type( receiver )
752 dprint "vartype: %s" % vartype
Bram Moolenaar89bcfda2016-08-30 23:26:57 +0200753
754 invalid_vartype = ['', "gets"]
755 if !invalid_vartype.include?(vartype)
Bram Moolenaar9964e462007-05-05 17:54:07 +0000756 load_buffer_class( vartype )
757
758 begin
759 methods = eval("#{vartype}.instance_methods")
760 variables = eval("#{vartype}.instance_variables")
761 rescue Exception
762 dprint "load_buffer_class err: %s" % $!
763 end
764 elsif (cv).include?(receiver)
765 # foo.func and foo is local var.
766 methods = eval("#{receiver}.methods")
767 vartype = receiver
768 elsif /^[A-Z]/ =~ receiver and /\./ !~ receiver
769 vartype = receiver
770 # Foo::Bar.func
771 begin
772 methods = eval("#{receiver}.methods")
773 rescue Exception
774 end
775 else
776 # func1.func2
777 ObjectSpace.each_object(Module){|m|
778 next if m.name != "IRB::Context" and
779 /^(IRB|SLex|RubyLex|RubyToken)/ =~ m.name
780 methods.concat m.instance_methods(false)
781 }
782 end
Bram Moolenaar89bcfda2016-08-30 23:26:57 +0200783 variables += add_rails_columns( "#{vartype}" ) if vartype && !invalid_vartype.include?(vartype)
Bram Moolenaar9964e462007-05-05 17:54:07 +0000784
785 when /^\(?\s*[A-Za-z0-9:^@.%\/+*\(\)]+\.\.\.?[A-Za-z0-9:^@.%\/+*\(\)]+\s*\)?\.([^.]*)/
786 message = $1
787 methods = Range.instance_methods(true)
788
789 when /^\.([^.]*)$/ # unknown(maybe String)
790 message = Regexp.quote($1)
791 methods = String.instance_methods(true)
792
793 else
794 dprint "default/other"
795 inclass = eval( VIM::evaluate("s:IsInClassDef()") )
796
797 if inclass != nil
798 dprint "inclass"
799 classdef = "%s\n" % VIM::Buffer.current[ inclass.min ]
800 found = /^\s*class\s*([A-Za-z0-9_-]*)(\s*<\s*([A-Za-z0-9_:-]*))?\s*\n$/.match( classdef )
801
802 if found != nil
803 receiver = $1
804 message = input
805 load_buffer_class( receiver )
806 begin
807 methods = eval( "#{receiver}.instance_methods" )
808 variables += add_rails_columns( "#{receiver}" )
809 rescue Exception
810 found = nil
811 end
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000812 end
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000813 end
Bram Moolenaar9964e462007-05-05 17:54:07 +0000814
815 if inclass == nil || found == nil
816 dprint "inclass == nil"
817 methods = get_buffer_methods
818 methods += get_rails_view_methods
819
820 cls_const = Class.constants
821 constants = cls_const.select { |c| /^[A-Z_-]+$/.match( c ) }
822 classes = eval("self.class.constants") - constants
823 classes += get_buffer_classes
824 classes += get_buffer_modules
825
826 include_objectspace = VIM::evaluate("exists('g:rubycomplete_include_objectspace') && g:rubycomplete_include_objectspace")
827 ObjectSpace.each_object(Class) { |cls| classes << cls.to_s } if include_objectspace == "1"
828 message = receiver = input
829 end
830
831 methods += get_rails_helpers
832 methods += Kernel.public_methods
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000833 end
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000834
Bram Moolenaar9964e462007-05-05 17:54:07 +0000835 include_object = VIM::evaluate("exists('g:rubycomplete_include_object') && g:rubycomplete_include_object")
836 methods = clean_sel( methods, message )
837 methods = (methods-Object.instance_methods) if include_object == "0"
838 rbcmeth = (VimRubyCompletion.instance_methods-Object.instance_methods) # lets remove those rubycomplete methods
839 methods = (methods-rbcmeth)
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000840
Bram Moolenaar9964e462007-05-05 17:54:07 +0000841 variables = clean_sel( variables, message )
842 classes = clean_sel( classes, message ) - ["VimRubyCompletion"]
843 constants = clean_sel( constants, message )
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000844
Bram Moolenaar9964e462007-05-05 17:54:07 +0000845 valid = []
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200846 valid += methods.collect { |m| { :name => m.to_s, :type => 'm' } }
847 valid += variables.collect { |v| { :name => v.to_s, :type => 'v' } }
848 valid += classes.collect { |c| { :name => c.to_s, :type => 't' } }
849 valid += constants.collect { |d| { :name => d.to_s, :type => 'd' } }
Bram Moolenaar9964e462007-05-05 17:54:07 +0000850 valid.sort! { |x,y| x[:name] <=> y[:name] }
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000851
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000852 outp = ""
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000853
Bram Moolenaar9964e462007-05-05 17:54:07 +0000854 rg = 0..valid.length
855 rg.step(150) do |x|
856 stpos = 0+x
857 enpos = 150+x
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200858 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 +0000859 outp.sub!(/,$/, '')
860
861 VIM::command("call extend(g:rubycomplete_completions, [%s])" % outp)
862 outp = ""
863 end
864 end
865# }}} main completion code
866
867end # VimRubyCompletion
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000868# }}} ruby completion
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000869RUBYEOF
870endfunction
871
Bram Moolenaar9964e462007-05-05 17:54:07 +0000872let s:rubycomplete_rails_loaded = 0
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000873
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000874call s:DefRuby()
Bram Moolenaar9964e462007-05-05 17:54:07 +0000875"}}} ruby-side code
876
Bram Moolenaar9964e462007-05-05 17:54:07 +0000877" vim:tw=78:sw=4:ts=8:et:fdm=marker:ft=vim:norl: