From 2c7730b8b9b31b6046c7978f5c716e926cf9a1a1 Mon Sep 17 00:00:00 2001 From: lhark Date: Sat, 8 Aug 2015 22:44:49 +0200 Subject: [PATCH 1/3] Cleaned and iNdented vimrc --- vimrc | 284 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 144 insertions(+), 140 deletions(-) diff --git a/vimrc b/vimrc index cc0f911..00aecc2 100644 --- a/vimrc +++ b/vimrc @@ -36,52 +36,52 @@ set nocompatible "[Use Plug-ins and Plug-in Manager(Vundle) only on UNIX or MAC OS]" if has("unix") || has("mac") -filetype off -"[Download and install Vundle Plug-in Manager]" -try -if !isdirectory(expand("$HOME/.vim/bundle")) -silent !mkdir -p $HOME/.vim/bundle -silent cd $HOME/.vim/bundle -silent !git clone https://github.com/gmarik/Vundle.vim.git -silent cd $HOME -endif -"[Run Plug-ins]" -set runtimepath+=$HOME/.vim/bundle/Vundle.vim -call vundle#begin() -Plugin 'gmarik/Vundle.vim' -map :VundleInstall -map :VundleUpdate -if has("python") -Plugin 'klen/python-mode' -endif -if has("perl") -Plugin 'vim-perl/vim-perl' -endif -if has("ruby") -Plugin 'terryma/vim-multiple-cursors' -endif -Plugin 'majutsushi/tagbar' -map :TagbarToggle -Plugin 'mbbill/undotree' -map :UndotreeToggle -Plugin 'scrooloose/nerdtree' -map :NERDTreeToggle -Plugin 'AutoComplPop' -Plugin 'kien/ctrlp.vim' -Plugin 'godlygeek/tabular' -Plugin 'tpope/vim-surround' -Plugin 'mhinz/vim-startify' -Plugin 'Raimondi/delimitMate' -Plugin 'msanders/snipmate.vim' -Plugin 'Lokaltog/vim-easymotion' -"[Vim colorschemes]" -Plugin 'tomasr/molokai' -Plugin 'djjcast/mirodark' -call vundle#end() -catch -endtry + filetype off + "[Download and install Vundle Plug-in Manager]" + try + if !isdirectory(expand("$HOME/.vim/bundle")) + silent !mkdir -p $HOME/.vim/bundle + silent cd $HOME/.vim/bundle + silent !git clone https://github.com/gmarik/Vundle.vim.git + silent cd $HOME + endif + "[Run Plug-ins]" + set runtimepath+=$HOME/.vim/bundle/Vundle.vim + call vundle#begin() + Plugin 'gmarik/Vundle.vim' + map :VundleInstall + map :VundleUpdate + if has("python") + Plugin 'klen/python-mode' + endif + if has("perl") + Plugin 'vim-perl/vim-perl' + endif + if has("ruby") + Plugin 'terryma/vim-multiple-cursors' + endif + Plugin 'majutsushi/tagbar' + map :TagbarToggle + Plugin 'mbbill/undotree' + map :UndotreeToggle + Plugin 'scrooloose/nerdtree' + map :NERDTreeToggle + Plugin 'AutoComplPop' + Plugin 'kien/ctrlp.vim' + Plugin 'godlygeek/tabular' + Plugin 'tpope/vim-surround' + Plugin 'mhinz/vim-startify' + Plugin 'Raimondi/delimitMate' + Plugin 'msanders/snipmate.vim' + Plugin 'Lokaltog/vim-easymotion' + "[Vim colorschemes]" + Plugin 'tomasr/molokai' + Plugin 'djjcast/mirodark' + call vundle#end() + catch + endtry else -filetype on + filetype on endif "[Recognize the type/syntax of the file]" filetype plugin on @@ -93,7 +93,7 @@ runtime macros/matchit.vim syntax on "[Read the changes after the save .vimrc]" if has("autocmd") -autocmd! BufWritePost $MYVIMRC source $MYVIMRC + autocmd! BufWritePost $MYVIMRC source $MYVIMRC endif "[Define the leader key]" let mapleader="," @@ -112,6 +112,10 @@ nnoremap j nnoremap k nnoremap h nnoremap l +nnoremap k +nnoremap j +nnoremap h +nnoremap l "[Locate the desired objects in the center of the screen]" nnoremap n nzz nnoremap N Nzz @@ -148,7 +152,7 @@ set showfulltag "[Don't give the intro message when starting Vim]" set shortmess="" if has("unix") || has("mac") -set shortmess+=I + set shortmess+=I endif "[Always show StatusLine]" set laststatus=2 @@ -167,7 +171,7 @@ set splitright set equalalways "[Lisp coding settings]" if (&filetype == "lisp") -set lisp + set lisp endif "[Use the mouse in terminal]" set mouse=a @@ -187,43 +191,43 @@ set cmdwinheight=10 set virtualedit=all "[GUI/Color Scheme/Font settings]" if has("gui_running") -winsize 90 50 -silent cd $HOME -set linespace=0 -set guioptions="" -set guitablabel="" -if has("autocmd") -autocmd InsertEnter * set cursorline -autocmd InsertLeave * set nocursorline -endif -if has("win32") || has("win64") -try -colorscheme desert -set guifont=PT_Mono:h11 -catch -endtry -elseif has("unix") -try -colorscheme mirodark -set guifont=PT\ Mono\ 11 -catch -endtry -elseif has("mac") -try -set antialias -colorscheme molokai -set guifont=Monaco:h11 -catch -endtry -endif + winsize 90 50 + silent cd $HOME + set linespace=0 + set guioptions="" + set guitablabel="" + if has("autocmd") + autocmd InsertEnter * set cursorline + autocmd InsertLeave * set nocursorline + endif + if has("win32") || has("win64") + try + colorscheme desert + set guifont=PT_Mono:h11 + catch + endtry + elseif has("unix") + try + colorscheme mirodark + set guifont=PT\ Mono\ 11 + catch + endtry + elseif has("mac") + try + set antialias + colorscheme molokai + set guifont=Monaco:h11 + catch + endtry + endif else -if has("unix") || has("mac") -try -set t_Co=256 -colorscheme desert -catch -endtry -endif + if has("unix") || has("mac") + try + set t_Co=256 + colorscheme desert + catch + endtry + endif endif "[Backspace functions]" set backspace=indent,eol,start @@ -231,7 +235,7 @@ set backspace=indent,eol,start set scrolloff=10 set scrolljump=10 set showmatch -set matchpairs="" +set matchpairs=":" set matchpairs+=(:) set matchpairs+={:} set matchpairs+=[:] @@ -263,7 +267,7 @@ set textwidth=80 set number set numberwidth=2 "[Don't show current position]" -set noruler +set ruler "[For regular expressions turn magic on]" set magic "[Search settings]" @@ -331,19 +335,19 @@ set sessionoptions+=unix,slash,blank,buffers,curdir set sessionoptions+=folds,help,options,tabpages,winsize "[Completion settings]" if has("autocmd") -autocmd FileType c set omnifunc=ccomplete#Complete -autocmd FileType css set omnifunc=csscomplete#CompleteCSS -autocmd FileType html set omnifunc=htmlcomplete#CompleteTags -if has("ruby") -autocmd FileType ruby set omnifunc=rubycomplete#Complete -endif -if has("perl") -autocmd FileType perl set omnifunc=perlcomplete#CompletePERL -endif -if has("python") -autocmd FileType python set omnifunc=pythoncomplete#Complete -endif -autocmd FileType javascript set omnifunc=javascriptcomplete#CompleteJS + autocmd FileType c set omnifunc=ccomplete#Complete + autocmd FileType css set omnifunc=csscomplete#CompleteCSS + autocmd FileType html set omnifunc=htmlcomplete#CompleteTags + if has("ruby") + autocmd FileType ruby set omnifunc=rubycomplete#Complete + endif + if has("perl") + autocmd FileType perl set omnifunc=perlcomplete#CompletePERL + endif + if has("python") + autocmd FileType python set omnifunc=pythoncomplete#Complete + endif + autocmd FileType javascript set omnifunc=javascriptcomplete#CompleteJS endif "[Completion search settings]" set complete=.,b,d,i,k,s,t,u,U,w @@ -369,63 +373,63 @@ set wildignore=*.o,*.obj,*.pyc,*.pyo,*.swp,*.bak,*.exe,*.class set confirm "[Method used for encryption when the buffer is written to a file]" if (version <= 702) -set cryptmethod=zip -else -set cryptmethod=blowfish + set cryptmethod=zip + else + set cryptmethod=blowfish endif "[Make the scripts executable]" function! ChangeScriptMode() -if getline(1) =~ "#!" -if getline(1) =~ "bin/" -silent !chmod +x -endif -endif + if getline(1) =~ "#!" + if getline(1) =~ "bin/" + silent !chmod +x + endif + endif endfunction if has("unix") || has("mac") -if has("autocmd") -autocmd BufWritePost * call ChangeScriptMode() -endif + if has("autocmd") + autocmd BufWritePost * call ChangeScriptMode() + endif endif "[Python/Perl scripts templates]" function! InitScriptFile(type) -if (a:type == "python") -execute setline(1, "#!/usr/bin/env python") -execute setline(2, "# -*- coding: utf-8 -*-") -elseif (a:type == "perl") -execute setline(1, "#!/usr/bin/env perl") -execute setline(2, "") -execute setline(3, "use warnings;") -execute setline(4, "use strict;") -endif -normal Go + if (a:type == "python") + execute setline(1, "#!/usr/bin/env python") + execute setline(2, "# -*- coding: utf-8 -*-") + elseif (a:type == "perl") + execute setline(1, "#!/usr/bin/env perl") + execute setline(2, "") + execute setline(3, "use warnings;") + execute setline(4, "use strict;") + endif + normal Go endfunction if has("autocmd") -autocmd BufNewFile *.pl,*.pm call InitScriptFile("perl") -autocmd BufNewFile *.py,*.pyw call InitScriptFile("python") + autocmd BufNewFile *.pl,*.pm call InitScriptFile("perl") + autocmd BufNewFile *.py,*.pyw call InitScriptFile("python") endif "[Remove tabs and spaces at the end of lines]" function! DeleteTrailingTWS() -normal mb -silent %s/[ \t]*$//g -silent %s/\s\+$//ge -normal 'b + normal mb + silent %s/[ \t]*$//g + silent %s/\s\+$//ge + normal 'b endfunction if has("autocmd") -autocmd BufWritePre *.py,*.pyw retab -autocmd BufWritePre * call DeleteTrailingTWS() + autocmd BufWritePre *.py,*.pyw retab + autocmd BufWritePre * call DeleteTrailingTWS() endif "[Show current mode in StatusLine]" function! ShowModeInStatusLine() -let g:currentMode = mode() -let g:showMode = "" -if (g:currentMode ==# "i") -let g:showMode = "Insert" -elseif (g:currentMode ==# "R") -let g:showMode = "Replace" -elseif (g:currentMode ==# "n") -let g:showMode = "Normal" -else -let g:showMode = "Visual" -endif -return g:showMode + let g:currentMode = mode() + let g:showMode = "" + if (g:currentMode ==# "i") + let g:showMode = "Insert" + elseif (g:currentMode ==# "R") + let g:showMode = "Replace" + elseif (g:currentMode ==# "n") + let g:showMode = "Normal" + else + let g:showMode = "Visual" + endif + return g:showMode endfunction""""""""""""""""""" From 2af0808b3aafc6e4709e4c79bf836ff48b91a4a2 Mon Sep 17 00:00:00 2001 From: lhark Date: Sat, 8 Aug 2015 22:45:40 +0200 Subject: [PATCH 2/3] Added vim folder for plugins content and language specific config --- gitconfig | 6 +- install.sh | 2 +- vim/.netrwhist | 5 + vim/after/ftplugin/java.vim | 16 + vim/autoload/Reflection.java | 670 ++++ vim/autoload/java_parser.vim | 3500 +++++++++++++++++++++ vim/autoload/javacomplete.vim | 2932 +++++++++++++++++ vim/autoload/togglebg.vim | 55 + vim/bitmaps/togglebg.png | Bin 0 -> 1674 bytes vim/bundle/AutoComplPop | 1 + vim/bundle/Vundle.vim | 1 + vim/bundle/ctrlp.vim | 1 + vim/bundle/delimitMate | 1 + vim/bundle/mirodark | 1 + vim/bundle/molokai | 1 + vim/bundle/nerdtree | 1 + vim/bundle/python-mode | 1 + vim/bundle/snipmate.vim | 1 + vim/bundle/tabular | 1 + vim/bundle/tagbar | 1 + vim/bundle/undotree | 1 + vim/bundle/vim-easymotion | 1 + vim/bundle/vim-multiple-cursors | 1 + vim/bundle/vim-perl | 1 + vim/bundle/vim-startify | 1 + vim/bundle/vim-surround | 1 + vim/colors/solarized.vim | 5219 +++++++++++++++++++++++++++++++ vim/doc/javacomplete.txt | 568 ++++ vim/doc/solarized.txt | 254 ++ vim/doc/tags | 51 + 30 files changed, 13293 insertions(+), 2 deletions(-) create mode 100644 vim/.netrwhist create mode 100644 vim/after/ftplugin/java.vim create mode 100644 vim/autoload/Reflection.java create mode 100644 vim/autoload/java_parser.vim create mode 100644 vim/autoload/javacomplete.vim create mode 100644 vim/autoload/togglebg.vim create mode 100644 vim/bitmaps/togglebg.png create mode 160000 vim/bundle/AutoComplPop create mode 160000 vim/bundle/Vundle.vim create mode 160000 vim/bundle/ctrlp.vim create mode 160000 vim/bundle/delimitMate create mode 160000 vim/bundle/mirodark create mode 160000 vim/bundle/molokai create mode 160000 vim/bundle/nerdtree create mode 160000 vim/bundle/python-mode create mode 160000 vim/bundle/snipmate.vim create mode 160000 vim/bundle/tabular create mode 160000 vim/bundle/tagbar create mode 160000 vim/bundle/undotree create mode 160000 vim/bundle/vim-easymotion create mode 160000 vim/bundle/vim-multiple-cursors create mode 160000 vim/bundle/vim-perl create mode 160000 vim/bundle/vim-startify create mode 160000 vim/bundle/vim-surround create mode 100644 vim/colors/solarized.vim create mode 100644 vim/doc/javacomplete.txt create mode 100644 vim/doc/solarized.txt create mode 100644 vim/doc/tags diff --git a/gitconfig b/gitconfig index 877ddba..2fb77eb 100644 --- a/gitconfig +++ b/gitconfig @@ -1,3 +1,7 @@ [user] - email = lhark@hotmail.fr name = lhark + email = lhark@hotmail.fr +[master] + autosetuprebase = always +[color] + ui = auto diff --git a/install.sh b/install.sh index dbf312d..f7b8668 100755 --- a/install.sh +++ b/install.sh @@ -4,7 +4,7 @@ SCRIPT=$(readlink -f "$0") SCRIPTPATH=$(dirname "$SCRIPT") # List of the config files to install -FILES="vimrc zshrc gitconfig" +FILES="vimrc zshrc gitconfig vim" # Create symbolic links in the user's home dir for file in $FILES diff --git a/vim/.netrwhist b/vim/.netrwhist new file mode 100644 index 0000000..51d8aeb --- /dev/null +++ b/vim/.netrwhist @@ -0,0 +1,5 @@ +let g:netrw_dirhistmax =10 +let g:netrw_dirhist_cnt =3 +let g:netrw_dirhist_1='/home/lhark/.config/xfce4/xfconf' +let g:netrw_dirhist_2='/home/lhark/dev/paiji2/.git' +let g:netrw_dirhist_3='/usr/share/themes/Mire_v2_blue' diff --git a/vim/after/ftplugin/java.vim b/vim/after/ftplugin/java.vim new file mode 100644 index 0000000..8ef7c8b --- /dev/null +++ b/vim/after/ftplugin/java.vim @@ -0,0 +1,16 @@ +" Java specific config + +" Indentation +setlocal autoindent +setlocal si +setlocal shiftwidth=4 +setlocal noexpandtab +" Anonymous classes +setlocal cindent cinoptions& cinoptions+=j1 +" Repair extends and implements Indentation +setlocal indentkeys& indentkeys+=0=extends indentkeys+=0=implements + +"Syntax highlighting +let java_highlight_java_lang_ids=1 +let java_highlight_functions="style" +let java_highlight_debug=1 diff --git a/vim/autoload/Reflection.java b/vim/autoload/Reflection.java new file mode 100644 index 0000000..5452b0f --- /dev/null +++ b/vim/autoload/Reflection.java @@ -0,0 +1,670 @@ +/** + * Reflection.java + * + * A utility class for javacomplete mainly for reading class or package information. + * Version: 0.77 + * Maintainer: cheng fang + * Last Change: 2007-09-16 + * Copyright: Copyright (C) 2007 cheng fang. All rights reserved. + * License: Vim License (see vim's :help license) + * + */ + +import java.lang.reflect.*; +import java.io.*; +import java.util.*; +import java.util.zip.*; + +class Reflection { + static final String VERSION = "0.77"; + + static final int OPTION_FIELD = 1; + static final int OPTION_METHOD = 2; + static final int OPTION_STATIC_FIELD = 4; + static final int OPTION_STATIC_METHOD = 8; + static final int OPTION_CONSTRUCTOR = 16; + static final int OPTION_STATIC = 12; // compound static + static final int OPTION_INSTANCE = 15; // compound instance + static final int OPTION_ALL = 31; // compound all + static final int OPTION_SUPER = 32; + static final int OPTION_SAME_PACKAGE = 64; + + static final int STRATEGY_ALPHABETIC = 128; + static final int STRATEGY_HIERARCHY = 256; + static final int STRATEGY_DEFAULT = 512; + + static final int RETURN_ALL_PACKAGE_INFO = 0x1000; + + static final String KEY_NAME = "'n':"; // "'name':"; + static final String KEY_TYPE = "'t':"; // "'type':"; + static final String KEY_MODIFIER = "'m':"; // "'modifier':"; + static final String KEY_PARAMETERTYPES = "'p':"; // "'parameterTypes':"; + static final String KEY_RETURNTYPE = "'r':"; // "'returnType':"; + static final String KEY_DESCRIPTION = "'d':"; // "'description':"; + static final String KEY_DECLARING_CLASS = "'c':"; // "'declaringclass':"; + + static final String NEWLINE = ""; // "\r\n" + + static boolean debug_mode = false; + + static Hashtable htClasspath = new Hashtable(); + + public static boolean existed(String fqn) { + boolean result = false; + try { + Class.forName(fqn); + result = true; + } + catch (Exception ex) { + } + return result; + } + + public static String existedAndRead(String fqns) { + Hashtable mapPackages = new Hashtable(); // qualified name --> StringBuffer + Hashtable mapClasses = new Hashtable(); // qualified name --> StringBuffer + + for (StringTokenizer st = new StringTokenizer(fqns, ","); st.hasMoreTokens(); ) { + String fqn = st.nextToken(); + try { + Class clazz = Class.forName(fqn); + putClassInfo(mapClasses, clazz); + } + catch (Exception ex) { + String binaryName = fqn; + boolean found = false; + while (true) { + try { + int lastDotPos = binaryName.lastIndexOf('.'); + if (lastDotPos == -1) + break; + binaryName = binaryName.substring(0, lastDotPos) + '$' + binaryName.substring(lastDotPos+1, binaryName.length()); + Class clazz = Class.forName(binaryName); + putClassInfo(mapClasses, clazz); + found = true; + break; + } + catch (Exception e) { + } + } + if (!found) + putPackageInfo(mapPackages, fqn); + } + } + + if (mapPackages.size() > 0 || mapClasses.size() > 0) { + StringBuffer sb = new StringBuffer(4096); + sb.append("{"); + for (Enumeration e = mapPackages.keys(); e.hasMoreElements(); ) { + String s = (String)e.nextElement(); + sb.append("'").append( s.replace('$', '.') ).append("':").append(mapPackages.get(s)).append(","); + } + for (Enumeration e = mapClasses.keys(); e.hasMoreElements(); ) { + String s = (String)e.nextElement(); + sb.append("'").append( s.replace('$', '.') ).append("':").append(mapClasses.get(s)).append(","); + } + sb.append("}"); + return sb.toString(); + } + else + return ""; + } + + private static String getPackageList(String fqn) { + Hashtable mapPackages = new Hashtable(); + putPackageInfo(mapPackages, fqn); + return mapPackages.size() > 0 ? mapPackages.get(fqn).toString() : ""; + } + + private static Hashtable collectClassPath() { + if (!htClasspath.isEmpty()) + return htClasspath; + + // runtime classes + if ("Kaffe".equals(System.getProperty("java.vm.name"))) { + addClasspathesFromDir(System.getProperty("java.home") + File.separator + "share" + File.separator + "kaffe" + File.separator); + } + else if ("GNU libgcj".equals(System.getProperty("java.vm.name"))) { + if (new File(System.getProperty("sun.boot.class.path")).exists()) + htClasspath.put(System.getProperty("sun.boot.class.path"), ""); + } + + if (System.getProperty("java.vendor").toLowerCase(Locale.US).indexOf("microsoft") >= 0) { + // `*.ZIP` files in `Packages` directory + addClasspathesFromDir(System.getProperty("java.home") + File.separator + "Packages" + File.separator); + } + else { + // the following code works for several kinds of JDK + // - JDK1.1: classes.zip + // - JDK1.2+: rt.jar + // - JDK1.4+ of Sun and Apple: rt.jar + jce.jar + jsse.jar + // - JDK1.4 of IBM split rt.jar into core.jar, graphics.jar, server.jar + // combined jce.jar and jsse.jar into security.jar + // - JDK for MacOS X split rt.jar into classes.jar, ui.jar in Classes directory + addClasspathesFromDir(System.getProperty("java.home") + File.separator + "lib" + File.separator); + addClasspathesFromDir(System.getProperty("java.home") + File.separator + "jre" + File.separator + "lib" + File.separator); + addClasspathesFromDir(System.getProperty("java.home") + File.separator + ".." + File.separator + "Classes" + File.separator); + } + + // ext + String extdirs = System.getProperty("java.ext.dirs"); + for (StringTokenizer st = new StringTokenizer(extdirs, File.pathSeparator); st.hasMoreTokens(); ) { + addClasspathesFromDir(st.nextToken() + File.separator); + } + + // user classpath + String classPath = System.getProperty("java.class.path"); + StringTokenizer st = new StringTokenizer(classPath, File.pathSeparator); + while (st.hasMoreTokens()) { + String path = st.nextToken(); + File f = new File(path); + if (!f.exists()) + continue; + + if (path.endsWith(".jar") || path.endsWith(".zip")) + htClasspath.put(f.toString(), ""); + else { + if (f.isDirectory()) + htClasspath.put(f.toString(), ""); + } + } + + return htClasspath; + } + + private static void addClasspathesFromDir(String dirpath) { + File dir = new File(dirpath); + if (dir.isDirectory()) { + String[] items = dir.list(); // use list() instead of listFiles() since the latter are introduced in 1.2 + for (int i = 0; i < items.length; i++) { + File f = new File(dirpath + items[i]); + if (!f.exists()) + continue; + + if (items[i].endsWith(".jar") || items[i].endsWith(".zip") || items[i].endsWith(".ZIP")) { + htClasspath.put(f.toString(), ""); + } + else if (items.equals("classes")) { + if (f.isDirectory()) + htClasspath.put(f.toString(), ""); + } + } + } + } + + + /** + * If name is empty, put all loadable package info into map once. + */ + private static void putPackageInfo(Hashtable map, String name) { + String prefix = name.replace('.', '/') + "/"; + Hashtable subpackages = new Hashtable(); + Hashtable classes = new Hashtable(); + for (Enumeration e = collectClassPath().keys(); e.hasMoreElements(); ) { + String path = (String)e.nextElement(); + if (path.endsWith(".jar") || path.endsWith(".zip")) + appendListFromJar(subpackages, classes, path, prefix); + else + appendListFromFolder(subpackages, classes, path, prefix); + } + + if (subpackages.size() > 0 || classes.size() > 0) { + StringBuffer sb = new StringBuffer(1024); + sb.append("{'tag':'PACKAGE','subpackages':["); + for (Enumeration e = subpackages.keys(); e.hasMoreElements(); ) { + sb.append("'").append(e.nextElement()).append("',"); + } + sb.append("],'classes':["); + for (Enumeration e = classes.keys(); e.hasMoreElements(); ) { + sb.append("'").append(e.nextElement()).append("',"); + } + sb.append("]}"); + map.put(name, sb.toString()); + } + } + + public static void appendListFromJar(Hashtable subpackages, Hashtable classes, String path, String prefix) { + try { + for (Enumeration entries = new ZipFile(path).entries(); entries.hasMoreElements(); ) { + String entry = entries.nextElement().toString(); + int len = entry.length(); + if (entry.endsWith(".class") && entry.indexOf('$') == -1 + && entry.startsWith(prefix)) { + int splitPos = entry.indexOf('/', prefix.length()); + String shortname = entry.substring(prefix.length(), splitPos == -1 ? entry.length()-6 : splitPos); + if (splitPos == -1) { + if (!classes.containsKey(shortname)) + classes.put(shortname, ""); //classes.put(shortname, "{'tag':'CLASSDEF','name':'"+shortname+"'}"); + } + else { + if (!subpackages.containsKey(shortname)) + subpackages.put(shortname, ""); //subpackages.put(shortname, "{'tag':'PACKAGE','name':'" +shortname+"'}"); + } + } + } + } + catch (Throwable e) { + //e.printStackTrace(); + } + } + + public static void appendListFromFolder(Hashtable subpackages, Hashtable classes, String path, String prefix) { + try { + String fullPath = path + "/" + prefix; + File file = new File(fullPath); + if (file.isDirectory()) { + String[] descents = file.list(); + for (int i = 0; i < descents.length; i++) { + if (descents[i].indexOf('$') == -1) { + if (descents[i].endsWith(".class")) { + String shortname = descents[i].substring(0, descents[i].length()-6); + if (!classes.containsKey(shortname)) + classes.put(shortname, ""); + } + else if ((new File(fullPath + "/" + descents[i])).isDirectory()) { + if (!subpackages.containsKey(descents[i])) + subpackages.put(descents[i], ""); + } + } + } + } + } + catch (Throwable e) { + } + } + + private static int INDEX_PACKAGE = 0; + private static int INDEX_CLASS = 1; + + // generate information of all packages in jar files. + public static String getPackageList() { + Hashtable map = new Hashtable(); + + for (Enumeration e = collectClassPath().keys(); e.hasMoreElements(); ) { + String path = (String)e.nextElement(); + if (path.endsWith(".jar") || path.endsWith(".zip")) + appendListFromJar(path, map); + } + + StringBuffer sb = new StringBuffer(4096); + sb.append("{"); + //sb.append("'*':'").append( map.remove("") ).append("',"); // default package + for (Enumeration e = map.keys(); e.hasMoreElements(); ) { + String s = (String)e.nextElement(); + StringBuffer[] sbs = (StringBuffer[])map.get(s); + sb.append("'").append( s.replace('/', '.') ).append("':") + .append("{'tag':'PACKAGE'"); + if (sbs[INDEX_PACKAGE].length() > 0) + sb.append(",'subpackages':[").append(sbs[INDEX_PACKAGE]).append("]"); + if (sbs[INDEX_CLASS].length() > 0) + sb.append(",'classes':[").append(sbs[INDEX_CLASS]).append("]"); + sb.append("},"); + } + sb.append("}"); + return sb.toString(); + + } + + public static void appendListFromJar(String path, Hashtable map) { + try { + for (Enumeration entries = new ZipFile(path).entries(); entries.hasMoreElements(); ) { + String entry = entries.nextElement().toString(); + int len = entry.length(); + if (entry.endsWith(".class") && entry.indexOf('$') == -1) { + int slashpos = entry.lastIndexOf('/'); + String parent = entry.substring(0, slashpos); + String child = entry.substring(slashpos+1, len-6); + putItem(map, parent, child, INDEX_CLASS); + + slashpos = parent.lastIndexOf('/'); + if (slashpos != -1) { + AddToParent(map, parent.substring(0, slashpos), parent.substring(slashpos+1)); + } + } + } + } + catch (Throwable e) { + //e.printStackTrace(); + } + } + + public static void putItem(Hashtable map, String parent, String child, int index) { + StringBuffer[] sbs = (StringBuffer[])map.get(parent); + if (sbs == null) { + sbs = new StringBuffer[] { new StringBuffer(256), // packages + new StringBuffer(256) // classes + }; + } + if (sbs[index].toString().indexOf("'" + child + "',") == -1) + sbs[index].append("'").append(child).append("',"); + map.put(parent, sbs); + } + + public static void AddToParent(Hashtable map, String parent, String child) { + putItem(map, parent, child, INDEX_PACKAGE); + + int slashpos = parent.lastIndexOf('/'); + if (slashpos != -1) { + AddToParent(map, parent.substring(0, slashpos), parent.substring(slashpos+1)); + } + } + + + public static String getClassInfo(String className) { + Hashtable mapClasses = new Hashtable(); + try { + Class clazz = Class.forName(className); + putClassInfo(mapClasses, clazz); + } + catch (Exception ex) { + } + + if (mapClasses.size() == 1) { + return mapClasses.get(className).toString(); // return {...} + } + else if (mapClasses.size() > 1) { + StringBuffer sb = new StringBuffer(4096); + sb.append("["); + for (Enumeration e = mapClasses.keys(); e.hasMoreElements(); ) { + String s = (String)e.nextElement(); + sb.append(mapClasses.get(s)).append(","); + } + sb.append("]"); + return sb.toString(); // return [...] + } + else + return ""; + } + + private static void putClassInfo(Hashtable map, Class clazz) { + if (map.containsKey(clazz.getName())) + return ; + + try { + StringBuffer sb = new StringBuffer(1024); + sb.append("{") + .append("'tag':'CLASSDEF',").append(NEWLINE) + .append("'flags':'").append(Integer.toString(clazz.getModifiers(), 2)).append("',").append(NEWLINE) + .append("'name':'").append(clazz.getName().replace('$', '.')).append("',").append(NEWLINE) + //.append("'package':'").append(clazz.getPackage().getName()).append("',").append(NEWLINE) // no getPackage() in JDK1.1 + .append("'classpath':'1',").append(NEWLINE) + .append("'fqn':'").append(clazz.getName().replace('$', '.')).append("',").append(NEWLINE); + + Class[] interfaces = clazz.getInterfaces(); + if (clazz.isInterface()) { + sb.append("'extends':["); + } else { + Class superclass = clazz.getSuperclass(); + if (superclass != null && !"java.lang.Object".equals(superclass.getName())) { + sb.append("'extends':['").append(superclass.getName().replace('$', '.')).append("'],").append(NEWLINE); + putClassInfo(map, superclass); // !! + } + sb.append("'implements':["); + } + for (int i = 0, n = interfaces.length; i < n; i++) { + sb.append("'").append(interfaces[i].getName().replace('$', '.')).append("',"); + putClassInfo(map, interfaces[i]); // !! + } + sb.append("],").append(NEWLINE);; + + Constructor[] ctors = clazz.getConstructors(); + sb.append("'ctors':["); + for (int i = 0, n = ctors.length; i < n; i++) { + Constructor ctor = ctors[i]; + sb.append("{"); + appendModifier(sb, ctor.getModifiers()); + appendParameterTypes(sb, ctor.getParameterTypes()); + sb.append(KEY_DESCRIPTION).append("'").append(ctors[i].toString()).append("'"); + sb.append("},").append(NEWLINE); + } + sb.append("], ").append(NEWLINE); + + Field[] fields = clazz.getFields(); + //java.util.Arrays.sort(fields, comparator); + sb.append("'fields':["); + for (int i = 0, n = fields.length; i < n; i++) { + Field f = fields[i]; + int modifier = f.getModifiers(); + sb.append("{"); + sb.append(KEY_NAME).append("'").append(f.getName()).append("',"); + if (!f.getDeclaringClass().getName().equals(clazz.getName())) + sb.append(KEY_DECLARING_CLASS).append("'").append(f.getDeclaringClass().getName()).append("',"); + appendModifier(sb, modifier); + sb.append(KEY_TYPE).append("'").append(f.getType().getName()).append("'"); + sb.append("},").append(NEWLINE); + } + sb.append("], ").append(NEWLINE); + + Method[] methods = clazz.getMethods(); + //java.util.Arrays.sort(methods, comparator); + sb.append("'methods':["); + for (int i = 0, n = methods.length; i < n; i++) { + Method m = methods[i]; + int modifier = m.getModifiers(); + sb.append("{"); + sb.append(KEY_NAME).append("'").append(m.getName()).append("',"); + if (!m.getDeclaringClass().getName().equals(clazz.getName())) + sb.append(KEY_DECLARING_CLASS).append("'").append(m.getDeclaringClass().getName()).append("',"); + appendModifier(sb, modifier); + sb.append(KEY_RETURNTYPE).append("'").append(m.getReturnType().getName()).append("',"); + appendParameterTypes(sb, m.getParameterTypes()); + sb.append(KEY_DESCRIPTION).append("'").append(m.toString()).append("'"); + sb.append("},").append(NEWLINE); + } + sb.append("], ").append(NEWLINE); + + Class[] classes = clazz.getClasses(); + sb.append("'classes': ["); + for (int i = 0, n = classes.length; i < n; i++) { + Class c = classes[i]; + sb.append("'").append(c.getName().replace('$', '.')).append("',"); + putClassInfo(map, c); // !! + } + sb.append("], ").append(NEWLINE); + + appendDeclaredMembers(map, clazz, sb); + + sb.append("}"); + map.put(clazz.getName(), sb); + } + catch (Exception ex) { + //ex.printStackTrace(); + } + } + + private static void appendDeclaredMembers(Hashtable map, Class clazz, StringBuffer sb) { + Constructor[] ctors = clazz.getDeclaredConstructors(); + sb.append("'declared_ctors':["); + for (int i = 0, n = ctors.length; i < n; i++) { + Constructor ctor = ctors[i]; + if (!Modifier.isPublic(ctor.getModifiers())) { + sb.append("{"); + appendModifier(sb, ctor.getModifiers()); + appendParameterTypes(sb, ctor.getParameterTypes()); + sb.append(KEY_DESCRIPTION).append("'").append(ctors[i].toString()).append("'"); + sb.append("},").append(NEWLINE); + } + } + sb.append("], ").append(NEWLINE); + + Field[] fields = clazz.getDeclaredFields(); + sb.append("'declared_fields':["); + for (int i = 0, n = fields.length; i < n; i++) { + Field f = fields[i]; + int modifier = f.getModifiers(); + if (!Modifier.isPublic(modifier)) { + sb.append("{"); + sb.append(KEY_NAME).append("'").append(f.getName()).append("',"); + if (!f.getDeclaringClass().getName().equals(clazz.getName())) + sb.append(KEY_DECLARING_CLASS).append("'").append(f.getDeclaringClass().getName()).append("',"); + appendModifier(sb, modifier); + sb.append(KEY_TYPE).append("'").append(f.getType().getName()).append("'"); + sb.append("},").append(NEWLINE); + } + } + sb.append("], ").append(NEWLINE); + + Method[] methods = clazz.getDeclaredMethods(); + sb.append("'declared_methods':["); + for (int i = 0, n = methods.length; i < n; i++) { + Method m = methods[i]; + int modifier = m.getModifiers(); + if (!Modifier.isPublic(modifier)) { + sb.append("{"); + sb.append(KEY_NAME).append("'").append(m.getName()).append("',"); + if (!m.getDeclaringClass().getName().equals(clazz.getName())) + sb.append(KEY_DECLARING_CLASS).append("'").append(m.getDeclaringClass().getName()).append("',"); + appendModifier(sb, modifier); + sb.append(KEY_RETURNTYPE).append("'").append(m.getReturnType().getName()).append("',"); + appendParameterTypes(sb, m.getParameterTypes()); + sb.append(KEY_DESCRIPTION).append("'").append(m.toString()).append("'"); + sb.append("},").append(NEWLINE); + } + } + sb.append("], ").append(NEWLINE); + + Class[] classes = clazz.getDeclaredClasses(); + sb.append("'declared_classes': ["); + for (int i = 0, n = classes.length; i < n; i++) { + Class c = classes[i]; + if (!Modifier.isPublic(c.getModifiers())) { + sb.append("'").append(c.getName().replace('$', '.')).append("',"); + putClassInfo(map, c); // !! + } + } + sb.append("], ").append(NEWLINE); + } + + private static void appendModifier(StringBuffer sb, int modifier) { + sb.append(KEY_MODIFIER).append("'").append(Integer.toString(modifier, 2)).append("', "); + } + + private static void appendParameterTypes(StringBuffer sb, Class[] paramTypes) { + if (paramTypes.length == 0) return ; + + sb.append(KEY_PARAMETERTYPES).append("["); + for (int j = 0; j < paramTypes.length; j++) { + sb.append("'").append(paramTypes[j].getName()).append("',"); + } + sb.append("],"); + } + + private static boolean isBlank(String str) { + int len; + if (str == null || (len = str.length()) == 0) + return true; + for (int i = 0; i < len; i++) + if ((Character.isWhitespace(str.charAt(i)) == false)) + return false; + return true; + } + + // test methods + + static void debug(String s) { + if (debug_mode) + System.out.println(s); + } + static void output(String s) { + if (!debug_mode) + System.out.print(s); + } + + + private static void usage() { + System.out.println("Reflection for javacomplete (" + VERSION + ")"); + System.out.println(" java [-classpath] Reflection [-c] [-d] [-e] [-h] [-v] [-p] [-s] name[,comma_separated_name_list]"); + System.out.println("Options:"); + System.out.println(" -a list all members in alphabetic order"); + System.out.println(" -c list constructors"); + System.out.println(" -C return class info"); + System.out.println(" -d default strategy, i.e. instance fields, instance methods, static fields, static methods"); + System.out.println(" -e check class existed"); + System.out.println(" -E check class existed and read class information"); + System.out.println(" -D debug mode"); + System.out.println(" -p list package content"); + System.out.println(" -P print all package info in the Vim dictionary format"); + System.out.println(" -s list static fields and methods"); + System.out.println(" -h help"); + System.out.println(" -v version"); + } + + public static void main(String[] args) { + String className = null; + int option = 0x0; + boolean wholeClassInfo = false; + boolean onlyStatic = false; + boolean onlyConstructor = false; + boolean listPackageContent = false; + boolean checkExisted = false; + boolean checkExistedAndRead = false; + boolean allPackageInfo = false; + + for (int i = 0, n = args.length; i < n && !isBlank(args[i]); i++) { + //debug(args[i]); + if (args[i].charAt(0) == '-') { + if (args[i].length() > 1) { + switch (args[i].charAt(1)) { + case 'a': + break; + case 'c': // request constructors + option = option | OPTION_CONSTRUCTOR; + onlyConstructor = true; + break; + case 'C': // class info + wholeClassInfo = true; + break; + case 'd': // default strategy + option = option | STRATEGY_DEFAULT; + break; + case 'D': // debug mode + debug_mode = true; + break; + case 'e': // class existed + checkExisted = true; + break; + case 'E': // check existed and read class information + checkExistedAndRead = true; + break; + case 'h': // help + usage(); + return ; + case 'v': // version + System.out.println("Reflection for javacomplete (" + VERSION + ")"); + break; + case 'p': + listPackageContent = true; + break; + case 'P': + option = RETURN_ALL_PACKAGE_INFO; + break; + case 's': // request static members + option = option | OPTION_STATIC_METHOD | OPTION_STATIC_FIELD; + onlyStatic = true; + break; + default: + } + } + } + else { + className = args[i]; + } + } + if (className == null && (option & RETURN_ALL_PACKAGE_INFO) != RETURN_ALL_PACKAGE_INFO) { + return; + } + if (option == 0x0) + option = OPTION_INSTANCE; + + if (wholeClassInfo) + output( getClassInfo(className) ); + else if ((option & RETURN_ALL_PACKAGE_INFO) == RETURN_ALL_PACKAGE_INFO) + output( getPackageList() ); + else if (checkExistedAndRead) + output( existedAndRead(className) ); + else if (checkExisted) + output( String.valueOf(existed(className)) ); + else if (listPackageContent) + output( getPackageList(className) ); + } +} diff --git a/vim/autoload/java_parser.vim b/vim/autoload/java_parser.vim new file mode 100644 index 0000000..5e1ec38 --- /dev/null +++ b/vim/autoload/java_parser.vim @@ -0,0 +1,3500 @@ +" Vim autoload script for a JAVA PARSER and more. +" Language: Java +" Maintainer: cheng fang +" Last Changed: 2007-09-16 +" Version: 0.67 +" Copyright: Copyright (C) 2007 cheng fang. All rights reserved. +" License: Vim License (see vim's :help license) + + +if exists("g:loaded_javaparser") || version < 700 || &cp + finish +endif +let g:loaded_javaparser = 'v0.67' + + +" Constants used by scanner and parser {{{1 +let s:EOI = '' + +let s:keywords = {'+': 'PLUS', '-': 'SUB', '!': 'BANG', '%': 'PERCENT', '^': 'CARET', '&': 'AMP', '*': 'STAR', '|': 'BAR', '~': 'TILDE', '/': 'SLASH', '>': 'GT', '<': 'LT', '?': 'QUES', ':': 'COLON', '=': 'EQ', '++': 'PLUSPLUS', '--': 'SUBSUB', '==': 'EQEQ', '<=': 'LTEQ', '>=': 'GTEQ', '!=': 'BANGEQ', '<<': 'LTLT', '>>': 'GTGT', '>>>': 'GTGTGT', '+=': 'PLUSEQ', '-=': 'SUBEQ', '*=': 'STAREQ', '/=': 'SLASHEQ', '&=': 'AMPEQ', '|=': 'BAREQ', '^=': 'CARETEQ', '%=': 'PERCENTEQ', '<<=': 'LTLTEQ', '>>=': 'GTGTEQ', '>>>=': 'GTGTGTEQ', '||': 'BARBAR', '&&': 'AMPAMP', 'abstract': 'ABSTRACT', 'assert': 'ASSERT', 'boolean': 'BOOLEAN', 'break': 'BREAK', 'byte': 'BYTE', 'case': 'CASE', 'catch': 'CATCH', 'char': 'CHAR', 'class': 'CLASS', 'const': 'CONST', 'continue': 'CONTINUE', 'default': 'DEFAULT', 'do': 'DO', 'double': 'DOUBLE', 'else': 'ELSE', 'extends': 'EXTENDS', 'final': 'FINAL', 'finally': 'FINALLY', 'float': 'FLOAT', 'for': 'FOR', 'goto': 'GOTO', 'if': 'IF', 'implements': 'IMPLEMENTS', 'import': 'IMPORT', 'instanceof': 'INSTANCEOF', 'int': 'INT', 'interface': 'INTERFACE', 'long': 'LONG', 'native': 'NATIVE', 'new': 'NEW', 'package': 'PACKAGE', 'private': 'PRIVATE', 'protected': 'PROTECTED', 'public': 'PUBLIC', 'return': 'RETURN', 'short': 'SHORT', 'static': 'STATIC', 'strictfp': 'STRICTFP', 'super': 'SUPER', 'switch': 'SWITCH', 'synchronized': 'SYNCHRONIZED', 'this': 'THIS', 'throw': 'THROW', 'throws': 'THROWS', 'transient': 'TRANSIENT', 'try': 'TRY', 'void': 'VOID', 'volatile': 'VOLATILE', 'while': 'WHILE', 'true': 'TRUE', 'false': 'FALSE', 'null': 'NULL', '(': 'LPAREN', ')': 'RPAREN', '{': 'LBRACE', '}': 'RBRACE', '[': 'LBRACKET', ']': 'RBRACKET', ';': 'SEMI', ',': 'COMMA', '.': 'DOT', 'enum': 'ENUM', '...': 'ELLIPSIS', '@': 'MONKEYS_AT'} + +let s:Flags = {'PUBLIC': 0x1, 'PRIVATE': 0x2, 'PROTECTED': 0x4, 'STATIC': 0x8, 'FINAL': 0x10, 'SYNCHRONIZED': 0x20, 'VOLATILE': 0x40, 'TRANSIENT': 0x80, 'NATIVE': 0x100, 'INTERFACE': 0x200, 'ABSTRACT': 0x400, 'STRICTFP': 0x800, 'SYNTHETIC': 0x1000, 'ANNOTATION': 0x2000, 'ENUM': 0x4000, 'StandardFlags':0x0fff, 'ACC_SUPER': 0x20, 'ACC_BRIDGE': 0x40, 'ACC_VARARGS': 0x80, 'DEPRECATED': 0x20000, 'HASINIT': 0x40000, 'BLOCK': 0x100000, 'IPROXY': 0x200000, 'NOOUTERTHIS': 0x400000, 'EXISTS': 0x800000, 'COMPOUND': 0x1000000, 'CLASS_SEEN': 0x2000000, 'SOURCE_SEEN': 0x4000000, 'LOCKED': 0x8000000, 'UNATTRIBUTED': 0x10000000, 'ANONCONSTR': 0x20000000, 'ACYCLIC': 0x40000000, 'BRIDGE': 1.repeat('0', 31), 'PARAMETER': 1.repeat('0', 33), 'VARARGS': 1.repeat('0', 34), 'ACYCLIC_ANN': 1.repeat('0', 35), 'GENERATEDCONSTR': 1.repeat('0', 36), 'HYPOTHETICAL': 1.repeat('0', 37), 'PROPRIETARY': 1.repeat('0', 38)} + +let s:RE_ANYTHING_AND_NEWLINE = '\(\(.\|\n\)*\)' +let s:RE_LINE_COMMENT = '//.*$' +let s:RE_COMMENT_SP = '/\*\*/' +let s:RE_COMMENT = '' +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_TYPE_NAME = s:RE_QUALID +let s:RE_REFERENCE_TYPE = s:RE_QUALID . s:RE_BRACKETS . '*' " TypeName || Type[] +let s:RE_TYPE = s:RE_REFERENCE_TYPE " PrimitiveType || ReferenceType +let s:RE_TYPE_VARIABLE = s:RE_IDENTIFIER +let s:RE_VAR_DECL_ID = s:RE_IDENTIFIER . s:RE_BRACKETS . '*' + +let s:RE_TYPE_PARAMS = '' + +let s:RE_THROWS = 'throws\s\+' . s:RE_TYPE_NAME . '\(\s*,\s*' . s:RE_TYPE_NAME . '\)*' +let s:RE_FORMAL_PARAM = '\(final\s*\)\='. s:RE_TYPE . '\s\+' . s:RE_VAR_DECL_ID +let s:RE_FORMAL_PARAM_LIST = s:RE_FORMAL_PARAM . '\(\s*,\s*' . s:RE_FORMAL_PARAM . '\)*' +let s:RE_FORMAL_PARAM2 = '^\s*\(final\s*\)\=\('. s:RE_TYPE . '\)\s\+\(' . s:RE_IDENTIFIER . '\)' . s:RE_BRACKETS . '*' + +let s:RE_MEMBER_MODS = '\%(PUBLIC\|PROTECTED\|PRIVATE\|ABSTRACT\|STATIC\|FINAL\|TRANSIENT\|VOLATILE\|SYNCHRONIZED\|NATIVE\|STRICTFP\)' +let s:RE_MEMBER_HEADER = '\s*\(\%(' .s:RE_MEMBER_MODS. '\s\+\)\+\)\(' .s:RE_IDENTIFIER. '\%(\s*\.\s*' .s:RE_IDENTIFIER. '\)*\%(\s*\[\s*\]\)*\)\s\+\(' .s:RE_IDENTIFIER. '\)' + +" API {{{1 + +let s:PROTOTYPE = {'s:options': {}, 'b:buf': '', 'b:buflen': 0, 'b:lines': [], 'b:idxes': [0], 'b:bp': -1, 'b:ch': '', 'b:line': 0, 'b:col': 0, 'b:pos': 0, 'b:endPos': 0, 'b:prevEndPos': 0, 'b:errPos': -1, 'b:errorEndPos': -1, 'b:sbuf': '', 'b:name': '', 'b:token': '', 'b:docComment': '', 'b:radix': 0, 'b:unicodeConversionBp': -1, 'b:scanStrategy': 0, 'b:allowGenerics': 1, 'b:allowVarargs': 1, 'b:allowAsserts': 1, 'b:allowEnums': 1, 'b:allowForeach': 1, 'b:allowStaticImport': 1, 'b:allowAnnotations': 1, 'b:keepDocComments': 1, 'b:mode': 0, 'b:lastmode': 0, 'b:log': [], 'b:et_perf': '', 'b:et_nextToken_count': 0} + +" Function to initialize the parser +" parameters: +" - lines List of code text +" - options A set of options +fu! java_parser#InitParser(lines, ...) + let s:options = a:0 == 0 ? {} : a:1 + + " internal variables for scanning +" let b:buf = '' " The input buffer + let b:buflen = 0 " index of one past last character in buffer. also eofPos + let b:lines = a:lines " The input buffer + let b:idxes = [0] " Begin index of every lines + let b:bp = -1 " index of next character to be read. + let b:ch = '' " The current character. + let b:line = 0 " The line number position of the current character. + let b:col = 0 " The column number position of the current character. + let b:pos = 0 " The token's position, 0-based offset from beginning of text. + let b:endPos = 0 " Character position just after the last character of the token. + let b:prevEndPos = 0 " The last character position of the previous token. + let b:errPos = -1 " The position where a lexical error occurred + let b:errorEndPos = -1 " + let b:sbuf = '' " A character buffer for literals. + let b:name = '' " The name of an identifier or token: + let b:token = 0 " The token, set by s:nextToken(). + let b:docComment = '' + let b:radix = 0 " The radix of a numeric literal token. + let b:unicodeConversionBp =-1 " The buffer index of the last converted unicode character + + let b:scanStrategy = get(s:options, 'scanStrategy', -1) " 0 - only class members when parse full file; + " 1 - keep statement as a whole string; + " 2 - all + " -1 - enable quick recognition of declarations in common form. + + " language feature options. + let b:allowGenerics = get(s:options, 'allowGenerics', 1) + let b:allowVarargs = get(s:options, 'allowVarargs', 1) + let b:allowAsserts = get(s:options, 'allowAsserts', 1) + let b:allowEnums = get(s:options, 'allowEnums', 1) + let b:allowForeach = get(s:options, 'allowForeach', 1) + let b:allowStaticImport = get(s:options, 'allowStaticImport', 1) + let b:allowAnnotations = get(s:options, 'allowAnnotations', 1) + let b:keepDocComments = get(s:options, 'keepDocComments', 1) + + let b:mode = 0 " The current mode. + let b:lastmode = 0 " The mode of the term that was parsed last. + + + let b:log = [] + + let b:et_perf = '' + let b:et_nextToken_count = 0 + +" let b:buf = join(a:lines, "\r") +" let b:buflen = strlen(b:buf) + for line in a:lines + let b:buflen += strlen(line) + 1 + call add(b:idxes, b:buflen) + endfor + call add(b:lines, s:EOI) " avoid 'list index out of range' error from lines[] in java_scanChar + " if b:bp >= b:buflen + " return s:EOI + " endif + + " initialize scanner + call s:scanChar() " be ready for scanning + call s:nextToken() " prime the pump +endfu + +fu! java_parser#FreeParser() + for varname in keys(s:PROTOTYPE) + exe "if exists(" . string(varname) . ") | unlet " . varname . " | endif" + endfor +endfu + +fu! java_parser#GetSnapshot() + let snapshot = {} + for varname in keys(s:PROTOTYPE) + exe "let snapshot[" . string(varname) . "] = " . varname + endfor + return snapshot +endfu + +fu! java_parser#Restore(snapshot) + for key in keys(a:snapshot) + exe "let " . key . "=" . string(a:snapshot[key]) + endfor +endfu + +" move b:bp and b:pos to the specified position +fu! java_parser#GotoPosition(pos) + let b:bp = a:pos-1 + let b:pos = a:pos + let p = java_parser#DecodePos(b:bp) + let b:line = p.line + let b:col = p.col + call s:scanChar() + call s:nextToken() +endfu + +fu! java_parser#compilationUnit() + return s:compilationUnit() +endfu + +fu! java_parser#block() + return s:block() +endfu + +fu! java_parser#statement() + return s:blockStatements() +endfu + +fu! java_parser#expression() + return s:expression() +endfu + +fu! java_parser#nextToken() + return s:nextToken() +endfu + + +" Tree {{{1 +let s:TTree = {'tag': '', 'pos': 0} " Root class for AST nodes. + +" Tree maker functions {{{2 +fu! s:ClassDef(pos, mods, ...) + return {'tag': 'CLASSDEF', 'pos': a:pos, 'mods': a:mods, 'name': ''} +endfu + +fu! s:VarDef(pos, mods, name, vartype) + return {'tag': 'VARDEF', 'pos': a:pos, 'mods': a:mods, 'name': a:name, 'vartype': a:vartype} +endfu + +fu! s:Unary(pos, opcode, arg) + return {'tag': a:opcode, 'pos': a:pos, 'arg': a:arg} +endfu + +fu! s:Binary(pos, opcode, lhs, rhs, ...) + return {'tag': a:opcode, 'pos': a:pos, 'lhs': a:lhs, 'rhs': a:rhs} +endfu + +fu! s:TypeCast(pos, clazz, expr) + return {'tag': 'TYPECAST', 'pos': a:pos, 'clazz': a:clazz, 'expr': a:expr} +endfu + +fu! s:Select(pos, selected, name) + return {'tag': 'SELECT', 'pos': a:pos, 'selected': a:selected, 'name': a:name} +endfu + +fu! s:Ident(pos, name) + return {'tag': 'IDENT', 'pos': a:pos, 'name': a:name} +endfu + +fu! s:TypeArray(pos, elementtype) + return {'tag': 'TYPEARRAY', 'pos': a:pos, 'elementtype': a:elementtype} +endfu + +fu! s:Modifiers(pos, flags, annotations) + let noFlags = s:BitAnd(a:flags, s:Flags.StandardFlags) == 0 + let mods = {'tag': 'MODIFIERS', 'flags': a:flags} + let mods.pos = noFlags && empty(a:annotations) ? -1 : a:pos + if !empty(a:annotations) + let mods.annotations = a:annotations + endif + return mods +endfu + + +" {{{2 +fu! java_parser#IsStatement(tree) + return has_key(a:tree, 'tag') && a:tree.tag =~# '^\(CLASSDEF\|VARDEF\|BLOCK\|IF\|DOLOOP\|WHILELOOP\|FORLOOP\|FOREACHLOOP\|SWITCH\|TRY\|EXEC\|LABELLED\|SYNCHRONIZED\|CASE\|BREAK\|RETURN\|SKIP\|THROW\|ASSERT\|CONTINUE\)$' +endfu + +" Tree Helper {{{1 +fu! java_parser#type2Str(type) + if type(a:type) == type("") + return a:type + endif + + let t = a:type + if t.tag == 'IDENTIFIER' + return t.value + elseif t.tag == 'IDENT' + return t.name + elseif t.tag == 'TYPEIDENT' + return t.typetag + elseif t.tag == 'SELECT' + return java_parser#type2Str(t.selected) . '.' . t.name + elseif t.tag == 'TYPEARRAY' + return java_parser#type2Str(t.elementtype) . '[]' + elseif t.tag == 'TYPEAPPLY' + return t.clazz.name + elseif t.tag == 'TEMPLATE' + let s = t.clazz.value . '<' + for arg in t.arguments + let s .= arg.value + endfor + let s .= '>' + return s + endif +endfu + +fu! s:vardef2Str(vardef) + return java_parser#type2Str(a:vardef.vartype) . ' ' . a:vardef.name +endfu + +fu! s:method2Str(methoddef) + let m = a:methoddef + let desc = m.r . ' ' . m.n . '(' + for item in m.params + let desc .= s:vardef2Str(item) . ',' + endfor + let desc = substitute(desc, ',$', '', '') + let desc .= ')' + return desc +endfu + +" Scanner {{{1 + +" nextToken() {{{2 +fu! s:nextToken() + let b:prevEndPos = b:endPos + let b:et_nextToken_count += 1 + let b:sbuf = '' + while 1 + let b:pos = b:bp + if b:ch =~ '[ \t\r\n ]' " - FF + " OAO optimized code: skip spaces + let b:col = match(b:lines[b:line], '[^ \t\r\n ]\|$', b:col) + let b:bp = b:idxes[b:line] + b:col + call s:scanChar() + let b:endPos = b:bp + continue + + elseif b:ch =~ '[a-zA-Z$_]' + " read a identifier + call s:scanIdent() + return + + elseif b:ch == '0' + call s:scanChar() + " hex + if b:ch == 'x' || b:ch == 'X' + call s:scanChar() + if b:ch == '.' + call s:scanHexFractionAndSuffix(0) + elseif s:isDigit(16) + call s:scanNumber(16) + else + call s:LexError("invalid.hex.number") + endif + " oct + else + let b:sbuf .= '0' + call s:scanNumber(8) + endif + return + + elseif b:ch =~ '[1-9]' + " read number + call s:scanNumber(10) + return + + elseif b:ch == '.' + call s:scanChar() + if b:ch =~ '[0-9]' + let b:sbuf .= '.' + call s:scanFractionAndSuffix() + " JAVA5 ELLIPSIS + elseif b:ch == '.' + let b:sbuf .= '..' + call s:scanChar() + if b:ch == '.' + call s:scanChar() + let b:sbuf .= '.' + let b:token = 'ELLIPSIS' + else + call s:LexError('malformed.fp.lit') + endif + else + let b:token = 'DOT' + endif + return + + elseif b:ch =~ '[,;(){}[\]]' + let b:token = s:keywords[b:ch] + call s:scanChar() + return + + elseif b:ch == '/' + let status = s:scanComment() + if status == 1 + continue + elseif status == 2 + return + elseif b:ch == '=' + let b:name = '/=' + let b:token = 'SLASHEQ' + call s:scanChar() + else + let b:name = '/' + let b:token = 'SLASH' + endif + return + + + elseif b:ch == "'" + call s:scanSingleQuote() + return + + elseif b:ch == '"' + call s:scanDoubleQuote() + return + + else + if s:IsSpecial(b:ch) + call s:scanOperator() + elseif b:ch =~ '[a-zA-Z_]' + call s:scanIdent() + elseif b:bp >= b:buflen + let b:token = 'EOF' + else + call s:LexError("illegal.char '" . b:ch . "'") + call s:scanChar() + endif + return + endif + endwhile +endfu + +" scanChar() Read next character. {{{2 +" one buf version +"fu! s:scanChar() +" let b:bp += 1 +" if b:bp % 256 == 0 +" let b:buf2 = strpart(b:buf, b:bp, 256) +" endif +" let b:ch = b:buf2[b:bp % 256] +"" let b:ch = b:buf[b:bp] " it will be extremely slow when buf is large +" "call s:Trace( "'" . b:ch . "'" ) +" +" if b:ch == "\r" +" let b:line += 1 +" let b:col = 0 +" elseif b:ch == "\n" +" if b:bp == 0 || b:buf[b:bp-1] == "\r" +" let b:line += 1 +" let b:col = 0 +" endif +" else +" let b:col += 1 +" endif +"endfu + +" Read next character. multiple lines version +fu! s:scanChar() + let b:bp+=1 + let b:ch=b:lines[b:line][b:col] + let b:col+=1 + if b:ch=='' + let b:ch="\r" + let b:line+=1 + let b:col=0 + endif + + if b:ch == '\' + call s:convertUnicode() + endif +endfu + +fu! java_parser#CharAt(line, col) + let ch=b:lines[a:line][a:col] + if ch == '' + let ch = "\r" + endif + return ch +endfu + +fu! s:convertUnicode() + if b:ch == '\' && b:unicodeConversionBp != b:bp + "if java_parser#CharAt(b:bp+1) == 'u' + "call s:scanChar() + "endif + endif +endfu + +" putChar() is substituted with +" let b:sbuf .= '.' + +" scanIdent() {{{2 +" OAO optimized code +fu! s:scanIdent() + let col_old = b:col + let b:col = match(b:lines[b:line], '[^a-zA-Z0-9$_]\|$', b:col) + let b:name = strpart(b:lines[b:line], col_old-1, b:col-col_old+1) + let b:token = get(s:keywords, b:name, 'IDENTIFIER') + call s:Debug('name: "' . b:name . '" of token type ' . b:token ) + let b:bp = b:idxes[b:line] + b:col + call s:scanChar() +endfu + +" Standard implementation +fu! s:scanIdent_old() + " do ... while () + let b:sbuf .= b:ch + call s:scanChar() + if b:ch !~ '[a-zA-Z0-9$_]' || b:bp >= b:buflen + let b:name = b:sbuf + let b:token = has_key(s:keywords, b:name) ? s:keywords[b:name] : 'IDENTIFIER' + call s:Debug('name: "' . b:name . '" of token type ' . b:token ) + return + endif + + while (1) + let b:sbuf .= b:ch + call s:scanChar() + if b:ch !~ '[a-zA-Z0-9$_]' || b:bp >= b:buflen + let b:name = b:sbuf + let b:token = has_key(s:keywords, b:name) ? s:keywords[b:name] : 'IDENTIFIER' + call s:Debug('name: "' . b:name . '" of token type ' . b:token ) + break + endif + endwhile +endfu + +" digit() {{{2 +" Convert an ASCII digit from its base (8, 10, or 16) to its value. +" NOTE: This only implement isdigit() check +fu! s:digit(base) + let c = b:ch + "let result = +endfu + +fu! s:isDigit(base) + if a:base == 8 + return b:ch =~ '[0-7]' + elseif a:base == 16 + return b:ch =~ '[0-9a-fA-F]' + elseif a:base == 10 + return b:ch =~ '[0-9]' + endif +endfu + +" scanNumber() {{{2 +fu! s:scanNumber(radix) + let b:radix = a:radix + let digitRadix = a:radix <= 10 ? 10 : 16 + let seendigit = 0 + while s:isDigit(a:radix) + let seendigit = 1 + let b:sbuf .= b:ch + call s:scanChar() + endwhile + if a:radix == 16 && b:ch == '.' + call s:scanHexFractionAndSuffix(seendigit) + elseif seendigit && a:radix == 16 && (b:ch == 'p' || b:ch == 'P') + call s:scanHexExponentAndSuffix() + elseif a:radix <= 10 && b:ch == '.' + let b:sbuf .= b:ch + call s:scanChar() + call s:scanFractionAndSuffix() + elseif a:radix <= 10 && b:ch =~ '[eEfFdD]' + call s:scanFractionAndSuffix() + else + if b:ch == 'l' || b:ch == 'L' + call s:scanChar() + let b:token = 'LONGLITERAL' + else + let b:token = 'INTLITERAL' + endif + endif +endfu + +fu! s:scanHexExponentAndSuffix() + if b:ch == 'p' || b:ch == 'P' + let b:sbuf .= b:ch + call s:scanChar() + if b:ch == '+' || b:ch == '-' + let b:sbuf .= b:ch + call s:scanChar() + endif + + if '0' <= b:ch && b:ch <= '9' + let b:sbuf .= b:ch + call s:scanChar() + while '0' <= b:ch && b:ch <= '9' + let b:sbuf .= b:ch + call s:scanChar() + endwhile + + "if !b:allowHexFloats + "elseif !b:hexFloatsWork + " call s:LexError("unsupported.cross.fp.lit") + "endif + else + call s:LexError("malformed.fp.lit") + endif + else + call s:LexError("malformed.fp.lit") + endif + + if b:ch == 'f' || b:ch == 'F' + let b:sbuf .= b:ch + call s:scanChar() + let b:token = 'FLOATLITERAL' + else + if b:ch == 'f' || b:ch == 'F' + let b:sbuf .= b:ch + call s:scanChar() + endif + let b:token = 'DOUBLELITERAL' + endif +endfu + +fu! s:scanFraction() + " scan fraction + while b:ch =~ '[0-9]' + let b:sbuf .= b:ch + call s:scanChar() + endwhile + + " floating point number + if b:ch == 'e' || b:ch == 'E' + let b:sbuf .= b:ch + call s:scanChar() + + if b:ch == '+' || b:ch == '-' + let b:sbuf .= b:ch + call s:scanChar() + endif + + if b:ch =~ '[0-9]' + let b:sbuf .= b:ch + call s:scanChar() + while b:ch =~ '[0-9]' + let b:sbuf .= b:ch + call s:scanChar() + endwhile + return + endif + + call s:LexError("malformed.fp.lit") + endif +endfu + +" Read fractional part and 'd' or 'f' suffix of floating point number. +fu! s:scanFractionAndSuffix() + call s:scanFraction() + if b:ch == 'f' || b:ch == 'F' + let b:sbuf .= b:ch + call s:scanChar() + let b:token = 'FLOATLITERAL' + else + if b:ch == 'd' || b:ch == 'D' + let b:sbuf .= b:ch + call s:scanChar() + endif + let b:token = 'DOUBLELITERAL' + endif +endfu + +fu! s:scanHexFractionAndSuffix(seendigit) + let seendigit = a:seendigit + let b:radix = 16 + if b:ch != '.' | echoerr "b:ch != '.'" | endif + + let b:sbuf .= b:ch + call s:scanChar() + while s:isDigit(16) + let seendigit = 1 + let b:sbuf .= b:ch + call s:scanChar() + endwhile + + if !seendigit + call s:LexError("invalid.hex.number") + else + call s:scanHexExponentAndSuffix() + endif +endfu + +" scanLitChar() {{{2 +fu! s:scanLitChar() + if b:ch == '\' + call s:scanChar() + if b:ch =~ '[0-7]' + let leadch = b:ch + let oct = b:ch + call s:scanChar() + if b:ch =~ '[0-7]' + let oct = oct * 8 + b:ch + call s:scanChar() + if leadch <= '3' && '0' <= b:ch && b:ch <= '7' + let oct = oct * 8 + b:ch + call s:scanChar() + endif + endif + let b:sbuf .= oct + + elseif b:ch == "'" || b:ch =~ '[btnfr"\\]' + let b:sbuf .= b:ch + call s:scanChar() + + " unicode escape + elseif b:ch == 'u' + while b:ch =~ '[a-zA-Z0-9]' + call s:scanChar() + endwhile + + else + call s:LexError("illegal.esc.char") + endif + + elseif b:bp < b:buflen + let b:sbuf .= b:ch + call s:scanChar() + endif +endfu + +" scanOperator() {{{2 +fu! s:scanOperator() + while 1 + if !has_key(s:keywords, b:sbuf . b:ch) + break + endif + + let b:sbuf .= b:ch + let b:token = get(s:keywords, b:sbuf, 'IDENTIFIER') + call s:Debug('sbuf: "' . b:sbuf . '" of token type ' . b:token ) + call s:scanChar() + if !s:IsSpecial(b:ch) + break + endif + endwhile +endfu + +" NOTE: add @ for JAVA5 +fu! s:IsSpecial(ch) + return a:ch =~ '[!%&*?+-:<=>^|~@]' +endfu + +" scan comment {{{2 +" return 0 - not comment, 1 - succeeded to scan comment, 2 - unclosed comment +fu! s:scanComment() + call s:scanChar() + " line comment + if b:ch == '/' + let b:token = 'LINECOMMENT' + call s:Info('line comment') + call s:SkipLineComment() + let b:endPos = b:bp + return 1 + + " classic comment + " test cases: /**/, /***/, /*******/, /*** astatement; /***/, /*/ + elseif b:ch == '*' + let b:token = 'BLOCKCOMMENT' + call s:Info('block comment') + call s:scanChar() + let time = reltime() + " javadoc + if b:ch == '*' + let b:docComment = s:scanDocComment() + " normal comment + else + call s:skipComment() + endif + let b:et_perf .= "\r" . 'comment ' . reltimestr(reltime(time)) + + if b:ch == '/' + call s:Info('end block comment') + call s:scanChar() + let b:endPos = b:bp + return 1 + else + call s:LexError('unclosed.comment') + return 2 + endif + endif + return 0 +endfu + +fu! s:SkipLineComment() + " OAO optimized code + let b:ch = "\r" + let b:line += 1 + let b:col = 0 + let b:bp = b:idxes[b:line] + b:col + + " OLD + "call s:scanChar() + "while (b:ch != "\r") + " call s:scanChar() + "endwhile +endfu + +fu! s:skipComment() + if b:ch == '*' + call s:scanChar() + if b:ch == '/' + return + else " NOTE: go back + let b:ch = '*' + let b:bp -= 1 + let b:col -= 1 + endif + endif + + " OAO optimized code + if s:Stridx('*/') > -1 + call s:scanChar() + endif + +" " Standard implementation +" while b:bp < b:buflen +" if b:ch == '*' +" call s:scanChar() +" if b:ch == '/' +" break +" endif +" else +" call s:scanChar() +" endif +" endwhile +endfu + +fu! s:scanDocComment() + call s:Info('It is javadoc') + return s:skipComment() + + " skip star '*' + while (b:bp < b:buflen && b:ch == '*') + call s:scanChar() + endwhile + + if b:bp < b:buflen && b:ch == '/' + return '' + endif + + let result = '' + while b:bp < b:buflen + if b:ch == '*' + call s:scanChar() + if b:ch == '/' + break + else + let result .= b:ch + endif + else + call s:scanChar() + let result .= b:ch + endif + endwhile + + return result +endfu + +" scan single quote {{{2 +fu! s:scanSingleQuote() + call s:scanChar() + if (b:ch == "'") + call s:LexError("empty.char.lit") + else + if (b:ch =~ '[\r\n]') + call s:LexError("illegal.line.end.in.char.lit") + endif + + call s:scanLitChar() + if b:ch == "'" + call s:scanChar() + let b:token = 'CHARLITERAL' + else + call s:LexError("unclosed.char.lit") + endif + endif +endfu + +" scan double quote {{{2 +" test cases: +" 'a"";' +" 'a"b,c";' +" 'a"b,\"c";' +" 'a"b,\\"c";' +" 'a"b,\\\"c";' +" 'a"b,\\\\"c";' +" 'a"b,\\\"c;' " NOTE: cannot handle +fu! s:scanDoubleQuote() + if match(b:lines[b:line], '\\"', b:col) == -1 + let idx = matchend(b:lines[b:line], '\(\\\(["\\''ntbrf]\)\|[^"]\)*"', b:col) + if idx != -1 + let b:sbuf = strpart(b:lines[b:line], b:col, idx-b:col-1) + let b:col = idx-1 " back to the end + let b:bp = b:idxes[b:line] + b:col + call s:scanChar() + let b:token = 'STRINGLITERAL' + else + call s:LexError("unclosed.str.lit") + endif + call s:scanChar() + return + endif + + + call s:scanChar() + while b:ch !~ '["\r\n]' && b:bp < b:buflen + call s:scanLitChar() + endwhile + + if b:ch == '"' + let b:token = 'STRINGLITERAL' + call s:scanChar() + else + call s:LexError("unclosed.str.lit") + endif +endfu + +" lex errors {{{2 +fu! s:LexError(key, ...) + let pos = a:0 == 0 ? b:pos : a:1 + let b:token = 'ERROR' + let b:errPos = pos + call s:Log(4, b:pos, '[lex error]:' . s:Pos2Str(pos) . ': ' . a:key) +endfu + +" Scanner Helper {{{1 +" gotoMatchEnd {{{2 +fu! s:gotoMatchEnd(one, another, ...) + while b:bp < b:buflen + if b:ch == a:another + call s:scanChar() + if has_key(s:keywords, a:another) + let b:token = s:keywords[a:another] + else + echoerr '' + endif + break + + elseif b:ch == a:one + call s:scanChar() + call s:gotoMatchEnd(a:one, a:another) + + " skip commment + elseif b:ch == '/' + call s:scanComment() + + " skip literal character + elseif b:ch == "'" + call s:scanSingleQuote() + + " skip literal string + elseif b:ch == '"' + call s:scanDoubleQuote() + + else + " OAO + call s:Match('[' . a:one . a:another . '/"'']') + " OLD + "call s:scanChar() + endif + endwhile + + " For such situation: after accept one token, the next token is just the same. + let nextTokenIsLBRACE = a:0 == 0 ? 0 : a:1 + if nextTokenIsLBRACE + call s:gotoMatchEnd(a:one, a:another) + endif + + return b:bp +endfu + +" gotoSemi {{{2 +fu! s:gotoSemi() + while b:bp < b:buflen + if b:ch == ';' + let b:pos = b:bp + call s:scanChar() + let b:token = 'SEMI' + return + + " skip commment + elseif b:ch == '/' + call s:scanComment() + + " skip literal character + elseif b:ch == "'" + call s:scanSingleQuote() + + " skip literal string + elseif b:ch == '"' + call s:scanDoubleQuote() + + elseif b:ch == '{' + call s:scanChar() + call s:gotoMatchEnd('{', '}') + + elseif b:ch == '(' + call s:scanChar() + call s:gotoMatchEnd('(', ')') + + elseif b:ch == '[' + call s:scanChar() + call s:gotoMatchEnd('[', ']') + + else + " OAO + call s:Match('[;({[/"'']') + " OLD + "call s:scanChar() + endif + endwhile +endfu + +" s:Strpart(), s:Stridx(), s:Match() {{{2 +fu! Strpart(start, len) + let startline = java_parser#DecodePos(a:start).line + let endline = java_parser#DecodePos(a:start + a:len).line + let str = join(b:lines[startline:endline-1]) . b:lines[endline] + return strpart(str, a:start-b:idxes[startline], a:len) +endfu + +fu! s:Stridx(needle) + let found = 0 + while b:line < len(b:lines)-1 + let idx = stridx(b:lines[b:line], a:needle, b:col) + if idx > -1 + let found = 1 + let b:col = idx + break + endif + let b:line += 1 + let b:col = 0 + endwhile + + if found + let b:bp = b:idxes[b:line] + b:col + call s:scanChar() + return b:bp + else + let b:bp = b:buflen + let b:ch = s:EOI + return -1 + endif +endfu + +fu! s:Match(pat) + let bp_old = b:bp + let line_old = b:line + let col_old = b:col + + let found = 0 + while b:line < len(b:lines)-1 + let idx = match(b:lines[b:line], a:pat, b:col) + if idx > -1 + let found = 1 + let b:col = idx + break + endif + let b:line += 1 + let b:col = 0 + endwhile + + if found + let b:bp = b:idxes[b:line] + b:col-1 + call s:scanChar() + return b:bp + else + let b:bp = bp_old + let b:line = line_old + let b:col = col_old + call s:scanChar() + return -1 + endif +endfu + + +" conversion between position and (line, col) {{{2 +fu! java_parser#MakePos(line, col) + return b:idxes[a:line] + a:col +endfu + +fu! java_parser#DecodePos(pos) + let line = -1 + for idx in b:idxes + if idx > a:pos + break + endif + let line += 1 + endfor + let col = a:pos - b:idxes[line] + return {'line': line, 'col': col} +endfu + +fu! s:Pos2Str(pos) + let o = java_parser#DecodePos(a:pos) + return '(' . (o.line+1) . ',' . (o.col+1) . ')' +endfu + +" Bitwise operator emulators {{{1 +" NOTE: For more than 32 bit number, use the string bits form. + +" bit operator ~ +fu! s:BitNot(v) + return s:Bits2Number( s:BitNot_binary(s:Number2Bits(a:v)) ) +endfu + +fu! s:BitNot_binary(v) + let v = substitute(a:v, '^0*\([01]\+\)', '\1', 'g') + let v = substitute(v, '1', '2', 'g') + let v = substitute(v, '0', '1', 'g') + let v = substitute(v, '2', '0', 'g') + return v +endfu + +" bit operator & +fu! s:BitAnd(n1, n2) + if a:n1 == 0 || a:n2 == 0 | return 0 | endif + if a:n1 == a:n2 | return 1 | endif + return s:Bits2Number( s:BitOperator_binary(s:Number2Bits(a:n1), s:Number2Bits(a:n2), 'n1[i] == "1" && n2[i] == "1"') ) +endfu + +" bit operator | +fu! s:BitOr(n1, n2, ...) + if a:0 == 0 + if a:n1 == 0 + return a:n2 + elseif a:n2 == 0 + return a:n1 + endif + endif + let result = s:BitOperator_binary(s:Number2Bits(a:n1), s:Number2Bits(a:n2), 'n1[i] == "1" || n2[i] == "1"') + for a in a:000 + let result = s:BitOperator_binary(result, s:Number2Bits(a), 'n1[i] == "1" || n2[i] == "1"') + endfor + return s:Bits2Number( result ) +endfu + +" bit operator ^ +fu! s:BitXor(n1, n2) + if a:n1 == a:n2 | return 0 | endif + return s:Bits2Number( s:BitOperator_binary(s:Number2Bits(a:n1), s:Number2Bits(a:n2), 'n1[i] != n2[i]') ) +endfu + +fu! s:BitOperator_binary(n1, n2, comparator) + let n1 = a:n1 + let n2 = a:n2 + + let len1 = len(n1) + let len2 = len(n2) + let len = len1 + if len1 > len2 + let n2 = repeat('0', len1-len2) . n2 + else + let len = len2 + let n1 = repeat('0', len2-len1) . n1 + endif + + let i = 0 + let bits = '' + while i < len + let bits .= eval(a:comparator) ? '1' : '0' + let i += 1 + endwhile + return bits +endfu + +" bit operator << +fu! s:BitMoveLeft() +endfu + +" bit operator >> +fu! s:BitMoveRight() +endfu + +" helper function: convert a number to a string consisted of '0' or '1' indicating number +fu! s:Number2Bits(n, ...) + if type(a:n) == type("") | return a:n | endif + if a:n == 0 | return '0' | endif + + let n = a:n + let bits = '' + while n != 0 + let bit = n % 2 + "echo 'n: ' . n . ' bit: ' . bit + let bits = bit . bits + let n = (n-bit)/ 2 + endwhile + if a:0 > 0 + let bits = repeat(a:1 - len(bits)) . bits + endif + return bits +endfu + +" helper function: convert a string consisted of '0' or '1' indicating number to a number +" precondition: bits must not be empty string +fu! s:Bits2Number(bits) + let len = len(a:bits) + let n = a:bits[0] + let i = 1 + while i < len + let n = n * 2 + a:bits[i] + let i += 1 + endwhile + return n +endfu + + +let s:modifier_keywords = ['strictfp', 'abstract', 'interface', 'native', 'transient', 'volatile', 'synchronized', 'final', 'static', 'protected', 'private', 'public'] +fu! s:String2Flags(str) + let mod = [0,0,0,0,0,0,0,0,0,0,0,0,] + for item in split(a:str, '\s\+') + if index(s:modifier_keywords, item) != -1 + let mod[index(s:modifier_keywords, item)] = '1' + endif + endfor + return join(mod[index(mod, '1'):], '') +endfu + +" Log utilities {{{1 +" level +" 5 off/fatal +" 4 error +" 3 warn +" 2 info +" 1 debug +" 0 trace +fu! java_parser#SetLogLevel(level) + let b:loglevel = a:level +endfu + +fu! java_parser#GetLogLevel() + return exists('b:loglevel') ? b:loglevel : 3 +endfu + +fu! java_parser#GetLogContent() + return b:log +endfu + +fu! s:Trace(msg) + call s:Log(0, b:pos, a:msg) +endfu + +fu! s:Debug(msg) + call s:Log(1, b:pos, a:msg) +endfu + +fu! s:Info(msg) + call s:Log(2, b:pos, a:msg) +endfu + +fu! s:Log(level, pos, key, ...) + if a:level >= java_parser#GetLogLevel() + echo a:key + call add(b:log, a:key) + endif +endfu + +fu! s:ShowWatch(...) + let at = a:0 > 0 ? a:1 : '' + echo '-- b:bp ' . b:bp . string(java_parser#DecodePos(b:bp)) . ' b:ch "' . b:ch . '" b:name ' . b:name . ' b:token ' . b:token . ' b:pos ' .b:pos . ' endPos ' . b:endPos . ' prevEndPos ' . b:prevEndPos . ' errPos ' . b:errPos . ' errorEndPos ' . b:errorEndPos . at +endfu + +fu! java_parser#Exe(cmd) + exe a:cmd +endfu + +" Parser {{{1 +" skip() Skip forward until a suitable stop token is found. {{{2 +fu! s:skip(stopAtImport, stopAtMemberDecl, stopAtIdentifier, stopAtStatement) + while 1 + if b:token == 'SEMI' + call s:nextToken() + return + elseif b:token =~# '^\(PUBLIC\|FINAL\|ABSTRACT\|MONKEYS_AT\|EOF\|CLASS\|INTERFACE\|ENUM\)$' + return + elseif b:token == 'IMPORT' + if a:stopAtImport + return + endif + elseif b:token =~# '^\(LBRACE\|RBRACE\|PRIVATE\|PROTECTED\|STATIC\|TRANSIENT\|NATIVE\|VOLATILE\|SYNCHRONIZED\|STRICTFP\|LT\|BYTE\|SHORT\|CHAR\|INT\|LONG\|FLOAT\|DOUBLE\|BOOLEAN\|VOID\)$' + if a:stopAtMemberDecl + return + endif + elseif b:token == 'IDENTIFIER' + if a:stopAtIdentifier + return + endif + elseif b:token =~# '^\(CASE\|DEFAULT\|IF\|FOR\|WHILE\|DO\|TRY\|SWITCH\|RETURN\|THROW\|BREAK\|CONTINUE\|ELSE\|FINALLY\|CATCH\)$' + if a:stopAtStatement + return + endif + endif + call s:nextToken() + endwhile +endfu + +" syntax errors {{{2 +fu! s:SyntaxError(key, ...) + let pos = a:0 == 0 ? b:pos : a:1 + let errs = a:0 > 1 ? a:2 : [] + call s:setErrorEndPos(pos) + call s:ReportSyntaxError(pos, a:key) + return {'tag': 'ERRONEOUS', 'pos': pos, 'errs': errs} +endfu + +fu! s:ReportSyntaxError(pos, key, ...) + if a:pos > b:errPos || a:pos == -1 + let key = a:key . (b:token == 'EOF' ? ' and premature.eof' : '') + call s:Log(4, a:pos, '[syntax error]' . s:Pos2Str(a:pos) . ': ' . key) + endif + let b:errPos = a:pos +endfu + +" accept() {{{2 +fu! s:accept(token_type) + "call s:Debug(b:token . ' == ' . a:token_type . (b:token == a:token_type)) + if b:token == a:token_type + call s:nextToken() + else + call s:setErrorEndPos(b:pos) + call s:ReportSyntaxError(b:prevEndPos, s:token2string(a:token_type) . " expected") + "call s:nextToken() + endif +endfu + +fu! s:token2string(token) + if a:token =~# '^\(DOT\|COMMA\|SEMI\|LPAREN\|RPAREN\|LBRACKET\|RBRACKET\|LBRACE\|RBRACE\)$' + return "'" . a:token . "'" + endif + return a:token +endfu + + +" illegal() {{{2 +fu! s:illegal(...) + call s:setErrorEndPos(b:pos) + return s:SyntaxError(s:modeAndEXPR() ? 'illegal.start.of.expr' : 'illegal.start.of.type', a:0 == 0 ? b:pos : a:1) +endfu + +" setErrorEndPos() {{{2 +fu! s:setErrorEndPos(errPos) + if a:errPos > b:errorEndPos + let b:errorEndPos = a:errPos + endif +endfu + +" ident() {{{2 +" Ident = IDENTIFIER +fu! s:ident() + call s:Trace('s:ident ' . b:token) + + if b:token == 'IDENTIFIER' + let name = b:name + call s:nextToken() + return name + + elseif b:token == 'ASSERT' + if s:allowAsserts + call s:Log(4, b:pos, 'assert.as.identifier') + call s:nextToken() + return '' + else + call s:Log(3, b:pos, 'assert.as.identifier') + let name = b:name + call s:nextToken() + return name + endif + + elseif b:token == 'ENUM' + if b:allowEnums + call s:Log(4, b:pos, 'enum.as.identifier') + call s:nextToken() + return '' + else + call s:Log(3, b:pos, 'enum.as.identifier') + let name = b:name + call s:nextToken() + return name + endif + + else + call s:accept('IDENTIFIER') + return '' + endif +endfu + +" qualident() {{{2 +" Qualident = Ident { DOT Ident } +fu! s:qualident() + let t = s:Ident(b:pos, s:ident()) + while b:token == 'DOT' + let pos = b:pos + call s:nextToken() + let t = s:Select(b:pos, t, s:ident()) + "let t.name .= '.' . s:ident() " FIXME + endwhile + return t +endfu + +" literal() {{{2 +" Literal = INTLITERAL | LONGLITERAL | FLOATLITERAL | DOUBLELITERAL | CHARLITERAL | STRINGLITERAL | TRUE | FALSE | NULL +fu! s:literal(prefix) + let t = {'tag': 'LITERAL', 'pos': b:pos} + if b:token == 'INTLITERAL' + let t.typetag = 'INT' + let t.value = b:sbuf + elseif b:token == 'LONGLITERAL' + let t.typetag = 'LONG' + let t.value = b:sbuf + elseif b:token == 'FLOATLITERAL' + let t.typetag = 'FLOAT' + let t.value = b:sbuf + elseif b:token == 'DOUBLELITERAL' + let t.typetag = 'DOUBLE' + let t.value = b:sbuf + elseif b:token == 'CHARLITERAL' + let t.typetag = 'CHAR' + let t.value = b:sbuf + elseif b:token == 'STRINGLITERAL' + let t.typetag = 'CLASS' + let t.value = b:sbuf + elseif b:token == 'TRUE' + let t.typetag = 'BOOLEAN' + let t.value = 1 + elseif b:token == 'FALSE' + let t.typetag = 'BOOLEAN' + let t.value = 0 + elseif b:token == 'NULL' + let t.typetag = 'BOT' + let t.value = 'null' + else + echoerr 'can not reach here' + endif + call s:nextToken() + return t +endfu + +" {{{2 +" terms, expression, type {{{2 +" When terms are parsed, the mode determines which is expected: +" mode = EXPR : an expression +" mode = TYPE : a type +" mode = NOPARAMS : no parameters allowed for type +" mode = TYPEARG : type argument +let s:EXPR = 1 +let s:TYPE = 2 +let s:NOPARAMS = 4 +let s:TYPEARG = 8 +let s:EXPR_OR_TYPE = 3 +let s:EXPR_OR_TYPE_OR_NOPARAMS = 7 +let s:TYPEARG_OR_NOPARAMS = 12 + +fu! s:modeAndEXPR() + return b:mode % 2 +endfu + +fu! s:modeAndTYPE() + return s:BitAnd(b:mode, s:TYPE) +endfu + +" terms can be either expressions or types. +fu! s:expression() + return s:term(s:EXPR) +endfu + +fu! s:type() + return s:term(s:TYPE) +endfu + +fu! s:typeList() + let ts = [] + call add(ts, s:type()) + while b:token == 'COMMA' + call s:nextToken() + call add(ts, s:type()) + endwhile + return ts +endfu + +" Expression = Expression1 [ExpressionRest] +" ExpressionRest = [AssignmentOperator Expression1] +" AssignmentOperator = "=" | "+=" | "-=" | "*=" | "/=" | "&=" | "|=" | "^=" | +" "%=" | "<<=" | ">>=" | ">>>=" +" Type = Type1 +" TypeNoParams = TypeNoParams1 +" StatementExpression = Expression +" ConstantExpression = Expression +fu! s:term(...) + let prevmode = b:mode + let b:mode = a:0 == 0 ? b:mode : a:1 + + let t = s:term1() + if s:modeAndEXPR() && b:token == 'EQ' || b:token =~# '^\(PLUSEQ\|SUBEQ\|STAREQ\|SLASHEQ\|AMPEQ\|BAREQ\|CARETEQ\|PERCENTEQ\|LTLTEQ\|GTGTEQ\|GTGTGTEQ\)$' + let t = s:termRest(t) + endif + + let b:lastmode = b:mode + let b:mode = prevmode + return t +endfu + +fu! s:termRest(t) + if b:token == 'EQ' + let pos = b:pos + call s:nextToken() + let b:mode = s:EXPR + return {'tag': 'ASSIGN', 'pos': pos, 'lhs': a:t, 'rhs': s:term()} + + elseif b:token =~# '^\(PLUSEQ\|SUBEQ\|STAREQ\|SLASHEQ\|PERCENTEQ\|AMPEQ\|BAREQ\|CARETEQ\|LTLTEQ\|GTGTEQ\|GTGTGTEQ\)$' + let pos = b:pos + let token = b:token + call s:nextToken() + let b:mode = s:EXPR + return {'tag': s:optag(token), 'pos': pos, 'lhs': a:t, 'rhs': s:term()} + + else + return a:t + endif +endfu + +" Expression1 = Expression2 [Expression1Rest] +" Type1 = Type2 +" TypeNoParams1 = TypeNoParams2 +fu! s:term1() + let t = s:term2() + if s:modeAndEXPR() && b:token == 'QUES' + let b:mode = s:EXPR + return s:term1Rest(t) + else + return t + endif +endfu + +" Expression1Rest = ["?" Expression ":" Expression1] +fu! s:term1Rest(t) + if b:token == 'QUES' + let t = {'tag': 'CONDEXPR', 'pos': b:pos, 'cond': a:t} + call s:nextToken() + let t.truepart = s:term() + call s:accept('COLON') + let t.falsepart = s:term1() + return t + else + return a:t + endif +endfu + +" Expression2 = Expression3 [Expression2Rest] +" Type2 = Type3 +" TypeNoParams2 = TypeNoParams3 +fu! s:term2() + let t = s:term3() + if s:modeAndEXPR() && s:prec(b:token) >= s:opprecedences.orPrec + let b:mode = s:EXPR + return s:term2Rest(t, s:opprecedences.orPrec) + else + return t + endif +endfu + +" Expression2Rest = {infixop Expression3} +"" | Expression3 instanceof Type +" infixop = "||" +" | "&&" +" | "|" +" | "^" +" | "&" +" | "==" | "!=" +" | "<" | ">" | "<=" | ">=" +" | "<<" | ">>" | ">>>" +" | "+" | "-" +" | "*" | "/" | "%" +fu! s:term2Rest(t, minprec) + let odStack = [a:t] " for expressions + let opStack = [] " for tokens + let top = 0 + let startPos = b:pos + let topOp = 'ERROR' + while s:prec(b:token) >= a:minprec + call add(opStack, topOp) + let top += 1 + let topOp = b:token + let pos = b:pos + call s:nextToken() + call add(odStack, topOp == 'INSTANCEOF' ? s:type() : s:term3()) + while top > 0 && s:prec(topOp) >= s:prec(b:token) + let odStack[top-1] = s:makeOp(pos, topOp, odStack[top-1], odStack[top]) + let top -= 1 + let topOp = opStack[top] + endwhile + endwhile + "assert top == 0 + let t = odStack[0] + + if t.tag == 'PLUS' + let buf = s:foldStrings(t) + if buf != '' + let t = {'tag': 'LITERAL', 'pos': startPos, 'typetag': 'CLASS', 'value': t} + endif + endif + return t +endfu + +fu! s:makeOp(pos, topOp, od1, od2) + if a:topOp == 'INSTANCEOF' + return {'tag': 'TYPETEST', 'pos': a:pos, 'expr': a:od1, 'clazz': a:od2} + else + return s:Binary(a:pos, s:optag(a:topOp), a:od1, a:od2) + endif +endfu + +fu! s:foldStrings(tree) + let tree = a:tree + let buf = '' + while 1 + if tree.tag == 'LITERAL' + let lit = tree + if lit.typetag == 'CLASS' + let sbuf = lit.value + if buf != '' + let sbuf .= buf + endif + return sbuf + endif + elseif tree.tag == 'PLUS' + let op = tree + if op.rhs.tag == 'LITERAL' + let lit = op.rhs + if lit.typetag == 'CLASS' + let buf = lit.value . buf + let tree = op.lhs + continue + endif + endif + endif + return '' + endwhile +endfu + +" Expression3 = PrefixOp Expression3 {{{2 +" | "(" Expr | TypeNoParams ")" Expression3 +" | Primary {Selector} {PostfixOp} +" Primary = "(" Expression ")" +" | Literal +" | [TypeArguments] THIS [Arguments] +" | [TypeArguments] SUPER SuperSuffix +" | NEW [TypeArguments] Creator +" | Ident { "." Ident } +" [ "[" ( "]" BracketsOpt "." CLASS | Expression "]" ) +" | Arguments +" | "." ( CLASS | THIS | [TypeArguments] SUPER Arguments | NEW [TypeArguments] InnerCreator ) +" ] +" | BasicType BracketsOpt "." CLASS +" PrefixOp = "++" | "--" | "!" | "~" | "+" | "-" +" PostfixOp = "++" | "--" +" Type3 = Ident { "." Ident } [TypeArguments] {TypeSelector} BracketsOpt +" | BasicType +" TypeNoParams3 = Ident { "." Ident } BracketsOpt +" Selector = "." [TypeArguments] Ident [Arguments] +" | "." THIS +" | "." [TypeArguments] SUPER SuperSuffix +" | "." NEW [TypeArguments] InnerCreator +" | "[" Expression "]" +" TypeSelector = "." Ident [TypeArguments] +" SuperSuffix = Arguments | "." Ident [Arguments] +" NOTE: We need only type expression. +fu! s:term3() + let pos = b:pos + let t = copy(s:TTree) + + call s:Debug('term3() b:token is ' . b:token) + let typeArgs = s:typeArgumentsOpt(s:EXPR) + + if b:token == 'QUES' + if s:modeAndTYPE() && s:BitAnd(b:mode, s:TYPEARG_OR_NOPARAMS) == s:TYPEARG + let b:mode = s:TYPE + return s:typeArgument() + else + return s:illegal() + endif + + elseif b:token =~# '^\(PLUSPLUS\|SUBSUB\|BANG\|TILDE\|PLUS\|SUB\)$' + if typeArgs == [] && s:modeAndEXPR() + let token = b:token + call s:nextToken() + let b:mode = s:EXPR + if token == 'SUB' && (b:token == 'INTLITERAL' || b:token == 'LONGLITERAL') && b:radix == 10 + let b:mode = s:EXPR + let t = s:literal('-') + else + let t = s:term3() + return s:Unary(pos, s:unoptag(token), t) + endif + else + return s:illegal() + endif + + elseif b:token == 'LPAREN' + if typeArgs == [] && s:modeAndEXPR() + call s:nextToken() + let b:mode = s:EXPR_OR_TYPE_OR_NOPARAMS + let t = s:term3() + if s:modeAndEXPR() && b:token == 'LT' + let op = 'LT' + let pos1 = b:pos + call s:nextToken() + let b:mode = s:BitAnd(b:mode, s:EXPR_OR_TYPE) + let b:mode = s:BitOr(b:mode, s:TYPEARG) + let t1 = s:term3() + if s:modeAndTYPE() && (b:token == 'COMMA' || b:token == 'GT') + let b:mode = s:TYPE + let args = [] + call add(args, t1) + while b:token == 'COMMA' + call s:nextToken() + call add(args, s:typeArgument()) + endwhile + call s:accept('GT') + let t = {'tag': 'TYPEAPPLY', 'pos': pos1, 'clazz': t, 'arguments': args} + " checkGenerics + let t = s:bracketsOpt(t) + elseif s:modeAndEXPR() + let b:mode = s:EXPR + let t = s:Binary(pos1, op, t, s:term2Rest(t1, s:opprecedences.shiftPrec)) + let t = s:termRest(s:term1Rest(s:term2Rest(t, s:opprecedences.orPrec))) + else + call s:accept('GT') + endif + else + let t = s:termRest(s:term1Rest(s:term2Rest(t, s:opprecedences.orPrec))) + endif + call s:accept('RPAREN') + let b:lastmode = b:mode + let b:mode = s:EXPR + if s:BitAnd(b:lastmode, s:EXPR) == 0 + return s:TypeCast(pos, t, s:term3()) + elseif s:BitAnd(b:lastmode, s:TYPE) != 0 + if b:token =~# '^\(BANG\|TILDE\|LPAREN\|THIS\|SUPER\|INTLITERAL\|LONGLITERAL\|FLOATLITERAL\|DOUBLELITERAL\|CHARLITERAL\|STRINGLITERAL\|TRUE\|FALSE\|NULL\|NEW\|IDENTIFIER\|ASSERT\|ENUM\|BYTE\|SHORT\|CHAR\|INT\|LONG\|FLOAT\|DOUBLE\|BOOLEAN\|VOID\)$' + return s:TypeCast(pos, t, s:term3()) + endif + endif + else + return s:illegal() + endif + let t = {'tag': 'PARENS', 'pos': pos, 'expr': t} + + elseif b:token == 'THIS' + if s:modeAndEXPR() + let b:mode = s:EXPR + let t = s:Ident(pos, 'this') + call s:nextToken() + if typeArgs == [] + let t = s:argumentsOpt([], t) + else + let t = s:arguments(typeArgs, t) + endif + let typeArgs = [] + else + return s:illegal() + endif + + elseif b:token == 'SUPER' + if s:modeAndEXPR() + let b:mode = s:EXPR + let t = s:superSuffix(typeArgs, s:Ident(pos, 'super')) + let typeArgs = [] + else + return s:illegal() + endif + + elseif b:token =~# '^\(INTLITERAL\|LONGLITERAL\|FLOATLITERAL\|DOUBLELITERAL\|CHARLITERAL\|STRINGLITERAL\|TRUE\|FALSE\|NULL\)$' + if typeArgs == [] && s:modeAndEXPR() + let b:mode = s:EXPR + let t = s:literal('') + else + return s:illegal() + endif + + elseif b:token == 'NEW' + if typeArgs != [] + return s:illegal() + endif + + if s:modeAndEXPR() + let b:mode = s:EXPR + call s:nextToken() + if b:token == 'LT' + let typeArgs = s:typeArguments() + endif + let t = s:creator(pos, typeArgs) + let typeArgs = [] + else + return s:illegal() + endif + + elseif b:token =~# '^\(IDENTIFIER\|ASSERT\|ENUM\)$' + if typeArgs != [] + return s:illegal() + endif + + let t = s:Ident(pos, s:ident()) + while 1 + if b:token == 'LBRACKET' + "let t.value = '[' " FIXME + call s:nextToken() + if b:token == 'RBRACKET' + "let t.value .= ']' + call s:nextToken() + let t = s:bracketsSuffix(s:TypeArray(pos, s:bracketsOpt(t))) + else + if s:modeAndEXPR() + let b:mode = s:EXPR + let t = {'tag': 'INDEXED', 'pos': pos, 'indexed': t, 'index': s:term()} + endif + call s:accept('RBRACKET') + "let t.value .= ']' + endif + break + + elseif b:token == 'LPAREN' + if s:modeAndEXPR() + let b:mode = s:EXPR + let t = s:arguments(typeArgs, t) + let typeArgs = [] + "call s:accept('LPAREN') + "call s:gotoMatchEnd('(', ')', b:token == 'LPAREN') + "call s:accept('RPAREN') + endif + break + + elseif b:token == 'DOT' + call s:nextToken() + let typeArgs = s:typeArgumentsOpt(s:EXPR) + if s:modeAndEXPR() + if b:token == 'CLASS' || b:token == 'THIS' + if typeArgs != [] + return s:illegal() + endif + let b:mode = s:EXPR + let t = s:Select(pos, t, b:token == 'CLASS' ? 'class' : 'this') + call s:nextToken() + break + elseif b:token == 'SUPER' + let b:mode = s:EXPR + let t = s:Select(pos, t, 'super') + let t = s:superSuffix(typeArgs, t) + let typeArgs = [] + break + elseif b:token == 'NEW' + if typeArgs != [] + return s:illegal() + endif + let b:mode = s:EXPR + let pos1 = b:pos + call s:nextToken() + if b:token == 'LT' + let typeArgs = s:typeArguments() + endif + let t = s:innerCreator(pos1, typeArgs, t) + let typeArgs = [] + break + endif + endif + let t = s:Select(pos, t, s:ident()) + else + break + endif + endwhile + if typeArgs != [] | call s:illegal() | endif + let t = s:typeArgumentsOpt2(t) + + elseif b:token =~# '^\(BYTE\|SHORT\|CHAR\|INT\|LONG\|FLOAT\|DOUBLE\|BOOLEAN\)$' + if typeArgs != [] | call s:illegal() | endif + let t = s:bracketsSuffix(s:bracketsOpt(s:basicType())) + + elseif b:token == 'VOID' + if typeArgs != [] | call s:illegal() | endif + if s:modeAndEXPR() + call s:nextToken() + if b:token == 'DOT' + let ti = {'tag': 'TYPEIDENT', 'pos': pos, 'typetag': 'void'} " FIXME + let t = s:bracketsSuffix(ti) + else + return s:illegal(pos) + endif + else + return s:illegal() + endif + + else + return s:illegal() + endif + + if typeArgs != [] + return s:illegal() + endif + + while 1 + let pos1 = b:pos + if b:token == 'LBRACKET' + call s:nextToken() + if s:modeAndEXPR() + let oldmode = b:mode + let b:mode = s:TYPE + if b:token == 'RBRACKET' + call s:nextToken() + let t = s:bracketsOpt(t) + let t = s:TypeArray(pos1, t) + return t + endif + let b:mode = oldmode + endif + if s:modeAndEXPR() + let b:mode = s:EXPR + let t = {'tag': 'INDEXED', 'pos': pos1, 'indexed': t, 'index': s:term()} + endif + call s:accept('RBRACKET') + + elseif b:token == 'DOT' + call s:nextToken() + let typeArgs = s:typeArgumentsOpt(s:EXPR) + if b:token == 'SUPER' && s:modeAndEXPR() + let b:mode = s:EXPR + let t = s:Select(pos1, t, 'super') + call s:nextToken() + let t = s:arguments(typeArgs, t) + let typeArgs = [] + elseif b:token == 'NEW' && s:modeAndEXPR() + if typeArgs != [] + return s:illegal() + endif + let b:mode = s:EXPR + let pos2 = b:pos + call s:nextToken() + if b:token == 'LT' + let typeArgs = s:typeArguments() + endif + let t = s:innerCreator(pos2, typeArgs, t) + let typeArgs = [] + else + let t = s:Select(pos1, t, s:ident()) + let t = s:argumentsOpt(typeArgs, s:typeArgumentsOpt2(t)) + let typeArgs = [] + endif + else + break + endif + endwhile + + + while (b:token == 'PLUSPLUS' || b:token == 'SUBSUB') && s:modeAndEXPR() + let b:mode = s:EXPR + let t = s:Unary(b:pos, b:token == 'PLUSPLUS' ? 'POSTINC' : 'POSTDEC', t) + call s:nextToken() + endwhile + return t +endfu + +fu! s:superSuffix(typeArgs, t) + let typeArgs = a:typeArgs + let t = a:t + call s:nextToken() + if b:token == 'LPAREN' || typeArgs != [] + let t = s:arguments(typeArgs, t) + else + let pos = b:pos + call s:accept('DOT') + let typeArgs = b:token == 'LT' ? s:typeArguments() : [] + let t = s:Select(pos, t, s:ident()) + let t = s:argumentsOpt(typeArgs, t) + endif + return t +endfu + +" BasicType = BYTE | SHORT | CHAR | INT | LONG | FLOAT | DOUBLE | BOOLEAN {{{2 +fu! s:basicType() + let t = {'tag': 'TYPEIDENT', 'pos': b:pos, 'typetag': s:typetag(b:token)} + call s:nextToken() + return t +endfu + +" ArgumentsOpt = [ Arguments ] {{{2 +fu! s:argumentsOpt(typeArgs, t) + if s:modeAndEXPR() && b:token == 'LPAREN' || a:typeArgs != [] + let b:mode = s:EXPR + return s:arguments(a:typeArgs, a:t) + else + return a:t + endif +endfu + +" Arguments = "(" [Expression { COMMA Expression }] ")" +fu! s:arguments(...) + let pos = b:pos + let args = [] + if b:token == 'LPAREN' + call s:nextToken() + if b:token != 'RPAREN' + call add(args, s:expression()) + while b:token == 'COMMA' + call s:nextToken() + call add(args, s:expression()) + endwhile + endif + call s:accept('RPAREN') + else + call s:SyntaxError(') expected') + endif + + if a:0 == 0 + return args + else + let typeArgs = a:1 + let t = a:2 + return {'tag': 'APPLY', 'pos': pos, 'typeargs': typeArgs, 'meth': t, 'args': args} + endif +endfu + +" typeArgument generic type {{{2 +fu! s:typeArgumentsOpt2(t) + if b:token == 'LT' && s:modeAndTYPE() && s:BitAnd(b:mode, s:NOPARAMS) == 0 + let b:mode = s:TYPE + " checkGenerics() + return s:typeArguments(a:t) + else + return a:t + endif +endfu + +fu! s:typeArgumentsOpt(...) + let useMode = a:0 == 0 ? s:TYPE : a:1 + + if b:token == 'LT' + " checkGenerics() + if s:BitAnd(b:mode, useMode) == 0 || s:BitAnd(b:mode, s:NOPARAMS) != 0 + return s:illegal() + endif + let b:mode = useMode + return s:typeArguments() + endif + return [] +endfu + +" TypeArguments = "<" TypeArgument {"," TypeArgument} ">" +fu! s:typeArguments(...) + let pos = b:pos + + let args = [] + if b:token == 'LT' + call s:nextToken() + call add(args, s:modeAndEXPR() ? s:type() : s:typeArgument()) + while b:token == 'COMMA' + call s:nextToken() + call add(args, s:modeAndEXPR() ? s:type() : s:typeArgument()) + endwhile + + if b:token == 'GTGTGTEQ' + let b:token = 'GTGTEQ' + elseif b:token == 'GTGTEQ' + let b:token = 'GTEQ' + elseif b:token == 'GTEQ' + let b:token = 'EQ' + elseif b:token == 'GTGTGT' + let b:token = 'GTGT' + elseif b:token == 'GTGT' + let b:token = 'GT' + else + call s:accept('GT') + endif + else + call s:SyntaxError("LT expected") + endif + + if a:0 == 0 + return args + else + return {'tag': 'TYPEAPPLY', 'pos': pos, 'clazz': a:1, 'arguments': args} + endif +endfu + +" TypeArgument = Type +" | "?" +" | "?" EXTENDS Type {"&" Type} +" | "?" SUPER Type +fu! s:typeArgument() + if b:token != 'QUES' + return s:type() + endif + + call s:nextToken() + if b:token == 'EXTENDS' + call s:nextToken() + return s:type() + elseif b:token == 'SUPER' + call s:nextToken() + return s:type() + elseif b:token == 'IDENTIFIER' + let id = ident() + else + endif +endfu + + +" BracketsOpt = {"[" "]"} {{{2 +fu! s:bracketsOpt(t) + let t = a:t + while b:token == 'LBRACKET' + let pos = b:pos + call s:nextToken() + let t = s:bracketsOptCont(t, pos) + endwhile + return t +endfu + +fu! s:bracketsOptCont(t, pos) + let t = a:t + call s:accept('RBRACKET') + let t = s:bracketsOpt(t) + return s:TypeArray(a:pos, t) +endfu + +" BracketsSuffixExpr = "." CLASS +" BracketsSuffixType = +fu! s:bracketsSuffix(t) + let t = a:t + if s:modeAndEXPR() && b:token == 'DOT' + let b:mode = s:EXPR + let pos = b:pos + call s:nextToken() + call s:accept('CLASS') + if b:pos == b:errorEndPos + let name = '' + if b:token == 'IDENTIFIER' + let name = b:name + call s:nextToken() + else + let name = '' + endif + let t = {'tag': 'ERRONEOUS', 'pos': pos, 'errs': [s:Select(pos, t, name)]} + else + let t = s:Select(pos, t, 'class') + endif + elseif s:modeAndTYPE() + let b:mode = s:TYPE + else + call s:SyntaxError('dot.class.expected') + endif + return t +endfu + +" creator {{{2 +fu! s:creator(newpos, typeArgs) + if b:token =~# '^\(BYTE\|SHORT\|CHAR\|INT\|LONG\|FLOAT\|DOUBLE\|BOOLEAN\)$' + if a:typeArgs == [] + return s:arrayCreatorRest(a:newpos, s:basicType()) + endif + endif + + let t = s:qualident() + let oldmode = b:mode + let b:mode = s:TYPE + if b:token == 'LT' + "checkGenerics + let t = s:typeArguments(t) + endif + let b:mode = oldmode + + if b:token == 'LBRACKET' + return s:arrayCreatorRest(a:newpos, t) + elseif b:token == 'LPAREN' + return s:classCreatorRest(a:newpos, {}, a:typeArgs, t) + else + call s:ReportSyntaxError(b:pos, '( or [ expected') + let t = {'tag': 'NEWCLASS', 'encl': {}, 'typeargs': a:typeArgs, 'clazz': t, 'args': [], 'def': {}} + return {'tag': 'ERRONEOUS', 'pos': a:newpos, 'errs': [t]} + endif +endfu + +fu! s:innerCreator(newpos, typeArgs, encl) + let t = s:Ident(b:pos, s:ident()) + if b:token == 'LT' + " checkGenerics + let t = TypeArguments(t) + endif + return s:classCreatorRest(a:newpos, a:encl, a:typeArgs, t) +endfu + +fu! s:arrayCreatorRest(newpos, elemtype) + let elemtype = a:elemtype + call s:accept('LBRACKET') + if b:token == 'RBRACKET' + call s:accept('RBRACKET') + let elemtype = s:bracketsOpt(elemtype) + if b:token == 'LBRACE' + return s:arrayInitializer(a:newpos, elemtype) + else + return s:SyntaxError('array.dimension.missing') + endif + else + let dims = [s:expression()] + call s:accept('RBRACKET') + while b:token == 'LBRACKET' + let pos = b:pos + call s:nextToken() + if b:token == 'RBRACKET' + let elemtype = s:bracketsOptCont(elemtype, pos) + else + call add(dims, s:expression()) + call s:accept('RBRACKET') + endif + endwhile + return {'tag': 'NEWARRAY', 'pos': a:newpos, 'elemtype': elemtype, 'dims': dims, 'elems': {}} + endif +endfu + +fu! s:classCreatorRest(newpos, encl, typeArgs, t) + let args = s:arguments() + let body = {} + if b:token == 'LBRACE' + let body = s:ClassDef(b:pos, {}) + let body.defs = s:classOrInterfaceBody('', 0) + let body.endpos = b:pos + endif + return {'tag': 'NEWCLASS', 'encl': a:encl, 'typeargs': a:typeArgs, 'clazz': a:t, 'args': args, 'def': body} +endfu + +" ArrayInitializer = "{" [VariableInitializer {"," VariableInitializer}] [","] "}" {{{2 +fu! s:arrayInitializer(newpos, t) + call s:accept('LBRACE') + let elems = [] + if b:token == 'COMMA' + call s:nextToken() + elseif b:token != 'RBRACE' + call add(elems, s:variableInitializer()) + while b:token == 'COMMA' + call s:nextToken() + if b:token == 'RBRACE' + break + endif + call add(elems, s:variableInitializer()) + endwhile + endif + call s:accept('RBRACE') + return {'tag': 'NEWARRAY', 'pos': a:newpos, 'elemtype': a:t, 'dims': [], 'elems': elems} +endfu + +" VariableInitializer = ArrayInitializer | Expression {{{2 +fu! s:variableInitializer() + return b:token == 'LBRACE' ? s:arrayInitializer(b:pos, {}) : s:expression() +endfu + +" ParExpression = "(" Expression ")" {{{2 +fu! s:parExpression() + call s:accept('LPAREN') + let t = s:expression() + call s:accept('RPAREN') + return t +endfu + +" {{{2 +" Block = "{" BlockStatements "}" {{{2 +fu! s:block(...) + let t = {'tag': 'BLOCK', 'stats': []} + let t.pos = a:0 > 0 ? a:1 : b:pos + let t.flags = a:0 > 1 ? a:2 : 0 + + call s:accept('LBRACE') + + " scan strategy: ignore statements? + if a:0 > 2 && a:3 + if b:token !=# 'RBRACE' + let b:pos = s:gotoMatchEnd('{', '}', b:token == 'LBRACE') + endif + "let t.stats = Strpart(t.pos, t.endpos-t.pos-1) + else + let t.stats = s:blockStatements() + while b:token == 'CASE' || b:token == 'DEFAULT' + call s:SyntaxError("orphaned") + call s:switchBlockStatementGroups() + endwhile + endif + + let t.endpos = b:pos + call s:accept('RBRACE') + return t +endfu + +" BlockStatements = { BlockStatement } {{{2 +" BlockStatement = LocalVariableDeclarationStatement +" | ClassOrInterfaceOrEnumDeclaration +" | [Ident ":"] Statement +" LocalVariableDeclarationStatement +" = { FINAL | '@' Annotation } Type VariableDeclarators ";" +fu! s:blockStatements() + let lastErrPos = -1 + let stats = [] + while 1 + let pos = b:pos + if b:token =~# '^\(RBRACE\|CASE\|DEFAULT\|EOF\)$' + return stats + elseif b:token =~# '^\(LBRACE\|IF\|FOR\|WHILE\|DO\|TRY\|SWITCH\|SYNCHRONIZED\|RETURN\|THROW\|BREAK\|CONTINUE\|SEMI\|ELSE\|FINALLY\|CATCH\)$' + call add(stats, s:statement()) + elseif b:token =~# '^\(MONKEYS_AT\|FINAL\)' + let dc = b:docComment + let mods = s:modifiersOpt() + if b:token =~# '^\(INTERFACE\|CLASS\|ENUM\)$' + call add(stats, s:classOrInterfaceOrEnumDeclaration(mods, dc)) + else + let t = s:type() + let stats = stats + s:variableDeclarators(mods, t, []) + call s:accept('SEMI') + endif + elseif b:token =~# '^\(ABSTRACT\|STRICTFP\|CLASS\|INTERFACE\|ENUM\)$' + if b:token == 'ENUM' + call s:Log(4, b:pos, 'local.enum') + endif + call add(stats, s:classOrInterfaceOrEnumDeclaration(s:modifiersOpt(), b:docComment)) + elseif b:token == 'ASSERT' + call add(stats, s:statement()) + else + let name = b:name + let t = s:term(s:EXPR_OR_TYPE) + if b:token == 'COLON' && t.tag == 'IDENT' + call s:nextToken() + let stat = s:statement() + call add(stats, {'tag': 'LABELLED', 'pos': b:pos, 'label': name, 'body': stat}) + elseif s:BitAnd(b:lastmode, s:TYPE) && b:token =~# '^\(IDENTIFIER\|ASSERT\|ENUM\)$' + let pos = b:pos + let mods = {} " {'tag': 'MODIFIERS', 'pos': -1, 'flags': 0} + let stats = stats + s:variableDeclarators(mods, t, []) + call s:accept('SEMI') + else + call add(stats, {'tag': 'EXEC', 'pos': pos, 'expr': s:checkExprStat(t)}) " TODO + call s:accept('SEMI') + endif + endif + + if b:pos == lastErrPos + return stats + endif + if b:pos <= b:errorEndPos + call s:skip(0, 1, 1, 1) + let lastErrPos = b:pos + endif + + " resetDeprecatedFlag() + endwhile +endfu + +" Statement = Block {{{2 +" | IF ParExpression Statement [ELSE Statement] +" | FOR "(" ForInitOpt ";" [Expression] ";" ForUpdateOpt ")" Statement +" | FOR "(" FormalParameter : Expression ")" Statement +" | WHILE ParExpression Statement +" | DO Statement WHILE ParExpression ";" +" | TRY Block ( Catches | [Catches] FinallyPart ) +" | SWITCH ParExpression "{" SwitchBlockStatementGroups "}" +" | SYNCHRONIZED ParExpression Block +" | RETURN [Expression] ";" +" | THROW Expression ";" +" | BREAK [Ident] ";" +" | CONTINUE [Ident] ";" +" | ASSERT Expression [ ":" Expression ] ";" +" | ";" +" | ExpressionStatement +" | Ident ":" Statement +" called only by BlockStatements or self +fu! s:statement() + let pos = b:pos + if b:token == 'LBRACE' + return s:block() + elseif b:token == 'IF' + call s:nextToken() + let t = {'tag': 'IF', 'pos': pos, 'cond': s:parExpression(), 'thenpart': s:statement()} + if b:token == 'ELSE' + call s:nextToken() + let t.elsepart = s:statement() + endif + let t.endpos = b:pos + return t + + elseif b:token == 'FOR' + call s:nextToken() + call s:accept('LPAREN') + let inits = b:token == 'SEMI' ? [] : s:forInit() + " foreach + if len(inits) == 1 && inits[0].tag == 'VARDEF' && (!has_key(inits[0], 'init') || inits[0].init == {}) && b:token == 'COLON' + " checkForeach + let var = inits[0] + call s:accept('COLON') + let expr = s:expression() + call s:accept('RPAREN') + let body = s:statement() + return {'tag': 'FOREACHLOOP', 'pos': pos, 'endpos': b:pos, 'var': var, 'expr': expr, 'body': body} + else + call s:accept('SEMI') + let cond = b:token == 'SEMI' ? {} : s:expression() + call s:accept('SEMI') + let steps = b:token == 'RPAREN' ? [] : s:forUpdate() + call s:accept('RPAREN') + let body = s:statement() + return {'tag': 'FORLOOP', 'pos': pos, 'endpos': b:pos, 'init': inits, 'cond': cond, 'step': steps, 'body': body} + endif + + elseif b:token == 'WHILE' + call s:nextToken() + let cond = s:parExpression() + let body = s:statement() + return {'tag': 'WHILELOOP', 'pos': pos, 'endpos': b:pos, 'cond': cond, 'body': body} + + elseif b:token == 'DO' + call s:nextToken() + let body = s:statement() + call s:accept('WHILE') + let cond = s:parExpression() + let t = {'tag': 'DOLOOP', 'pos': pos, 'endpos': b:pos, 'cond': cond, 'body': body} + call s:accept('SEMI') + return t + + elseif b:token == 'TRY' + call s:nextToken() + let body = s:block() + let catchers = [] + let finalizer = {} + if b:token == 'CATCH' || b:token == 'FINALLY' + while b:token == 'CATCH' + call add(catchers, s:catchClause()) + endwhile + if b:token == 'FINALLY' + call s:nextToken() + let finalizer = s:block() + endif + else + call s:Log(4, b:pos, 'try.without.catch.or.finally') + endif + return {'tag': 'TRY', 'pos': pos, 'endpos': b:pos, 'body': body, 'catchers': catchers, 'finalizer': finalizer} + + elseif b:token == 'SWITCH' + call s:nextToken() + let selector = s:parExpression() + call s:accept('LBRACE') + let cases = s:switchBlockStatementGroups() + call s:accept('RBRACE') + return {'tag': 'SWITCH', 'pos': pos, 'endpos': b:pos, 'selector': selector, 'cases': cases} + + elseif b:token == 'SYNCHRONIZED' + call s:nextToken() + let lock = s:parExpression() + let body = s:block() + return {'tag': 'SYNCHRONIZED', 'pos': pos, 'endpos': b:pos, 'lock': lock, 'cases': body} + + elseif b:token == 'RETURN' + call s:nextToken() + let result = b:token == 'SEMI' ? {} : s:expression() + call s:accept('SEMI') + return {'tag': 'RETURN', 'pos': pos, 'endpos': b:pos, 'expr': result} + + elseif b:token == 'THROW' + call s:nextToken() + let exc = s:expression() + call s:accept('SEMI') + return {'tag': 'THROW', 'pos': pos, 'endpos': b:pos, 'expr': exc} + + elseif b:token == 'BREAK' || b:token == 'CONTINUE' + let token = b:token + call s:nextToken() + let label = b:token =~# '^\(IDENTIFIER\|ASSERT\|ENUM\)$' ? s:ident() : '' + call s:accept('SEMI') + return {'tag': token, 'pos': pos, 'endpos': b:pos, 'label': label} + + elseif b:token == 'SEMI' + call s:nextToken() + return {'tag': 'SKIP', 'pos': pos} + + elseif b:token == 'ELSE' + return s:SyntaxError("else.without.if") + elseif b:token == 'FINALLY' + return s:SyntaxError("finally.without.try") + elseif b:token == 'CATCH' + return s:SyntaxError("catch.without.try") + + elseif b:token == 'ASSERT' + "if b:allowAsserts && b:token == 'ASSERT' + call s:nextToken() + let t = {'tag': 'ASSERT', 'pos': pos, 'cond': s:expression()} + if b:token == 'COLON' + call s:nextToken() + let t.detail = s:expression() + endif + call s:accept('SEMI') + let t.endpos = b:pos + return t + "endif + + else " also ENUM + let name = b:name + let expr = s:expression() + if b:token == 'COLON' && expr.tag == 'IDENT' + call s:nextToken() + let stat = s:statement() + return {'tag': 'LABELLED', 'pos': pos, 'endpos': b:pos, 'label': name, 'body': stat} + else + let stat = {'tag': 'EXEC', 'pos': pos, 'endpos': b:pos, 'expr': s:checkExprStat(expr)} + call s:accept('SEMI') + return stat + endif + endif +endfu + +" CatchClause = CATCH "(" FormalParameter ")" Block +fu! s:catchClause() + let pos = b:pos + call s:accept('CATCH') + call s:accept('LPAREN') + let formal = s:variableDeclaratorId(s:optFinalParameter(), s:qualident()) + call s:accept('RPAREN') + let body = s:block() + return {'tag': 'CATCH', 'pos': pos, 'endpos': b:pos, 'param': formal, 'body': body} +endfu + +" SwitchBlockStatementGroups = { SwitchBlockStatementGroup } +" SwitchBlockStatementGroup = SwitchLabel BlockStatements +" SwitchLabel = CASE ConstantExpression ":" | DEFAULT ":" +fu! s:switchBlockStatementGroups() + let cases = [] + while 1 + let pos = b:pos + if b:token == 'CASE' || b:token == 'DEFAULT' + let token = b:token + call s:nextToken() + let pat = token == 'CASE' ? s:expression() : {} + call s:accept('COLON') + let stats = s:blockStatements() + call add(cases, {'tag': 'CASE', 'pos': pos, 'pat': pat, 'stats': stats}) + elseif b:token == 'RBRACE' || b:token == 'EOF' + return cases + else + call s:nextToken() + call s:SyntaxError('case.default.rbrace.expected') + endif + endwhile +endfu + +" MoreStatementExpressions = { COMMA StatementExpression } +fu! s:moreStatementExpressions(pos, first, stats) + call add(a:stats, {'tag': 'EXEC', 'pos': a:pos, 'expr': s:checkExprStat(a:first)}) + while b:token == 'COMMA' + call s:nextToken() + let pos = b:pos + let t = s:expression() + call add(a:stats, {'tag': 'EXEC', 'pos': pos, 'expr': s:checkExprStat(t)}) + endwhile + return a:stats +endfu + +" ForInit = StatementExpression MoreStatementExpressions +" | { FINAL | '@' Annotation } Type VariableDeclarators +fu! s:forInit() + let stats = [] + let pos = b:pos + if b:token == 'FINAL' || b:token == 'MONKEYS_AT' + return s:variableDeclarators(s:optFinal(0), s:type(), stats) + else + let t = s:term(s:EXPR_OR_TYPE) + if s:BitAnd(b:lastmode, s:TYPE) && b:token =~# '^\(IDENTIFIER\|ASSERT\|ENUM\)$' + return s:variableDeclarators(s:modifiersOpt(), t, stats) + else + return s:moreStatementExpressions(pos, t, stats) + endif + endif +endfu + +" ForUpdate = StatementExpression MoreStatementExpressions +fu! s:forUpdate() + return s:moreStatementExpressions(b:pos, s:expression(), []) +endfu + +" ModifiersOpt = { Modifier } {{{2 +" Modifier = PUBLIC | PROTECTED | PRIVATE | STATIC | ABSTRACT | FINAL +" | NATIVE | SYNCHRONIZED | TRANSIENT | VOLATILE | "@" +" | "@" Annotation +" NOTE: flags is a string, not a long number +fu! s:modifiersOpt(...) + let partial = a:0 == 0 ? {} : a:1 + + let flags = partial == {} ? 0 : partial.flags + let annotations = partial == {} ? [] : partial.annotations + " TODO: deprecatedFlag + + let pos = b:pos + let lastPos = -1 + while 1 + let flag = 0 + if b:token =~# '^\(PUBLIC\|PROTECTED\|PRIVATE\|STATIC\|ABSTRACT\|FINAL\|NATIVE\|SYNCHRONIZED\|TRANSIENT\|VOLATILE\|STRICTFP\|MONKEYS_AT\)$' + let flag = b:token == 'MONKEYS_AT' ? s:Flags.ANNOTATION : get(s:Flags, b:token, 0) + else + break + endif + "if s:BitAnd(flags, flag) != 0 + " "log.error(S.pos(), "repeated.modifier") + "endif + + let lastPos = b:pos + call s:nextToken() + + if flag == s:Flags.ANNOTATION + "call s:checkAnnotations() + if b:token != 'INTERFACE' + let ann = s:annotation(lastPos) + if flags == 0 && annotations == [] + let pos = ann.pos + endif + call add(annotations, ann) + let lastPos = ann.pos + let flag = 0 + endif + endif + let flags = s:BitOr(flags, flag) + endwhile + + if b:token == 'ENUM' + let flags = s:BitOr(flags, s:Flags.ENUM) + elseif b:token == 'INTERFACE' + let flags = s:BitOr(flags, s:Flags.INTERFACE) + endif + + if flags == 0 && empty(annotations) + let pos = -1 + endif + + return {'tag': 'MODIFIERS', 'pos': pos, 'flags': flags, 'annotations': annotations} +endfu + +" Annotation = "@" Qualident [ "(" AnnotationFieldValues ")" ] {{{2 +fu! s:annotation(pos) + "call s:checkAnnotations() + let ident = s:qualident() + "let endPos = b:prevEndPos + let fieldValues = s:annotationFieldValuesOpt() + + return {'tag': 'ANNOTATION', 'pos': a:pos, 'annotationType': ident, 'args': fieldValues} +endfu + +fu! s:annotationFieldValuesOpt() + return b:token == 'LPAREN' ? s:annotationFieldValues() : [] +endfu + +" AnnotationFieldValues = "(" [ AnnotationFieldValue { "," AnnotationFieldValue } ] ")" +fu! s:annotationFieldValues() + let buf = [] + call s:accept('LPAREN') + if b:token != 'RPAREN' + call add(buf, s:annotationFieldValue()) + while b:token == 'COMMA' + call s:nextToken() + call add(buf, s:annotationFieldValue()) + endwhile + endif + call s:accept('RPAREN') + return buf +endfu + +" AnnotationFieldValue = AnnotationValue | Identifier "=" AnnotationValue +fu! s:annotationFieldValue() + call s:Trace('s:annotationFieldValue ' . b:token) + if b:token == 'IDENTIFIER' + let b:mode = s:EXPR + let t1 = s:term1() + if t1.tag == 'IDENT' && b:token == 'EQ' + let pos = b:pos + call s:accept('EQ') + return {'tag': 'ASSIGN', 'pos': pos, 'lhs': t1, 'rhs': s:annotationValue()} + else + return t1 + endif + endif + return s:annotationValue() +endfu + +" AnnotationValue = ConditionalExpression | Annotation | "{" [ AnnotationValue { "," AnnotationValue } ] "}" +fu! s:annotationValue() + let pos = 0 + if b:token == 'MONKEYS_AT' + let pos = b:bp + call s:nextToken() + return s:annotation(pos) + elseif b:token == 'LBRACE' + let pos = b:pos + call s:accept('LBRACE') + let buf = [] + if b:token != 'RBRACE' + call add(buf, s:annotationValue()) + while b:token == 'COMMA' + call s:nextToken() + if b:token == 'RPAREN' + break + endif + call add(buf, s:annotationValue()) + endwhile + endif + call s:accept('RBRACE') + return buf + else + let b:mode = s:EXPR + return s:term1() + endif +endfu + +" AnnotationsOpt = { '@' Annotation } +fu! s:annotationsOpt() + if b:token != 'MONKEYS_AT' + return [] + endif + + let buf = [] + while b:token != 'MONKEYS_AT' + let pos = b:pos + call s:nextToken() + call add(buf, s:annotation(pos)) + endwhile + return buf +endfu + +" {{{2 +" CompilationUnit {{{2 +" CompilationUnit = [ { "@" Annotation } PACKAGE Qualident ";"] {ImportDeclaration} {TypeDeclaration} +fu! s:compilationUnit() + let unit = {'tag': 'TOPLEVEL', 'pos': b:pos} + + let mods = {} + if b:token == 'MONKEYS_AT' + let mods = s:modifiersOpt() + endif + + if b:token == 'PACKAGE' + if mods != {} + " checkNoMods(mods.flags) + let unit.packageAnnotations = mods.annotations + let mods = {} + endif + call s:nextToken() + let unit.pid = s:qualident() + let unit.package = java_parser#type2Str(unit.pid) + call s:accept('SEMI') + endif + + let imports = [] + let s:types = [] + let checkForImports = 1 + while b:token != 'EOF' + if b:pos <= b:errorEndPos + call s:skip(checkForImports, 0, 0, 0) + if b:token == 'EOF' + break + endif + endif + if checkForImports && mods == {} && b:token == 'IMPORT' + call add(imports, s:importDeclaration()) + else + let def = s:typeDeclaration(mods) + "if (def instanceof JCExpressionStatement) + " def = ((JCExpressionStatement)def).expr + "endif + call add(s:types, def) + if def.tag == 'CLASSDEF' + let checkForImports = 0 + endif + let mods = {} + endif + endwhile + let unit.imports = imports + let unit.types = s:types + unlet s:types + + return unit +endfu + +" ImportDeclaration = IMPORT [ STATIC ] Ident { "." Ident } [ "." "*" ] ";" {{{2 +" return fqn +fu! s:importDeclaration() + " OAO: Usualy it is in one line. + if b:scanStrategy < 0 + let idx = matchend(b:lines[b:line], '\(\s\+static\>\)\?\s\+\([_$a-zA-Z][_$a-zA-Z0-9_]*\)\(\s*\.\s*[_$a-zA-Z][_$a-zA-Z0-9_]*\)*\(\s*\.\s*\*\)\?;') + if idx != -1 + let fqn = strpart(b:lines[b:line], b:col, idx-b:col-1) + let b:col = idx + let b:bp = b:idxes[b:line] + b:col + call s:nextToken() + return fqn + endif + endif + + + call s:Info('==import==') + let pos = b:pos + call s:nextToken() + + let importStatic = 0 + if b:token == 'STATIC' + " checkStaticImports() + let importStatic = 1 + call s:nextToken() + endif + + let pid = s:Ident(b:pos, s:ident()) + + " + let pos1 = b:pos + call s:accept('DOT') + if b:token == 'STAR' + let pid = s:Select(pos1, pid, '*') + call s:nextToken() + else + let pid = s:Select(pos1, pid, s:ident()) + endif + while b:token == 'DOT' + let pos1 = b:pos + call s:accept('DOT') + if b:token == 'STAR' + let pid = s:Select(pos1, pid, '*') + call s:nextToken() + break + else + let pid = s:Select(pos1, pid, s:ident()) + endif + endwhile + let fqn = java_parser#type2Str(pid) + if b:token != 'SEMI' + let fqn .= '' + endif + call s:accept('SEMI') + "return {'tag': 'IMPORT', 'pos': b:pos, 'qualid': pid, 'staticImport': importStatic} + return fqn +endfu + +" TypeDeclaration = ClassOrInterfaceOrEnumDeclaration | ";" {{{2 +fu! s:typeDeclaration(mods) + let pos = b:pos + if a:mods == {} && b:token == 'SEMI' + call s:nextToken() + return {'tag': 'SKIP', 'pos': pos} + else + let dc = b:docComment + let mods = s:modifiersOpt(a:mods) + return s:classOrInterfaceOrEnumDeclaration(mods, dc) + endif +endfu + +fu! s:classOrInterfaceOrEnumDeclaration(mods, dc) + call s:Info('== type ==') + if b:token == 'CLASS' + return s:classDeclaration(a:mods, a:dc) + elseif b:token == 'INTERFACE' + return s:interfaceDeclaration(a:mods, a:dc) + elseif b:token == 'ENUM' + "if !exists('b:allowEnums') || !b:allowEnums + " call s:SyntaxError("enums.not.supported.in.source") + "endif + return s:enumDeclaration(a:mods, a:dc) + else + let pos = b:pos + let errs = [] + if b:token == 'IDENTIFIER' + call add(errs, s:ident()) + call s:setErrorEndPos(b:bp) + endif + return s:SyntaxError("class.or.intf.or.enum.expected", pos, errs) + endif +endfu + +" ClassDeclaration = CLASS Ident TypeParametersOpt [EXTENDS Type] [IMPLEMENTS TypeList] ClassBody {{{2 +fu! s:classDeclaration(mods, dc) + let type = s:ClassDef(b:pos, a:mods) + + call s:accept('CLASS') + let type.name = s:ident() + + let type.typarams = s:typeParametersOpt() + + " extends + if b:token == 'EXTENDS' + call s:nextToken() + let type.extends = [s:type()] + endif + + " implements + if b:token == 'IMPLEMENTS' + call s:nextToken() + let type.implements = s:typeList() + endif + + let type.defs = s:classOrInterfaceBody(type.name, 0) + let type.endpos = b:pos + return type +endfu + +" InterfaceDeclaration = INTERFACE Ident TypeParametersOpt [EXTENDS TypeList] InterfaceBody {{{2 +fu! s:interfaceDeclaration(mods, dc) + let type = s:ClassDef(b:pos, a:mods) + + call s:accept('INTERFACE') + let type.name = s:ident() + + let type.typarams = s:typeParametersOpt() + + " extends + if b:token == 'EXTENDS' + call s:nextToken() + let type.extends = s:typeList() + endif + + let type.defs = s:classOrInterfaceBody(type.name, 1) + let type.endpos = b:pos + return type +endfu + +" EnumDeclaration = ENUM Ident [IMPLEMENTS TypeList] EnumBody {{{2 +fu! s:enumDeclaration(mods, dc) + let type = s:ClassDef(b:pos, a:mods) + + call s:accept('ENUM') + let type.name = s:ident() + + if b:token == 'IMPLEMENTS' + call s:nextToken() + let type.implements = s:typeList() + endif + + let type.defs = s:enumBody(type.name) + let type.endpos = b:pos + return type +endfu + +" EnumBody = "{" { EnumeratorDeclarationList } [","] +" [ ";" {ClassBodyDeclaration} ] "}" +fu! s:enumBody(enumName) + let defs = [] + call s:accept('LBRACE') + + if b:token == 'COMMA' + call s:nextToken() + elseif b:token != 'RBRACE' && b:token != 'SEMI' + call add(defs, s:enumeratorDeclaration(a:enumName)) + while b:token == 'COMMA' + call s:nextToken() + if b:token == 'RBRACE' || b:token == 'SEMI' + break + endif + call add(defs, s:enumeratorDeclaration(a:enumName)) + endwhile + if b:token != 'RBRACE' && b:token != 'SEMI' + call s:SyntaxError('comma.or.rbrace.or.semi. expected') + call s:nextToken() + endif + endif + + if b:token == 'SEMI' + call s:nextToken() + while b:token != 'RBRACE' && b:token != 'EOF' + call add(defs, s:classOrInterfaceBodyDeclaration(a:enumName, 0)) + if b:pos <= b:errorEndPos + call s:skip(0, 1, 1, 0) + endif + endwhile + endif + + call s:accept('RBRACE') + return defs +endfu + +" EnumeratorDeclaration = AnnotationsOpt [TypeArguments] IDENTIFIER [ Arguments ] [ "{" ClassBody "}" ] +fu! s:enumeratorDeclaration(enumName) + let vardef = {'tag': 'VARDEF', 'pos': b:pos} + + let dc = b:docComment + let flags = 16409 " s:BitOr(s:Flags.PUBLIC, s:Flags.STATIC, s:Flags.FINAL, s:Flags.ENUM) + " if b:deprecatedFlag + " let flags = 147481 " s:BitOr(flags, s:Flags.DEPRECATED) + " let b:deprecatedFlag = 1 + " endif + let pos = b:pos + let annotations = s:annotationsOpt() + let vardef.mods = s:Modifiers(pos, flags, annotations) + let vardef.mods.pos = empty(annotations) ? -1 : pos + let vardef.m = s:Number2Bits(flags) + + let typeArgs = s:typeArgumentsOpt() + let identPos = b:pos + let vardef.name = s:ident() + let vardef.n = vardef.name + + let args = b:token == 'LPAREN' ? s:arguments() : [] + + " NOTE: It may be either a class body or a method body. I dont care, just record it + if b:token == 'LBRACE' + "call s:accept('LBRACE') + "if b:token !=# 'RBRACE' + " call s:gotoMatchEnd('{', '}') + "endif + "call s:accept('RBRACE') + + "let mods1 = s:Modifiers(-1, s:BitOr(s:Flags.ENUM, s:Flags.STATIC), []) + let defs = s:classOrInterfaceBody('', 0) + "let body = s:ClassDef(identPos, mods1) + "let body.defs = defs + endif + let vardef.endpos = b:bp + + " TODO: create new class + + return vardef +endfu + +" classOrInterfaceBody {{{2 +" ClassBody = "{" {ClassBodyDeclaration} "}" +" InterfaceBody = "{" {InterfaceBodyDeclaration} "}" +fu! s:classOrInterfaceBody(classname, isInterface) + call s:Info('== type definition body ==') + call s:accept('LBRACE') + + if b:pos <= b:errorEndPos + call s:skip(0, 1, 0, 0) + if b:token == 'LBRACE' + call s:nextToken() + endif + endif + + let defs = [] + while b:token != 'RBRACE' && b:token != 'EOF' + let defs += s:classOrInterfaceBodyDeclaration(a:classname, a:isInterface) + + if b:pos <= b:errorEndPos + call s:skip(0, 1, 1, 0) + endif + endwhile + call s:accept('RBRACE') + return defs +endfu + +" classOrInterfaceBodyDeclaration {{{2 +" ClassBodyDeclaration = +" ";" +" | [STATIC] Block +" | ModifiersOpt +" ( Type Ident +" ( VariableDeclaratorsRest ";" | MethodDeclaratorRest ) +" | VOID Ident MethodDeclaratorRest +" | TypeParameters (Type | VOID) Ident MethodDeclaratorRest +" | Ident ConstructorDeclaratorRest +" | TypeParameters Ident ConstructorDeclaratorRest +" | ClassOrInterfaceOrEnumDeclaration +" ) +" InterfaceBodyDeclaration = +" ";" +" | ModifiersOpt Type Ident +" ( ConstantDeclaratorsRest | InterfaceMethodDeclaratorRest ";" ) +fu! s:classOrInterfaceBodyDeclaration(classname, isInterface) + call s:Info('s:classOrInterfaceBodyDeclaration') + if b:token == 'SEMI' + call s:nextToken() + return [{'tag': 'BLOCK', 'pos': -1, 'endpos': -1, 'stats': []}] + else + if b:scanStrategy < 0 + let result = s:classOrInterfaceBodyDeclaration_opt(a:classname, a:isInterface) + if !empty(result) + return result + endif + endif + + + let dc = b:docComment + let pos = b:bp + let mods = s:modifiersOpt() + + if b:token =~# '^\(CLASS\|INTERFACE\|ENUM\)$' + return [s:classOrInterfaceOrEnumDeclaration(mods, dc)] + + " [STATIC] block + elseif b:token == 'LBRACE' && !a:isInterface + return [s:block(pos, mods.flags, b:scanStrategy < 1)] + + else + let typarams = s:typeParametersOpt() + + let token = b:token + let name = b:name + + let type = copy(s:TTree) + let isVoid = b:token == 'VOID' + if isVoid + let type = {'tag': 'TYPEIDENT', 'pos': pos, 'typetag': 'void'} " FIXME + let type.value = '' + call s:nextToken() + else + let time = reltime() + let type = s:type() + let b:et_perf .= "\r" . reltimestr(reltime(time)) . ' s:type() ' + endif + + + " ctor + if b:token == 'LPAREN' && !a:isInterface && type.tag == 'IDENT' + if a:isInterface || name != a:classname + call s:SyntaxError('invalid.meth.decl.ret.type.req') + endif + return [s:methodDeclaratorRest(pos, mods, type, name, typarams, a:isInterface, 1, dc)] + else + let name = s:ident() + " method + if b:token == 'LPAREN' + return [s:methodDeclaratorRest(pos, mods, type, name, typarams, a:isInterface, isVoid, dc)] + " field + elseif !isVoid && len(typarams) == 0 + let defs = s:variableDeclaratorsRest(pos, mods, type, name, a:isInterface, dc, copy([])) + call s:accept('SEMI') + return defs + else + call s:SyntaxError("LPAREN expected") + return [{}] + endif + endif + endif + endif +endfu + +" OAO: short way for common declaration of field or method, not for generic type yet. +fu! s:classOrInterfaceBodyDeclaration_opt(classname, isInterface) + let str = b:lines[b:line] + let idx = matchend(str, s:RE_MEMBER_HEADER) + if idx != -1 + let subs = split(substitute(strpart(str, 0, idx), s:RE_MEMBER_HEADER, '\1;\2;\3', ''), ';') + let name_ = subs[2] + let type_ = subs[1] + let flag_ = s:String2Flags(subs[0]) + +" if type_ =~# '^\(class\|interface\|enum\)$' +" return [s:classOrInterfaceOrEnumDeclaration(mods, dc)] +" else + " methodDeclarator + let idx = matchend(str, '^\s*[,=;(]', idx)-1 + if str[idx] == '(' + let methoddef = s:methodDeclaratorRest_opt(b:pos, flag_, type_, name_, [], a:isInterface, type_ == 'void', '', str, idx) + if !empty(methoddef) + return [methoddef] + endif + + " variableDeclarator + elseif str[idx] =~ '[=;]' + let vardef = {'tag': 'VARDEF', 'pos': b:pos, 'name': name_, 'n': name_, 'vartype': type_, 't': type_, 'm': flag_} + call s:gotoSemi() + call s:accept('SEMI') + let vardef.pos_end = b:pos + return [vardef] + + " variableDeclarators + elseif str[idx] == ',' + let ie = matchend(str, '^\(,\s*'. s:RE_IDENTIFIER .'\s*\)*;', idx) + if ie != -1 + let vardef = {'tag': 'VARDEF', 'pos': b:pos, 'name': name_, 'n': name_, 'vartype': type_, 't': type_, 'm': flag_} + let vars = [vardef] + for item in split(substitute(strpart(str, idx+1, ie-idx-2), '\s', '', 'g'), ',') + let vardef = copy(vardef) + let vardef.name = item + let vardef.n = item + call add(vars, vardef) + endfor + let b:col = ie + let b:bp = b:idxes[b:line] + b:col + call s:nextToken() + return vars + endif + endif +" endif + endif + + let RE_CTOR_HEADER = '^\s*\(\(public\|protected\|private\)\s\+\)\=\C' .a:classname. '\s*(' + let ie = matchend(str, RE_CTOR_HEADER) + if ie != -1 && !a:isInterface + let flag_ = s:String2Flags(substitute(strpart(str, 0, ie), RE_CTOR_HEADER, '\1', '')) + let methoddef = s:methodDeclaratorRest_opt(b:pos, flag_, a:classname, a:classname, [], a:isInterface, 1, '', str, ie-1) + if !empty(methoddef) + return [methoddef] + endif + endif + + let RE_METHOD_HEADER = '^\s*\(' .s:RE_IDENTIFIER. '\%(\s*\.\s*' .s:RE_IDENTIFIER. '\)*\%(\s*\[\s*\]\)\=\)\s\+\(' .s:RE_IDENTIFIER. '\)\s*(' + let ie = matchend(str, RE_METHOD_HEADER) + if ie != -1 + let subs = split(substitute(strpart(str, 0, ie), RE_METHOD_HEADER, '\1;\2', ''), ';') + let methoddef = s:methodDeclaratorRest_opt(b:pos, 0, subs[0], subs[1], [], a:isInterface, subs[0] == 'void', '', str, ie-1) + if !empty(methoddef) + return [methoddef] + endif + endif +endfu + + +" MethodDeclaratorRest = {{{2 +" FormalParameters BracketsOpt [Throws TypeList] ( MethodBody | [DEFAULT AnnotationValue] ";") +" VoidMethodDeclaratorRest = FormalParameters [Throws TypeList] ( MethodBody | ";") +" InterfaceMethodDeclaratorRest = FormalParameters BracketsOpt [THROWS TypeList] ";" +" VoidInterfaceMethodDeclaratorRest = FormalParameters [THROWS TypeList] ";" +" ConstructorDeclaratorRest = "(" FormalParameterListOpt ")" [THROWS TypeList] MethodBody +fu! s:methodDeclaratorRest(pos, mods, type, name, typarams, isInterface, isVoid, dc) + let time = reltime() + let methoddef = {'tag': 'METHODDEF', 'pos': a:pos, 'name': a:name, 'mods': a:mods, 'restype': a:type, 'typarams': a:typarams} + let methoddef.n = a:name + let methoddef.m = s:Number2Bits(a:mods.flags) + let methoddef.r = java_parser#type2Str(a:type) + + " parameters + let methoddef.params = s:formalParameters() + + " BracketsOpt + if !a:isVoid + let methoddef.r = java_parser#type2Str(s:bracketsOpt(a:type)) + endif + + + " throws + if b:token == 'THROWS' + call s:nextToken() + + " thrown = qualidentList() + let ts = [s:qualident()] + while b:token == 'COMMA' + call s:nextToken() + call add(ts, s:qualident()) + endwhile + let methoddef.throws = ts + endif + + " method body + if b:token == 'LBRACE' + let methoddef.body = s:block(b:pos, 0, b:scanStrategy < 1) + else + if b:token == 'DEFAULT' + call s:accept('DEFAULT') + let methoddef.defaultValue = s:annotationValue() + endif + call s:accept('SEMI') + + if b:pos <= b:errorEndPos + call s:skip(0, 1, 0, 0) + if b:token == 'LBRACE' + let methoddef.body = s:block(b:pos, 0, b:scanStrategy < 1) + endif + endif + endif + + let methoddef.d = s:method2Str(methoddef) + let b:et_perf .= "\r" . reltimestr(reltime(time)) . ' methodrest() ' + return methoddef +endfu + +" method header declared in one line, +" NOTE: RE_FORMAL_PARAM_LIST do not recognize varargs and nested comments +fu! s:methodDeclaratorRest_opt(pos, mods, type, name, typarams, isInterface, isVoid, dc, str, idx) + let str = a:str + let idx = a:idx + + " params + let idxend = matchend(str, '^(\s*)', idx) " no params + if idxend == -1 + let idxend = matchend(str, '^(\s*' . s:RE_FORMAL_PARAM_LIST . '\s*)', idx) + endif + if idxend == -1 + return + endif + + let methoddef = {'tag': 'METHODDEF', 'pos': a:pos, 'name': a:name, 'n': a:name, 'm': a:mods, 'r': a:type} + + " params + let methoddef.params = [] + let s = strpart(str, idx+1, idxend-idx-2) + if s !~ '^\s*$' + for item in split(s, ',') + let subs = split(substitute(item, s:RE_FORMAL_PARAM2, '\2;\5', ''), ';') + let param = {'tag': 'VARDEF', 'pos': -1} + let param.name = subs[1] + let param.vartype = substitute(subs[0], ' ', '', 'g') + let param.m = s:Flags.PARAMETER + call add(methoddef.params, param) + endfor + endif + + " throws + let idx2 = matchend(str, '^\s*' . s:RE_THROWS, idxend) + let idx = idx2 == -1 ? idxend : idx2 + if idx2 != -1 + "let throws = strpart(str, idxend, idx-idxend) + endif + + " in interface + if a:isInterface + let idx = matchend(str, '^\s*;', idx) + if idx != -1 + let b:token = 'SEMI' + let b:col = idx + let b:bp = b:idxes[b:line] + b:col + let b:pos = b:bp - 1 + let methoddef.d = substitute(str, '^\s*\([^{]*\)\s*;\=$', '\1', '') + return methoddef + endif + endif + + let idx = matchend(str, '^\s*{', idx) + if idx == -1 + let idx = matchend(b:lines[b:line+1], '^\s*{') + if idx != -1 + let b:line += 1 + endif + endif + if idx != -1 + let b:token = 'LBRACE' + let b:col = idx + let b:bp = b:idxes[b:line] + b:col + let b:pos = b:bp - 1 + let methoddef.d = substitute(str, '^\s*\([^{]*\)\s*{\=$', '\1', '') + let methoddef.body = s:block(b:pos, 0, b:scanStrategy < 1) + return methoddef + endif +endfu + +" VariableDeclarators = VariableDeclarator { "," VariableDeclarator } {{{2 +fu! s:variableDeclarators(mods, type, vdefs) + return s:variableDeclaratorsRest(b:pos, a:mods, a:type, s:ident(), 0, '', a:vdefs) +endfu + +" VariableDeclaratorsRest = VariableDeclaratorRest { "," VariableDeclarator } +" ConstantDeclaratorsRest = ConstantDeclaratorRest { "," ConstantDeclarator } +fu! s:variableDeclaratorsRest(pos, mods, type, name, reqInit, dc, vdefs) + call add(a:vdefs, s:variableDeclaratorRest(a:pos, a:mods, a:type, a:name, a:reqInit, a:dc)) + while b:token == 'COMMA' + call s:nextToken() + call add(a:vdefs, s:variableDeclarator(a:mods, a:type, a:reqInit, a:dc)) + endwhile + return a:vdefs +endfu + +" VariableDeclarator = Ident VariableDeclaratorRest +" ConstantDeclarator = Ident ConstantDeclaratorRest +fu! s:variableDeclarator(mods, type, reqInit, dc) + return s:variableDeclaratorRest(b:pos, a:mods, a:type, s:ident(), a:reqInit, a:dc) +endfu + +" VariableDeclaratorRest = BracketsOpt ["=" VariableInitializer] +" ConstantDeclaratorRest = BracketsOpt "=" VariableInitializer +fu! s:variableDeclaratorRest(pos, mods, type, name, reqInit, dc) + let vardef = s:VarDef(a:pos, a:mods, a:name, s:bracketsOpt(a:type)) + let vardef.n = vardef.name + let vardef.m = a:mods == {} ? '0' : s:Number2Bits(a:mods.flags) + let vardef.t = java_parser#type2Str(vardef.vartype) + + if b:token == 'EQ' + call s:nextToken() + call s:Info('field init ' . b:token) + let vardef.init = s:variableInitializer() + elseif a:reqInit + echo '[syntax error]:' . s:token2string('EQ') . " expected" + endif + + let vardef.endpos = b:pos + return vardef +endfu + +fu! s:variableDeclaratorId(mods, type) + let vardef = s:VarDef(b:pos, a:mods, s:ident(), a:type) + if len(a:mods.flags) <= 34 " (a:mods.flags & s:Flags.VARARGS) == 0 + let vardef.type = s:bracketsOpt(vardef.vartype) + endif + return vardef +endfu + +" {{{2 +" TypeParametersOpt = ["<" TypeParameter {"," TypeParameter} ">"] {{{2 +fu! s:typeParametersOpt() + if b:token == 'LT' + "call checkGenerics() + let typarams = [] + call s:nextToken() + call add(typarams, s:typeParameter()) + while b:token == 'COMMA' + call s:nextToken() + call add(typarams, s:typeParameter()) + endwhile + call s:accept('GT') + return typarams + else + return [] + endif +endfu + +" TypeParameter = TypeVariable [TypeParameterBound] {{{2 +" TypeParameterBound = EXTENDS Type {"&" Type} +" TypeVariable = Ident +fu! s:typeParameter() + let pos = b:pos + let name = s:ident() + let bounds = [] + if b:token == 'EXTENDS' + call s:nextToken() + call add(bounds, s:type()) + while b:token == 'AMP' + call s:nextToken() + call add(bounds, s:type()) + endwhile + endif + + return {'tag': 'TYPEPARAMETER', 'pos': pos, 'name': name, 'bounds': bounds} +endfu + +" FormalParameters = "(" [ FormalParameterList ] ")" {{{2 +" FormalParameterList = [ FormalParameterListNovarargs , ] LastFormalParameter +" FormalParameterListNovarargs = [ FormalParameterListNovarargs , ] FormalParameter +fu! s:formalParameters() + let params = [] + let lastParam = {} + call s:accept('LPAREN') + if b:token != 'RPAREN' + let lastParam = s:formalParameter() + call add(params, lastParam) + while b:token == 'COMMA' && len(lastParam.mods.flags) <= 34 " (lastParam.mods.flags & s:Flags.VARARGS) == 0 + call s:nextToken() + let lastParam = s:formalParameter() + call add(params, lastParam) + endwhile + endif + call s:accept('RPAREN') + return params +endfu + +" FormalParameter = { FINAL | '@' Annotation } Type VariableDeclaratorId {{{2 +" LastFormalParameter = { FINAL | '@' Annotation } Type '...' Ident | FormalParameter +fu! s:optFinal(flags) + let mods = s:modifiersOpt() + " checkNoMods(mods.flags & ~(Flags.FINAL | Flags.DEPRECATED)) + let mods.flags = s:BitOr(mods.flags, a:flags) + return mods +endfu + +" OAO: optional FINAL for parameter +fu! s:optFinalParameter() + let mods = {'tag': 'MODIFIERS', 'pos': b:pos, 'flags': s:Flags.PARAMETER, 'annotations': []} + if b:token == 'FINAL' + let mods.flags = '1000000000000000000000000000010000' + call s:nextToken() + endif + return mods +endfu + +fu! s:formalParameter() + let mods = s:optFinalParameter() + let type = s:type() + + if b:token == 'ELLIPSIS' + " checkVarargs() + let mods.flags = '1' . mods.flags " s:BitOr_binary(mods.flags, s:Flags.VARARGS) + let type = s:TypeArray(b:pos, type) + call s:nextToken() + endif + + return s:variableDeclaratorId(mods, type) +endfu + +" {{{2 +" auxiliary methods {{{2 +let s:MapToken2Tag = {'BARBAR': 'OR', 'AMPAMP': 'AND', 'BAR': 'BITOR', 'BAREQ': 'BITOR_ASG', 'CARET': 'BITXOR', 'CARETEQ': 'BITXOR_ASG', 'AMP': 'BITAND', 'AMPEQ': 'BITAND_ASG', 'EQEQ': 'EQ', 'BANGEQ': 'NE', 'LT': 'LT', 'GT': 'GT', 'LTEQ': 'LE', 'GTEQ': 'GE', 'LTLT': 'SL', 'LTLTEQ': 'SL_ASG', 'GTGT': 'SR', 'GTGTEQ': 'SR_ASG', 'GTGTGT': 'USR', 'GTGTGTEQ': 'USR_ASG', 'PLUS': 'PLUS', 'PLUSEQ': 'PLUS_ASG', 'SUB': 'MINUS', 'SUBEQ': 'MINUS_ASG', 'STAR': 'MUL', 'STAREQ': 'MUL_ASG', 'SLASH': 'DIV', 'SLASHEQ': 'DIV_ASG', 'PERCENT': 'MOD', 'PERCENTEQ': 'MOD_ASG', 'INSTANCEOF': 'TYPETEST'} +let s:opprecedences = {'notExpression': -1, 'noPrec': 0, 'assignPrec': 1, 'assignopPrec': 2, 'condPrec': 3, 'orPrec': 4, 'andPrec': 5, 'bitorPrec': 6, 'bitxorPrec': 7, 'bitandPrec': 8, 'eqPrec': 9, 'ordPrec': 10, 'shiftPrec': 11, 'addPrec': 12, 'mulPrec': 13, 'prefixPrec': 14, 'postfixPrec': 15, 'precCount': 16} + +fu! s:checkExprStat(t) + if a:t.tag =~# '^\(PREINC\|PREDEC\|POSTINC\|POSTDEC\|ASSIGN\|BITOR_ASG\|BITXOR_ASG\|BITAND_ASG\|SL_ASG\|SR_ASG\|USR_ASG\|PLUS_ASG\|MINUS_ASG\|MUL_ASG\|DIV_ASG\|MOD_ASG\|APPLY\|NEWCLASS\|ERRONEOUS\)$' + return a:t + else + call s:SyntaxError('not.stmt') + return {'tag': 'ERRONEOUS', 'pos': b:pos, 'errs': [a:t]} + endif +endfu + +fu! s:prec(token) + let oc = s:optag(a:token) + return oc == -1 ? -1 : s:opPrec(oc) +endfu + +fu! s:opPrec(tag) + if a:tag =~# '^\(POS\|NEG\|NOT\|COMPL\|PREINC\|PREDEC\)$' + return s:opprecedences.prefixPrec + elseif a:tag =~# '^\(POSTINC\|POSTDEC\|NULLCHK\)$' + return s:opprecedences.postfixPrec + elseif a:tag == 'ASSIGN' + return s:opprecedences.assignPrec + elseif a:tag =~# '^\(BITOR_ASG\|BITXOR_ASG\|BITAND_ASG\|SL_ASG\|SR_ASG\|USR_ASG\|PLUS_ASG\|MINUS_ASG\|MUL_ASG\|DIV_ASG\|MOD_ASG\)$' + return s:opprecedences.assignopPrec + elseif a:tag == 'OR' + return s:opprecedences.orPrec + elseif a:tag == 'AND' + return s:opprecedences.andPrec + elseif a:tag =~# '^\(EQ\|NE\)$' + return s:opprecedences.eqPrec + elseif a:tag =~# '^\(LT\|GT\|LE\|GE\)$' + return s:opprecedences.ordPrec + elseif a:tag == 'BITOR' + return s:opprecedences.bitorPrec + elseif a:tag == 'BITXOR' + return s:opprecedences.bitxorPrec + elseif a:tag == 'BITAND' + return s:opprecedences.bitandPrec + elseif a:tag =~# '^\(SL\|SR\|USR\)$' + return s:opprecedences.shiftPrec + elseif a:tag =~# '^\(PLUS\|MINUS\)$' + return s:opprecedences.addPrec + elseif a:tag =~# '^\(MUL\|DIV\|MOD\)$' + return s:opprecedences.mulPrec + elseif a:tag == 'TYPETEST' + return s:opprecedences.ordPrec + else + return -1 + endif +endfu + +fu! s:optag(token) + return get(s:MapToken2Tag, a:token, -1) +endfu + +fu! s:unoptag(token) + if a:token == 'PLUS' + return 'POS' + elseif a:token == 'SUB' + return 'NEG' + elseif a:token == 'BANG' + return 'NOT' + elseif a:token == 'TILDE' + return 'COMPL' + elseif a:token == 'PLUSPLUS' + return 'PREINC' + elseif a:token == 'SUBSUB' + return 'PREDEC' + else + return -1 + endif +endfu + +fu! s:typetag(token) + if a:token =~# '\(BYTE\|CHAR\|SHORT\|INT\|LONG\|FLOAT\|DOUBLE\|BOOLEAN\)' + return tolower(a:token) + else + return -1 + endif +endfu + +"}}}1 +" vim:set fdm=marker sw=2 nowrap: diff --git a/vim/autoload/javacomplete.vim b/vim/autoload/javacomplete.vim new file mode 100644 index 0000000..ee55612 --- /dev/null +++ b/vim/autoload/javacomplete.vim @@ -0,0 +1,2932 @@ +" Vim completion script - hit 80% complete tasks +" Version: 0.77.1.2 +" Language: Java +" Maintainer: cheng fang +" 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 =~# '\ 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\' || 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), '\= 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, '\= 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('\' || str =~ '' + 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('\', '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]\+\)\?\(\\|\\)[ \t\n\r]\+HelloWorld[^a-zA-Z0-9_$]', 'W') +" echo substitute(getline('.'), '.*\(\(public\|protected\|private\)[ \t\n\r]\+\)\?\(\(static\)[ \t\n\r]\+\)\?\(\\|\\)\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('\(\\|\\)[ \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, '.*\(\\|\\)\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('\(\\|\\|\\)[ \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, '.*\(\\|\\)\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('') + + 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($HOME, '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 "' . $HOME . '" "' . srcfile . '"' + let classfile = globpath($HOME, 'Reflection.class') + if classfile == '' + echo srcfile . ' can not be compiled. Please check it' + endif + else + echo 'No Reflection.class found in $HOME 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('') + 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('') + if cword == a:name + return 0 + endif + if getline('.')[col('.')-1] == '{' + normal % + endif + endwhile + endif + return 1 +endfu +"nmap :call Searchdecl(expand('')) + +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\' || 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 =~ '\' + 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 =~ '\' + 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, '\ 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: diff --git a/vim/autoload/togglebg.vim b/vim/autoload/togglebg.vim new file mode 100644 index 0000000..108511f --- /dev/null +++ b/vim/autoload/togglebg.vim @@ -0,0 +1,55 @@ +" Toggle Background +" Modified: 2011 Apr 29 +" Maintainer: Ethan Schoonover +" License: OSI approved MIT license + +if exists("g:loaded_togglebg") + finish +endif +let g:loaded_togglebg = 1 + +" noremap is a bit misleading here if you are unused to vim mapping. +" in fact, there is remapping, but only of script locally defined remaps, in +" this case TogBG. The + + + + + + diff --git a/vim/doc/javacomplete.txt b/vim/doc/javacomplete.txt new file mode 100644 index 0000000..ef5f211 --- /dev/null +++ b/vim/doc/javacomplete.txt @@ -0,0 +1,568 @@ +*javacomplete.txt* For Vim version 7.0 and above. Last change: 2011-01-30 + + JAVACOMPLETE REFERENCE MANUAL by cheng fang~ + fangread@yahoo.com.cn~ + + +1. Overview |javacomplete-overview| + 1.1 Features |javacomplete-features| + 1.2 Requirements |javacomplete-requirements| + 1.3 Download |javacomplete-download| + 1.4 Install |javacomplete-install| +2. Usage |javacomplete-usage| + 2.1 Input contexts |javacomplete-contexts| + 2.2 Kind letter |javacomplete-kindletter| + 2.3 Options |javacomplete-options| + 2.4 Commands |javacomplete-commands| +3. Java parser in Vim |javacomplete-parser| + 3.1 Abstract Syntax Tree |javacomplete-ast| + 3.2 Global Constants |javacomplete-constants| + 3.3 Parsing Functions |javacomplete-functions| + 3.4 Sample |javacomplete-sample| +4. FAQ |javacomplete-faq| +5. Limitations |javacomplete-limitations| +6. History + 6.1 javacomplete |javacomplete-history| + 6.2 Parser |java-parser-history| + 6.2 Reflection.java |javacomplete-reflection| +7. Todo |javacomplete-todo| +8. Thanks |javacomplete-thanks| + +============================================================================== +1. Overview *javacomplete-overview* + +This is javacomplete, an omni-completion script of JAVA language for vim 7 and +above. It includes javacomplete.vim, java_parser.vim, Reflection.java, and +javacomplete.txt. + +1.1 Features *javacomplete-features* + +- List members of a class, including (static) fields, (static) methods and ctors. +- List classes or subpackages of a package. +- Provide parameters information of a method, list all overload methods. +- Complete an incomplete word. +- Provide a complete JAVA parser written in Vim script language. +- Use the JVM to obtain most information. +- Use the embedded parser to obtain the class information from source files. +- Tags generated by ctags can also be used. +- JSP is supported, Builtin objects such as request, session can be recognized. + The classes and jar files in the WEB-INF will be appended automatically to the classpath. + +1.2 Requirements *javacomplete-requirements* + +It works on all the platforms wherever +- Vim version 7.0 and above, +- JDK version 1.1 and above, +exists. + +1.3 Download *javacomplete-download* + +You can download the lastest version from this url: + http://www.vim.org/scripts/script.php?script_id=1785 + +1.4 Install *javacomplete-install* + +1. Unzip javacomplete.zip to a directory of 'runtimepath', e.g. +$HOME/.vim (unix/linux), $VIM/vimfiles (windows). > + > unzip javacomplete.zip -d ~/.vim + +< To update Vim help tags, run vim and run command: > + :helptags $HOME/.vim/doc +< or > + :helptags $VIM/vimfiles/doc + +NOTE: javacomplete.vim, java_parser.vim and Reflection.java should be in one +autoload directory of 'runtimepath'. +javacomplete.txt should be in one doc directory of 'runtimepath'. + +2. Set 'omnifunc' option. e.g. > + :setlocal omnifunc=javacomplete#Complete +< Or, use autocmd: > + :" Only do this part when compiled with support for autocommands. + :if has("autocmd") + : autocmd Filetype java setlocal omnifunc=javacomplete#Complete + :endif +You can add this command to your .vimrc or _vimrc. + +3. Set 'completefunc' option to show parameters information IF YOU LIKE. e.g. > + :setlocal completefunc=javacomplete#CompleteParamsInfo +You can map as follows for better display: > + :inoremap + :inoremap + +4. Reflection.java will be automatcally compiled and placed to $HOME when you +use first time. Assure that Reflection.java is in the same directory with +javacomplete.vim to be searched in autoload subdirectory of &rtp. +If no Reflection.class is generated, check that you have the write permission +in $HOME directory. +If a previous Reflection.java is not compatible with the new version +javacomplete.vim, please compile Reflection.java manually. + +============================================================================== +2. Usage *javacomplete-usage* + +You can use it like other omni-completion script. Many samples of input context +are gived in the following section. + +Make sure a JVM launcher (default 'java') can be searched in the PATH enviroment +variable, otherwise Use javacomplete#SetJVMLauncher() to specify one. See option +`javacomplete-launcher`. + +See FAQ in time if some problem occurs. When meeting other problems not +described in FAQ, you can contact with the auther by the following e-mail: +fangread@yahoo.com.cn + +2.1 Input contexts |javacomplete-contexts| +It recognize nearly all kinds of Primary Expressions (see langspec-3.0) +except for "Primary.new Indentifier". Casting conversion is also supported. + +Samples of input contexts are as following: (Note that '|' indicates cursor) + (1). after '.', list members of a class or a package + - package.| subpackages and classes of a package + - Type.| static members of the 'Type' class and "class" + - var.| or field.| members of a variable or a field + - method().| members of result of method() + - this.| members of the current class + - ClassName.this.| members of the qualified class + - super.| members of the super class + - array.| members of an array object + - array[i].| array access, return members of the element of array + - "String".| String literal, return members of java.lang.String + - int.| or void.| primitive type or pseudo-type, return "class" + - int[].| array type, return members of a array type and "class" + - java.lang.String[].| + - new int[].| members of the new array instance + - new java.lang.String[i=1][].| + - new Type().| members of the new class instance + - Type.class.| class literal, return members of java.lang.Class + - void.class.| or int.class.| + - ((Type)var).| cast var as Type, return members of Type. + - (var.method()).| same with "var.|" + - (new Class()).| same with "new Class().|" + + (2). after '(', list matching methods with parameters information. + - method(|) methods matched + - var.method(|) methods matched + - new ClassName(|) constructors matched + - this(|) constructors of current class matched + - super(|) constructors of super class matched + Any place between '(' and ')' will be supported soon. + Help information of javadoc is not supported yet. + + (3). after an incomplete word, list all the matched beginning with it. + - var.ab| subset of members of var beginning with `ab` + - ab| list of all maybes + + (4). import statement + - " import java.util.|" + - " import java.ut|" + - " import ja|" + - " import java.lang.Character.|" e.g. "Subset" + - " import static java.lang.Math.|" e.g. "PI, abs" + + (5). package declaration + - " package com.|" + + The above are in simple expression. + (6). after compound expression: + - PrimaryExpr.var.| + - PrimaryExpr.method().| + - PrimaryExpr.method(|) + - PrimaryExpr.var.ab| + e.g. + - "java.lang . System.in .|" + - "java.lang.System.getenv().|" + - "int.class.toString().|" + - "list.toArray().|" + - "new ZipFile(path).|" + - "new ZipFile(path).entries().|" + + (7). Nested expression: + - "System.out.println( str.| )" + - "System.out.println(str.charAt(| )" + - "for (int i = 0; i < str.|; i++)" + - "for ( Object o : a.getCollect| )" + + +2.2 Kind letter *javacomplete-kindletter* + +A single letter indicates the kind of compeltion item. These kinds are: + + ctor + v local variable or parameter + f nonstatic field + F static field + m nonstatic method + M static method + P package + C class type + I interface type + +2.3 Options *javacomplete-options* + +1. Set java compiler (default 'javac') using the following function: + javacomplete#SetCompiler('javac') *javacomplete-compiler* + +2. Set java launcher (default 'java') using the following function: + javacomplete#SetJVMLauncher('java') *javacomplete-launcher* + +3. Set classpath using the following function: > + javacomplete#AddClassPath('jarfile_or_classes_path') + javacomplete#DelClassPath('jarfile_or_classes_path') + javacomplete#SetClassPath('semicolon_separated_string') +< + Another two variables will be used if they are existing: + |g:java_classpath| global classpath + |b:classpath| associated with current buffer + In one sense, s:classpath is like a classpath option for a PROJECT. + If some of them are body set, the priority of these variables is: + first, b:classpath first, + second, s:classpath + third, g:java_classpath + last, $CLASSPATH + +4. Set sourcepath using the following function: > + javacomplete#AddSourcePath('sources_file_path') + javacomplete#DelSourcePath('sources_file_path') + javacomplete#SetSourcePath('sources_file_path') + +5. Set option for using JDK1.1 if you meet the problem described in FAQ 3: > + javacomplete#UseJDK11() + +6. Set methods to search declaration: > + " 1 - by builtin searchdecl(), quickest but inaccurate in many cases. + " 2 - by special Searchdecl(), work NOT WELL YET. + " 4 - by java_parser, slowest but accurate in most cases. Not for JSP. + javacomplete#SetSearchdeclMethod() + +2.4 Commands *javacomplete-commands* + +============================================================================== +3. Java parser in Vim *javacomplete-parser* + +3.1 Abstract Syntax Tree *javacomplete-ast* + +3.2 Global Constants *javacomplete-constants* + +3.3 Parsing Functions *javacomplete-functions* + +3.4 Sample Codes *javacomplete-sample* +This parser can be a good candidate for anyone who needs a java parser to get +a abstract syntax tree for many use. The following are sample codes: > + + " NOTE: The script contains a single parser instance. You cannot create + " another parser! The only way to parse another JAVA code is reset the + " parser by calling java_parser#InitParser(). + + " 1. Initialize the parser + " for a code snippet, + call java_parser#InitParser(['for (int i = 0; i < N; i++) {', '', '}']) + " or for the current buffer, + call java_parser#InitParser(getline('^', '$')) + " or for a whole source file + call java_parser#InitParser(readfile('java/util/Arrays.java')) + + " 2. Get the result tree + call java_parser#compilationUnit() + " or others according to the input code + call java_parser#expression() + call java_parser#block() + call java_parser#statement() + + " 3. Use the tree as you like + + " 4. The default scan strategy is scanning only sklenton. + " You can change it by set the option 'scanStrategy'. + " The values for 'scanStrategy' option are: + " 0 - only class members when parse full file; + " 1 - keep statement as a whole string; + " 2 - all + call java_parser#InitParser(getline('^', '$'), {'scanStrategy': 2}) + + " 5. I recommend that keeping scanStrategy as default. + " If you want to parse a code snippet such as a method body of the whole + " file, you can call java_parser#GotoPosition() to go to what you are going + " to start parsing. + " Then, call java_parser#block(), java_parser#statement() or + " java_parser#expression() to parse the smaller snippet. + " NOTE: This way will keep the result tree reserved. + call java_parser#GotoPosition(def.body.pos) + call java_parser#block() + +============================================================================== +4. FAQ *javacomplete-faq* + +(1). When you meets the following problem: > + omni-completion error: Exception in thread "main" + java.lang.NoClassDefFoundError: Reflection +It is Reflection.class not found in autoload directory or $HOME that cause +this problem. +There are several reasons causing this problem: + o No compiler. Use javacomplete#SetCompiler() to specify one. + o No write permission for $HOME directory. + +(2). Reflection.java should be searched in autoload subdirectory of &rtp. +Reflection.class should be searched in $HOME or autoload subdirectory of &rtp. +If not found, javacomplete try to compile it and place the generated class +file in $HOME. + +(3). A error when using JDK1.1: + Unable to initialize threads: cannot find class java/lang/Thread +When I tested JDK1.1.8 on Windows XP, I found -classpath options cause it. +There are two way to avoid it is: + o Add the runtime classes to classpath, like + "${JDK118}\classes;${JDK118}\lib\classes.zip;${JDK118}\lib\classes.jar;" + o Add Reflection.class and others to the CLASSPATH enviroment variable. + And call javacomplete#UseJDK11() to set option. + +============================================================================== +5. Limitations *javacomplete-limitations* + +The embedded parser works a bit slower than expected. + +============================================================================== +6. History + +6.1 javacomplete *javacomplete-history* + +v0.77.1.2 + 2011-01-30 Fixed to adapt globpath() (vim < 7.2). Patched by Sam Lidder. + +v0.77.1.1 + 2010-11-12 Fixed to ignore the 'suffixes' and 'wildignore' options which + make Reflection.class can not be found. + +v0.77.1 + 2007-09-19 Supported showing method parameters information in any place + between parenthesises. + +v0.77 + 2007-09-19 bug fix + 2007-09-18 Added GetCurrentFileKey() avoid empty key of s:files for current buffer. + 2007-09-16 Use a new strategy for searching inherited members. + 2007-09-11 + - Supported new contexts "jav|", "var|", just after an incomplete word. + - Supported new context "abs(|)", a imported static method. + 2007-09-10 + - Improved FoundClassDeclaration() + - Fixed bug calling cursor(0, 0) + 2007-09-09 Rewrote DoGetClassInfo(), GetFQN() and IsFQN()ĄŁ + 2007-09-08 Fixed a bug when merging superclass's members + 2007-09-05 -- 07 + - Improved s:MergeLines() and s:ExtractCleanExpr(). + - Rewrote CompleteAfterDot(). Added ParseExpr(). Removed s:GetNextSubexprType() + - Supported accessible static imported members. + - Supported accessible inherited members. + + 2007-09-04 Used b:changedtick and getftime() to check buffer (or other file) for changing. + 2007-09-01 Supported not-file-name toplevel or static member class in source files. + +v0.76.8 + 2007-08-30 + - Created the s:TreeVisitor to search type or symbol names. + - Supported local and anonymous class. + + 2007-08-29 + - Supported appending automatically classpath under WEB-INF for jsp files. + +v0.76.7 + 2007-08-28 + - Fixed case of "new java.util.zip.ZipFile().|" + - Improved process of type arguments and method parameters. JAVA5+ + - Reorganize codes in javacomplete#Complete() + - Added CONTEXT_NEED_TYPE, removed CONTEXT_INCOMPLETE_WORD + + 2007-08-24 Add Context types for type declaration: CONTEXT_NEED_TYPE + +v0.76.6 + 2007-08-23 Improved GetStatement() and related. Bug fixed. + +v0.76.5 + 2007-08-21 + - Fixed bug: "foo().|", "getFoo().foo().|", + "for (Enumeration entries = ; entries.|; )". + - Supported input contexts: "((Object)o).|", "((Object)o).getClass().|", + "new ZipFile(path).|", "(new String().)|". + +v0.76.4 + 2007-08-17 + - Improved input contexts: "int.class.toString().|", "list.toArray().|". + - Fixed recognizing "this(|)", "method1(|)" + - Added the 'kind' letter to distinguish between classes and packages. + 2007-08-14 + - Support accessible nested classes. + - Support import static members and import accessible nested classes. + 2007-08-11 + - Fixed a bug when Reflection.java is in the path which contains space. + - Improved process of this and super in JSP. + - Fixed an severe bug parsing current jsp file. + +v0.76.3 + 2007-08-10 + - Add an option 'searchdecl' set by javacomplete#SetSearchdeclMethod(). + - Make an improvement for jsp file. + - Clear cache when set options affecting classpath. + - Improved DoGetPackageList() and s:GenerateImports(). + - Replace codes searching list of string with index(). + +v0.76.2 + 2007-08-08 + - Fix failing to list members of nested class. + - Combine members of local packages and loadable packages. + - Add quick recognition of package or import. + 2007-08-06 Add inherited fields and methods to local class. + +v0.76.1 + 2007-08-04 + - Fix using a: in javacomplete#SetClassPath() + - Fix a bug in javacomplete#GetClassPath() + +v0.76 2007-08-04 + 2007-08-04 + - Fix a infinite loop bug in s:GetMatchedIndexEx() + - Fix that array type not recognised in compound expression. + - Add a option for JDK1.1. See FAQ 3. + 2007-08-03 + - Improve for 'this' or 'super'. + - Support searching toplevel class in sourcepath. + - Clean + 2007-08-02 + - Improve the process of checking a class in one of packages. + 2007-08-01 + - Add Searchdecl() using java_parser.vim to provide quick information. + - Supports input context: "StringLiteral".|, "int.|", "void.|" + 2007-07-28 + - Automatcally compile Reflection.java and place it to $HOME. + - Add option 'javacompiler', default 'javac' + - Add option 'java', default 'java' + +v0.75 2007-02-13 + - Add java_parser.vim. + - Add b:sourcepath option. + - Improve recognition of classes defined in current buffer or in source path. + - Support generating class information from tags instead of returning list directly. + +v0.74 2007-02-03 + - Support jre1.2 (and above). + - Support input context like "boolean.class.|" + - Handle java primitive types like 'int'. + +v0.73 2007-02-01 + - Fix bug that CLASSPATH not used when b:classpath or g:java_classpath not set. + - Fix bug that call filter() without making a copy for incomplete. + - Improve recognition of declaration of this class + +v0.72 2007-01-31 Handle nested expression. +v0.71 2007-01-28 Add Basic support for class in current folder. +v0.70 2007-01-27 Complete the reflection part. +v0.60 2007-01-25 Design TClassInfo, etc. +v0.50 2007-01-21 Use java and Reflection.class directly. + + +6.2 Parser *java-parser-history* + +v0.67 + 2007-09-11 Append a error string to imported qid when error occurs. + 2007-09-10 Improved regexp constants. + 2007-09-07 Fixed type2Str(). Removed qualident2Str(). + +v0.66.1 08-30 Changed classCreatorRest(). +v0.66 08-27 Minor changes + +v0.65 + 2007-08-23 + - Improved s:scanComment(), s:Strpart(), s:String2Flags(). + - Improved recognizing methods, ctors, and variable declarators declared in most common form. + - Added s:optFinalParameter(), s:methodDeclaratorRest_opt(). + - Removed s:GetLine() and s:GetCol(). + - Rewrote binary functions. + +v0.64 + 2007-08-21 + - Added quick recognizing fields or methods declared in most common form. + - Optimized code: s:modeAndEXPR(), formalParameter(), and others. + +v0.63 + 2007-08-10 + - Removed the unclear s:tokens and s:modifier_keywords. + - Add java_parser#GetSnapshot() and java_parser#Restore(). + 2007-08-09 Fixed a bug when no top level class defined + +v0.62 2007-08-08 + 2007-08-08 Fix values in s:Flags and s:modifiersOpt() and the related. + 2007-08-07 Optimize code of scanDoubleQuote() and importDeclaration(). + +v0.61 2007-08-04 + 2007-08-01 Fix a bug typetag(). return a:token -> return tolower(a:token) + 2007-07-31 + - Rename all script functions matching "s:Java_\(\i\+\)" to "s:\1". + - Change s:EOI = '' + - Use get() instead of s:GetOption(). Remove it. + - Use repeat() instead of s:PrependChar(). Remove it. + - Improve scanChar() + +v0.60 2007-07-31 Now it nearly is a complete parser and support Java5,6. + And tested correctly on all java files in jdk6 src.zip. + 2007-07-19 Support new language features in java 5 and above. + 2007-07-25 Add supports for parsing statement, block or expression + 2007-07-28 Place it to autoload directory. + 2007-07-30 Clean this script. + +v0.51 2007-02-13 Optimize several scan function. +v0.50 2007-02-10 Complete the skeleton. + + +6.3 Reflection.java *javacomplete-reflection* + +v0.77 + 2007-09-14 Improved generating information of all packages in jar files. + 2007-09-06 + - Improved getting paths of all system jar files for different JDKs + in different platforms. + 2007-08-14 Major improvement. Support nontoplevel classes. + +v0.76.3 + 2007-08-09 Redefined '-P' option for returning all packages and subpackages info in one time. + +v0.76.2 + 2007-08-06 Return a modifier value as a string because it more than 32bit. + +v0.76 + 2007-08-04 Support checking and reading package members for '-E'. + 2007-08-02 + - Add an option '-E'. + - Use ZipFile and ZipEntry instead of JarFile and JarEntry, + so that it can be compiled by and run on JDK1.1 and above. +v0.7 2007-02-17 + +============================================================================== +7. Todo *javacomplete-todo* + +- Improve performance of the embedded parser. Incremental parser. +- Add quick information using balloonexpr, ballooneval, balloondelay. +- Add javadoc +- Give a hint for class name conflict in different packages. +- Support parameter information for template + +============================================================================== +8. Thanks *javacomplete-thanks* + * Bram Moolenaar and all Vim contributors for Vim + * The insenvim project + * The javac and gjc sources + * All of you for using this script :) + + * For help, documentation, bug report : + Martin Stubenschrott author of IComplete + Vissale NEANG author of OmniCppComplete + David Fishburn author of SQLComplete and others + Nico Weber testing on the Mac + Thomas Link testing on cygwin+bash + Zhixing Yu + * For the bug of 'wildignore' options + Rodrigo Rosenfeld Rosas + Alexandru Mo?oi + +FeedBack: +Any problem, bug or suggest are welcome to send to fangread@yahoo.com.cn + +============================================================================== + vim:tw=78:ts=8:ft=help:norl: diff --git a/vim/doc/solarized.txt b/vim/doc/solarized.txt new file mode 100644 index 0000000..93ebd68 --- /dev/null +++ b/vim/doc/solarized.txt @@ -0,0 +1,254 @@ +*solarized.vim* for Vim version 7.3 or newer. Modified: 2011 May 05 + + + Solarized Vim Colorscheme by Ethan Schoonover ~ + +Solarized Colorscheme *solarized* + *solarized-help* + *solarized-colors* + *solarized-colorscheme* + *vim-colors-solarized* + +Solarized is a carefully designed selective contrast colorscheme with dual +light and dark modes that runs in both GUI, 256 and 16 color modes. + +See the homepage at http://ethanschoonover.com/solarized for screenshots and +details. + +0. Install |solarized-install| +1. Solarized Menu |solarized-menu| +2. Options |solarized-options| +3. Toggle Background |solarized-togglebg| +4. Terminal Issues |solarized-term| + +============================================================================== +0. Install *solarized-install* + +Note: I recommend using Tim Pope's pathogen plugin to install this +colorscheme. See https://github.com/tpope/vim-pathogen . If you've installed +pathogen properly you can install Solarized with the following commands, +followed by the .vimrc configuration below. + + $ cd ~/.vim/bundle + $ git clone https://github.com/altercation/vim-colors-solarized.git + +If you aren't using pathogen, you can use the following three steps to install +Solarized: + +1. Download the solarized distribution (available on the homepage above) + and unarchive the file. + +2. Move `solarized.vim` to your `.vim/colors` directory. + +3. Move each of the files in each subdirectories to the corresponding .vim + subdirectory (e.g. autoload/togglebg.vim goes into your .vim/autoload + directory as .vim/autoload/togglebg.vim). + + +After installation, place the following lines in your .vimrc: + + syntax enable + set background=dark + colorscheme solarized + +or, for the light background mode of Solarized: + + syntax enable + set background=light + colorscheme solarized + +============================================================================== +1. Solarized Menu *solarized-menu* + +Solarized makes available a menu when used in Vim GUI mode (gvim, macvim). +This menu includes many of the options detailed below so that you can test out +different values quickly without modifying your .vimrc file. If you wish to +turn off this menu permanently, simply place the following line in your .vimrc +above the "colorscheme solarized" line. + + let g:solarized_menu=0 + +============================================================================== +2. Toggle Background *solarized-togglebg* + *toggle-bg* *togglebg* + *toggle-background* + +Solarized comes with Toggle Background, a simple plugin to switch between +light and dark background modes and reset the colorscheme. This is most useful +for colorschemes that support both light and dark modes and in terminals or +gui vim windows where the background will be properly set. + +Toggle Background can be accessed by: + + * the Solarized menu (in Vim gui mode) + * the Window menu (in Vim gui mode, even if the Solarized menu is off) + * the "yin/yang" toolbar button (in Vim gui mode) + * the default mapping of + * custom key mapping you set in your .vimrc (see below) + * command line via ":ToggleBG" (no quotes) + +Toggle Background starts with a default mapping to function key . If you +are already using this in a mapping, Toggle Background will not map itself to +a default and you will have to map it manually in your .vimrc file, or +remove/change your existing mapping to another value. To customize the +keyboard mapping in your .vimrc file, use the following line, changing the +"" value to the key or key combination you wish to use: + + call togglebg#map("") + +Note that you'll want to use a single function key or equivalent if you want +the plugin to work in all modes (normal, insert, visual). + +When using the plugin during normal, visual, or insert mode, there should be +no interruption in workflow. However, if you activate the plugin during +REPLACE mode, you will switch to standard insert mode (you will leave the +overwrite replace mode). + +============================================================================== +3. Solarized Terminal Issues *solarized-term* + +If you are going to use Solarized in Terminal mode (i.e. not in a GUI version +like gvim or macvim), **please please please** consider setting your terminal +emulator's colorscheme to used the Solarized palette. I've included palettes +for some popular terminal emulator as well as Xdefaults in the official +Solarized download available from the Solarized homepage listed at the top of +this help document. If you use Solarized *without* these colors, Solarized +will need to be told to degrade its colorscheme to a set compatible with the +limited 256 terminal palette (whereas by using the terminal's 16 ansi color +values, you can set the correct, specific values for the Solarized palette). + +If you do use the custom terminal colors, solarized.vim should work out of +the box for you. If you are using a terminal emulator that supports 256 +colors and don't want to use the custom Solarized terminal colors, you will +need to use the degraded 256 colorscheme. To do so, simply add the following +line *before* the `colorschem solarized` line: + + let g:solarized_termcolors=256 + +Again, I recommend just changing your terminal colors to Solarized values +either manually or via one of the many terminal schemes available for import. + +============================================================================== +4. Solarized Options *solarized-options* + + +AUTOGENERATE OPTIONS + +You can easily modify and experiment with Solarized display options using the +Solarized menu when using Vim in gui mode. Once you have things set to your +liking, you can autogenerate the current option list in a format ready for +insertion into your .vimrc file using the Solarized menu "Autogenerate +Options" command or at the command line with: + + :SolarizedOptions + + +OPTION LIST + +Set these in your vimrc file prior to calling the colorscheme. + +option name default optional +------------------------------------------------ +g:solarized_termcolors= 16 | 256 +g:solarized_termtrans = 0 | 1 +g:solarized_degrade = 0 | 1 +g:solarized_bold = 1 | 0 +g:solarized_underline = 1 | 0 +g:solarized_italic = 1 | 0 +g:solarized_contrast = "normal"| "high" or "low" +g:solarized_visibility= "normal"| "high" or "low" +g:solarized_hitrail = 0 | 1 +g:solarized_menu = 1 | 0 +------------------------------------------------ + + +OPTION DETAILS + +------------------------------------------------ +g:solarized_termcolors= 256 | 16 *'solarized_termcolors'* +------------------------------------------------ +The most important option if you are using vim in terminal (non gui) mode! +This tells Solarized to use the 256 degraded color mode if running in a 256 +color capable terminal. Otherwise, if set to `16` it will use the terminal +emulators colorscheme (best option as long as you've set the emulators colors +to the Solarized palette). + +If you are going to use Solarized in Terminal mode (i.e. not in a GUI +version like gvim or macvim), **please please please** consider setting your +terminal emulator's colorscheme to used the Solarized palette. I've included +palettes for some popular terminal emulator as well as Xdefaults in the +official Solarized download available from: +http://ethanschoonover.com/solarized . If you use Solarized without these +colors, Solarized will by default use an approximate set of 256 colors. It +isn't bad looking and has been extensively tweaked, but it's still not quite +the real thing. + +------------------------------------------------ +g:solarized_termtrans = 0 | 1 *'solarized_termtrans'* +------------------------------------------------ +If you use a terminal emulator with a transparent background and Solarized +isn't displaying the background color transparently, set this to 1 and +Solarized will use the default (transparent) background of the terminal +emulator. *urxvt* required this in my testing; iTerm2 did not. + +Note that on Mac OS X Terminal.app, solarized_termtrans is set to 1 by +default as this is almost always the best option. The only exception to this +is if the working terminfo file supports 256 colors (xterm-256color). + +------------------------------------------------ +g:solarized_degrade = 0 | 1 *'solarized_degrade'* +------------------------------------------------ +For test purposes only; forces Solarized to use the 256 degraded color mode +to test the approximate color values for accuracy. + +------------------------------------------------ +g:solarized_bold = 1 | 0 *'solarized_bold'* +------------------------------------------------ +------------------------------------------------ +g:solarized_underline = 1 | 0 *'solarized_underline'* +------------------------------------------------ +------------------------------------------------ +g:solarized_italic = 1 | 0 *'solarized_italic'* +------------------------------------------------ +If you wish to stop Solarized from displaying bold, underlined or +italicized typefaces, simply assign a zero value to the appropriate +variable, for example: `let g:solarized_italic=0` + +------------------------------------------------ +g:solarized_contrast = "normal"| "high" or "low" *'solarized_contrast'* +------------------------------------------------ +Stick with normal! It's been carefully tested. Setting this option to high +or low does use the same Solarized palette but simply shifts some values up +or down in order to expand or compress the tonal range displayed. + +------------------------------------------------ +g:solarized_visibility = "normal"| "high" or "low" *'solarized_visibility'* +------------------------------------------------ +Special characters such as trailing whitespace, tabs, newlines, when +displayed using ":set list" can be set to one of three levels depending on +your needs. + +------------------------------------------------ +g:solarized_hitrail = 0 | 1 *'solarized_hitrail'* +------------------------------------------------ +Visibility can make listchar entities more visible, but if one has set +cursorline on, these same listchar values standout somewhat less due to the +background color of the cursorline. g:solarized_hitrail enables highlighting +of trailing spaces (only one of the listchar types, but a particularly +important one) while in the cursoline in a different manner in order to make +them more visible. This may not work consistently as Solarized is using +a pattern match than can be overridden by a more encompassing syntax-native +match such as a comment line. + + +------------------------------------------------ +g:solarized_menu = 1 | 0 *'solarized_menu'* +------------------------------------------------ +Solarized includes a menu providing access to several of the above +display related options, including contrast and visibility. This allows +for an easy method of testing different values quickly before settling +on a final assignment for your .vimrc. If you wish to turn off this menu, +assign g:solarized_menu a value of 0. + + + vim:tw=78:noet:ts=8:ft=help:norl: diff --git a/vim/doc/tags b/vim/doc/tags new file mode 100644 index 0000000..22e5d5c --- /dev/null +++ b/vim/doc/tags @@ -0,0 +1,51 @@ +'solarized_bold' solarized.txt /*'solarized_bold'* +'solarized_contrast' solarized.txt /*'solarized_contrast'* +'solarized_degrade' solarized.txt /*'solarized_degrade'* +'solarized_hitrail' solarized.txt /*'solarized_hitrail'* +'solarized_italic' solarized.txt /*'solarized_italic'* +'solarized_menu' solarized.txt /*'solarized_menu'* +'solarized_termcolors' solarized.txt /*'solarized_termcolors'* +'solarized_termtrans' solarized.txt /*'solarized_termtrans'* +'solarized_underline' solarized.txt /*'solarized_underline'* +'solarized_visibility' solarized.txt /*'solarized_visibility'* +before solarized.txt /*before* +java-parser-history javacomplete.txt /*java-parser-history* +javacomplete-ast javacomplete.txt /*javacomplete-ast* +javacomplete-commands javacomplete.txt /*javacomplete-commands* +javacomplete-compiler javacomplete.txt /*javacomplete-compiler* +javacomplete-constants javacomplete.txt /*javacomplete-constants* +javacomplete-download javacomplete.txt /*javacomplete-download* +javacomplete-faq javacomplete.txt /*javacomplete-faq* +javacomplete-features javacomplete.txt /*javacomplete-features* +javacomplete-functions javacomplete.txt /*javacomplete-functions* +javacomplete-history javacomplete.txt /*javacomplete-history* +javacomplete-install javacomplete.txt /*javacomplete-install* +javacomplete-kindletter javacomplete.txt /*javacomplete-kindletter* +javacomplete-launcher javacomplete.txt /*javacomplete-launcher* +javacomplete-limitations javacomplete.txt /*javacomplete-limitations* +javacomplete-options javacomplete.txt /*javacomplete-options* +javacomplete-overview javacomplete.txt /*javacomplete-overview* +javacomplete-parser javacomplete.txt /*javacomplete-parser* +javacomplete-reflection javacomplete.txt /*javacomplete-reflection* +javacomplete-requirements javacomplete.txt /*javacomplete-requirements* +javacomplete-sample javacomplete.txt /*javacomplete-sample* +javacomplete-thanks javacomplete.txt /*javacomplete-thanks* +javacomplete-todo javacomplete.txt /*javacomplete-todo* +javacomplete-usage javacomplete.txt /*javacomplete-usage* +javacomplete.txt javacomplete.txt /*javacomplete.txt* +solarized solarized.txt /*solarized* +solarized-colors solarized.txt /*solarized-colors* +solarized-colorscheme solarized.txt /*solarized-colorscheme* +solarized-help solarized.txt /*solarized-help* +solarized-install solarized.txt /*solarized-install* +solarized-menu solarized.txt /*solarized-menu* +solarized-options solarized.txt /*solarized-options* +solarized-term solarized.txt /*solarized-term* +solarized-togglebg solarized.txt /*solarized-togglebg* +solarized.vim solarized.txt /*solarized.vim* +toggle-background solarized.txt /*toggle-background* +toggle-bg solarized.txt /*toggle-bg* +togglebg solarized.txt /*togglebg* +urxvt solarized.txt /*urxvt* +vim-colors-solarized solarized.txt /*vim-colors-solarized* +without solarized.txt /*without* From b0f77bd2e5a64ce9e6bf2935cc8a2c78241c4fa2 Mon Sep 17 00:00:00 2001 From: lhark Date: Sat, 8 Aug 2015 22:50:09 +0200 Subject: [PATCH 3/3] Cleaned subrepos --- vim/bundle/AutoComplPop | 2 +- vim/bundle/snipmate.vim | 2 +- vim/bundle/vim-startify | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/vim/bundle/AutoComplPop b/vim/bundle/AutoComplPop index 0b075cf..84ec18d 160000 --- a/vim/bundle/AutoComplPop +++ b/vim/bundle/AutoComplPop @@ -1 +1 @@ -Subproject commit 0b075cf5e6ef05584ef6377e116e8d0fe1f8f788 +Subproject commit 84ec18dfc503bebd863ddce108ee4503c334b615 diff --git a/vim/bundle/snipmate.vim b/vim/bundle/snipmate.vim index f5a75d0..bb095e8 160000 --- a/vim/bundle/snipmate.vim +++ b/vim/bundle/snipmate.vim @@ -1 +1 @@ -Subproject commit f5a75d075d3c005ebe69e3f5e56cf99516e8aa3b +Subproject commit bb095e897118ff7a86b60f6ee7df254fa2d39f18 diff --git a/vim/bundle/vim-startify b/vim/bundle/vim-startify index a58f92e..64a40a9 160000 --- a/vim/bundle/vim-startify +++ b/vim/bundle/vim-startify @@ -1 +1 @@ -Subproject commit a58f92e78135d38aaf84b8d6392245418658641a +Subproject commit 64a40a916df0e3cd76fa11cc72a8bfaee4ef25a2