summaryrefslogtreecommitdiff
path: root/base/vim/bundle/javacomplete/autoload/javacomplete.vim
diff options
context:
space:
mode:
Diffstat (limited to 'base/vim/bundle/javacomplete/autoload/javacomplete.vim')
-rw-r--r--base/vim/bundle/javacomplete/autoload/javacomplete.vim2932
1 files changed, 2932 insertions, 0 deletions
diff --git a/base/vim/bundle/javacomplete/autoload/javacomplete.vim b/base/vim/bundle/javacomplete/autoload/javacomplete.vim
new file mode 100644
index 0000000..c095dad
--- /dev/null
+++ b/base/vim/bundle/javacomplete/autoload/javacomplete.vim
@@ -0,0 +1,2932 @@
+" Vim completion script - hit 80% complete tasks
+" Version: 0.77.1.2
+" Language: Java
+" Maintainer: cheng fang <fangread@yahoo.com.cn>
+" Last Change: 2011-01-30
+" Copyright: Copyright (C) 2006-2007 cheng fang. All rights reserved.
+" License: Vim License (see vim's :help license)
+
+
+" constants {{{1
+" input context type
+let s:CONTEXT_AFTER_DOT = 1
+let s:CONTEXT_METHOD_PARAM = 2
+let s:CONTEXT_IMPORT = 3
+let s:CONTEXT_IMPORT_STATIC = 4
+let s:CONTEXT_PACKAGE_DECL = 6
+let s:CONTEXT_NEED_TYPE = 7
+let s:CONTEXT_OTHER = 0
+
+
+let s:ARRAY_TYPE_MEMBERS = [
+\ {'kind': 'm', 'word': 'clone(', 'abbr': 'clone()', 'menu': 'Object clone()', },
+\ {'kind': 'm', 'word': 'equals(', 'abbr': 'equals()', 'menu': 'boolean equals(Object)', },
+\ {'kind': 'm', 'word': 'getClass(', 'abbr': 'getClass()', 'menu': 'Class Object.getClass()', },
+\ {'kind': 'm', 'word': 'hashCode(', 'abbr': 'hashCode()', 'menu': 'int hashCode()', },
+\ {'kind': 'f', 'word': 'length', 'menu': 'int'},
+\ {'kind': 'm', 'word': 'notify(', 'abbr': 'notify()', 'menu': 'void Object.notify()', },
+\ {'kind': 'm', 'word': 'notifyAll(', 'abbr': 'notifyAll()', 'menu': 'void Object.notifyAll()', },
+\ {'kind': 'm', 'word': 'toString(', 'abbr': 'toString()', 'menu': 'String toString()', },
+\ {'kind': 'm', 'word': 'wait(', 'abbr': 'wait()', 'menu': 'void Object.wait() throws InterruptedException', },
+\ {'kind': 'm', 'dup': 1, 'word': 'wait(', 'abbr': 'wait()', 'menu': 'void Object.wait(long timeout) throws InterruptedException', },
+\ {'kind': 'm', 'dup': 1, 'word': 'wait(', 'abbr': 'wait()', 'menu': 'void Object.wait(long timeout, int nanos) throws InterruptedException', }]
+
+let s:ARRAY_TYPE_INFO = {'tag': 'CLASSDEF', 'name': '[', 'ctors': [],
+\ 'fields': [{'n': 'length', 'm': '1', 't': 'int'}],
+\ 'methods':[
+\ {'n': 'clone', 'm': '1', 'r': 'Object', 'p': [], 'd': 'Object clone()'},
+\ {'n': 'equals', 'm': '1', 'r': 'boolean', 'p': ['Object'], 'd': 'boolean Object.equals(Object obj)'},
+\ {'n': 'getClass', 'm': '100010001', 'r': 'Class', 'p': [], 'd': 'Class Object.getClass()'},
+\ {'n': 'hashCode', 'm': '100000001', 'r': 'int', 'p': [], 'd': 'int Object.hashCode()'},
+\ {'n': 'notify', 'm': '100010001', 'r': 'void', 'p': [], 'd': 'void Object.notify()'},
+\ {'n': 'notifyAll','m': '100010001', 'r': 'void', 'p': [], 'd': 'void Object.notifyAll()'},
+\ {'n': 'toString', 'm': '1', 'r': 'String', 'p': [], 'd': 'String Object.toString()'},
+\ {'n': 'wait', 'm': '10001', 'r': 'void', 'p': [], 'd': 'void Object.wait() throws InterruptedException'},
+\ {'n': 'wait', 'm': '100010001', 'r': 'void', 'p': ['long'], 'd': 'void Object.wait(long timeout) throws InterruptedException'},
+\ {'n': 'wait', 'm': '10001', 'r': 'void', 'p': ['long','int'], 'd': 'void Object.wait(long timeout, int nanos) throws InterruptedException'},
+\ ]}
+
+let s:PRIMITIVE_TYPE_INFO = {'tag': 'CLASSDEF', 'name': '!', 'fields': [{'n': 'class','m': '1','t': 'Class'}]}
+
+let s:JSP_BUILTIN_OBJECTS = {'session': 'javax.servlet.http.HttpSession',
+\ 'request': 'javax.servlet.http.HttpServletRequest',
+\ 'response': 'javax.servlet.http.HttpServletResponse',
+\ 'pageContext': 'javax.servlet.jsp.PageContext',
+\ 'application': 'javax.servlet.ServletContext',
+\ 'config': 'javax.servlet.ServletConfig',
+\ 'out': 'javax.servlet.jsp.JspWriter',
+\ 'page': 'javax.servlet.jsp.HttpJspPage', }
+
+
+let s:PRIMITIVE_TYPES = ['boolean', 'byte', 'char', 'int', 'short', 'long', 'float', 'double']
+let s:KEYWORDS_MODS = ['public', 'private', 'protected', 'static', 'final', 'synchronized', 'volatile', 'transient', 'native', 'strictfp', 'abstract']
+let s:KEYWORDS_TYPE = ['class', 'interface', 'enum']
+let s:KEYWORDS = s:PRIMITIVE_TYPES + s:KEYWORDS_MODS + s:KEYWORDS_TYPE + ['super', 'this', 'void'] + ['assert', 'break', 'case', 'catch', 'const', 'continue', 'default', 'do', 'else', 'extends', 'finally', 'for', 'goto', 'if', 'implements', 'import', 'instanceof', 'interface', 'new', 'package', 'return', 'switch', 'throw', 'throws', 'try', 'while', 'true', 'false', 'null']
+
+let s:PATH_SEP = ':'
+let s:FILE_SEP = '/'
+if has("win32") || has("win64") || has("win16") || has("dos32") || has("dos16")
+ let s:PATH_SEP = ';'
+ let s:FILE_SEP = '\'
+endif
+
+let s:RE_BRACKETS = '\%(\s*\[\s*\]\)'
+let s:RE_IDENTIFIER = '[a-zA-Z_$][a-zA-Z0-9_$]*'
+let s:RE_QUALID = s:RE_IDENTIFIER. '\%(\s*\.\s*' .s:RE_IDENTIFIER. '\)*'
+
+let s:RE_REFERENCE_TYPE = s:RE_QUALID . s:RE_BRACKETS . '*'
+let s:RE_TYPE = s:RE_REFERENCE_TYPE
+
+let s:RE_TYPE_ARGUMENT = '\%(?\s\+\%(extends\|super\)\s\+\)\=' . s:RE_TYPE
+let s:RE_TYPE_ARGUMENTS = '<' . s:RE_TYPE_ARGUMENT . '\%(\s*,\s*' . s:RE_TYPE_ARGUMENT . '\)*>'
+let s:RE_TYPE_WITH_ARGUMENTS_I = s:RE_IDENTIFIER . '\s*' . s:RE_TYPE_ARGUMENTS
+let s:RE_TYPE_WITH_ARGUMENTS = s:RE_TYPE_WITH_ARGUMENTS_I . '\%(\s*' . s:RE_TYPE_WITH_ARGUMENTS_I . '\)*'
+
+let s:RE_TYPE_MODS = '\%(public\|protected\|private\|abstract\|static\|final\|strictfp\)'
+let s:RE_TYPE_DECL_HEAD = '\(class\|interface\|enum\)[ \t\n\r ]\+'
+let s:RE_TYPE_DECL = '\<\C\(\%(' .s:RE_TYPE_MODS. '\s\+\)*\)' .s:RE_TYPE_DECL_HEAD. '\(' .s:RE_IDENTIFIER. '\)[< \t\n\r ]'
+
+let s:RE_ARRAY_TYPE = '^\s*\(' .s:RE_QUALID . '\)\(' . s:RE_BRACKETS . '\+\)\s*$'
+let s:RE_SELECT_OR_ACCESS = '^\s*\(' . s:RE_IDENTIFIER . '\)\s*\(\[.*\]\)\=\s*$'
+let s:RE_ARRAY_ACCESS = '^\s*\(' . s:RE_IDENTIFIER . '\)\s*\(\[.*\]\)\+\s*$'
+let s:RE_CASTING = '^\s*(\(' .s:RE_QUALID. '\))\s*\(' . s:RE_IDENTIFIER . '\)\>'
+
+let s:RE_KEYWORDS = '\<\%(' . join(s:KEYWORDS, '\|') . '\)\>'
+
+
+" local variables {{{1
+let b:context_type = s:CONTEXT_OTHER
+"let b:statement = '' " statement before cursor
+let b:dotexpr = '' " expression ends with '.'
+let b:incomplete = '' " incomplete word: 1. dotexpr.method(|) 2. new classname(|) 3. dotexpr.ab|, 4. ja|, 5. method(|
+let b:errormsg = ''
+
+" script variables {{{1
+let s:cache = {} " FQN -> member list, e.g. {'java.lang.StringBuffer': classinfo, 'java.util': packageinfo, '/dir/TopLevelClass.java': compilationUnit}
+let s:files = {} " srouce file path -> properties, e.g. {filekey: {'unit': compilationUnit, 'changedtick': tick, }}
+let s:history = {} "
+
+
+" This function is used for the 'omnifunc' option. {{{1
+function! javacomplete#Complete(findstart, base)
+ if a:findstart
+ let s:et_whole = reltime()
+ let start = col('.') - 1
+ let s:log = []
+
+ " reset enviroment
+ let b:dotexpr = ''
+ let b:incomplete = ''
+ let b:context_type = s:CONTEXT_OTHER
+
+ let statement = s:GetStatement()
+ call s:WatchVariant('statement: "' . statement . '"')
+
+ if statement =~ '[.0-9A-Za-z_]\s*$'
+ let valid = 1
+ if statement =~ '\.\s*$'
+ let valid = statement =~ '[")0-9A-Za-z_\]]\s*\.\s*$' && statement !~ '\<\H\w\+\.\s*$' && statement !~ '\<\(abstract\|assert\|break\|case\|catch\|const\|continue\|default\|do\|else\|enum\|extends\|final\|finally\|for\|goto\|if\|implements\|import\|instanceof\|interface\|native\|new\|package\|private\|protected\|public\|return\|static\|strictfp\|switch\|synchronized\|throw\|throws\|transient\|try\|volatile\|while\|true\|false\|null\)\.\s*$'
+ endif
+ if !valid
+ return -1
+ endif
+
+ let b:context_type = s:CONTEXT_AFTER_DOT
+
+ " import or package declaration
+ if statement =~# '^\s*\(import\|package\)\s\+'
+ let statement = substitute(statement, '\s\+\.', '.', 'g')
+ let statement = substitute(statement, '\.\s\+', '.', 'g')
+ if statement =~ '^\s*import\s\+'
+ let b:context_type = statement =~# '\<static\s\+' ? s:CONTEXT_IMPORT_STATIC : s:CONTEXT_IMPORT
+ let b:dotexpr = substitute(statement, '^\s*import\s\+\(static\s\+\)\?', '', '')
+ else
+ let b:context_type = s:CONTEXT_PACKAGE_DECL
+ let b:dotexpr = substitute(statement, '\s*package\s\+', '', '')
+ endif
+
+ " String literal
+ elseif statement =~ '"\s*\.\s*$'
+ let b:dotexpr = substitute(statement, '\s*\.\s*$', '\.', '')
+ return start - strlen(b:incomplete)
+
+ else
+ " type declaration NOTE: not supported generic yet.
+ let idx_type = matchend(statement, '^\s*' . s:RE_TYPE_DECL)
+ if idx_type != -1
+ let b:dotexpr = strpart(statement, idx_type)
+ " return if not after extends or implements
+ if b:dotexpr !~ '^\(extends\|implements\)\s\+'
+ return -1
+ endif
+ let b:context_type = s:CONTEXT_NEED_TYPE
+ endif
+
+ let b:dotexpr = s:ExtractCleanExpr(statement)
+ endif
+
+ " all cases: " java.ut|" or " java.util.|" or "ja|"
+ let b:incomplete = strpart(b:dotexpr, strridx(b:dotexpr, '.')+1)
+ let b:dotexpr = strpart(b:dotexpr, 0, strridx(b:dotexpr, '.')+1)
+ return start - strlen(b:incomplete)
+
+
+ " method parameters, treat methodname or 'new' as an incomplete word
+ elseif statement =~ '(\s*$'
+ " TODO: Need to exclude method declaration?
+ let b:context_type = s:CONTEXT_METHOD_PARAM
+ let pos = strridx(statement, '(')
+ let s:padding = strpart(statement, pos+1)
+ let start = start - (len(statement) - pos)
+
+ let statement = substitute(statement, '\s*(\s*$', '', '')
+
+ " new ClassName?
+ let str = matchstr(statement, '\<new\s\+' . s:RE_QUALID . '$')
+ if str != ''
+ let str = substitute(str, '^new\s\+', '', '')
+ if !s:IsKeyword(str)
+ let b:incomplete = '+'
+ let b:dotexpr = str
+ return start - len(b:dotexpr)
+ endif
+
+ " normal method invocations
+ else
+ let pos = match(statement, '\s*' . s:RE_IDENTIFIER . '$')
+ " case: "method(|)", "this(|)", "super(|)"
+ if pos == 0
+ let statement = substitute(statement, '^\s*', '', '')
+ " treat "this" or "super" as a type name.
+ if statement == 'this' || statement == 'super'
+ let b:dotexpr = statement
+ let b:incomplete = '+'
+ return start - len(b:dotexpr)
+
+ elseif !s:IsKeyword(statement)
+ let b:incomplete = statement
+ return start - strlen(b:incomplete)
+ endif
+
+ " case: "expr.method(|)"
+ elseif statement[pos-1] == '.' && !s:IsKeyword(strpart(statement, pos))
+ let b:dotexpr = s:ExtractCleanExpr(strpart(statement, 0, pos))
+ let b:incomplete = strpart(statement, pos)
+ return start - strlen(b:incomplete)
+ endif
+ endif
+ endif
+
+ return -1
+ endif
+
+
+ " Return list of matches.
+
+ call s:WatchVariant('b:context_type: "' . b:context_type . '" b:incomplete: "' . b:incomplete . '" b:dotexpr: "' . b:dotexpr . '"')
+ if b:dotexpr =~ '^\s*$' && b:incomplete =~ '^\s*$'
+ return []
+ endif
+
+
+ let result = []
+ if b:dotexpr !~ '^\s*$'
+ if b:context_type == s:CONTEXT_AFTER_DOT
+ let result = s:CompleteAfterDot(b:dotexpr)
+ elseif b:context_type == s:CONTEXT_IMPORT || b:context_type == s:CONTEXT_IMPORT_STATIC || b:context_type == s:CONTEXT_PACKAGE_DECL || b:context_type == s:CONTEXT_NEED_TYPE
+ let result = s:GetMembers(b:dotexpr[:-2])
+ elseif b:context_type == s:CONTEXT_METHOD_PARAM
+ if b:incomplete == '+'
+ let result = s:GetConstructorList(b:dotexpr)
+ else
+ let result = s:CompleteAfterDot(b:dotexpr)
+ endif
+ endif
+
+ " only incomplete word
+ elseif b:incomplete !~ '^\s*$'
+ " only need methods
+ if b:context_type == s:CONTEXT_METHOD_PARAM
+ let methods = s:SearchForName(b:incomplete, 0, 1)[1]
+ call extend(result, eval('[' . s:DoGetMethodList(methods) . ']'))
+
+ else
+ let result = s:CompleteAfterWord(b:incomplete)
+ endif
+
+ " then no filter needed
+ let b:incomplete = ''
+ endif
+
+
+ if len(result) > 0
+ " filter according to b:incomplete
+ if len(b:incomplete) > 0 && b:incomplete != '+'
+ let result = filter(result, "type(v:val) == type('') ? v:val =~ '^" . b:incomplete . "' : v:val['word'] =~ '^" . b:incomplete . "'")
+ endif
+
+ if exists('s:padding') && !empty(s:padding)
+ for item in result
+ if type(item) == type("")
+ let item .= s:padding
+ else
+ let item.word .= s:padding
+ endif
+ endfor
+ unlet s:padding
+ endif
+
+ call s:Debug('finish completion' . reltimestr(reltime(s:et_whole)) . 's')
+ return result
+ endif
+
+ if strlen(b:errormsg) > 0
+ echoerr 'javacomplete error: ' . b:errormsg
+ let b:errormsg = ''
+ endif
+endfunction
+
+" Precondition: incomplete must be a word without '.'.
+" return all the matched, variables, fields, methods, types, packages
+fu! s:CompleteAfterWord(incomplete)
+ " packages in jar files
+ if !exists('s:all_packages_in_jars_loaded')
+ call s:DoGetInfoByReflection('-', '-P')
+ let s:all_packages_in_jars_loaded = 1
+ endif
+
+ let pkgs = []
+ let types = []
+ for key in keys(s:cache)
+ if key =~# '^' . a:incomplete
+ if type(s:cache[key]) == type('') || get(s:cache[key], 'tag', '') == 'PACKAGE'
+ call add(pkgs, {'kind': 'P', 'word': key})
+
+ " filter out type info
+ elseif b:context_type != s:CONTEXT_PACKAGE_DECL && b:context_type != s:CONTEXT_IMPORT && b:context_type != s:CONTEXT_IMPORT_STATIC
+ call add(types, {'kind': 'C', 'word': key})
+ endif
+ endif
+ endfor
+
+ let pkgs += s:DoGetPackageInfoInDirs(a:incomplete, b:context_type == s:CONTEXT_PACKAGE_DECL, 1)
+
+
+ " add accessible types which name beginning with the incomplete in source files
+ " TODO: remove the inaccessible
+ if b:context_type != s:CONTEXT_PACKAGE_DECL
+ " single type import
+ for fqn in s:GetImports('imports_fqn')
+ let name = fqn[strridx(fqn, ".")+1:]
+ if name =~ '^' . a:incomplete
+ call add(types, {'kind': 'C', 'word': name})
+ endif
+ endfor
+
+ " current file
+ let lnum_old = line('.')
+ let col_old = col('.')
+ call cursor(1, 1)
+ while 1
+ let lnum = search('\<\C\(class\|interface\|enum\)[ \t\n\r ]\+' . a:incomplete . '[a-zA-Z0-9_$]*[< \t\n\r ]', 'W')
+ if lnum == 0
+ break
+ elseif s:InCommentOrLiteral(line('.'), col('.'))
+ continue
+ else
+ normal w
+ call add(types, {'kind': 'C', 'word': matchstr(getline(line('.'))[col('.')-1:], s:RE_IDENTIFIER)})
+ endif
+ endwhile
+ call cursor(lnum_old, col_old)
+
+ " other files
+ let filepatterns = ''
+ for dirpath in s:GetSourceDirs(expand('%:p'))
+ let filepatterns .= escape(dirpath, ' \') . '/*.java '
+ endfor
+ exe 'vimgrep /\s*' . s:RE_TYPE_DECL . '/jg ' . filepatterns
+ for item in getqflist()
+ if item.text !~ '^\s*\*\s\+'
+ let text = matchstr(s:Prune(item.text, -1), '\s*' . s:RE_TYPE_DECL)
+ if text != ''
+ let subs = split(substitute(text, '\s*' . s:RE_TYPE_DECL, '\1;\2;\3', ''), ';', 1)
+ if subs[2] =~# '^' . a:incomplete && (subs[0] =~ '\C\<public\>' || fnamemodify(bufname(item.bufnr), ':p:h') == expand('%:p:h'))
+ call add(types, {'kind': 'C', 'word': subs[2]})
+ endif
+ endif
+ endif
+ endfor
+ endif
+
+
+ let result = []
+
+ " add variables and members in source files
+ if b:context_type == s:CONTEXT_AFTER_DOT
+ let matches = s:SearchForName(a:incomplete, 0, 0)
+ let result += sort(eval('[' . s:DoGetFieldList(matches[2]) . ']'))
+ let result += sort(eval('[' . s:DoGetMethodList(matches[1]) . ']'))
+ endif
+ let result += sort(pkgs)
+ let result += sort(types)
+
+ return result
+endfu
+
+
+" Precondition: expr must end with '.'
+" return members of the value of expression
+function! s:CompleteAfterDot(expr)
+ let items = s:ParseExpr(a:expr) " TODO: return a dict containing more than items
+ if empty(items)
+ return []
+ endif
+
+
+ " 0. String literal
+ call s:Info('P0. "str".|')
+ if items[-1] =~ '"$'
+ return s:GetMemberList("java.lang.String")
+ endif
+
+
+ let ti = {}
+ let ii = 1 " item index
+ let itemkind = 0
+
+ "
+ " optimized process
+ "
+ " search the longest expr consisting of ident
+ let i = 1
+ let k = i
+ while i < len(items) && items[i] =~ '^\s*' . s:RE_IDENTIFIER . '\s*$'
+ let ident = substitute(items[i], '\s', '', 'g')
+ if ident == 'class' || ident == 'this' || ident == 'super'
+ let k = i
+ " return when found other keywords
+ elseif s:IsKeyword(ident)
+ return []
+ endif
+ let items[i] = substitute(items[i], '\s', '', 'g')
+ let i += 1
+ endwhile
+
+ if i > 1
+ " cases: "this.|", "super.|", "ClassName.this.|", "ClassName.super.|", "TypeName.class.|"
+ if items[k] ==# 'class' || items[k] ==# 'this' || items[k] ==# 'super'
+ call s:Info('O1. ' . items[k] . ' ' . join(items[:k-1], '.'))
+ let ti = s:DoGetClassInfo(items[k] == 'class' ? 'java.lang.Class' : join(items[:k-1], '.'))
+ if !empty(ti)
+ let itemkind = items[k] ==# 'this' ? 1 : items[k] ==# 'super' ? 2 : 0
+ let ii = k+1
+ else
+ return []
+ endif
+
+ " case: "java.io.File.|"
+ else
+ let fqn = join(items[:i-1], '.')
+ let srcpath = join(s:GetSourceDirs(expand('%:p'), s:GetPackageName()), ',')
+ call s:Info('O2. ' . fqn)
+ call s:DoGetTypeInfoForFQN([fqn], srcpath)
+ if get(get(s:cache, fqn, {}), 'tag', '') == 'CLASSDEF'
+ let ti = s:cache[fqn]
+ let itemkind = 11
+ let ii = i
+ endif
+ endif
+ endif
+
+
+ "
+ " first item
+ "
+ if empty(ti)
+ " cases:
+ " 1) "int.|", "void.|" - primitive type or pseudo-type, return `class`
+ " 2) "this.|", "super.|" - special reference
+ " 3) "var.|" - variable or field
+ " 4) "String.|" - type imported or defined locally
+ " 5) "java.|" - package
+ if items[0] =~ '^\s*' . s:RE_IDENTIFIER . '\s*$'
+ let ident = substitute(items[0], '\s', '', 'g')
+
+ if s:IsKeyword(ident)
+ " 1)
+ call s:Info('F1. "' . ident . '.|"')
+ if ident ==# 'void' || s:IsBuiltinType(ident)
+ let ti = s:PRIMITIVE_TYPE_INFO
+ let itemkind = 11
+
+ " 2)
+ call s:Info('F2. "' . ident . '.|"')
+ elseif ident ==# 'this' || ident ==# 'super'
+ let itemkind = ident ==# 'this' ? 1 : ident ==# 'super' ? 2 : 0
+ let ti = s:DoGetClassInfo(ident)
+ endif
+
+ else
+ " 3)
+ let typename = s:GetDeclaredClassName(ident)
+ call s:Info('F3. "' . ident . '.|" typename: "' . typename . '"')
+ if (typename != '')
+ if typename[0] == '[' || typename[-1:] == ']'
+ let ti = s:ARRAY_TYPE_INFO
+ elseif typename != 'void' && !s:IsBuiltinType(typename)
+ let ti = s:DoGetClassInfo(typename)
+ endif
+
+ else
+ " 4)
+ call s:Info('F4. "TypeName.|"')
+ let ti = s:DoGetClassInfo(ident)
+ let itemkind = 11
+
+ if get(ti, 'tag', '') != 'CLASSDEF'
+ let ti = {}
+ endif
+
+ " 5)
+ if empty(ti)
+ call s:Info('F5. "package.|"')
+ unlet ti
+ let ti = s:GetMembers(ident) " s:DoGetPackegInfo(ident)
+ let itemkind = 20
+ endif
+ endif
+ endif
+
+ " method invocation: "method().|" - "this.method().|"
+ elseif items[0] =~ '^\s*' . s:RE_IDENTIFIER . '\s*('
+ let ti = s:MethodInvocation(items[0], ti, itemkind)
+
+ " array type, return `class`: "int[] [].|", "java.lang.String[].|", "NestedClass[].|"
+ elseif items[0] =~# s:RE_ARRAY_TYPE
+ call s:Info('array type. "' . items[0] . '"')
+ let qid = substitute(items[0], s:RE_ARRAY_TYPE, '\1', '')
+ if s:IsBuiltinType(qid) || (!s:HasKeyword(qid) && !empty(s:DoGetClassInfo(qid)))
+ let ti = s:PRIMITIVE_TYPE_INFO
+ let itemkind = 11
+ endif
+
+ " class instance creation expr: "new String().|", "new NonLoadableClass().|"
+ " array creation expr: "new int[i=1] [val()].|", "new java.lang.String[].|"
+ elseif items[0] =~ '^\s*new\s\+'
+ call s:Info('creation expr. "' . items[0] . '"')
+ let subs = split(substitute(items[0], '^\s*new\s\+\(' .s:RE_QUALID. '\)\s*\([([]\)', '\1;\2', ''), ';')
+ if subs[1][0] == '['
+ let ti = s:ARRAY_TYPE_INFO
+ elseif subs[1][0] == '('
+ let ti = s:DoGetClassInfo(subs[0])
+ " exclude interfaces and abstract class. TODO: exclude the inaccessible
+ if get(ti, 'flags', '')[-10:-10] || get(ti, 'flags', '')[-11:-11]
+ echo 'cannot instantiate the type ' . subs[0]
+ let ti = {}
+ return []
+ endif
+ endif
+
+ " casting conversion: "(Object)o.|"
+ elseif items[0] =~ s:RE_CASTING
+ call s:Info('Casting conversion. "' . items[0] . '"')
+ let subs = split(substitute(items[0], s:RE_CASTING, '\1;\2', ''), ';')
+ let ti = s:DoGetClassInfo(subs[0])
+
+ " array access: "var[i][j].|" Note: "var[i][]" is incorrect
+ elseif items[0] =~# s:RE_ARRAY_ACCESS
+ let subs = split(substitute(items[0], s:RE_ARRAY_ACCESS, '\1;\2', ''), ';')
+ if get(subs, 1, '') !~ s:RE_BRACKETS
+ let typename = s:GetDeclaredClassName(subs[0])
+ call s:Info('ArrayAccess. "' .items[0]. '.|" typename: "' . typename . '"')
+ if (typename != '')
+ let ti = s:ArrayAccess(typename, items[0])
+ endif
+ endif
+ endif
+ endif
+
+
+ "
+ " next items
+ "
+ while !empty(ti) && ii < len(items)
+ " method invocation: "PrimaryExpr.method(parameters)[].|"
+ if items[ii] =~ '^\s*' . s:RE_IDENTIFIER . '\s*('
+ let ti = s:MethodInvocation(items[ii], ti, itemkind)
+ let itemkind = 0
+ let ii += 1
+ continue
+
+
+ " expression of selection, field access, array access
+ elseif items[ii] =~ s:RE_SELECT_OR_ACCESS
+ let subs = split(substitute(items[ii], s:RE_SELECT_OR_ACCESS, '\1;\2', ''), ';')
+ let ident = subs[0]
+ let brackets = get(subs, 1, '')
+
+ " package members
+ if itemkind/10 == 2 && empty(brackets) && !s:IsKeyword(ident)
+ let qn = join(items[:ii], '.')
+ if type(ti) == type([])
+ let idx = s:Index(ti, ident, 'word')
+ if idx >= 0
+ if ti[idx].kind == 'P'
+ unlet ti
+ let ti = s:GetMembers(qn)
+ let ii += 1
+ continue
+ elseif ti[idx].kind == 'C'
+ unlet ti
+ let ti = s:DoGetClassInfo(qn)
+ let itemkind = 11
+ let ii += 1
+ continue
+ endif
+ endif
+ endif
+
+
+ " type members
+ elseif itemkind/10 == 1 && empty(brackets)
+ if ident ==# 'class' || ident ==# 'this' || ident ==# 'super'
+ let ti = s:DoGetClassInfo(ident == 'class' ? 'java.lang.Class' : join(items[:ii-1], '.'))
+ let itemkind = ident ==# 'this' ? 1 : ident ==# 'super' ? 2 : 0
+ let ii += 1
+ continue
+
+ elseif !s:IsKeyword(ident) && type(ti) == type({}) && get(ti, 'tag', '') == 'CLASSDEF'
+ " accessible static field
+ "let idx = s:Index(get(ti, 'fields', []), ident, 'n')
+ "if idx >= 0 && s:IsStatic(ti.fields[idx].m)
+ " let ti = s:ArrayAccess(ti.fields[idx].t, items[ii])
+ let members = s:SearchMember(ti, ident, 1, itemkind, 1, 0)
+ if !empty(members[2])
+ let ti = s:ArrayAccess(members[2][0].t, items[ii])
+ let itemkind = 0
+ let ii += 1
+ continue
+ endif
+
+ " accessible nested type
+ "if !empty(filter(copy(get(ti, 'classes', [])), 'strpart(v:val, strridx(v:val, ".")) ==# "' . ident . '"'))
+ if !empty(members[0])
+ let ti = s:DoGetClassInfo(join(items[:ii], '.'))
+ let ii += 1
+ continue
+ endif
+ endif
+
+
+ " instance members
+ elseif itemkind/10 == 0 && !s:IsKeyword(ident)
+ if type(ti) == type({}) && get(ti, 'tag', '') == 'CLASSDEF'
+ "let idx = s:Index(get(ti, 'fields', []), ident, 'n')
+ "if idx >= 0
+ " let ti = s:ArrayAccess(ti.fields[idx].t, items[ii])
+ let members = s:SearchMember(ti, ident, 1, itemkind, 1, 0)
+ let itemkind = 0
+ if !empty(members[2])
+ let ti = s:ArrayAccess(members[2][0].t, items[ii])
+ let ii += 1
+ continue
+ endif
+ endif
+ endif
+ endif
+
+ return []
+ endwhile
+
+
+ " type info or package info --> members
+ if !empty(ti)
+ if type(ti) == type({})
+ if get(ti, 'tag', '') == 'CLASSDEF'
+ if get(ti, 'name', '') == '!'
+ return [{'kind': 'f', 'word': 'class', 'menu': 'Class'}]
+ elseif get(ti, 'name', '') == '['
+ return s:ARRAY_TYPE_MEMBERS
+ elseif itemkind < 20
+ return s:DoGetMemberList(ti, itemkind)
+ endif
+ elseif get(ti, 'tag', '') == 'PACKAGE'
+ " TODO: ti -> members, in addition to packages in dirs
+ return s:GetMembers( substitute(join(items, '.'), '\s', '', 'g') )
+ endif
+ elseif type(ti) == type([])
+ return ti
+ endif
+ endif
+
+ return []
+endfunction
+
+
+fu! s:MethodInvocation(expr, ti, itemkind)
+ let subs = split(substitute(a:expr, '\s*\(' . s:RE_IDENTIFIER . '\)\s*\((.*\)', '\1;\2', ''), ';')
+
+ " all methods matched
+ if empty(a:ti)
+ let methods = s:SearchForName(subs[0], 0, 1)[1]
+ elseif type(a:ti) == type({}) && get(a:ti, 'tag', '') == 'CLASSDEF'
+ let methods = s:SearchMember(a:ti, subs[0], 1, a:itemkind, 1, 0, a:itemkind == 2)[1]
+" let methods = s:filter(get(a:ti, 'methods', []), 'item.n == "' . subs[0] . '"')
+" if a:itemkind == 1 || a:itemkind == 2
+" let methods += s:filter(get(a:ti, 'declared_methods', []), 'item.n == "' . subs[0] . '"')
+" endif
+ else
+ let methods = []
+ endif
+
+ let method = s:DetermineMethod(methods, subs[1])
+ if !empty(method)
+ return s:ArrayAccess(method.r, a:expr)
+ endif
+ return {}
+endfu
+
+fu! s:ArrayAccess(arraytype, expr)
+ if a:expr =~ s:RE_BRACKETS | return {} | endif
+ let typename = a:arraytype
+
+ let dims = 0
+ if typename[0] == '[' || typename[-1:] == ']' || a:expr[-1:] == ']'
+ let dims = s:CountDims(a:expr) - s:CountDims(typename)
+ if dims == 0
+ let typename = matchstr(typename, s:RE_IDENTIFIER)
+ elseif dims < 0
+ return s:ARRAY_TYPE_INFO
+ else
+ "echoerr 'dims exceeds'
+ endif
+ endif
+ if dims == 0
+ if typename != 'void' && !s:IsBuiltinType(typename)
+ return s:DoGetClassInfo(typename)
+ endif
+ endif
+ return {}
+endfu
+
+
+" Quick information {{{1
+function! MyBalloonExpr()
+ if (searchdecl(v:beval_text, 1, 0) == 0)
+ return s:GetVariableDeclaration()
+ endif
+ return ''
+" return 'Cursor is at line ' . v:beval_lnum .
+" \', column ' . v:beval_col .
+" \ ' of file ' . bufname(v:beval_bufnr) .
+" \ ' on word "' . v:beval_text . '"'
+endfunction
+"set bexpr=MyBalloonExpr()
+"set ballooneval
+
+" parameters information {{{1
+fu! javacomplete#CompleteParamsInfo(findstart, base)
+ if a:findstart
+ return col('.') - 1
+ endif
+
+
+ let mi = s:GetMethodInvocationExpr(s:GetStatement())
+ if empty(mi.method)
+ return []
+ endif
+
+ " TODO: how to determine overloaded functions
+ "let mi.params = s:EvalParams(mi.params)
+ if empty(mi.expr)
+ let methods = s:SearchForName(mi.method, 0, 1)[1]
+ let result = eval('[' . s:DoGetMethodList(methods) . ']')
+ elseif mi.method == '+'
+ let result = s:GetConstructorList(mi.expr)
+ else
+ let result = s:CompleteAfterDot(mi.expr)
+ endif
+
+ if !empty(result)
+ if !empty(mi.method) && mi.method != '+'
+ let result = filter(result, "type(v:val) == type('') ? v:val ==# '" . mi.method . "' : v:val['word'] ==# '" . mi.method . "('")
+ endif
+ return result
+ endif
+endfu
+
+" scanning and parsing {{{1
+
+" Search back from the cursor position till meeting '{' or ';'.
+" '{' means statement start, ';' means end of a previous statement.
+" Return: statement before cursor
+" Note: It's the base for parsing. And It's OK for most cases.
+function! s:GetStatement()
+ if getline('.') =~ '^\s*\(import\|package\)\s\+'
+ return strpart(getline('.'), match(getline('.'), '\(import\|package\)'), col('.')-1)
+ endif
+
+ let lnum_old = line('.')
+ let col_old = col('.')
+
+ while 1
+ if search('[{};]\|<%\|<%!', 'bW') == 0
+ let lnum = 1
+ let col = 1
+ else
+ if s:InCommentOrLiteral(line('.'), col('.'))
+ continue
+ endif
+
+ normal w
+ let lnum = line('.')
+ let col = col('.')
+ endif
+ break
+ endwhile
+
+ silent call cursor(lnum_old, col_old)
+ return s:MergeLines(lnum, col, lnum_old, col_old)
+endfunction
+
+fu! s:MergeLines(lnum, col, lnum_old, col_old)
+ let lnum = a:lnum
+ let col = a:col
+
+ let str = ''
+ if lnum < a:lnum_old
+ let str = s:Prune(strpart(getline(lnum), a:col-1))
+ let lnum += 1
+ while lnum < a:lnum_old
+ let str .= s:Prune(getline(lnum))
+ let lnum += 1
+ endwhile
+ let col = 1
+ endif
+ let lastline = strpart(getline(a:lnum_old), col-1, a:col_old-col)
+ let str .= s:Prune(lastline, col)
+ let str = s:RemoveBlockComments(str)
+ " generic in JAVA 5+
+ while match(str, s:RE_TYPE_ARGUMENTS) != -1
+ let str = substitute(str, '\(' . s:RE_TYPE_ARGUMENTS . '\)', '\=repeat(" ", len(submatch(1)))', 'g')
+ endwhile
+ let str = substitute(str, '\s\s\+', ' ', 'g')
+ let str = substitute(str, '\([.()]\)[ \t]\+', '\1', 'g')
+ let str = substitute(str, '[ \t]\+\([.()]\)', '\1', 'g')
+ return s:Trim(str) . matchstr(lastline, '\s*$')
+endfu
+
+" Extract a clean expr, removing some non-necessary characters.
+fu! s:ExtractCleanExpr(expr)
+ let cmd = substitute(a:expr, '[ \t\r\n ]\+\([.()[\]]\)', '\1', 'g')
+ let cmd = substitute(cmd, '\([.()[\]]\)[ \t\r\n ]\+', '\1', 'g')
+
+ let pos = strlen(cmd)-1
+ while pos >= 0 && cmd[pos] =~ '[a-zA-Z0-9_.)\]]'
+ if cmd[pos] == ')'
+ let pos = s:SearchPairBackward(cmd, pos, '(', ')')
+ elseif cmd[pos] == ']'
+ let pos = s:SearchPairBackward(cmd, pos, '[', ']')
+ endif
+ let pos -= 1
+ endwhile
+
+ " try looking back for "new"
+ let idx = match(strpart(cmd, 0, pos+1), '\<new[ \t\r\n ]*$')
+
+ return strpart(cmd, idx != -1 ? idx : pos+1)
+endfu
+
+fu! s:ParseExpr(expr)
+ let items = []
+ let s = 0
+ " recognize ClassInstanceCreationExpr as a whole
+ let e = matchend(a:expr, '^\s*new\s\+' . s:RE_QUALID . '\s*[([]')-1
+ if e < 0
+ let e = match(a:expr, '[.([]')
+ endif
+ let isparen = 0
+ while e >= 0
+ if a:expr[e] == '.'
+ let subexpr = strpart(a:expr, s, e-s)
+ call extend(items, isparen ? s:ProcessParentheses(subexpr) : [subexpr])
+ let isparen = 0
+ let s = e + 1
+ elseif a:expr[e] == '('
+ let e = s:GetMatchedIndexEx(a:expr, e, '(', ')')
+ let isparen = 1
+ if e < 0
+ break
+ else
+ let e = matchend(a:expr, '^\s*[.[]', e+1)-1
+ continue
+ endif
+ elseif a:expr[e] == '['
+ let e = s:GetMatchedIndexEx(a:expr, e, '[', ']')
+ if e < 0
+ break
+ else
+ let e = matchend(a:expr, '^\s*[.[]', e+1)-1
+ continue
+ endif
+ endif
+ let e = match(a:expr, '[.([]', s)
+ endwhile
+ let tail = strpart(a:expr, s)
+ if tail !~ '^\s*$'
+ call extend(items, isparen ? s:ProcessParentheses(tail) : [tail])
+ endif
+
+ return items
+endfu
+
+" Given optional argument, call s:ParseExpr() to parser the nonparentheses expr
+fu! s:ProcessParentheses(expr, ...)
+ let s = matchend(a:expr, '^\s*(')
+ if s != -1
+ let e = s:GetMatchedIndexEx(a:expr, s-1, '(', ')')
+ if e >= 0
+ let tail = strpart(a:expr, e+1)
+ if tail =~ '^\s*[\=$'
+ return s:ProcessParentheses(strpart(a:expr, s, e-s), 1)
+ elseif tail =~ '^\s*\w'
+ return [strpart(a:expr, 0, e+1) . 'obj.']
+ endif
+ endif
+
+ " multi-dot-expr except for new expr
+ elseif a:0 > 0 && stridx(a:expr, '.') != match(a:expr, '\.\s*$') && a:expr !~ '^\s*new\s\+'
+ return s:ParseExpr(a:expr)
+ endif
+ return [a:expr]
+endfu
+
+" return {'expr': , 'method': , 'params': }
+fu! s:GetMethodInvocationExpr(expr)
+ let idx = strlen(a:expr)-1
+ while idx >= 0
+ if a:expr[idx] == '('
+ break
+ elseif a:expr[idx] == ')'
+ let idx = s:SearchPairBackward(a:expr, idx, '(', ')')
+ elseif a:expr[idx] == ']'
+ let idx = s:SearchPairBackward(a:expr, idx, '[', ']')
+ endif
+ let idx -= 1
+ endwhile
+
+ let mi = {'expr': strpart(a:expr, 0, idx+1), 'method': '', 'params': strpart(a:expr, idx+1)}
+ let idx = match(mi.expr, '\<new\s\+' . s:RE_QUALID . '\s*(\s*$')
+ if idx >= 0
+ let mi.method = '+'
+ let mi.expr = substitute(matchstr(strpart(mi.expr, idx+4), s:RE_QUALID), '\s', '', 'g')
+ else
+ let idx = match(mi.expr, '\<' . s:RE_IDENTIFIER . '\s*(\s*$')
+ if idx >= 0
+ let subs = s:SplitAt(mi.expr, idx-1)
+ let mi.method = substitute(subs[1], '\s*(\s*$', '', '')
+ let mi.expr = s:ExtractCleanExpr(subs[0])
+ endif
+ endif
+ return mi
+endfu
+
+" imports {{{1
+function! s:GenerateImports()
+ let imports = []
+
+ let lnum_old = line('.')
+ let col_old = col('.')
+ call cursor(1, 1)
+
+ if &ft == 'jsp'
+ while 1
+ let lnum = search('\<import\s*=[''"]', 'W')
+ if (lnum == 0)
+ break
+ endif
+
+ let str = getline(lnum)
+ if str =~ '<%\s*@\s*page\>' || str =~ '<jsp:\s*directive.page\>'
+ let str = substitute(str, '.*import=[''"]\([a-zA-Z0-9_$.*, \t]\+\)[''"].*', '\1', '')
+ for item in split(str, ',')
+ call add(imports, substitute(item, '\s', '', 'g'))
+ endfor
+ endif
+ endwhile
+ else
+ while 1
+ let lnum = search('\<import\>', 'W')
+ if (lnum == 0)
+ break
+ elseif !s:InComment(line("."), col(".")-1)
+ normal w
+ " TODO: search semicolon or import keyword, excluding comment
+ let stat = matchstr(getline(lnum)[col('.')-1:], '\(static\s\+\)\?\(' .s:RE_QUALID. '\%(\s*\.\s*\*\)\?\)\s*;')
+ if !empty(stat)
+ call add(imports, stat[:-2])
+ endif
+ endif
+ endwhile
+ endif
+
+ call cursor(lnum_old, col_old)
+ return imports
+endfunction
+
+fu! s:GetImports(kind, ...)
+ let filekey = a:0 > 0 && !empty(a:1) ? a:1 : s:GetCurrentFileKey()
+ let props = get(s:files, filekey, {})
+ if !has_key(props, a:kind)
+ let props['imports'] = filekey == s:GetCurrentFileKey() ? s:GenerateImports() : props.unit.imports
+ let props['imports_static'] = []
+ let props['imports_fqn'] = []
+ let props['imports_star'] = ['java.lang.']
+ if &ft == 'jsp' || filekey =~ '\.jsp$'
+ let props.imports_star += ['javax.servlet.', 'javax.servlet.http.', 'javax.servlet.jsp.']
+ endif
+
+ for import in props.imports
+ let subs = split(substitute(import, '^\s*\(static\s\+\)\?\(' .s:RE_QUALID. '\%(\s*\.\s*\*\)\?\)\s*$', '\1;\2', ''), ';', 1)
+ let qid = substitute(subs[1] , '\s', '', 'g')
+ if !empty(subs[0])
+ call add(props.imports_static, qid)
+ elseif qid[-1:] == '*'
+ call add(props.imports_star, qid[:-2])
+ else
+ call add(props.imports_fqn, qid)
+ endif
+ endfor
+ let s:files[filekey] = props
+ endif
+ return get(props, a:kind, [])
+endfu
+
+" search for name in
+" return the fqn matched
+fu! s:SearchSingleTypeImport(name, fqns)
+ let matches = s:filter(a:fqns, 'item =~# ''\<' . a:name . '$''')
+ if len(matches) == 1
+ return matches[0]
+ elseif !empty(matches)
+ echoerr 'Name "' . a:name . '" conflicts between ' . join(matches, ' and ')
+ return matches[0]
+ endif
+ return ''
+endfu
+
+" search for name in static imports, return list of members with the same name
+" return [types, methods, fields]
+fu! s:SearchStaticImports(name, fullmatch)
+ let result = [[], [], []]
+ let candidates = [] " list of the canonical name
+ for item in s:GetImports('imports_static')
+ if item[-1:] == '*' " static import on demand
+ call add(candidates, item[:-3])
+ elseif item[strridx(item, '.')+1:] ==# a:name
+ \ || (!a:fullmatch && item[strridx(item, '.')+1:] =~ '^' . a:name)
+ call add(candidates, item[:strridx(item, '.')])
+ endif
+ endfor
+ if empty(candidates)
+ return result
+ endif
+
+
+ " read type info which are not in cache
+ let commalist = ''
+ for typename in candidates
+ if !has_key(s:cache, typename)
+ let commalist .= typename . ','
+ endif
+ endfor
+ if commalist != ''
+ let res = s:RunReflection('-E', commalist, 's:SearchStaticImports in Batch')
+ if res =~ "^{'"
+ let dict = eval(res)
+ for key in keys(dict)
+ let s:cache[key] = s:Sort(dict[key])
+ endfor
+ endif
+ endif
+
+ " search in all candidates
+ for typename in candidates
+ let ti = get(s:cache, typename, 0)
+ if type(ti) == type({}) && get(ti, 'tag', '') == 'CLASSDEF'
+ let members = s:SearchMember(ti, a:name, a:fullmatch, 12, 1, 0)
+ let result[1] += members[1]
+ let result[2] += members[2]
+ "let pattern = 'item.n ' . (a:fullmatch ? '==# ''' : '=~# ''^') . a:name . ''' && s:IsStatic(item.m)'
+ "let result[1] += s:filter(get(ti, 'methods', []), pattern)
+ "let result[2] += s:filter(get(ti, 'fields', []), pattern)
+ else
+ " TODO: mark the wrong import declaration.
+ endif
+ endfor
+ return result
+endfu
+
+
+" search decl {{{1
+" Return: The declaration of identifier under the cursor
+" Note: The type of a variable must be imported or a fqn.
+function! s:GetVariableDeclaration()
+ let lnum_old = line('.')
+ let col_old = col('.')
+
+ silent call search('[^a-zA-Z0-9$_.,?<>[\] \t\r\n ]', 'bW') " call search('[{};(,]', 'b')
+ normal w
+ let lnum = line('.')
+ let col = col('.')
+ if (lnum == lnum_old && col == col_old)
+ return ''
+ endif
+
+" silent call search('[;){]')
+" let lnum_end = line('.')
+" let col_end = col('.')
+" let declaration = ''
+" while (lnum <= lnum_end)
+" let declaration = declaration . getline(lnum)
+" let lnum = lnum + 1
+" endwhile
+" let declaration = strpart(declaration, col-1)
+" let declaration = substitute(declaration, '\.[ \t]\+', '.', 'g')
+
+ silent call cursor(lnum_old, col_old)
+ return s:MergeLines(lnum, col, lnum_old, col_old)
+endfunction
+
+function! s:FoundClassDeclaration(type)
+ let lnum_old = line('.')
+ let col_old = col('.')
+ call cursor(1, 1)
+ while 1
+ let lnum = search('\<\C\(class\|interface\|enum\)[ \t\n\r ]\+' . a:type . '[< \t\n\r ]', 'W')
+ if lnum == 0 || !s:InCommentOrLiteral(line('.'), col('.'))
+ break
+ endif
+ endwhile
+
+ " search mainly for the cases: " class /* block comment */ Ident"
+ " " class // comment \n Ident "
+ if lnum == 0
+ let found = 0
+ while !found
+ let lnum = search('\<\C\(class\|interface\|enum\)[ \t\n\r ]\+', 'W')
+ if lnum == 0
+ break
+ elseif s:InCommentOrLiteral(line('.'), col('.'))
+ continue
+ else
+ normal w
+ " skip empty line
+ while getline(line('.'))[col('.')-1] == ''
+ normal w
+ endwhile
+ let lnum = line('.')
+ let col = col('.')
+ while 1
+ if match(getline(lnum)[col-1:], '^[ \t\n\r ]*' . a:type . '[< \t\n\r ]') >= 0
+ let found = 1
+ " meets comment
+ elseif match(getline(lnum)[col-1:], '^[ \t\n\r ]*\(//\|/\*\)') >= 0
+ if getline(lnum)[col-1:col] == '//'
+ normal $eb
+ else
+ let lnum = search('\*\/', 'W')
+ if lnum == 0
+ break
+ endif
+ normal web
+ endif
+ let lnum = line('.')
+ let col = col('.')
+ continue
+ endif
+ break
+ endwhile
+ endif
+ endwhile
+ endif
+
+ silent call cursor(lnum_old, col_old)
+ return lnum
+endfu
+
+fu! s:FoundClassLocally(type)
+ " current path
+ if globpath(expand('%:p:h'), a:type . '.java') != ''
+ return 1
+ endif
+
+ "
+ let srcpath = javacomplete#GetSourcePath(1)
+ let file = globpath(srcpath, substitute(fqn, '\.', '/', 'g') . '.java')
+ if file != ''
+ return 1
+ endif
+
+ return 0
+endfu
+
+" regexp samples:
+" echo search('\(\(public\|protected|private\)[ \t\n\r]\+\)\?\(\(static\)[ \t\n\r]\+\)\?\(\<class\>\|\<interface\>\)[ \t\n\r]\+HelloWorld[^a-zA-Z0-9_$]', 'W')
+" echo substitute(getline('.'), '.*\(\(public\|protected\|private\)[ \t\n\r]\+\)\?\(\(static\)[ \t\n\r]\+\)\?\(\<class\>\|\<interface\>\)\s\+\([a-zA-Z0-9_]\+\)\s\+\(\(implements\|extends\)\s\+\([^{]\+\)\)\?\s*{.*', '["\1", "\2", "\3", "\4", "\5", "\6", "\8", "\9"]', '')
+" code sample:
+function! s:GetClassDeclarationOf(type)
+ call cursor(1, 1)
+ let decl = []
+
+ let lnum = search('\(\<class\>\|\<interface\>\)[ \t\n\r]\+' . a:type . '[^a-zA-Z0-9_$]', 'W')
+ if (lnum != 0)
+ " TODO: search back for optional 'public | private' and 'static'
+ " join lines till to '{'
+ let lnum_end = search('{')
+ if (lnum_end != 0)
+ let str = ''
+ while (lnum <= lnum_end)
+ let str = str . getline(lnum)
+ let lnum = lnum + 1
+ endwhile
+
+ exe "let decl = " . substitute(str, '.*\(\<class\>\|\<interface\>\)\s\+\([a-zA-Z0-9_]\+\)\s\+\(\(implements\|extends\)\s\+\([^{]\+\)\)\?\s*{.*', '["\1", "\2", "\4", "\5"]', '')
+ endif
+ endif
+
+ return decl
+endfunction
+
+" return list
+" 0 class | interface
+" 1 name
+" [2 implements | extends ]
+" [3 parent list ]
+function! s:GetThisClassDeclaration()
+ let lnum_old = line('.')
+ let col_old = col('.')
+
+ while (1)
+ call search('\(\<class\C\>\|\<interface\C\>\|\<enum\C\>\)[ \t\r\n]\+', 'bW')
+ if !s:InComment(line("."), col(".")-1)
+ if getline('.')[col('.')-2] !~ '\S'
+ break
+ endif
+ end
+ endwhile
+
+ " join lines till to '{'
+ let str = ''
+ let lnum = line('.')
+ call search('{')
+ let lnum_end = line('.')
+ while (lnum <= lnum_end)
+ let str = str . getline(lnum)
+ let lnum = lnum + 1
+ endwhile
+
+
+ let declaration = substitute(str, '.*\(\<class\>\|\<interface\>\)\s\+\([a-zA-Z0-9_]\+\)\(\s\+\(implements\|extends\)\s\+\([^{]\+\)\)\?\s*{.*', '["\1", "\2", "\4", "\5"]', '')
+ call cursor(lnum_old, col_old)
+ if declaration !~ '^['
+ echoerr 'Some error occurs when recognizing this class:' . declaration
+ return ['', '']
+ endif
+ exe "let list = " . declaration
+ return list
+endfunction
+
+" searches for name of a var or a field and determines the meaning {{{1
+
+" The standard search order of a variable or field is as follows:
+" 1. Local variables declared in the code block, for loop, or catch clause
+" from current scope up to the most outer block, a method or an initialization block
+" 2. Parameters if the code is in a method or ctor
+" 3. Fields of the type
+" 4. Accessible inherited fields.
+" 5. If the type is a nested type,
+" local variables of the enclosing block or fields of the enclosing class.
+" Note that if the type is a static nested type, only static members of an enclosing block or class are searched
+" Reapply this rule to the upper block and class enclosing the enclosing type recursively
+" 6. Accessible static fields imported.
+" It is allowed that several fields with the same name.
+
+" The standard search order of a method is as follows:
+" 1. Methods of the type
+" 2. Accessible inherited methods.
+" 3. Methods of the enclosing class if the type is a nested type.
+" 4. Accessible static methods imported.
+" It is allowed that several methods with the same name and signature.
+
+" first return at once if found one.
+" fullmatch 1 - equal, 0 - match beginning
+" return [types, methods, fields, vars]
+fu! s:SearchForName(name, first, fullmatch)
+ let result = [[], [], [], []]
+ if s:IsKeyword(a:name)
+ return result
+ endif
+
+ " use java_parser.vim
+ if javacomplete#GetSearchdeclMethod() == 4
+ " declared in current file
+ let unit = javacomplete#parse()
+ let targetPos = java_parser#MakePos(line('.')-1, col('.')-1)
+ let trees = s:SearchNameInAST(unit, a:name, targetPos, a:fullmatch)
+ for tree in trees
+ if tree.tag == 'VARDEF'
+ call add(result[2], tree)
+ elseif tree.tag == 'METHODDEF'
+ call add(result[1], tree)
+ elseif tree.tag == 'CLASSDEF'
+ call add(result[0], tree.name)
+ endif
+ endfor
+
+ if a:first && result != [[], [], [], []] | return result | endif
+
+ " Accessible inherited members
+ let type = get(s:SearchTypeAt(unit, targetPos), -1, {})
+ if !empty(type)
+ let members = s:SearchMember(type, a:name, a:fullmatch, 2, 1, 0, 1)
+ let result[0] += members[0]
+ let result[1] += members[1]
+ let result[2] += members[2]
+" "let ti = s:AddInheritedClassInfo({}, type)
+" if !empty(ti)
+" let comparator = a:fullmatch ? "=~# '^" : "==# '"
+" let result[0] += s:filter(get(ti, 'classes', []), 'item ' . comparator . a:name . "'")
+" let result[1] += s:filter(get(ti, 'methods', []), 'item.n ' . comparator . a:name . "'")
+" let result[2] += s:filter(get(ti, 'fields', []), 'item.n ' . comparator . a:name . "'")
+" if a:0 > 0
+" let result[1] += s:filter(get(ti, 'declared_methods', []), 'item.n ' . comparator . a:name . "'")
+" let result[2] += s:filter(get(ti, 'declared_fields', []), 'item.n ' . comparator . a:name . "'")
+" endif
+" if a:first && result != [[], [], [], []] | return result | endif
+" endif
+ endif
+
+ " static import
+ let si = s:SearchStaticImports(a:name, a:fullmatch)
+ let result[1] += si[1]
+ let result[2] += si[2]
+ endif
+ return result
+endfu
+
+" TODO: how to determine overloaded functions
+fu! s:DetermineMethod(methods, parameters)
+ return get(a:methods, 0, {})
+endfu
+
+" Parser.GetType() in insenvim
+function! s:GetDeclaredClassName(var)
+ let var = s:Trim(a:var)
+ call s:Trace('GetDeclaredClassName for "' . var . '"')
+ if var =~# '^\(this\|super\)$'
+ return var
+ endif
+
+
+ " Special handling for builtin objects in JSP
+ if &ft == 'jsp'
+ if get(s:JSP_BUILTIN_OBJECTS, a:var, '') != ''
+ return s:JSP_BUILTIN_OBJECTS[a:var]
+ endif
+ endif
+
+ " use java_parser.vim
+ if javacomplete#GetSearchdeclMethod() == 4
+ let variable = get(s:SearchForName(var, 1, 1)[2], -1, {})
+ return get(variable, 'tag', '') == 'VARDEF' ? java_parser#type2Str(variable.vartype) : get(variable, 't', '')
+ endif
+
+
+ let ic = &ignorecase
+ setlocal noignorecase
+
+ let searched = javacomplete#GetSearchdeclMethod() == 2 ? s:Searchdecl(var, 1, 0) : searchdecl(var, 1, 0)
+ if (searched == 0)
+ " code sample:
+ " String tmp; java.
+ " lang. String str, value;
+ " for (int i = 0, j = 0; i < 10; i++) {
+ " j = 0;
+ " }
+ let declaration = s:GetVariableDeclaration()
+ " Assume it a class member, and remove modifiers
+ let class = substitute(declaration, '^\(public\s\+\|protected\s\+\|private\s\+\|abstract\s\+\|static\s\+\|final\s\+\|native\s\+\)*', '', '')
+ let class = substitute(class, '\s*\([a-zA-Z0-9_.]\+\)\(\[\]\)\?\s\+.*', '\1\2', '')
+ let class = substitute(class, '\([a-zA-Z0-9_.]\)<.*', '\1', '')
+ call s:Info('class: "' . class . '" declaration: "' . declaration . '" for ' . a:var)
+ let &ignorecase = ic
+ if class != '' && class !=# a:var && class !=# 'import' && class !=# 'class'
+ return class
+ endif
+ endif
+
+ let &ignorecase = ic
+ call s:Trace('GetDeclaredClassName: cannot find')
+ return ''
+endfunction
+
+" using java_parser.vim {{{1
+" javacomplete#parse() {{{2
+fu! javacomplete#parse(...)
+ let filename = a:0 == 0 ? '%' : a:1
+
+ let changed = 0
+ if filename == '%'
+ let filename = s:GetCurrentFileKey()
+ let props = get(s:files, filename, {})
+ if get(props, 'changedtick', -1) != b:changedtick
+ let changed = 1
+ let props.changedtick = b:changedtick
+ let lines = getline('^', '$')
+ endif
+ else
+ let props = get(s:files, filename, {})
+ if get(props, 'modifiedtime', 0) != getftime(filename)
+ let changed = 1
+ let props.modifiedtime = getftime(filename)
+ let lines = readfile(filename)
+ endif
+ endif
+
+ if changed
+ call java_parser#InitParser(lines)
+ call java_parser#SetLogLevel(5)
+ let props.unit = java_parser#compilationUnit()
+
+ let package = has_key(props.unit, 'package') ? props.unit.package . '.' : ''
+ call s:UpdateFQN(props.unit, package)
+ endif
+ let s:files[filename] = props
+ return props.unit
+endfu
+
+" update fqn for toplevel types or nested types.
+" not for local type or anonymous type
+fu! s:UpdateFQN(tree, qn)
+ if a:tree.tag == 'TOPLEVEL'
+ for def in a:tree.types
+ call s:UpdateFQN(def, a:qn)
+ endfor
+ elseif a:tree.tag == 'CLASSDEF'
+ let a:tree.fqn = a:qn . a:tree.name
+ for def in a:tree.defs
+ if def.tag == 'CLASSDEF'
+ call s:UpdateFQN(def, a:tree.fqn . '.')
+ endif
+ endfor
+ endif
+endfu
+
+" TreeVisitor {{{2
+fu! s:visitTree(tree, param) dict
+ if type(a:tree) == type({})
+ exe get(self, get(a:tree, 'tag', ''), '')
+ elseif type(a:tree) == type([])
+ for tree in a:tree
+ call self.visit(tree, a:param)
+ endfor
+ endif
+endfu
+
+let s:TreeVisitor = {'visit': function('s:visitTree'),
+ \ 'TOPLEVEL' : 'call self.visit(a:tree.types, a:param)',
+ \ 'BLOCK' : 'let stats = a:tree.stats | if stats == [] | call java_parser#GotoPosition(a:tree.pos) | let stats = java_parser#block().stats | endif | call self.visit(stats, a:param)',
+ \ 'DOLOOP' : 'call self.visit(a:tree.body, a:param) | call self.visit(a:tree.cond, a:param)',
+ \ 'WHILELOOP' : 'call self.visit(a:tree.cond, a:param) | call self.visit(a:tree.body, a:param)',
+ \ 'FORLOOP' : 'call self.visit(a:tree.init, a:param) | call self.visit(a:tree.cond, a:param) | call self.visit(a:tree.step, a:param) | call self.visit(a:tree.body, a:param)',
+ \ 'FOREACHLOOP' : 'call self.visit(a:tree.var, a:param) | call self.visit(a:tree.expr, a:param) | call self.visit(a:tree.body, a:param)',
+ \ 'LABELLED' : 'call self.visit(a:tree.body, a:param)',
+ \ 'SWITCH' : 'call self.visit(a:tree.selector, a:param) | call self.visit(a:tree.cases, a:param)',
+ \ 'CASE' : 'call self.visit(a:tree.pat, a:param) | call self.visit(a:tree.stats, a:param)',
+ \ 'SYNCHRONIZED': 'call self.visit(a:tree.lock, a:param) | call self.visit(a:tree.body, a:param)',
+ \ 'TRY' : 'call self.visit(a:tree.body, a:param) | call self.visit(a:tree.catchers, a:param) | call self.visit(a:tree.finalizer, a:param) ',
+ \ 'CATCH' : 'call self.visit(a:tree.param,a:param) | call self.visit(a:tree.body, a:param)',
+ \ 'CONDEXPR' : 'call self.visit(a:tree.cond, a:param) | call self.visit(a:tree.truepart, a:param) | call self.visit(a:tree.falsepart, a:param)',
+ \ 'IF' : 'call self.visit(a:tree.cond, a:param) | call self.visit(a:tree.thenpart, a:param) | if has_key(a:tree, "elsepart") | call self.visit(a:tree.elsepart, a:param) | endif',
+ \ 'EXEC' : 'call self.visit(a:tree.expr, a:param)',
+ \ 'APPLY' : 'call self.visit(a:tree.meth, a:param) | call self.visit(a:tree.args, a:param)',
+ \ 'NEWCLASS' : 'call self.visit(a:tree.def, a:param)'
+ \}
+
+let s:TV_CMP_POS = 'a:tree.pos <= a:param.pos && a:param.pos <= get(a:tree, "endpos", -1)'
+let s:TV_CMP_POS_BODY = 'has_key(a:tree, "body") && a:tree.body.pos <= a:param.pos && a:param.pos <= get(a:tree.body, "endpos", -1)'
+
+" Return a stack of enclosing types (including local or anonymous classes).
+" Given the optional argument, return all (toplevel or static member) types besides enclosing types.
+fu! s:SearchTypeAt(tree, targetPos, ...)
+ let s:TreeVisitor.CLASSDEF = 'if a:param.allNonLocal || ' . s:TV_CMP_POS . ' | call add(a:param.result, a:tree) | call self.visit(a:tree.defs, a:param) | endif'
+ let s:TreeVisitor.METHODDEF = 'if ' . s:TV_CMP_POS_BODY . ' | call self.visit(a:tree.body, a:param) | endif'
+ let s:TreeVisitor.VARDEF = 'if has_key(a:tree, "init") && !a:param.allNonLocal && ' . s:TV_CMP_POS . ' | call self.visit(a:tree.init, a:param) | endif'
+
+ let result = []
+ call s:TreeVisitor.visit(a:tree, {'result': result, 'pos': a:targetPos, 'allNonLocal': a:0 == 0 ? 0 : 1})
+ return result
+endfu
+
+" a:1 match beginning
+" return a stack of matching name
+fu! s:SearchNameInAST(tree, name, targetPos, fullmatch)
+ let comparator = a:fullmatch ? '==#' : '=~# "^" .'
+ let cmd = 'if a:tree.name ' .comparator. ' a:param.name | call add(a:param.result, a:tree) | endif'
+ let s:TreeVisitor.CLASSDEF = 'if ' . s:TV_CMP_POS . ' | ' . cmd . ' | call self.visit(a:tree.defs, a:param) | endif'
+ let s:TreeVisitor.METHODDEF = cmd . ' | if ' . s:TV_CMP_POS_BODY . ' | call self.visit(a:tree.params, a:param) | call self.visit(a:tree.body, a:param) | endif'
+ let s:TreeVisitor.VARDEF = cmd . ' | if has_key(a:tree, "init") && ' . s:TV_CMP_POS . ' | call self.visit(a:tree.init, a:param) | endif'
+
+ let result = []
+ call s:TreeVisitor.visit(a:tree, {'result': result, 'pos': a:targetPos, 'name': a:name})
+ "call s:Info(a:name . ' ' . string(result) . ' line: ' . line('.') . ' col: ' . col('.')) . ' ' . a:targetPos
+ return result
+endfu
+
+
+" javacomplete#Searchdecl {{{2
+" TODO:
+fu! javacomplete#Searchdecl()
+ let var = expand('<cword>')
+
+ let line = line('.')-1
+ let col = col('.')-1
+
+
+ if var =~# '^\(this\|super\)$'
+ if &ft == 'jsp'
+ return ''
+ endif
+
+ let matchs = s:SearchTypeAt(javacomplete#parse(), java_parser#MakePos(line, col))
+
+ let stat = s:GetStatement()
+ for t in matchs
+ if stat =~ t.name
+ let coor = java_parser#DecodePos(t.pos)
+ return var . '(' . (coor.line+1) . ',' . (coor.col) . ') ' . getline(coor.line+1)
+ endif
+ endfor
+ if len(matchs) > 0
+ let coor = java_parser#DecodePos(matchs[len(matchs)-1].pos)
+ return var . '(' . (coor.line+1) . ',' . (coor.col) . ') ' . getline(coor.line+1)
+ endif
+ return ''
+ endif
+
+ " Type.this.
+ " new Type()
+ " new Type(param1, param2)
+ " this.field
+ " super.field
+
+ let s:log = []
+
+
+ " It may be an imported class.
+ let imports = []
+ for fqn in s:GetImports('imports_fqn')
+ if fqn =~# '\<' . var . '\>$'
+ call add(imports, fqn)
+ endif
+ endfor
+ if len(imports) > 1
+ echoerr 'Imports conflicts between ' . join(imports, ' and ')
+ endif
+
+
+ " Search in this buffer
+ let matchs = s:SearchNameInAST(javacomplete#parse(), var, java_parser#MakePos(line, col), 1)
+
+
+ let hint = var . ' '
+ if !empty(matchs)
+ let tree = matchs[len(matchs)-1]
+ let coor = java_parser#DecodePos(tree.pos)
+ let hint .= '(' . (coor.line+1) . ',' . (coor.col) . ') '
+ let hint .= getline(coor.line+1) "string(tree)
+ else
+ for fqn in imports
+ let ci = s:DoGetClassInfo(fqn)
+ if !empty(ci)
+ let hint .= ' ' . fqn
+ endif
+ " TODO: get javadoc
+ endfor
+
+ endif
+ return hint
+endfu
+
+
+" java {{{1
+
+fu! s:IsBuiltinType(name)
+ return index(s:PRIMITIVE_TYPES, a:name) >= 0
+endfu
+
+fu! s:IsKeyword(name)
+ return index(s:KEYWORDS, a:name) >= 0
+endfu
+
+fu! s:HasKeyword(name)
+ return a:name =~# s:RE_KEYWORDS
+endfu
+
+fu! s:TailOfQN(qn)
+ return a:qn[strridx(a:qn, '.')+1:]
+endfu
+
+" options {{{1
+" Methods to search declaration {{{2
+" 1 - by builtin searchdecl()
+" 2 - by special Searchdecl()
+" 4 - by java_parser
+fu! javacomplete#GetSearchdeclMethod()
+ if &ft == 'jsp'
+ return 1
+ endif
+ return exists('s:searchdecl') ? s:searchdecl : 4
+endfu
+
+fu! javacomplete#SetSearchdeclMethod(method)
+ let s:searchdecl = a:method
+endfu
+
+" JDK1.1 {{{2
+fu! javacomplete#UseJDK11()
+ let s:isjdk11 = 1
+endfu
+
+" java compiler {{{2
+fu! javacomplete#GetCompiler()
+ return exists('s:compiler') && s:compiler !~ '^\s*$' ? s:compiler : 'javac'
+endfu
+
+fu! javacomplete#SetCompiler(compiler)
+ let s:compiler = a:compiler
+endfu
+
+" jvm launcher {{{2
+fu! javacomplete#GetJVMLauncher()
+ return exists('s:interpreter') && s:interpreter !~ '^\s*$' ? s:interpreter : 'java'
+endfu
+
+fu! javacomplete#SetJVMLauncher(interpreter)
+ if javacomplete#GetJVMLauncher() != a:interpreter
+ let s:cache = {}
+ endif
+ let s:interpreter = a:interpreter
+endfu
+
+" sourcepath {{{2
+fu! javacomplete#AddSourcePath(s)
+ if !isdirectory(a:s)
+ echoerr 'invalid source path: ' . a:s
+ return
+ endif
+ let path = fnamemodify(a:s, ':p:h')
+ if !exists('s:sourcepath')
+ let s:sourcepath = [path]
+ elseif index(s:sourcepath, path) == -1
+ call add(s:sourcepath, path)
+ endif
+endfu
+
+fu! javacomplete#DelSourcePath(s)
+ if !exists('s:sourcepath') || !isdirectory(a:s)| return | endif
+ let idx = index(s:sourcepath, a:s)
+ if idx != -1
+ call remove(s:sourcepath, idx)
+ endif
+endfu
+
+fu! javacomplete#SetSourcePath(s)
+ let paths = type(a:s) == type([]) ? a:s : split(a:s, javacomplete#GetClassPathSep())
+ let s:sourcepath = []
+ for path in paths
+ if isdirectory(path)
+ call add(s:sourcepath, fnamemodify(path, ':p:h'))
+ endif
+ endfor
+endfu
+
+" return the sourcepath. Given argument, add current path or default package root path
+" NOTE: Avoid path duplicate, otherwise globpath() will return duplicate result.
+fu! javacomplete#GetSourcePath(...)
+ return join(s:GetSourceDirs(a:0 > 0 && a:1 ? expand('%:p') : ''), s:PATH_SEP)
+endfu
+
+fu! s:GetSourceDirs(filepath, ...)
+ let dirs = exists('s:sourcepath') ? s:sourcepath : []
+
+ if !empty(a:filepath)
+ let filepath = fnamemodify(a:filepath, ':p:h')
+
+ " get source path according to file path and package name
+ let packageName = a:0 > 0 ? a:1 : s:GetPackageName()
+ if packageName != ''
+ let path = fnamemodify(substitute(filepath, packageName, '', 'g'), ':p:h')
+ if index(dirs, path) < 0
+ call add(dirs, path)
+ endif
+ endif
+
+ " Consider current path as a sourcepath
+ if index(dirs, filepath) < 0
+ call add(dirs, filepath)
+ endif
+ endif
+ return dirs
+endfu
+
+" classpath {{{2
+fu! javacomplete#AddClassPath(s)
+ if !isdirectory(a:s)
+ echoerr 'invalid classpath: ' . a:s
+ return
+ endif
+
+ if !exists('s:classpath')
+ let s:classpath = [a:s]
+ elseif index(s:classpath, a:s) == -1
+ call add(s:classpath, a:s)
+ endif
+ let s:cache = {}
+endfu
+
+fu! javacomplete#DelClassPath(s)
+ if !exists('s:classpath') | return | endif
+ let idx = index(s:classpath, a:s)
+ if idx != -1
+ call remove(s:classpath, idx)
+ endif
+endfu
+
+fu! javacomplete#SetClassPath(s)
+ if type(a:s) == type("")
+ let s:classpath = split(a:s, javacomplete#GetClassPathSep())
+ elseif type(a:s) == type([])
+ let s:classpath = a:s
+ endif
+ let s:cache = {}
+endfu
+
+fu! javacomplete#GetClassPathSep()
+ return s:PATH_SEP
+endfu
+
+fu! javacomplete#GetClassPath()
+ return exists('s:classpath') ? join(s:classpath, javacomplete#GetClassPathSep()) : ''
+endfu
+
+" s:GetClassPath() {{{2
+fu! s:GetClassPath()
+ let path = s:GetJavaCompleteClassPath() . javacomplete#GetClassPathSep()
+
+ if &ft == 'jsp'
+ let path .= s:GetClassPathOfJsp()
+ endif
+
+ if exists('b:classpath') && b:classpath !~ '^\s*$'
+ return path . b:classpath
+ endif
+
+ if exists('s:classpath')
+ return path . javacomplete#GetClassPath()
+ endif
+
+ if exists('g:java_classpath') && g:java_classpath !~ '^\s*$'
+ return path . g:java_classpath
+ endif
+
+ return path . $CLASSPATH
+endfu
+
+fu! s:GetJavaCompleteClassPath()
+ " remove *.class from wildignore if it exists, so that globpath doesn't ignore Reflection.class
+ " vim versions >= 702 can add the 1 flag to globpath which ignores '*.class" in wildingore
+ let has_class = 0
+ if &wildignore =~# "*.class"
+ set wildignore-=*.class
+ let has_class = 1
+ endif
+
+ let classfile = globpath(&rtp, 'autoload/Reflection.class')
+ if classfile == ''
+ let classfile = globpath($CLASS_DEST, 'Reflection.class')
+ endif
+ if classfile == ''
+ " try to find source file and compile to $HOME
+ let srcfile = globpath(&rtp, 'autoload/Reflection.java')
+ if srcfile != ''
+ exe '!' . javacomplete#GetCompiler() . ' -d "' . $CLASS_DEST . '" "' . srcfile . '"'
+ let classfile = globpath($CLASS_DEST, 'Reflection.class')
+ if classfile == ''
+ echo srcfile . ' can not be compiled. Please check it'
+ endif
+ else
+ echo 'No Reflection.class found in $CLASS_DEST or any autoload directory of the &rtp. And no Reflection.java found in any autoload directory of the &rtp to compile.'
+ endif
+ endif
+
+ " add *.class to wildignore if it existed before
+ if has_class == 1
+ set wildignore+=*.class
+ endif
+
+ return fnamemodify(classfile, ':p:h')
+endfu
+
+fu! s:GetClassPathOfJsp()
+ if exists('b:classpath_jsp')
+ return b:classpath_jsp
+ endif
+
+ let b:classpath_jsp = ''
+ let path = expand('%:p:h')
+ while 1
+ if isdirectory(path . '/WEB-INF' )
+ if isdirectory(path . '/WEB-INF/classes')
+ let b:classpath_jsp .= s:PATH_SEP . path . '/WEB-INF/classes'
+ endif
+ if isdirectory(path . '/WEB-INF/lib')
+ let libs = globpath(path . '/WEB-INF/lib', '*.jar')
+ if libs != ''
+ let b:classpath_jsp .= s:PATH_SEP . substitute(libs, "\n", s:PATH_SEP, 'g')
+ endif
+ endif
+ return b:classpath_jsp
+ endif
+
+ let prev = path
+ let path = fnamemodify(path, ":p:h:h")
+ if path == prev
+ break
+ endif
+ endwhile
+ return ''
+endfu
+
+" return only classpath which are directories
+fu! s:GetClassDirs()
+ let dirs = []
+ for path in split(s:GetClassPath(), s:PATH_SEP)
+ if isdirectory(path)
+ call add(dirs, fnamemodify(path, ':p:h'))
+ endif
+ endfor
+ return dirs
+endfu
+
+" s:GetPackageName() {{{2
+fu! s:GetPackageName()
+ let lnum_old = line('.')
+ let col_old = col('.')
+
+ call cursor(1, 1)
+ let lnum = search('^\s*package[ \t\r\n]\+\([a-zA-Z][a-zA-Z0-9.]*\);', 'w')
+ let packageName = substitute(getline(lnum), '^\s*package\s\+\([a-zA-Z][a-zA-Z0-9.]*\);', '\1', '')
+
+ call cursor(lnum_old, col_old)
+ return packageName
+endfu
+
+fu! s:IsStatic(modifier)
+ return a:modifier[strlen(a:modifier)-4]
+endfu
+
+" utilities {{{1
+" Convert a file name into the unique form.
+" Similar with fnamemodify(). NOTE that ':gs' should not be used.
+fu! s:fnamecanonize(fname, mods)
+ return fnamemodify(a:fname, a:mods . ':gs?[\\/]\+?/?')
+endfu
+
+" Similar with filter(), but returns a new list instead of operating in-place.
+" `item` has the value of the current item.
+fu! s:filter(expr, string)
+ if type(a:expr) == type([])
+ let result = []
+ for item in a:expr
+ if eval(a:string)
+ call add(result, item)
+ endif
+ endfor
+ return result
+ else
+ let result = {}
+ for item in items(a:expr)
+ if eval(a:string)
+ let result[item[0]] = item[1]
+ endif
+ endfor
+ return result
+ endif
+endfu
+
+fu! s:Index(list, expr, key)
+ let i = 0
+ while i < len(a:list)
+ if get(a:list[i], a:key, '') == a:expr
+ return i
+ endif
+ let i += 1
+ endwhile
+ return -1
+endfu
+
+fu! s:Match(list, expr, key)
+ let i = 0
+ while i < len(a:list)
+ if get(a:list[i], a:key, '') =~ a:expr
+ return i
+ endif
+ let i += 1
+ endwhile
+ return -1
+endfu
+
+fu! s:KeepCursor(cmd)
+ let lnum_old = line('.')
+ let col_old = col('.')
+ exe a:cmd
+ call cursor(lnum_old, col_old)
+endfu
+
+fu! s:InCommentOrLiteral(line, col)
+ if has("syntax") && &ft != 'jsp'
+ return synIDattr(synID(a:line, a:col, 1), "name") =~? '\(Comment\|String\|Character\)'
+ endif
+endfu
+
+function! s:InComment(line, col)
+ if has("syntax") && &ft != 'jsp'
+ return synIDattr(synID(a:line, a:col, 1), "name") =~? 'comment'
+ endif
+" if getline(a:line) =~ '\s*\*'
+" return 1
+" endif
+" let idx = strridx(getline(a:line), '//')
+" if idx >= 0 && idx < a:col
+" return 1
+" endif
+" return 0
+endfunction
+
+" set string literal empty, remove comments, trim begining or ending spaces
+" test case: ' sb. /* block comment*/ append( "stringliteral" ) // comment '
+function! s:Prune(str, ...)
+ if a:str =~ '^\s*$' | return '' | endif
+
+ let str = substitute(a:str, '"\(\\\(["\\''ntbrf]\)\|[^"]\)*"', '""', 'g')
+ let str = substitute(str, '\/\/.*', '', 'g')
+ let str = s:RemoveBlockComments(str)
+ return a:0 > 0 ? str : str . ' '
+endfunction
+
+" Given argument, replace block comments with spaces of same number
+fu! s:RemoveBlockComments(str, ...)
+ let result = a:str
+ let ib = match(result, '\/\*')
+ let ie = match(result, '\*\/')
+ while ib != -1 && ie != -1 && ib < ie
+ let result = strpart(result, 0, ib) . (a:0 == 0 ? ' ' : repeat(' ', ie-ib+2)) . result[ie+2: ]
+ let ib = match(result, '\/\*')
+ let ie = match(result, '\*\/')
+ endwhile
+ return result
+endfu
+
+fu! s:Trim(str)
+ let str = substitute(a:str, '^\s*', '', '')
+ return substitute(str, '\s*$', '', '')
+endfu
+
+fu! s:SplitAt(str, index)
+ return [strpart(a:str, 0, a:index+1), strpart(a:str, a:index+1)]
+endfu
+
+" TODO: search pair used in string, like
+" 'create(ao.fox("("), new String).foo().'
+function! s:GetMatchedIndexEx(str, idx, one, another)
+ let pos = a:idx
+ while 0 <= pos && pos < len(a:str)
+ let pos = match(a:str, '['. a:one . escape(a:another, ']') .']', pos+1)
+ if pos != -1
+ if a:str[pos] == a:one
+ let pos = s:GetMatchedIndexEx(a:str, pos, a:one, a:another)
+ elseif a:str[pos] == a:another
+ break
+ endif
+ endif
+ endwhile
+ return 0 <= pos && pos < len(a:str) ? pos : -3
+endfunction
+
+function! s:SearchPairBackward(str, idx, one, another)
+ let idx = a:idx
+ let n = 0
+ while idx >= 0
+ let idx -= 1
+ if a:str[idx] == a:one
+ if n == 0
+ break
+ endif
+ let n -= 1
+ elseif a:str[idx] == a:another " nested
+ let n += 1
+ endif
+ endwhile
+ return idx
+endfunction
+
+fu! s:CountDims(str)
+ if match(a:str, '[[\]]') == -1
+ return 0
+ endif
+
+ " int[] -> [I, String[] ->
+ let dims = len(matchstr(a:str, '^[\+'))
+ if dims == 0
+ let idx = len(a:str)-1
+ while idx >= 0 && a:str[idx] == ']'
+ let dims += 1
+ let idx = s:SearchPairBackward(a:str, idx, '[', ']')-1
+ endwhile
+ endif
+ return dims
+endfu
+
+fu! s:GotoUpperBracket()
+ let searched = 0
+ while (!searched)
+ call search('[{}]', 'bW')
+ if getline('.')[col('.')-1] == '}'
+ normal %
+ else
+ let searched = 1
+ endif
+ endwhile
+endfu
+
+" Improve recognition of variable declaration using my version of searchdecl() for accuracy reason.
+" TODO:
+fu! s:Searchdecl(name, ...)
+ let global = a:0 > 0 ? a:1 : 0
+ let thisblock = a:0 > 1 ? a:2 : 1
+
+ call search('\<' . a:name . '\>', 'bW')
+ let lnum_old = line('.')
+ let col_old = col('.')
+
+ call s:GotoUpperBracket()
+ let lnum_bracket = line('.')
+ let col_bracket = col('.')
+ call search('\<' . a:name . '\>', 'W', lnum_old)
+ if line('.') != lnum_old || col('.') != col_old
+ return 0
+ endif
+
+ " search globally
+ if global
+ call cursor(lnum_bracket, col_bracket)
+ " search backward
+ while (1)
+ if search('\([{}]\|\<' . a:name . '\>\)', 'bW') == 0
+ break
+ endif
+ if s:InComment(line('.'), col('.')) "|| s:InStringLiteral()
+ continue
+ endif
+ let cword = expand('<cword>')
+ if cword == a:name
+ return 0
+ endif
+ if getline('.')[col('.')-1] == '}'
+ normal %
+ endif
+ endwhile
+
+ call cursor(lnum_old, col_old)
+ " search forward
+ call search('[{};]', 'W')
+ while (1)
+ if search('\([{}]\|\<' . a:name . '\>\)', 'W') == 0
+ break
+ endif
+ if s:InComment(line('.'), col('.')) "|| s:InStringLiteral()
+ continue
+ endif
+ let cword = expand('<cword>')
+ if cword == a:name
+ return 0
+ endif
+ if getline('.')[col('.')-1] == '{'
+ normal %
+ endif
+ endwhile
+ endif
+ return 1
+endfu
+"nmap <F8> :call <SID>Searchdecl(expand('<cword>'))<CR>
+
+fu! javacomplete#Exe(cmd)
+ exe a:cmd
+endfu
+
+" cache utilities {{{1
+
+" key of s:files for current buffer. It may be the full path of current file or the bufnr of unnamed buffer, and is updated when BufEnter, BufLeave.
+fu! s:GetCurrentFileKey()
+ return has("autocmd") ? s:curfilekey : empty(expand('%')) ? bufnr('%') : expand('%:p')
+endfu
+
+fu! s:SetCurrentFileKey()
+ let s:curfilekey = empty(expand('%')) ? bufnr('%') : expand('%:p')
+endfu
+
+call s:SetCurrentFileKey()
+if has("autocmd")
+ autocmd BufEnter *.java call s:SetCurrentFileKey()
+ autocmd FileType java call s:SetCurrentFileKey()
+endif
+
+
+" Log utilities {{{1
+fu! s:WatchVariant(variant)
+ "echoerr a:variant
+endfu
+
+" level
+" 5 off/fatal
+" 4 error
+" 3 warn
+" 2 info
+" 1 debug
+" 0 trace
+fu! javacomplete#SetLogLevel(level)
+ let s:loglevel = a:level
+endfu
+
+fu! javacomplete#GetLogLevel()
+ return exists('s:loglevel') ? s:loglevel : 3
+endfu
+
+fu! javacomplete#GetLogContent()
+ return s:log
+endfu
+
+fu! s:Trace(msg)
+ call s:Log(0, a:msg)
+endfu
+
+fu! s:Debug(msg)
+ call s:Log(1, a:msg)
+endfu
+
+fu! s:Info(msg)
+ call s:Log(2, a:msg)
+endfu
+
+fu! s:Log(level, key, ...)
+ if a:level >= javacomplete#GetLogLevel()
+ echo a:key
+ call add(s:log, a:key)
+ endif
+endfu
+
+fu! s:System(cmd, caller)
+ call s:WatchVariant(a:cmd)
+ let t = reltime()
+ let res = system(a:cmd)
+ call s:Debug(reltimestr(reltime(t)) . 's to exec "' . a:cmd . '" by ' . a:caller)
+ return res
+endfu
+
+" functions to get information {{{1
+" utilities {{{2
+fu! s:MemberCompare(m1, m2)
+ return a:m1['n'] == a:m2['n'] ? 0 : a:m1['n'] > a:m2['n'] ? 1 : -1
+endfu
+
+fu! s:Sort(ci)
+ let ci = a:ci
+ if has_key(ci, 'fields')
+ call sort(ci['fields'], 's:MemberCompare')
+ endif
+ if has_key(ci, 'methods')
+ call sort(ci['methods'], 's:MemberCompare')
+ endif
+ return ci
+endfu
+
+" Function to run Reflection {{{2
+fu! s:RunReflection(option, args, log)
+ let classpath = ''
+ if !exists('s:isjdk11')
+ let classpath = ' -classpath "' . s:GetClassPath() . '" '
+ endif
+
+ let cmd = javacomplete#GetJVMLauncher() . classpath . ' Reflection ' . a:option . ' "' . a:args . '"'
+ return s:System(cmd, a:log)
+endfu
+" class information {{{2
+
+
+" The standard search order of a FQN is as follows:
+" 1. a file-name toplevel type or static member type accessed by the file-name type declared in source files
+" 2. other types declared in source files
+" 3. an accessible loadable type.
+" parameters:
+" fqns - list of fqn
+" srcpaths - a comma-separated list of directory names.
+" a:1 - search all.
+" return a dict of fqn -> type info
+" precondition:
+" NOTE: call expand() to convert path to standard form
+fu! s:DoGetTypeInfoForFQN(fqns, srcpath, ...)
+ if empty(a:fqns) || empty(a:srcpath)
+ return
+ endif
+
+ " 1
+ let files = {} " fqn -> java file path
+ for fqn in a:fqns
+ " toplevel type
+ let filepath = globpath(a:srcpath, substitute(fqn, '\.', '/', 'g') . '.java')
+ if filepath != ''
+ let files[fqn] = expand(filepath)
+
+ " nested type
+ elseif stridx(fqn, '.') >= 0
+ let idents = split(fqn, '\.')
+ let i = len(idents)-2
+ while i >= 0
+ let filepath = globpath(a:srcpath, join(idents[:i], '/') . '.java')
+ if filepath != ''
+ let files[fqn] = expand(filepath)
+ break
+ endif
+ let i -= 1
+ endwhile
+ endif
+ endfor
+
+
+ " 2
+ let dirs = {} " dir.idents -> names of nested type
+ " dir.qfitems -> items of quick fix
+ " dir.fqn -> fqn
+ for fqn in a:fqns
+ if !has_key(files, fqn)
+ for path in split(a:srcpath, ',')
+ let idents = split(fqn, '\.')
+ let i = len(idents)-2
+ while i >= 0
+ let dirpath = path . '/' . join(idents[:i], '/')
+ " it is a package
+ if isdirectory(dirpath)
+ let dirs[fnamemodify(dirpath, ':p:h:gs?[\\/]\+?/?')] = {'fqn': fqn, 'idents': idents[i+1:]}
+ break
+ endif
+ let i -= 1
+ endwhile
+ endfor
+ endif
+ endfor
+
+ if !empty(dirs)
+ let items = {} " dir -> items of quick fix
+
+ let filepatterns = ''
+ for dirpath in keys(dirs)
+ let filepatterns .= escape(dirpath, ' \') . '/*.java '
+ endfor
+
+ let cwd = fnamemodify(expand('%:p:h'), ':p:h:gs?[\\/]\+?/?')
+ exe 'vimgrep /\s*' . s:RE_TYPE_DECL . '/jg ' . filepatterns
+ for item in getqflist()
+ if item.text !~ '^\s*\*\s\+'
+ let text = matchstr(s:Prune(item.text, -1), '\s*' . s:RE_TYPE_DECL)
+ if text != ''
+ let subs = split(substitute(text, '\s*' . s:RE_TYPE_DECL, '\1;\2;\3', ''), ';', 1)
+ let dirpath = fnamemodify(bufname(item.bufnr), ':p:h:gs?[\\/]\+?/?')
+ let idents = dirs[dirpath].idents
+ if index(idents, subs[2]) >= 0 && (subs[0] =~ '\C\<public\>' || dirpath == cwd) " FIXME?
+ let item.subs = subs
+ let dirs[dirpath].qfitems = get(dirs[dirpath], 'qfitems', []) + [item]
+ endif
+ endif
+ endif
+ endfor
+
+ for dirpath in keys(dirs)
+ " a. names of nested type must be existed in the same file
+ " PackageName.NonFileNameTypeName.NestedType.NestedNestedType
+ let qfitems = get(dirs[dirpath], 'qfitems', [])
+ let nr = 0
+ for ident in dirs[dirpath].idents
+ for item in qfitems
+ if item.subs[2] == ident
+ let nr += 1
+ endif
+ endfor
+ endfor
+ if nr == len(dirs[dirpath].idents)
+ " b. TODO: Check whether one enclosed another is correct
+ let files[fqn] = expand(bufname(qfitems[0].bufnr))
+ endif
+ endfor
+ endif
+
+
+ call s:Info('FQN1&2: ' . string(keys(files)))
+ for fqn in keys(files)
+ if !has_key(s:cache, fqn) || get(get(s:files, files[fqn], {}), 'modifiedtime', 0) != getftime(files[fqn])
+ let ti = s:GetClassInfoFromSource(fqn[strridx(fqn, '.')+1:], files[fqn])
+ if !empty(ti)
+ let s:cache[fqn] = s:Sort(ti)
+ endif
+ endif
+ if (a:0 == 0 || !a:1)
+ return
+ endif
+ endfor
+
+
+ " 3
+ let commalist = ''
+ for fqn in a:fqns
+ if has_key(s:cache, fqn) && (a:0 == 0 || !a:1)
+ return
+ else "if stridx(fqn, '.') >= 0
+ let commalist .= fqn . ','
+ endif
+ endfor
+ if !empty(commalist)
+ let res = s:RunReflection('-E', commalist, 'DoGetTypeInfoForFQN in Batch')
+ if res =~ "^{'"
+ let dict = eval(res)
+ for key in keys(dict)
+ if !has_key(s:cache, key)
+ if type(dict[key]) == type({})
+ let s:cache[key] = s:Sort(dict[key])
+ elseif type(dict[key]) == type([])
+ let s:cache[key] = sort(dict[key])
+ endif
+ endif
+ endfor
+ endif
+ endif
+endfu
+
+" a:1 filepath
+" a:2 package name
+fu! s:DoGetClassInfo(class, ...)
+ if has_key(s:cache, a:class)
+ return s:cache[a:class]
+ endif
+
+ " array type: TypeName[] or '[I' or '[[Ljava.lang.String;'
+ if a:class[-1:] == ']' || a:class[0] == '['
+ return s:ARRAY_TYPE_INFO
+ endif
+
+ " either this or super is not qualified
+ if a:class == 'this' || a:class == 'super'
+ if &ft == 'jsp'
+ let ci = s:DoGetReflectionClassInfo('javax.servlet.jsp.HttpJspPage')
+ if a:class == 'this'
+ " TODO: search methods defined in <%! [declarations] %>
+ " search methods defined in other jsp files included
+ " avoid including self directly or indirectly
+ endif
+ return ci
+ endif
+
+ call s:Info('A0. ' . a:class)
+ " this can be a local class or anonymous class as well as static type
+ let t = get(s:SearchTypeAt(javacomplete#parse(), java_parser#MakePos(line('.')-1, col('.')-1)), -1, {})
+ if !empty(t)
+ " What will be returned for super?
+ " - the protected or public inherited fields and methods. No ctors.
+ " - the (public static) fields of interfaces.
+ " - the methods of the Object class.
+ " What will be returned for this?
+ " - besides the above, all fields and methods of current class. No ctors.
+ return s:Sort(s:Tree2ClassInfo(t))
+ "return s:Sort(s:AddInheritedClassInfo(a:class == 'this' ? s:Tree2ClassInfo(t) : {}, t, 1))
+ endif
+
+ return {}
+ endif
+
+
+ if a:class !~ '^\s*' . s:RE_QUALID . '\s*$' || s:HasKeyword(a:class)
+ return {}
+ endif
+
+
+ let typename = substitute(a:class, '\s', '', 'g')
+ let filekey = a:0 > 0 ? a:1 : s:GetCurrentFileKey()
+ let packagename = a:0 > 1 ? a:2 : s:GetPackageName()
+ let srcpath = join(s:GetSourceDirs(a:0 > 0 && a:1 != bufnr('%') ? a:1 : expand('%:p'), packagename), ',')
+
+ let names = split(typename, '\.')
+ " remove the package name if in the same packge
+ if len(names) > 1
+ if packagename == join(names[:-2], '.')
+ let names = names[-1:]
+ endif
+ endif
+
+ " a FQN
+ if len(names) > 1
+ call s:DoGetTypeInfoForFQN([typename], srcpath)
+ let ci = get(s:cache, typename, {})
+ if get(ci, 'tag', '') == 'CLASSDEF'
+ return s:cache[typename]
+ elseif get(ci, 'tag', '') == 'PACKAGE'
+ return {}
+ endif
+ endif
+
+
+ " The standard search order of a simple type name is as follows:
+ " 1. The current type including inherited types.
+ " 2. A nested type of the current type.
+ " 3. Explicitly named imported types (single type import).
+ " 4. Other types declared in the same package. Not only current directory.
+ " 5. Implicitly named imported types (import on demand).
+
+ " 1 & 2.
+ " NOTE: inherited types are treated as normal
+ if filekey == s:GetCurrentFileKey()
+ let simplename = typename[strridx(typename, '.')+1:]
+ if s:FoundClassDeclaration(simplename) != 0
+ call s:Info('A1&2')
+ let ci = s:GetClassInfoFromSource(simplename, '%')
+ " do not cache it
+ if !empty(ci)
+ return ci
+ endif
+ endif
+ else
+ let ci = s:GetClassInfoFromSource(typename, filekey)
+ if !empty(ci)
+ return ci
+ endif
+ endif
+
+ " 3.
+ " NOTE: PackageName.Ident, TypeName.Ident
+ let fqn = s:SearchSingleTypeImport(typename, s:GetImports('imports_fqn', filekey))
+ if !empty(fqn)
+ call s:Info('A3')
+ call s:DoGetTypeInfoForFQN([fqn], srcpath)
+ let ti = get(s:cache, fqn, {})
+ if get(ti, 'tag', '') != 'CLASSDEF'
+ " TODO: mark the wrong import declaration.
+ endif
+ return ti
+ endif
+
+ " 4 & 5
+ " NOTE: Keeps the fqn of the same package first!!
+ call s:Info('A4&5')
+ let fqns = [empty(packagename) ? typename : packagename . '.' . typename]
+ for p in s:GetImports('imports_star', filekey)
+ call add(fqns, p . typename)
+ endfor
+ call s:DoGetTypeInfoForFQN(fqns, srcpath)
+ for fqn in fqns
+ if has_key(s:cache, fqn)
+ return get(s:cache[fqn], 'tag', '') == 'CLASSDEF' ? s:cache[fqn] : {}
+ endif
+ endfor
+
+ return {}
+endfu
+
+" Rules of overriding and hiding:
+" 1. Fields cannot be overridden; they can only be hidden.
+" In the subclass, the hidden field of superclass can no longer be accessed
+" directly by its simple name. `super` or another reference must be used.
+" 2. A method can be overriden only if it is accessible.
+" When overriding methods, both the signature and return type must be the
+" same as in the superclass.
+" 3. Static members cannot be overridden; they can only be hidden
+" -- whether a field or a method. But hiding static members has little effect,
+" because static should be accessed via the name of its declaring class.
+" Given optional argument, add protected, default (package) access, private members.
+"fu! s:MergeClassInfo(ci, another, ...)
+" if empty(a:another) | return a:ci | endif
+"
+" if empty(a:ci)
+" let ci = copy(a:another)
+"" if a:0 > 0 && a:1
+"" call extend(ci.fields, get(a:another, 'declared_fields', []))
+"" call extend(ci.methods, get(a:another, 'declared_methods', []))
+"" endif
+" return ci
+" endif
+"
+" call extend(a:ci.methods, a:another.methods)
+"
+" for f in a:another.fields
+" if s:Index(a:ci.fields, f.n, 'n') < 0
+" call add(a:ci.fields, f)
+" endif
+" endfor
+" return a:ci
+"endfu
+
+
+" Parameters:
+" class the qualified class name
+" Return: TClassInfo or {} when not found
+" See ClassInfoFactory.getClassInfo() in insenvim.
+function! s:DoGetReflectionClassInfo(fqn)
+ if !has_key(s:cache, a:fqn)
+ let res = s:RunReflection('-C', a:fqn, 's:DoGetReflectionClassInfo')
+ if res =~ '^{'
+ let s:cache[a:fqn] = s:Sort(eval(res))
+ elseif res =~ '^['
+ for type in eval(res)
+ if get(type, 'name', '') != ''
+ let s:cache[type.name] = s:Sort(type)
+ endif
+ endfor
+ else
+ let b:errormsg = res
+ endif
+ endif
+ return get(s:cache, a:fqn, {})
+endfunction
+
+fu! s:GetClassInfoFromSource(class, filename)
+ let ci = {}
+ if len(tagfiles()) > 0
+ let ci = s:DoGetClassInfoFromTags(a:class)
+ endif
+
+ if empty(ci)
+ call s:Info('Use java_parser.vim to generate class information')
+ let unit = javacomplete#parse(a:filename)
+ let targetPos = a:filename == '%' ? java_parser#MakePos(line('.')-1, col('.')-1) : -1
+ for t in s:SearchTypeAt(unit, targetPos, 1)
+ if t.name == a:class
+ let t.filepath = a:filename == '%' ? s:GetCurrentFileKey() : expand(a:filename)
+ return s:Tree2ClassInfo(t)
+ "return s:AddInheritedClassInfo(s:Tree2ClassInfo(t), t)
+ endif
+ endfor
+ endif
+ return ci
+endfu
+
+fu! s:Tree2ClassInfo(t)
+ let t = a:t
+
+ " fill fields and methods
+ let t.fields = []
+ let t.methods = []
+ let t.ctors = []
+ let t.classes = []
+ for def in t.defs
+ if def.tag == 'METHODDEF'
+ call add(def.n == t.name ? t.ctors : t.methods, def)
+ elseif def.tag == 'VARDEF'
+ call add(t.fields, def)
+ elseif def.tag == 'CLASSDEF'
+ call add(t.classes, t.fqn . '.' . def.name)
+ endif
+ endfor
+
+ " convert type name in extends to fqn for class defined in source files
+ if !has_key(a:t, 'classpath') && has_key(a:t, 'extends')
+ if has_key(a:t, 'filepath') && a:t.filepath != s:GetCurrentFileKey()
+ let filepath = a:t.filepath
+ let packagename = get(s:files[filepath].unit, 'package', '')
+ else
+ let filepath = expand('%:p')
+ let packagename = s:GetPackageName()
+ endif
+
+ let extends = a:t.extends
+ let i = 0
+ while i < len(extends)
+ let ci = s:DoGetClassInfo(java_parser#type2Str(extends[i]), filepath, packagename)
+ if has_key(ci, 'fqn')
+ let extends[i] = ci.fqn
+ endif
+ let i += 1
+ endwhile
+ endif
+
+ return t
+endfu
+
+"fu! s:AddInheritedClassInfo(ci, t, ...)
+" let ci = a:ci
+" " add inherited fields and methods
+" let list = []
+" for i in get(a:t, 'extends', [])
+" call add(list, java_parser#type2Str(i))
+" endfor
+"
+" if has_key(a:t, 'filepath') && a:t.filepath != expand('%:p')
+" let filepath = a:t.filepath
+" let props = get(s:files, a:t.filepath, {})
+" let packagename = get(props.unit, 'package', '')
+" else
+" let filepath = expand('%:p')
+" let packagename = s:GetPackageName()
+" endif
+"
+" for id in list
+" let ci = s:MergeClassInfo(ci, s:DoGetClassInfo(id, filepath, packagename), a:0 > 0 && a:1)
+" endfor
+" return ci
+"endfu
+
+" To obtain information of the class in current file or current folder, or
+" even in current project.
+function! s:DoGetClassInfoFromTags(class)
+ " find tag of a:class declaration
+ let tags = taglist('^' . a:class)
+ let filename = ''
+ let cmd = ''
+ for tag in tags
+ if has_key(tag, 'kind')
+ if tag['kind'] == 'c'
+ let filename = tag['filename']
+ let cmd = tag['cmd']
+ break
+ endif
+ endif
+ endfor
+
+ let tags = taglist('^' . (empty(b:incomplete) ? '.*' : b:incomplete) )
+ if filename != ''
+ call filter(tags, "v:val['filename'] == '" . filename . "' && has_key(v:val, 'class') ? v:val['class'] == '" . a:class . "' : 1")
+ endif
+
+ let ci = {'name': a:class}
+ " extends and implements
+ let ci['ctors'] = []
+ let ci['fields'] = []
+ let ci['methods'] = []
+
+ " members
+ for tag in tags
+ let member = {'n': tag['name']}
+
+ " determine kind
+ let kind = 'm'
+ if has_key(tag, 'kind')
+ let kind = tag['kind']
+ endif
+
+ let cmd = tag['cmd']
+ if cmd =~ '\<static\>'
+ let member['m'] = '1000'
+ else
+ let member['m'] = ''
+ endif
+
+ let desc = substitute(cmd, '/^\s*', '', '')
+ let desc = substitute(desc, '\s*{\?\s*$/$', '', '')
+
+ if kind == 'm'
+ " description
+ if cmd =~ '\<static\>'
+ let desc = substitute(desc, '\s\+static\s\+', ' ', '')
+ endif
+ let member['d'] = desc
+
+ let member['p'] = ''
+ let member['r'] = ''
+ if tag['name'] == a:class
+ call add(ci['ctors'], member)
+ else
+ call add(ci['methods'], member)
+ endif
+ elseif kind == 'f'
+ let member['t'] = substitute(desc, '\([a-zA-Z0-9_[\]]\)\s\+\<' . tag['name'] . '\>.*$', '\1', '')
+ call add(ci['fields'], member)
+ endif
+ endfor
+ return ci
+endfu
+
+" package information {{{2
+
+fu! s:DoGetInfoByReflection(class, option)
+ if has_key(s:cache, a:class)
+ return s:cache[a:class]
+ endif
+
+ let res = s:RunReflection(a:option, a:class, 's:DoGetInfoByReflection')
+ if res =~ '^[{\[]'
+ let v = eval(res)
+ if type(v) == type([])
+ let s:cache[a:class] = sort(v)
+ elseif type(v) == type({})
+ if get(v, 'tag', '') =~# '^\(PACKAGE\|CLASSDEF\)$'
+ let s:cache[a:class] = v
+ else
+ call extend(s:cache, v, 'force')
+ endif
+ endif
+ unlet v
+ else
+ let b:errormsg = res
+ endif
+
+ return get(s:cache, a:class, {})
+endfu
+
+" search in members {{{2
+" TODO: what about default access?
+" public for all
+" protected for this or super
+" private for this
+fu! s:CanAccess(mods, kind)
+ return (a:mods[-4:-4] || a:kind/10 == 0)
+ \ && (a:kind == 1 || a:mods[-1:]
+ \ || (a:mods[-3:-3] && (a:kind == 1 || a:kind == 2))
+ \ || (a:mods[-2:-2] && a:kind == 1))
+endfu
+
+fu! s:SearchMember(ci, name, fullmatch, kind, returnAll, memberkind, ...)
+ let result = [[], [], []]
+
+ if a:kind != 13
+ for m in (a:0 > 0 && a:1 ? [] : get(a:ci, 'fields', [])) + ((a:kind == 1 || a:kind == 2) ? get(a:ci, 'declared_fields', []) : [])
+ if empty(a:name) || (a:fullmatch ? m.n ==# a:name : m.n =~# '^' . a:name)
+ if s:CanAccess(m.m, a:kind)
+ call add(result[2], m)
+ endif
+ endif
+ endfor
+
+ for m in (a:0 > 0 && a:1 ? [] : get(a:ci, 'methods', [])) + ((a:kind == 1 || a:kind == 2) ? get(a:ci, 'declared_methods', []) : [])
+ if empty(a:name) || (a:fullmatch ? m.n ==# a:name : m.n =~# '^' . a:name)
+ if s:CanAccess(m.m, a:kind)
+ call add(result[1], m)
+ endif
+ endif
+ endfor
+ endif
+
+ if a:kind/10 != 0
+ let types = get(a:ci, 'classes', [])
+ for t in types
+ if empty(a:name) || (a:fullmatch ? t[strridx(t, '.')+1:] ==# a:name : t[strridx(t, '.')+1:] =~# '^' . a:name)
+ if !has_key(s:cache, t) || !has_key(s:cache[t], 'flags') || a:kind == 1 || s:cache[t].flags[-1:]
+ call add(result[0], t)
+ endif
+ endif
+ endfor
+ endif
+
+ " key `classpath` indicates it is a loaded class from classpath
+ " All public members of a loaded class are stored in current ci
+ if !has_key(a:ci, 'classpath') || (a:kind == 1 || a:kind == 2)
+ for i in get(a:ci, 'extends', [])
+ let ci = s:DoGetClassInfo(java_parser#type2Str(i))
+ let members = s:SearchMember(ci, a:name, a:fullmatch, a:kind == 1 ? 2 : a:kind, a:returnAll, a:memberkind)
+ let result[0] += members[0]
+ let result[1] += members[1]
+ let result[2] += members[2]
+ endfor
+ endif
+ return result
+endfu
+
+
+" generate member list {{{2
+
+fu! s:DoGetFieldList(fields)
+ let s = ''
+ for field in a:fields
+ let s .= "{'kind':'" . (s:IsStatic(field.m) ? "F" : "f") . "','word':'" . field.n . "','menu':'" . field.t . "','dup':1},"
+ endfor
+ return s
+endfu
+
+fu! s:DoGetMethodList(methods, ...)
+ let paren = a:0 == 0 || !a:1 ? '(' : ''
+ let s = ''
+ for method in a:methods
+ let s .= "{'kind':'" . (s:IsStatic(method.m) ? "M" : "m") . "','word':'" . method.n . paren . "','abbr':'" . method.n . "()','menu':'" . method.d . "','dup':'1'},"
+ endfor
+ return s
+endfu
+
+" kind:
+" 0 - for instance, 1 - this, 2 - super, 3 - class, 4 - array, 5 - method result, 6 - primitive type
+" 11 - for type, with `class` and static member and nested types.
+" 12 - for import static, no lparen for static methods
+" 13 - for import or extends or implements, only nested types
+" 20 - for package
+fu! s:DoGetMemberList(ci, kind)
+ if type(a:ci) != type({}) || a:ci == {}
+ return []
+ endif
+
+ let s = a:kind == 11 ? "{'kind': 'C', 'word': 'class', 'menu': 'Class'}," : ''
+
+ let members = s:SearchMember(a:ci, '', 1, a:kind, 1, 0, a:kind == 2)
+
+ " add accessible member types
+ if a:kind / 10 != 0
+ " Use dup here for member type can share name with field.
+ for class in members[0]
+ "for class in get(a:ci, 'classes', [])
+ let v = get(s:cache, class, {})
+ if v == {} || v.flags[-1:]
+ let s .= "{'kind': 'C', 'word': '" . substitute(class, a:ci.name . '\.', '\1', '') . "','dup':1},"
+ endif
+ endfor
+ endif
+
+ if a:kind != 13
+ let fieldlist = []
+ let sfieldlist = []
+ for field in members[2]
+ "for field in get(a:ci, 'fields', [])
+ if s:IsStatic(field['m'])
+ call add(sfieldlist, field)
+ elseif a:kind / 10 == 0
+ call add(fieldlist, field)
+ endif
+ endfor
+
+ let methodlist = []
+ let smethodlist = []
+ for method in members[1]
+ if s:IsStatic(method['m'])
+ call add(smethodlist, method)
+ elseif a:kind / 10 == 0
+ call add(methodlist, method)
+ endif
+ endfor
+
+ if a:kind / 10 == 0
+ let s .= s:DoGetFieldList(fieldlist)
+ let s .= s:DoGetMethodList(methodlist)
+ endif
+ let s .= s:DoGetFieldList(sfieldlist)
+ let s .= s:DoGetMethodList(smethodlist, a:kind == 12)
+
+ let s = substitute(s, '\<' . a:ci.name . '\.', '', 'g')
+ let s = substitute(s, '\<java\.lang\.', '', 'g')
+ let s = substitute(s, '\<\(public\|static\|synchronized\|transient\|volatile\|final\|strictfp\|serializable\|native\)\s\+', '', 'g')
+ endif
+ return eval('[' . s . ']')
+endfu
+
+" interface {{{2
+
+function! s:GetMemberList(class)
+ if s:IsBuiltinType(a:class)
+ return []
+ endif
+
+ return s:DoGetMemberList(s:DoGetClassInfo(a:class), 0)
+endfunction
+
+fu! s:GetStaticMemberList(class)
+ return s:DoGetMemberList(s:DoGetClassInfo(a:class), 11)
+endfu
+
+function! s:GetConstructorList(class)
+ let ci = s:DoGetClassInfo(a:class)
+ if empty(ci)
+ return []
+ endif
+
+ let s = ''
+ for ctor in get(ci, 'ctors', [])
+ let s .= "{'kind': '+', 'word':'". a:class . "(','abbr':'" . ctor.d . "','dup':1},"
+ endfor
+
+ let s = substitute(s, '\<java\.lang\.', '', 'g')
+ let s = substitute(s, '\<public\s\+', '', 'g')
+ return eval('[' . s . ']')
+endfunction
+
+" Name can be a (simple or qualified) package name, or a (simple or qualified)
+" type name.
+fu! s:GetMembers(fqn, ...)
+ let list = []
+ let isClass = 0
+
+ let v = s:DoGetInfoByReflection(a:fqn, '-E')
+ if type(v) == type([])
+ let list = v
+ elseif type(v) == type({}) && v != {}
+ if get(v, 'tag', '') == 'PACKAGE'
+ if b:context_type == s:CONTEXT_IMPORT_STATIC || b:context_type == s:CONTEXT_IMPORT
+ call add(list, {'kind': 'P', 'word': '*;'})
+ endif
+ if b:context_type != s:CONTEXT_PACKAGE_DECL
+ for c in sort(get(v, 'classes', []))
+ call add(list, {'kind': 'C', 'word': c})
+ endfor
+ endif
+ for p in sort(get(v, 'subpackages', []))
+ call add(list, {'kind': 'P', 'word': p})
+ endfor
+ else " elseif get(v, 'tag', '') == 'CLASSDEF'
+ let isClass = 1
+ let list += s:DoGetMemberList(v, b:context_type == s:CONTEXT_IMPORT || b:context_type == s:CONTEXT_NEED_TYPE ? 13 : b:context_type == s:CONTEXT_IMPORT_STATIC ? 12 : 11)
+ endif
+ endif
+
+ if !isClass
+ let list += s:DoGetPackageInfoInDirs(a:fqn, b:context_type == s:CONTEXT_PACKAGE_DECL)
+ endif
+
+ return list
+endfu
+
+" a:1 incomplete mode
+" return packages in classes directories or source pathes
+fu! s:DoGetPackageInfoInDirs(package, onlyPackages, ...)
+ let list = []
+
+ let pathes = s:GetSourceDirs(expand('%:p'))
+ for path in s:GetClassDirs()
+ if index(pathes, path) <= 0
+ call add(pathes, path)
+ endif
+ endfor
+
+ let globpattern = a:0 > 0 ? a:package . '*' : substitute(a:package, '\.', '/', 'g') . '/*'
+ let matchpattern = a:0 > 0 ? a:package : a:package . '[\\/]'
+ for f in split(globpath(join(pathes, ','), globpattern), "\n")
+ for path in pathes
+ let idx = matchend(f, escape(path, ' \') . '[\\/]\?\C' . matchpattern)
+ if idx != -1
+ let name = (a:0 > 0 ? a:package : '') . strpart(f, idx)
+ if f[-5:] == '.java'
+ if !a:onlyPackages
+ call add(list, {'kind': 'C', 'word': name[:-6]})
+ endif
+ elseif name =~ '^' . s:RE_IDENTIFIER . '$' && isdirectory(f) && f !~# 'CVS$'
+ call add(list, {'kind': 'P', 'word': name})
+ endif
+ endif
+ endfor
+ endfor
+ return list
+endfu
+" }}}
+"}}}
+" vim:set fdm=marker sw=2 nowrap: