summaryrefslogtreecommitdiffstats
path: root/runtime/autoload/phpcomplete.vim
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2014-03-22 21:02:50 +0100
committerBram Moolenaar <Bram@vim.org>2014-03-22 21:02:50 +0100
commita68783751647e3243ca6f22df62907efbdbccb02 (patch)
tree372908fd79defec22190b6d2a48bbc6155bb22e7 /runtime/autoload/phpcomplete.vim
parented287f9a4e3f4ed5528ad2af65b7b23bce14a688 (diff)
Updated runtime files.
Diffstat (limited to 'runtime/autoload/phpcomplete.vim')
-rw-r--r--runtime/autoload/phpcomplete.vim7315
1 files changed, 2360 insertions, 4955 deletions
diff --git a/runtime/autoload/phpcomplete.vim b/runtime/autoload/phpcomplete.vim
index 07565558fe..2ff94ced22 100644
--- a/runtime/autoload/phpcomplete.vim
+++ b/runtime/autoload/phpcomplete.vim
@@ -1,27 +1,99 @@
" Vim completion script
" Language: PHP
-" Maintainer: Mikolaj Machowski ( mikmach AT wp DOT pl )
-" Last Change: 2011 Dec 08
+" Maintainer: Dávid Szabó ( complex857 AT gmail DOT com )
+" Previous Maintainer: Mikolaj Machowski ( mikmach AT wp DOT pl )
"
-" TODO:
-" - Class aware completion:
-" a) caching?
-" - Switching to HTML (XML?) completion (SQL) inside of phpStrings
-" - allow also for XML completion <- better do html_flavor for HTML
-" completion
-" - outside of <?php?> getting parent tag may cause problems. Heh, even in
-" perfect conditions GetLastOpenTag doesn't cooperate... Inside of
-" phpStrings this can be even a bonus but outside of <?php?> it is not the
-" best situation
-
-function! phpcomplete#CompletePHP(findstart, base)
+" OPTIONS:
+"
+" let g:phpcomplete_relax_static_constraint = 1/0 [default 0]
+" Enables completion for non-static methods when completing for static context (::).
+" This generates E_STRICT level warning, but php calls these methods nontheless.
+"
+" let g:phpcomplete_complete_for_unknown_classes = 1/0 [default 0]
+" Enables completion of variables and functions in "everything under the sun" fashion
+" when completing for an instance or static class context but the code can't tell the class
+" or locate the file that it lives in.
+" The completion list generated this way is only filtered by the completion base
+" and generally not much more accurate then simple keyword completion.
+"
+" let g:phpcomplete_search_tags_for_variables = 1/0 [default 0]
+" Enables use of tags when the plugin tries to find variables.
+" When enabled the plugin will search for the variables in the tag files with kind 'v',
+" lines like $some_var = new Foo; but these usually yield highly inaccurate results and
+" can be fairly slow.
+"
+" let g:phpcomplete_min_num_of_chars_for_namespace_completion = n [default 1]
+" This option controls the number of characters the user needs to type before
+" the tags will be searched for namespaces and classes in typed out namespaces in
+" "use ..." context. Setting this to 0 is not recommended because that means the code
+" have to scan every tag, and vim's taglist() function runs extremly slow with a
+" "match everything" pattern.
+"
+" let g:phpcomplete_parse_docblock_comments = 1/0 [default 0]
+" When enabled the preview window's content will include information
+" extracted from docblock comments of the completions.
+" Enabling this option will add return types to the completion menu for functions too.
+"
+" let g:phpcomplete_cache_taglists = 1/0 [default 1]
+" When enabled the taglist() lookups will be cached and subsequent searches
+" for the same pattern will not check the tagfiles any more, thus making the
+" lookups faster. Cache expiration is based on the mtimes of the tag files.
+"
+" TODO:
+" - Switching to HTML (XML?) completion (SQL) inside of phpStrings
+" - allow also for XML completion <- better do html_flavor for HTML
+" completion
+" - outside of <?php?> getting parent tag may cause problems. Heh, even in
+" perfect conditions GetLastOpenTag doesn't cooperate... Inside of
+" phpStrings this can be even a bonus but outside of <?php?> it is not the
+" best situation
+
+if !exists('g:phpcomplete_relax_static_constraint')
+ let g:phpcomplete_relax_static_constraint = 0
+endif
+
+if !exists('g:phpcomplete_complete_for_unknown_classes')
+ let g:phpcomplete_complete_for_unknown_classes = 0
+endif
+
+if !exists('g:phpcomplete_search_tags_for_variables')
+ let g:phpcomplete_search_tags_for_variables = 0
+endif
+
+if !exists('g:phpcomplete_min_num_of_chars_for_namespace_completion')
+ let g:phpcomplete_min_num_of_chars_for_namespace_completion = 1
+endif
+
+if !exists('g:phpcomplete_parse_docblock_comments')
+ let g:phpcomplete_parse_docblock_comments = 0
+endif
+
+if !exists('g:phpcomplete_cache_taglists')
+ let g:phpcomplete_cache_taglists = 1
+endif
+
+if !exists('s:cache_classstructures')
+ let s:cache_classstructures = {}
+endif
+
+if !exists('s:cache_tags')
+ let s:cache_tags = {}
+endif
+
+if !exists('s:cache_tags_checksum')
+ let s:cache_tags_checksum = ''
+endif
+
+let s:script_path = fnamemodify(resolve(expand('<sfile>:p')), ':h')
+
+function! phpcomplete#CompletePHP(findstart, base) " {{{
if a:findstart
unlet! b:php_menu
" Check if we are inside of PHP markup
let pos = getpos('.')
let phpbegin = searchpairpos('<?', '', '?>', 'bWn',
\ 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string\|comment"')
- let phpend = searchpairpos('<?', '', '?>', 'Wn',
+ let phpend = searchpairpos('<?', '', '?>', 'Wn',
\ 'synIDattr(synID(line("."), col("."), 0), "name") =~? "string\|comment"')
if phpbegin == [0,0] && phpend == [0,0]
@@ -37,5108 +109,2416 @@ function! phpcomplete#CompletePHP(findstart, base)
let start = col('.') - 1
let curline = line('.')
let compl_begin = col('.') - 2
- while start >= 0 && line[start - 1] =~ '[a-zA-Z_0-9\x7f-\xff$]'
+ while start >= 0 && line[start - 1] =~ '[\\a-zA-Z_0-9\x7f-\xff$]'
let start -= 1
endwhile
- let b:compl_context = getline('.')[0:compl_begin]
- return start
+ let b:phpbegin = phpbegin
+ let b:compl_context = phpcomplete#GetCurrentInstruction(line('.'), col('.') - 2, phpbegin)
+ return start
" We can be also inside of phpString with HTML tags. Deal with
" it later (time, not lines).
endif
-
endif
+
+
" If exists b:php_menu it means completion was already constructed we
" don't need to do anything more
if exists("b:php_menu")
return b:php_menu
endif
- " Initialize base return lists
- let res = []
- let res2 = []
+
+ if !exists('g:php_builtin_functions')
+ call phpcomplete#LoadData()
+ endif
+
" a:base is very short - we need context
if exists("b:compl_context")
let context = b:compl_context
unlet! b:compl_context
- endif
+ " chop of the "base" from the end of the current instruction
+ if a:base != ""
+ let context = substitute(context, '\s*\$\?\([a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\)*$', '', '')
+ end
+ end
- if !exists('g:php_builtin_functions')
- call phpcomplete#LoadData()
+ let [current_namespace, imports] = phpcomplete#GetCurrentNameSpace(getline(0, line('.')))
+
+ if context =~? '^use\s'
+ return phpcomplete#CompleteUse(a:base)
endif
- let scontext = substitute(context, '\$\?[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*$', '', '')
-
- if scontext =~ '\(=\s*new\|extends\)\s\+$'
- " Complete class name
- " Internal solution for finding classes in current file.
- let file = getline(1, '$')
- call filter(file,
- \ 'v:val =~ "class\\s\\+[a-zA-Z_\\x7f-\\xff][a-zA-Z_0-9\\x7f-\\xff]*\\s*("')
- let fnames = join(map(tagfiles(), 'escape(v:val, " \\#%")'))
- let jfile = join(file, ' ')
- let int_values = split(jfile, 'class\s\+')
- let int_classes = {}
- for i in int_values
- let c_name = matchstr(i, '^[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*')
- if c_name != ''
- let int_classes[c_name] = ''
+ if context =~ '\(->\|::\)$'
+ " {{{
+ " Get name of the class
+ let classname = phpcomplete#GetClassName(line('.'), context, current_namespace, imports)
+
+ " Get location of class definition, we have to iterate through all
+ if classname != ''
+ if classname =~ '\'
+ " split the last \ segment as a classname, everything else is the namespace
+ let classname_parts = split(classname, '\')
+ let namespace = join(classname_parts[0:-2], '\')
+ let classname = classname_parts[-1]
+ else
+ let namespace = '\'
endif
- endfor
+ let classlocation = phpcomplete#GetClassLocation(classname, namespace)
+ else
+ let classlocation = ''
+ endif
- " Prepare list of classes from tags file
- let ext_classes = {}
- let fnames = join(map(tagfiles(), 'escape(v:val, " \\#%")'))
- if fnames != ''
- exe 'silent! vimgrep /^'.a:base.'.*\tc\(\t\|$\)/j '.fnames
- let qflist = getqflist()
- if len(qflist) > 0
- for field in qflist
- " [:space:] thing: we don't have to be so strict when
- " dealing with tags files - entries there were already
- " checked by ctags.
- let item = matchstr(field['text'], '^[^[:space:]]\+')
- let ext_classes[item] = ''
- endfor
+ if classlocation != ''
+ if classlocation == 'VIMPHP_BUILTINOBJECT' && has_key(g:php_builtin_classes, tolower(classname))
+ return phpcomplete#CompleteBuiltInClass(context, classname, a:base)
endif
- endif
- " Prepare list of built in classes from g:php_builtin_functions
- if !exists("g:php_omni_bi_classes")
- let g:php_omni_bi_classes = {}
- for i in keys(g:php_builtin_object_functions)
- let g:php_omni_bi_classes[substitute(i, '::.*$', '', '')] = ''
- endfor
+ if filereadable(classlocation)
+ let classfile = readfile(classlocation)
+ let classcontent = ''
+ let classcontent .= "\n".phpcomplete#GetClassContents(classlocation, classname)
+ let sccontent = split(classcontent, "\n")
+ let visibility = expand('%:p') == fnamemodify(classlocation, ':p') ? 'private' : 'public'
+
+ return phpcomplete#CompleteUserClass(context, a:base, sccontent, visibility)
+ endif
endif
- let classes = sort(keys(int_classes))
- let classes += sort(keys(ext_classes))
- let classes += sort(keys(g:php_omni_bi_classes))
+ return phpcomplete#CompleteUnknownClass(a:base, context)
+ " }}}
+ elseif context =~? 'implements'
+ return phpcomplete#CompleteClassName(a:base, ['i'], current_namespace, imports)
+ elseif context =~? 'extends\s\+.\+$'
+ return ['implements']
+ elseif context =~? 'extends'
+ let kinds = context =~? 'class\s' ? ['c'] : ['i']
+ return phpcomplete#CompleteClassName(a:base, kinds, current_namespace, imports)
+ elseif context =~? 'class [a-zA-Z_\x7f-\xff\\][a-zA-Z_0-9\x7f-\xff\\]*'
+ " special case when you've typed the class keyword and the name too, only extends and implements allowed there
+ return filter(['extends', 'implements'], 'stridx(v:val, a:base) == 0')
+ elseif context =~? 'new'
+ return phpcomplete#CompleteClassName(a:base, ['c'], current_namespace, imports)
+ endif
- for m in classes
- if m =~ '^'.a:base
- call add(res, m)
+ if a:base =~ '^\$'
+ return phpcomplete#CompleteVariable(a:base)
+ else
+ return phpcomplete#CompleteGeneral(a:base, current_namespace, imports)
+ endif
+endfunction
+" }}}
+
+function! phpcomplete#CompleteUse(base) " {{{
+ " completes builtin class names regadless of g:phpcomplete_min_num_of_chars_for_namespace_completion
+ " completes namespaces from tags
+ " * requires patched ctags
+ " completes classnames from tags within the already typed out namespace using the "namespace" field of tags
+ " * requires patched ctags
+
+ let res = []
+
+ " class and namespace names are always considered absoltute in use ... expressions, leading slash is not recommended
+ " by the php manual, so we gonna get rid of that
+ if a:base =~? '^\'
+ let base = substitute(a:base, '^\', '', '')
+ else
+ let base = a:base
+ endif
+
+ let namespace_match_pattern = substitute(base, '\\', '\\\\', 'g')
+ let classname_match_pattern = matchstr(base, '[^\\]\+$')
+ let namespace_for_class = substitute(substitute(namespace_match_pattern, '\\\\', '\\', 'g'), '\\*'.classname_match_pattern.'$', '', '')
+
+ if len(namespace_match_pattern) >= g:phpcomplete_min_num_of_chars_for_namespace_completion
+ if len(classname_match_pattern) >= g:phpcomplete_min_num_of_chars_for_namespace_completion
+ let tags = phpcomplete#GetTaglist('^\('.namespace_match_pattern.'\|'.classname_match_pattern.'\)')
+ else
+ let tags = phpcomplete#GetTaglist('^'.namespace_match_pattern)
+ endif
+
+ let patched_ctags_detected = 0
+ let namespaced_matches = []
+ let no_namespace_matches = []
+ for tag in tags
+ if has_key(tag, 'namespace')
+ let patched_ctags_detected = 1
+ endif
+ if tag.kind ==? 'n' && tag.name =~? '^'.namespace_match_pattern
+ let patched_ctags_detected = 1
+ call add(namespaced_matches, {'word': tag.name, 'kind': 'n', 'menu': tag.filename, 'info': tag.filename })
+ elseif has_key(tag, 'namespace') && (tag.kind ==? 'c' || tag.kind ==? 'i') && tag.namespace ==? namespace_for_class
+ call add(namespaced_matches, {'word': namespace_for_class.'\'.tag.name, 'kind': tag.kind, 'menu': tag.filename, 'info': tag.filename })
+ elseif (tag.kind ==? 'c' || tag.kind ==? 'i')
+ call add(no_namespace_matches, {'word': namespace_for_class.'\'.tag.name, 'kind': tag.kind, 'menu': tag.filename, 'info': tag.filename })
endif
endfor
+ " if it seems that the tags file have namespace informations we can safely throw
+ " away namespaceless tag matches since we can be sure they are invalid
+ if patched_ctags_detected
+ no_namespace_matches = []
+ endif
+ let res += namespaced_matches + no_namespace_matches
+ endif
- let final_menu = []
- for i in res
- let final_menu += [{'word':i, 'kind':'c'}]
+ if base !~ '\'
+ let builtin_classnames = filter(keys(copy(g:php_builtin_classnames)), 'v:val =~? "^'.classname_match_pattern.'"')
+ for classname in builtin_classnames
+ call add(res, {'word': classname, 'kind': 'c'})
+ endfor
+ let builtin_interfacenames = filter(keys(copy(g:php_builtin_interfacenames)), 'v:val =~? "^'.classname_match_pattern.'"')
+ for interfacename in builtin_interfacenames
+ call add(res, {'word': interfacename, 'kind': 'i'})
endfor
+ endif
- return final_menu
+ return res
+endfunction
+" }}}
- elseif scontext =~ '\(->\|::\)$'
- " Complete user functions and variables
- " Internal solution for current file.
- " That seems as unnecessary repeating of functions but there are
- " few not so subtle differences as not appending of $ and addition
- " of 'kind' tag (not necessary in regular completion)
+function! phpcomplete#CompleteGeneral(base, current_namespace, imports) " {{{
+ " Complete everything else -
+ " + functions, DONE
+ " + keywords of language DONE
+ " + defines (constant definitions), DONE
+ " + extend keywords for predefined constants, DONE
+ " + classes (after new), DONE
+ " + limit choice after -> and :: to funcs and vars DONE
- if scontext =~ '->$' && scontext !~ '\$this->$'
+ " Internal solution for finding functions in current file.
- " Get name of the class
- let classname = phpcomplete#GetClassName(scontext)
+ if a:base =~? '^\'
+ let leading_slash = '\'
+ else
+ let leading_slash = ''
+ endif
- " Get location of class definition, we have to iterate through all
- " tags files separately because we need relative path from current
- " file to the exact file (tags file can be in different dir)
- if classname != ''
- let classlocation = phpcomplete#GetClassLocation(classname)
- else
- let classlocation = ''
- endif
+ let file = getline(1, '$')
+ call filter(file,
+ \ 'v:val =~ "function\\s\\+&\\?[a-zA-Z_\\x7f-\\xff][a-zA-Z_0-9\\x7f-\\xff]*\\s*("')
+ let jfile = join(file, ' ')
+ let int_values = split(jfile, 'function\s\+')
+ let int_functions = {}
+ for i in int_values
+ let f_name = matchstr(i,
+ \ '^&\?\zs[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\ze')
+ if f_name =~? '^'.substitute(a:base, '\\', '\\\\', 'g')
+ let f_args = matchstr(i,
+ \ '^&\?[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\s*(\zs.\{-}\ze)\_s*\({\|$\)')
+ let int_functions[f_name.'('] = f_args.')'
+ endif
+ endfor
- if classlocation == 'VIMPHP_BUILTINOBJECT'
+ " Internal solution for finding constants in current file
+ let file = getline(1, '$')
+ call filter(file, 'v:val =~ "define\\s*("')
+ let jfile = join(file, ' ')
+ let int_values = split(jfile, 'define\s*(\s*')
+ let int_constants = {}
+ for i in int_values
+ let c_name = matchstr(i, '\(["'']\)\zs[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\ze\1')
+ if c_name != '' && c_name =~# '^'.substitute(a:base, '\\', '\\\\', 'g')
+ let int_constants[leading_slash.c_name] = ''
+ endif
+ endfor
- for object in keys(g:php_builtin_object_functions)
- if object =~ '^'.classname
- let res += [{'word':substitute(object, '.*::', '', ''),
- \ 'info': g:php_builtin_object_functions[object]}]
- endif
- endfor
+ " Prepare list of functions from tags file
+ let ext_functions = {}
+ let ext_constants = {}
+ let ext_classes = {}
+ let ext_interfaces = {}
+ let ext_namespaces = {}
+
+ let base = substitute(a:base, '^\\', '', '')
+ let [tag_match_pattern, namespace_for_tag] = phpcomplete#ExpandClassName(a:base, a:current_namespace, a:imports)
+ let namespace_match_pattern = substitute((namespace_for_tag == '' ? '' : namespace_for_tag.'\').tag_match_pattern, '\\', '\\\\', 'g')
+
+ let tags = []
+ if len(namespace_match_pattern) >= g:phpcomplete_min_num_of_chars_for_namespace_completion && len(tag_match_pattern) >= g:phpcomplete_min_num_of_chars_for_namespace_completion && tag_match_pattern != namespace_match_pattern
+ let tags = phpcomplete#GetTaglist('\c^\('.tag_match_pattern.'\|'.namespace_match_pattern.'\)')
+ elseif len(namespace_match_pattern) >= g:phpcomplete_min_num_of_chars_for_namespace_completion
+ let tags = phpcomplete#GetTaglist('\c^'.namespace_match_pattern)
+ elseif len(tag_match_pattern) >= g:phpcomplete_min_num_of_chars_for_namespace_completion
+ let tags = phpcomplete#GetTaglist('\c^'.tag_match_pattern)
+ endif
- return res
+ for tag in tags
+ if !has_key(tag, 'namespace') || tag.namespace ==? a:current_namespace || tag.namespace ==? namespace_for_tag
+ if has_key(tag, 'namespace')
+ let full_name = tag.namespace.'\'.tag.name " absolute namespaced name (without leading '\')
+ let base_parts = split(a:base, '\')
+ if len(base_parts) > 1
+ let namespace_part = join(base_parts[0:-2], '\')
+ else
+ let namespace_part = ''
+ endif
+ let relative_name = (namespace_part == '' ? '' : namespace_part.'\').tag.name
endif
- if filereadable(classlocation)
- let classfile = readfile(classlocation)
- let classcontent = ''
- let classcontent .= "\n".phpcomplete#GetClassContents(classfile, classname)
- let sccontent = split(classcontent, "\n")
+ if tag.kind ==? 'n' && tag.name =~? '^'.namespace_match_pattern
+ let info = tag.name.' - '.tag.filename
+ " patched ctag provides absolute namespace names as tag name, namespace tags dont have namespace fields
+ let full_name = tag.name
+
+ let base_parts = split(a:base, '\')
+ let full_name_parts = split(full_name, '\')
+ if len(base_parts) > 1
+ " the first segment could be a renamed import, take the first segment from the user provided input
+ " so if it's a sub namespace of a renamed namespace, just use the typed in segments in place of the absolute path
+ " for example:
+ " you have a namespace NS1\SUBNS as SUB
+ " you have a sub-sub-namespace NS1\SUBNS\SUBSUB
+ " typed in SUB\SU
+ " the tags will return NS1\SUBNS\SUBSUB
+ " the completion should be: SUB\SUBSUB by replacing the NS1\SUBSN to SUB as in the import
+ if has_key(a:imports, base_parts[0]) && a:imports[base_parts[0]].kind == 'n'
+ let import = a:imports[base_parts[0]]
+ let relative_name = substitute(full_name, '^'.substitute(import.name, '\\', '\\\\', 'g'), base_parts[0], '')
+ else
+ let relative_name = strpart(full_name, stridx(full_name, a:base))
+ endif
+ else
+ let relative_name = strpart(full_name, stridx(full_name, a:base))
+ endif
- " YES, YES, YES! - we have whole content including extends!
- " Now we need to get two elements: public functions and public
- " vars
- " NO, NO, NO! - third separate filtering looking for content
- " :(, but all of them have differences. To squeeze them into
- " one implementation would require many additional arguments
- " and ifs. No good solution
- " Functions declared with public keyword or without any
- " keyword are public
- let functions = filter(deepcopy(sccontent),
- \ 'v:val =~ "^\\s*\\(static\\s\\+\\|public\\s\\+\\)*function"')
- let jfuncs = join(functions, ' ')
- let sfuncs = split(jfuncs, 'function\s\+')
- let c_functions = {}
- for i in sfuncs
- let f_name = matchstr(i,
- \ '^&\?\zs[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\ze')
- let f_args = matchstr(i,
- \ '^&\?[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\s*(\zs.\{-}\ze)\_s*{')
- if f_name != ''
- let c_functions[f_name.'('] = f_args
+ if leading_slash == ''
+ let ext_namespaces[relative_name.'\'] = info
+ else
+ let ext_namespaces['\'.full_name.'\'] = info
+ endif
+ elseif tag.kind ==? 'f' && !has_key(tag, 'class') " class related functions (methods) completed elsewhere, only works with patched ctags
+ if has_key(tag, 'signature')
+ let prototype = tag.signature[1:-2] " drop the ()s around the string
+ else
+ let prototype = matchstr(tag.cmd,
+ \ 'function\s\+&\?[^[:space:]]\+\s*(\s*\zs.\{-}\ze\s*)\s*{\?')
+ endif
+ let info = prototype.') - '.tag.filename
+
+ if !has_key(tag, 'namespace')
+ let ext_functions[tag.name.'('] = info
+ else
+ if tag.namespace ==? namespace_for_tag
+ if leading_slash == ''
+ let ext_functions[relative_name.'('] = info
+ else
+ let ext_functions['\'.full_name.'('] = info
+ endif
endif
- endfor
- " Variables declared with var or with public keyword are
- " public
- let variables = filter(deepcopy(sccontent),
- \ 'v:val =~ "^\\s*\\(public\\|var\\)\\s\\+\\$"')
- let jvars = join(variables, ' ')
- let svars = split(jvars, '\$')
- let c_variables = {}
- for i in svars
- let c_var = matchstr(i,
- \ '^\zs[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\ze')
- if c_var != ''
- let c_variables[c_var] = ''
+ endif
+ elseif tag.kind ==? 'd'
+ let info = ' - '.tag.filename
+ if !has_key(tag, 'namespace')
+ let ext_constants[tag.name] = info
+ else
+ if tag.namespace ==? namespace_for_tag
+ if leading_slash == ''
+ let ext_constants[relative_name] = info
+ else
+ let ext_constants['\'.full_name] = info
+ endif
endif
- endfor
-
- let all_values = {}
- call extend(all_values, c_functions)
- call extend(all_values, c_variables)
-
- for m in sort(keys(all_values))
- if m =~ '^'.a:base && m !~ '::'
- call add(res, m)
- elseif m =~ '::'.a:base
- call add(res2, m)
+ endif
+ elseif tag.kind ==? 'c' || tag.kind ==? 'i'
+ let info = ' - '.tag.filename
+
+ let key = ''
+ if !has_key(tag, 'namespace')
+ let key = tag.name
+ else
+ if tag.namespace ==? namespace_for_tag
+ if leading_slash == ''
+ let key = relative_name
+ else
+ let key = '\'.full_name
+ endif
endif
- endfor
-
- let start_list = res + res2
+ endif
- let final_list = []
- for i in start_list
- if has_key(c_variables, i)
- let class = ' '
- if all_values[i] != ''
- let class = i.' class '
- endif
- let final_list +=
- \ [{'word':i,
- \ 'info':class.all_values[i],
- \ 'kind':'v'}]
- else
- let final_list +=
- \ [{'word':substitute(i, '.*::', '', ''),
- \ 'info':i.all_values[i].')',
- \ 'kind':'f'}]
+ if key != ''
+ if tag.kind ==? 'c'
+ let ext_classes[key] = info
+ elseif tag.kind ==? 'i'
+ let ext_interfaces[key] = info
endif
- endfor
+ endif
+ endif
+ endif
+ endfor
+
+ let builtin_constants = {}
+ let builtin_classnames = {}
+ let builtin_interfaces = {}
+ let builtin_functions = {}
+ let builtin_keywords = {}
+ let base = substitute(a:base, '^\', '', '')
+ if a:current_namespace == '\' || (a:base =~ '^\\' && a:base =~ '^\\[^\\]*$')
+
+ " Add builtin class names
+ for [classname, info] in items(g:php_builtin_classnames)
+ if classname =~? '^'.base
+ let builtin_classnames[leading_slash.classname] = info
+ endif
+ endfor
+ for [interfacename, info] in items(g:php_builtin_interfacenames)
+ if interfacename =~? '^'.base
+ let builtin_interfaces[leading_slash.interfacename] = info
+ endif
+ endfor
+ endif
- return final_list
+ " Prepare list of constants from built-in constants
+ for [constant, info] in items(g:php_constants)
+ if constant =~# '^'.base
+ let builtin_constants[leading_slash.constant] = info
+ endif
+ endfor
+ if leading_slash == '' " keywords should not be completed when base starts with '\'
+ " Treat keywords as constants
+ for [constant, info] in items(g:php_keywords)
+ if constant =~? '^'.a:base
+ let builtin_keywords[constant] = info
endif
+ endfor
+ endif
+ for [function_name, info] in items(g:php_builtin_functions)
+ if function_name =~? '^'.base
+ let builtin_functions[leading_slash.function_name] = info
endif
+ endfor
- if a:base =~ '^\$'
- let adddollar = '$'
+ " All constants
+ call extend(int_constants, ext_constants)
+
+ " All functions
+ call extend(int_functions, ext_functions)
+ call extend(int_functions, builtin_functions)
+
+ for [imported_name, import] in items(a:imports)
+ if imported_name =~? '^'.base
+ if import.kind ==? 'c'
+ if import.builtin
+ let builtin_classnames[imported_name] = ' '.import.name
+ else
+ let ext_classes[imported_name] = ' '.import.name.' - '.import.filename
+ endif
+ elseif import.kind ==? 'i'
+ if import.builtin
+ let builtin_interfaces[imported_name] = ' '.import.name
+ else
+ let ext_interfaces[imported_name] = ' '.import.name.' - '.import.filename
+ endif
+ endif
+
+ " no builtin interfaces
+ if import.kind == 'n'
+ let ext_namespaces[imported_name.'\'] = ' '.import.name.' - '.import.filename
+ endif
+ end
+ endfor
+
+ let all_values = {}
+
+ " Add functions found in this file
+ call extend(all_values, int_functions)
+
+ " Add namespaces from tags
+ call extend(all_values, ext_namespaces)
+
+ " Add constants from the current file
+ call extend(all_values, int_constants)
+
+ " Add built-in constants
+ call extend(all_values, builtin_constants)
+
+ " Add external classes
+ call extend(all_values, ext_classes)
+
+ " Add external interfaces
+ call extend(all_values, ext_interfaces)
+
+ " Add built-in classes
+ call extend(all_values, builtin_classnames)
+
+ " Add built-in interfaces
+ call extend(all_values, builtin_interfaces)
+
+ " Add php keywords
+ call extend(all_values, builtin_keywords)
+
+ let final_list = []
+ let int_list = sort(keys(all_values))
+ for i in int_list
+ if has_key(ext_namespaces, i)
+ let final_list += [{'word':i, 'kind':'n', 'menu': ext_namespaces[i], 'info': ext_namespaces[i]}]
+ elseif has_key(int_functions, i)
+ let final_list +=
+ \ [{'word':i,
+ \ 'info':i.int_functions[i],
+ \ 'menu':int_functions[i],
+ \ 'kind':'f'}]
+ elseif has_key(ext_classes, i) || has_key(builtin_classnames, i)
+ let info = has_key(ext_classes, i) ? ext_classes[i] : builtin_classnames[i].' - builtin'
+ let final_list += [{'word':i, 'kind': 'c', 'menu': info, 'info': i.info}]
+ elseif has_key(ext_interfaces, i) || has_key(builtin_interfaces, i)
+ let info = has_key(ext_interfaces, i) ? ext_interfaces[i] : builtin_interfaces[i].' - builtin'
+ let final_list += [{'word':i, 'kind': 'i', 'menu': info, 'info': i.info}]
+ elseif has_key(int_constants, i) || has_key(builtin_constants, i)
+ let info = has_key(int_constants, i) ? int_constants[i] : ' - builtin'
+ let final_list += [{'word':i, 'kind': 'd', 'menu': info, 'info': i.info}]
else
- let adddollar = ''
+ let final_list += [{'word':i}]
+ endif
+ endfor
+
+ return final_list
+endfunction
+" }}}
+
+function! phpcomplete#CompleteUnknownClass(base, context) " {{{
+ let res = []
+
+ if g:phpcomplete_complete_for_unknown_classes != 1
+ return []
+ endif
+
+ if a:base =~ '^\$'
+ let adddollar = '$'
+ else
+ let adddollar = ''
+ endif
+
+ let file = getline(1, '$')
+
+ " Internal solution for finding object properties in current file.
+ if a:context =~ '::'
+ let variables = filter(deepcopy(file),
+ \ 'v:val =~ "^\\s*\\(static\\|static\\s\\+\\(public\\|var\\)\\|\\(public\\|var\\)\\s\\+static\\)\\s\\+\\$"')
+ elseif a:context =~ '->'
+ let variables = filter(deepcopy(file),
+ \ 'v:val =~ "^\\s*\\(public\\|var\\)\\s\\+\\$"')
+ endif
+ let jvars = join(variables, ' ')
+ let svars = split(jvars, '\$')
+ let int_vars = {}
+ for i in svars
+ let c_var = matchstr(i,
+ \ '^\zs[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\ze')
+ if c_var != ''
+ let int_vars[adddollar.c_var] = ''
endif
- let file = getline(1, '$')
- let jfile = join(file, ' ')
- let sfile = split(jfile, '\$')
- let int_vars = {}
- for i in sfile
- if i =~ '^\$[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\s*=\s*new'
- let val = matchstr(i, '^[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*').'->'
+ endfor
+
+ " Internal solution for finding functions in current file.
+ call filter(deepcopy(file),
+ \ 'v:val =~ "function\\s\\+&\\?[a-zA-Z_\\x7f-\\xff][a-zA-Z_0-9\\x7f-\\xff]*\\s*("')
+ let jfile = join(file, ' ')
+ let int_values = split(jfile, 'function\s\+')
+ let int_functions = {}
+ for i in int_values
+ let f_name = matchstr(i,
+ \ '^&\?\zs[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\ze')
+ let f_args = matchstr(i,
+ \ '^&\?[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\s*(\zs.\{-}\ze)\_s*\({\|$\)')
+
+ let int_functions[f_name.'('] = f_args.')'
+ endfor
+
+ " collect external functions from tags
+ let ext_functions = {}
+ let tags = phpcomplete#GetTaglist('^'.substitute(a:base, '^\$', '', ''))
+ for tag in tags
+ if tag.kind ==? 'f'
+ let item = tag.name
+ if has_key(tag, 'signature')
+ let prototype = tag.signature[1:-2]
else
- let val = matchstr(i, '^[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*')
+ let prototype = matchstr(tag.cmd,
+ \ 'function\s\+&\?[^[:space:]]\+\s*(\s*\zs.\{-}\ze\s*)\s*{\?')
endif
- if val !~ ''
- let int_vars[adddollar.val] = ''
- endif
- endfor
+ let ext_functions[item.'('] = prototype.') - '.tag['filename']
+ endif
+ endfor
- " ctags has good support for PHP, use tags file for external
- " variables
- let fnames = join(map(tagfiles(), 'escape(v:val, " \\#%")'))
- let ext_vars = {}
- if fnames != ''
- let sbase = substitute(a:base, '^\$', '', '')
- exe 'silent! vimgrep /^'.sbase.'.*\tv\(\t\|$\)/j '.fnames
- let qflist = getqflist()
- if len(qflist) > 0
- for field in qflist
- let item = matchstr(field['text'], '^[^[:space:]]\+')
- " Add -> if it is possible object declaration
- let classname = ''
- if field['text'] =~ item.'\s*=\s*new\s\+'
- let item = item.'->'
- let classname = matchstr(field['text'],
- \ '=\s*new\s\+\zs[a-zA-Z_0-9\x7f-\xff]\+\ze')
- endif
- let ext_vars[adddollar.item] = classname
- endfor
+ " All functions to one hash for later reference when deciding kind
+ call extend(int_functions, ext_functions)
+
+ let all_values = {}
+ call extend(all_values, int_functions)
+ call extend(all_values, int_vars) " external variables are already in
+ call extend(all_values, g:php_builtin_object_functions)
+
+ for m in sort(keys(all_values))
+ if m =~ '\(^\|::\)'.a:base
+ call add(res, m)
+ endif
+ endfor
+
+ let start_list = res
+
+ let final_list = []
+ for i in start_list
+ if has_key(int_vars, i)
+ let class = ' '
+ if all_values[i] != ''
+ let class = i.' class '
endif
+ let final_list += [{'word':i, 'info':class.all_values[i], 'kind':'v'}]
+ else
+ let final_list +=
+ \ [{'word':substitute(i, '.*::', '', ''),
+ \ 'info':i.all_values[i],
+ \ 'menu':all_values[i],
+ \ 'kind':'f'}]
endif
+ endfor
+ return final_list
+endfunction
+" }}}
- " Now we have all variables in int_vars dictionary
- call extend(int_vars, ext_vars)
+function! phpcomplete#CompleteVariable(base) " {{{
+ let res = []
- " Internal solution for finding functions in current file.
- let file = getline(1, '$')
- call filter(file,
- \ 'v:val =~ "function\\s\\+&\\?[a-zA-Z_\\x7f-\\xff][a-zA-Z_0-9\\x7f-\\xff]*\\s*("')
- let fnames = join(map(tagfiles(), 'escape(v:val, " \\#%")'))
- let jfile = join(file, ' ')
- let int_values = split(jfile, 'function\s\+')
- let int_functions = {}
- for i in int_values
- let f_name = matchstr(i,
- \ '^&\?\zs[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\ze')
- let f_args = matchstr(i,
- \ '^&\?[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\s*(\zs.\{-}\ze)\_s*{')
- let int_functions[f_name.'('] = f_args.')'
- endfor
+ " Internal solution for current file.
+ let file = getline(1, '$')
+ let jfile = join(file, ' ')
+ let int_vals = split(jfile, '\ze\$')
+ let int_vars = {}
+ for i in int_vals
+ if i =~? '^\$[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*\s*=\s*new'
+ let val = matchstr(i,
+ \ '^\$[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*')
+ else
+ let val = matchstr(i,
+ \ '^\$[a-zA-Z_\x7f-\xff][a-zA-Z_0-9\x7f-\xff]*')
+ endif
+ if val != ''
+ let int_vars[val] = ''
+ endif
+ endfor
- " Prepare list of functions from tags file
- let ext_functions = {}
- if fnames != ''
- exe 'silent! vimgrep /^'.a:base.'.*\tf\(\t\|$\)/j '.fnames
- let qflist = getqflist()
- if len(qflist) > 0
- for field in qflist
- " File name
- let item = matchstr(field['text'], '^[^[:space:]]\+')
- let fname = matchstr(field['text'], '\t\zs\f\+\ze')
- let prototype = matchstr(field['text'],
- \ 'function\s\+&\?[^[:space:]]\+\s*(\s*\zs.\{-}\ze\s*)\s*{\?')
- let ext_functions[item.'('] = prototype.') - '.fname
- endfor
+ call extend(int_vars, g:php_builtin_vars)
+
+ " ctags has support for PHP, use tags file for external variables
+ if g:phpcomplete_search_tags_for_variables
+ let ext_vars = {}
+ let tags = phpcomplete#GetTaglist('\C^'.substitute(a:base, '^\$', '', ''))
+ for tag in tags
+ if tag.kind ==? 'v'
+ let item = tag.name
+ let m_menu = ''
+ if tag.cmd =~? tag['name'].'\s*=\s*new\s\+'
+ let m_menu = matchstr(tag.cmd,
+ \ '\c=\s*new\s\+\zs[a-zA-Z_0-9\x7f-\xff]\+\ze')
+ endif
+ let ext_vars['$'.item] = m_menu
endif
+ endfor
+ call extend(int_vars, ext_vars)
+ endif
+
+ for m in sort(keys(int_vars))
+ if m =~# '^\'.a:base
+ call add(res, m)
endif
+ endfor
- let all_values = {}
- call extend(all_values, int_functions)
- call extend(all_values, ext_functions)
- call extend(all_values, int_vars) " external variables are already in
- call extend(all_values, g:php_builtin_object_functions)
+ let int_list = res
- for m in sort(keys(all_values))
- if m =~ '\(^\|::\)'.a:base
- call add(res, m)
+ let int_dict = []
+ for i in int_list
+ if int_vars[i] != ''
+ let class = ' '
+ if int_vars[i] != ''
+ let class = i.' class '
endif
- endfor
+ let int_dict += [{'word':i, 'info':class.int_vars[i], 'menu':int_vars[i], 'kind':'v'}]
+ else
+ let int_dict += [{'word':i, 'kind':'v'}]
+ endif
+ endfor
- let start_list = res
+ return int_dict
+endfunction
+" }}}
- let final_list = []
- for i in start_list
- if has_key(int_vars, i)
- let class = ' '
- if all_values[i] != ''
- let class = i.' class '
- endif
- let final_list += [{'word':i, 'info':class.all_values[i], 'kind':'v'}]
- else
- let final_list +=
- \ [{'word':substitute(i, '.*::', '', ''),
- \ 'info':i.all_values[i],
- \ 'kind':'f'}]
+function! phpcomplete#CompleteClassName(base, kinds, current_namespace, imports) " {{{
+ let kinds = sort(a:kinds)
+ " Complete class name
+ let res = []
+ if a:base =~? '^\'
+ let leading_slash = '\'
+ let base = substitute(a:base, '^\', '', '')
+ else
+ let leading_slash = ''
+ let base = a:base
+ endif
+
+ " Internal solution for finding classes in current file.
+ let file = getline(1, '$')
+ let filterstr = ''
+
+ if kinds == ['c', 'i']
+ let f