blob: 3677b25aebac72083c452692fe4e1cec6d04bc87 [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 Moolenaar4d8f4762021-06-27 15:18:56 +02006" Last Change: 2020 Apr 12
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 Moolenaar4d8f4762021-06-27 15:18:56 +0200504 bootfile = rails_base + "config/boot.rb"
505 envfile = rails_base + "config/environment.rb"
Bram Moolenaar9964e462007-05-05 17:54:07 +0000506 if File.exists?( bootfile ) && File.exists?( envfile )
507 begin
508 require bootfile
509 require envfile
510 begin
511 require 'console_app'
512 require 'console_with_helpers'
513 rescue Exception
514 dprint "Rails 1.1+ Error %s" % $!
515 # assume 1.0
516 end
517 #eval( "Rails::Initializer.run" ) #not necessary?
518 VIM::command('let s:rubycomplete_rails_loaded = 1')
519 dprint "rails loaded"
520 rescue Exception
521 dprint "Rails Error %s" % $!
522 VIM::evaluate( "s:ErrMsg('Error loading rails environment')" )
523 end
524 end
525 end
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000526
Bram Moolenaar9964e462007-05-05 17:54:07 +0000527 def get_rails_helpers
528 allow_rails = VIM::evaluate("exists('g:rubycomplete_rails') && g:rubycomplete_rails")
529 rails_loaded = VIM::evaluate('s:rubycomplete_rails_loaded')
Bram Moolenaarc236c162008-07-13 17:41:49 +0000530 return [] if allow_rails.to_i.zero? || rails_loaded.to_i.zero?
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000531
Bram Moolenaar9964e462007-05-05 17:54:07 +0000532 buf_path = VIM::evaluate('expand("%:p")')
533 buf_path.gsub!( /\\/, "/" )
534 path_elm = buf_path.split( "/" )
535 dprint "buf_path: %s" % buf_path
536 types = [ "app", "db", "lib", "test", "components", "script" ]
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000537
Bram Moolenaar9964e462007-05-05 17:54:07 +0000538 i = nil
539 ret = []
540 type = nil
541 types.each do |t|
542 i = path_elm.index( t )
543 break if i
544 end
545 type = path_elm[i]
546 type.downcase!
547
548 dprint "type: %s" % type
549 case type
550 when "app"
551 i += 1
552 subtype = path_elm[i]
553 subtype.downcase!
554
555 dprint "subtype: %s" % subtype
556 case subtype
557 when "views"
558 ret += ActionView::Base.instance_methods
559 ret += ActionView::Base.methods
560 when "controllers"
561 ret += ActionController::Base.instance_methods
562 ret += ActionController::Base.methods
563 when "models"
564 ret += ActiveRecord::Base.instance_methods
565 ret += ActiveRecord::Base.methods
566 end
567
568 when "db"
569 ret += ActiveRecord::ConnectionAdapters::SchemaStatements.instance_methods
570 ret += ActiveRecord::ConnectionAdapters::SchemaStatements.methods
571 end
572
Bram Moolenaar9964e462007-05-05 17:54:07 +0000573 return ret
574 end
575
576 def add_rails_columns( cls )
577 allow_rails = VIM::evaluate("exists('g:rubycomplete_rails') && g:rubycomplete_rails")
578 rails_loaded = VIM::evaluate('s:rubycomplete_rails_loaded')
Bram Moolenaarc236c162008-07-13 17:41:49 +0000579 return [] if allow_rails.to_i.zero? || rails_loaded.to_i.zero?
Bram Moolenaar9964e462007-05-05 17:54:07 +0000580
581 begin
582 eval( "#{cls}.establish_connection" )
583 return [] unless eval( "#{cls}.ancestors.include?(ActiveRecord::Base).to_s" )
584 col = eval( "#{cls}.column_names" )
585 return col if col
586 rescue
587 dprint "add_rails_columns err: (cls: %s) %s" % [ cls, $! ]
588 return []
589 end
590 return []
591 end
592
593 def clean_sel(sel, msg)
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200594 ret = sel.reject{|x|x.nil?}.uniq
595 ret = ret.grep(/^#{Regexp.quote(msg)}/) if msg != nil
596 ret
Bram Moolenaar9964e462007-05-05 17:54:07 +0000597 end
598
599 def get_rails_view_methods
600 allow_rails = VIM::evaluate("exists('g:rubycomplete_rails') && g:rubycomplete_rails")
601 rails_loaded = VIM::evaluate('s:rubycomplete_rails_loaded')
Bram Moolenaarc236c162008-07-13 17:41:49 +0000602 return [] if allow_rails.to_i.zero? || rails_loaded.to_i.zero?
Bram Moolenaar9964e462007-05-05 17:54:07 +0000603
604 buf_path = VIM::evaluate('expand("%:p")')
605 buf_path.gsub!( /\\/, "/" )
606 pelm = buf_path.split( "/" )
607 idx = pelm.index( "views" )
608
609 return [] unless idx
610 idx += 1
611
612 clspl = pelm[idx].camelize.pluralize
613 cls = clspl.singularize
614
615 ret = []
616 begin
617 ret += eval( "#{cls}.instance_methods" )
618 ret += eval( "#{clspl}Helper.instance_methods" )
619 rescue Exception
620 dprint "Error: Unable to load rails view helpers for %s: %s" % [ cls, $! ]
621 end
622
623 return ret
624 end
625# }}} buffer analysis magic
626
627# {{{ main completion code
628 def self.preload_rails
629 a = VimRubyCompletion.new
Bram Moolenaard09091d2019-01-17 16:07:22 +0100630 if VIM::evaluate("has('nvim')") == 0
631 require 'thread'
632 Thread.new(a) do |b|
633 begin
634 b.load_rails
635 rescue
636 end
Bram Moolenaar9964e462007-05-05 17:54:07 +0000637 end
638 end
639 a.load_rails
640 rescue
641 end
642
643 def self.get_completions(base)
644 b = VimRubyCompletion.new
645 b.get_completions base
646 end
647
648 def get_completions(base)
649 loading_allowed = VIM::evaluate("exists('g:rubycomplete_buffer_loading') && g:rubycomplete_buffer_loading")
Bram Moolenaarc236c162008-07-13 17:41:49 +0000650 if loading_allowed.to_i == 1
Bram Moolenaar9964e462007-05-05 17:54:07 +0000651 load_requires
652 load_rails
653 end
654
Bram Moolenaar89bcfda2016-08-30 23:26:57 +0200655 want_gems = VIM::evaluate("get(g:, 'rubycomplete_load_gemfile')")
656 load_gems unless want_gems.to_i.zero?
Bram Moolenaar89bcfda2016-08-30 23:26:57 +0200657
Bram Moolenaar9964e462007-05-05 17:54:07 +0000658 input = VIM::Buffer.current.line
659 cpos = VIM::Window.current.cursor[1] - 1
660 input = input[0..cpos]
661 input += base
662 input.sub!(/.*[ \t\n\"\\'`><=;|&{(]/, '') # Readline.basic_word_break_characters
663 input.sub!(/self\./, '')
664 input.sub!(/.*((\.\.[\[(]?)|([\[(]))/, '')
665
666 dprint 'input %s' % input
667 message = nil
668 receiver = nil
669 methods = []
670 variables = []
671 classes = []
672 constants = []
673
674 case input
675 when /^(\/[^\/]*\/)\.([^.]*)$/ # Regexp
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000676 receiver = $1
Bram Moolenaar9964e462007-05-05 17:54:07 +0000677 message = Regexp.quote($2)
678 methods = Regexp.instance_methods(true)
679
680 when /^([^\]]*\])\.([^.]*)$/ # Array
681 receiver = $1
682 message = Regexp.quote($2)
683 methods = Array.instance_methods(true)
684
685 when /^([^\}]*\})\.([^.]*)$/ # Proc or Hash
686 receiver = $1
687 message = Regexp.quote($2)
688 methods = Proc.instance_methods(true) | Hash.instance_methods(true)
689
690 when /^(:[^:.]*)$/ # Symbol
691 dprint "symbol"
692 if Symbol.respond_to?(:all_symbols)
693 receiver = $1
694 message = $1.sub( /:/, '' )
695 methods = Symbol.all_symbols.collect{|s| s.id2name}
696 methods.delete_if { |c| c.match( /'/ ) }
697 end
698
Bram Moolenaar2ed639a2019-12-09 23:11:18 +0100699 when /^::([A-Z][^:\.\(]*)?$/ # Absolute Constant or class methods
Bram Moolenaar9964e462007-05-05 17:54:07 +0000700 dprint "const or cls"
701 receiver = $1
Bram Moolenaar2ed639a2019-12-09 23:11:18 +0100702 methods = Object.constants.collect{ |c| c.to_s }.grep(/^#{receiver}/)
Bram Moolenaar9964e462007-05-05 17:54:07 +0000703
Bram Moolenaar1d689522010-05-28 20:54:39 +0200704 when /^(((::)?[A-Z][^:.\(]*)+?)::?([^:.]*)$/ # Constant or class methods
Bram Moolenaar9964e462007-05-05 17:54:07 +0000705 receiver = $1
706 message = Regexp.quote($4)
707 dprint "const or cls 2 [recv: \'%s\', msg: \'%s\']" % [ receiver, message ]
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000708 load_buffer_class( receiver )
Bram Moolenaard09091d2019-01-17 16:07:22 +0100709 load_buffer_module( receiver )
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000710 begin
Bram Moolenaar2ed639a2019-12-09 23:11:18 +0100711 constants = eval("#{receiver}.constants").collect{ |c| c.to_s }.grep(/^#{message}/)
712 methods = eval("#{receiver}.methods").collect{ |m| m.to_s }.grep(/^#{message}/)
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000713 rescue Exception
Bram Moolenaar9964e462007-05-05 17:54:07 +0000714 dprint "exception: %s" % $!
Bram Moolenaar2ed639a2019-12-09 23:11:18 +0100715 constants = []
Bram Moolenaar9964e462007-05-05 17:54:07 +0000716 methods = []
717 end
Bram Moolenaar9964e462007-05-05 17:54:07 +0000718
719 when /^(:[^:.]+)\.([^.]*)$/ # Symbol
720 dprint "symbol"
721 receiver = $1
722 message = Regexp.quote($2)
723 methods = Symbol.instance_methods(true)
724
725 when /^([0-9_]+(\.[0-9_]+)?(e[0-9]+)?)\.([^.]*)$/ # Numeric
726 dprint "numeric"
727 receiver = $1
728 message = Regexp.quote($4)
729 begin
730 methods = eval(receiver).methods
731 rescue Exception
732 methods = []
733 end
734
735 when /^(\$[^.]*)$/ #global
736 dprint "global"
737 methods = global_variables.grep(Regexp.new(Regexp.quote($1)))
738
Bram Moolenaar1d689522010-05-28 20:54:39 +0200739 when /^((\.?[^.]+)+?)\.([^.]*)$/ # variable
Bram Moolenaar9964e462007-05-05 17:54:07 +0000740 dprint "variable"
741 receiver = $1
742 message = Regexp.quote($3)
743 load_buffer_class( receiver )
744
745 cv = eval("self.class.constants")
746 vartype = get_var_type( receiver )
747 dprint "vartype: %s" % vartype
Bram Moolenaar89bcfda2016-08-30 23:26:57 +0200748
749 invalid_vartype = ['', "gets"]
750 if !invalid_vartype.include?(vartype)
Bram Moolenaar9964e462007-05-05 17:54:07 +0000751 load_buffer_class( vartype )
752
753 begin
754 methods = eval("#{vartype}.instance_methods")
755 variables = eval("#{vartype}.instance_variables")
756 rescue Exception
757 dprint "load_buffer_class err: %s" % $!
758 end
759 elsif (cv).include?(receiver)
760 # foo.func and foo is local var.
761 methods = eval("#{receiver}.methods")
762 vartype = receiver
763 elsif /^[A-Z]/ =~ receiver and /\./ !~ receiver
764 vartype = receiver
765 # Foo::Bar.func
766 begin
767 methods = eval("#{receiver}.methods")
768 rescue Exception
769 end
770 else
771 # func1.func2
772 ObjectSpace.each_object(Module){|m|
773 next if m.name != "IRB::Context" and
774 /^(IRB|SLex|RubyLex|RubyToken)/ =~ m.name
775 methods.concat m.instance_methods(false)
776 }
777 end
Bram Moolenaar89bcfda2016-08-30 23:26:57 +0200778 variables += add_rails_columns( "#{vartype}" ) if vartype && !invalid_vartype.include?(vartype)
Bram Moolenaar9964e462007-05-05 17:54:07 +0000779
780 when /^\(?\s*[A-Za-z0-9:^@.%\/+*\(\)]+\.\.\.?[A-Za-z0-9:^@.%\/+*\(\)]+\s*\)?\.([^.]*)/
781 message = $1
782 methods = Range.instance_methods(true)
783
784 when /^\.([^.]*)$/ # unknown(maybe String)
785 message = Regexp.quote($1)
786 methods = String.instance_methods(true)
787
788 else
789 dprint "default/other"
790 inclass = eval( VIM::evaluate("s:IsInClassDef()") )
791
792 if inclass != nil
793 dprint "inclass"
794 classdef = "%s\n" % VIM::Buffer.current[ inclass.min ]
795 found = /^\s*class\s*([A-Za-z0-9_-]*)(\s*<\s*([A-Za-z0-9_:-]*))?\s*\n$/.match( classdef )
796
797 if found != nil
798 receiver = $1
799 message = input
800 load_buffer_class( receiver )
801 begin
802 methods = eval( "#{receiver}.instance_methods" )
803 variables += add_rails_columns( "#{receiver}" )
804 rescue Exception
805 found = nil
806 end
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000807 end
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000808 end
Bram Moolenaar9964e462007-05-05 17:54:07 +0000809
810 if inclass == nil || found == nil
811 dprint "inclass == nil"
812 methods = get_buffer_methods
813 methods += get_rails_view_methods
814
815 cls_const = Class.constants
816 constants = cls_const.select { |c| /^[A-Z_-]+$/.match( c ) }
817 classes = eval("self.class.constants") - constants
818 classes += get_buffer_classes
819 classes += get_buffer_modules
820
821 include_objectspace = VIM::evaluate("exists('g:rubycomplete_include_objectspace') && g:rubycomplete_include_objectspace")
822 ObjectSpace.each_object(Class) { |cls| classes << cls.to_s } if include_objectspace == "1"
823 message = receiver = input
824 end
825
826 methods += get_rails_helpers
827 methods += Kernel.public_methods
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000828 end
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000829
Bram Moolenaar9964e462007-05-05 17:54:07 +0000830 include_object = VIM::evaluate("exists('g:rubycomplete_include_object') && g:rubycomplete_include_object")
831 methods = clean_sel( methods, message )
832 methods = (methods-Object.instance_methods) if include_object == "0"
833 rbcmeth = (VimRubyCompletion.instance_methods-Object.instance_methods) # lets remove those rubycomplete methods
834 methods = (methods-rbcmeth)
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000835
Bram Moolenaar9964e462007-05-05 17:54:07 +0000836 variables = clean_sel( variables, message )
837 classes = clean_sel( classes, message ) - ["VimRubyCompletion"]
838 constants = clean_sel( constants, message )
Bram Moolenaard8fc5c02006-04-29 21:55:22 +0000839
Bram Moolenaar9964e462007-05-05 17:54:07 +0000840 valid = []
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200841 valid += methods.collect { |m| { :name => m.to_s, :type => 'm' } }
842 valid += variables.collect { |v| { :name => v.to_s, :type => 'v' } }
843 valid += classes.collect { |c| { :name => c.to_s, :type => 't' } }
844 valid += constants.collect { |d| { :name => d.to_s, :type => 'd' } }
Bram Moolenaar9964e462007-05-05 17:54:07 +0000845 valid.sort! { |x,y| x[:name] <=> y[:name] }
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000846
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000847 outp = ""
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000848
Bram Moolenaar9964e462007-05-05 17:54:07 +0000849 rg = 0..valid.length
850 rg.step(150) do |x|
851 stpos = 0+x
852 enpos = 150+x
Bram Moolenaarec7944a2013-06-12 21:29:15 +0200853 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 +0000854 outp.sub!(/,$/, '')
855
856 VIM::command("call extend(g:rubycomplete_completions, [%s])" % outp)
857 outp = ""
858 end
859 end
860# }}} main completion code
861
862end # VimRubyCompletion
Bram Moolenaareb3593b2006-04-22 22:33:57 +0000863# }}} ruby completion
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000864RUBYEOF
865endfunction
866
Bram Moolenaar9964e462007-05-05 17:54:07 +0000867let s:rubycomplete_rails_loaded = 0
Bram Moolenaarfc1421e2006-04-20 22:17:20 +0000868
Bram Moolenaarc6249bb2006-04-15 20:25:09 +0000869call s:DefRuby()
Bram Moolenaar9964e462007-05-05 17:54:07 +0000870"}}} ruby-side code
871
Bram Moolenaar9964e462007-05-05 17:54:07 +0000872" vim:tw=78:sw=4:ts=8:et:fdm=marker:ft=vim:norl: