diff --git a/autoload/SpaceVim/layers/lang/java.vim b/autoload/SpaceVim/layers/lang/java.vim index 338b54784..fa3c1d034 100644 --- a/autoload/SpaceVim/layers/lang/java.vim +++ b/autoload/SpaceVim/layers/lang/java.vim @@ -132,7 +132,7 @@ let s:java_interpreter = 'java' function! SpaceVim#layers#lang#java#plugins() abort let plugins = [ - \ ['artur-shaik/vim-javacomplete2', { 'on_ft' : ['java','jsp'], 'loadconf' : 1}], + \ [g:_spacevim_root_dir . 'bundle/vim-javacomplete2', { 'on_ft' : ['java','jsp'], 'loadconf' : 1}], \ ] call add(plugins, [g:_spacevim_root_dir . 'bundle/JavaUnit.vim', {'on_ft' : 'java'}]) call add(plugins, [g:_spacevim_root_dir . 'bundle/java_getset.vim', {'on_ft' : 'java'}]) diff --git a/bundle/README.md b/bundle/README.md index 1f05f6906..41841d94e 100644 --- a/bundle/README.md +++ b/bundle/README.md @@ -13,6 +13,7 @@ In `bundle/` directory, there are two kinds of plugins: forked plugins without c - [`lang#go` layer](#langgo-layer) - [`tmux` layer](#tmux-layer) - [`incsearch` layer](#incsearch-layer) + - [`lang#java` layer](#langjava-layer) @@ -86,3 +87,7 @@ This plugins are changed based on a specific version of origin plugin. - [vim-asterisk@77e9706](https://github.com/haya14busa/vim-asterisk/tree/77e97061d6691637a034258cc415d98670698459) - [vim-over@878f83b](https://github.com/osyo-manga/vim-over/tree/878f83bdac0cda308f599d319f45c7877d5274a9) - [incsearch-easymotion.vim@fcdd3ae](https://github.com/haya14busa/incsearch-easymotion.vim/tree/fcdd3aee6f4c0eef1a515727199ece8d6c6041b5) + +#### `lang#java` layer + +- `vim-javacomplete2` based on `https://github.com/artur-shaik/vim-javacomplete2/tree/a716e32bbe36daaed6ebc9aae76525aad9536245` diff --git a/bundle/vim-javacomplete2/.bundle/config b/bundle/vim-javacomplete2/.bundle/config new file mode 100644 index 000000000..24072c5d7 --- /dev/null +++ b/bundle/vim-javacomplete2/.bundle/config @@ -0,0 +1,3 @@ +--- +BUNDLE_PATH: ".vim-flavor" +BUNDLE_DISABLE_SHARED_GEMS: "true" diff --git a/bundle/vim-javacomplete2/.github/ISSUE_TEMPLATE.md b/bundle/vim-javacomplete2/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..7373df505 --- /dev/null +++ b/bundle/vim-javacomplete2/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,28 @@ +## Actual behavior (Required!) + + +## Expected behavior (Required!) + + +## The steps to reproduce actual behavior (Required!) + 1. ... + 2. ... + 3. ... + + +## Environment (Required!) + * OS: + * Vim version: + * Neovim version: + + +## Q&A +* Yes, I tried minimal .vimrc configuraion. +* Yes, I have enabled logs with `JCdebugEnableLogs` and can put here content of `JCdebugGetLogContent` command, if you need. +* Even, if you wish, I can set `g:JavaComplete_JavaviLogLevel` to `'debug'`, then set `g:JavaComplete_JavaviLogDirectory`, and put here server logs, too. + + +## Screenshot (Optional) + + +## The output of :redir and :message (Optional) diff --git a/bundle/vim-javacomplete2/.github/workflows/vint.yml b/bundle/vim-javacomplete2/.github/workflows/vint.yml new file mode 100644 index 000000000..f3d0fb1b4 --- /dev/null +++ b/bundle/vim-javacomplete2/.github/workflows/vint.yml @@ -0,0 +1,15 @@ +name: Vint +on: [pull_request] +jobs: + vint: + strategy: + fail-fast: false + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@master + - name: Run vint with reviewdog + uses: reviewdog/action-vint@v1.0.1 + with: + github_token: ${{ secrets.github_token }} + reporter: github-pr-review diff --git a/bundle/vim-javacomplete2/.gitignore b/bundle/vim-javacomplete2/.gitignore new file mode 100644 index 000000000..ad98efdf0 --- /dev/null +++ b/bundle/vim-javacomplete2/.gitignore @@ -0,0 +1,26 @@ +*.class +*.py[cod] + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +*~ +.*.sw? +libs/javavi/target/ + +*.lock +.vim-flavor + +.vimrc + +# ignore vim's help tags +doc/tags +tags diff --git a/bundle/vim-javacomplete2/Gemfile b/bundle/vim-javacomplete2/Gemfile new file mode 100644 index 000000000..165b8f0f6 --- /dev/null +++ b/bundle/vim-javacomplete2/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' + +gem 'vim-flavor', '~> 2.2.1' diff --git a/bundle/vim-javacomplete2/LICENSE b/bundle/vim-javacomplete2/LICENSE new file mode 100644 index 000000000..dd685292b --- /dev/null +++ b/bundle/vim-javacomplete2/LICENSE @@ -0,0 +1,78 @@ +VIM LICENSE + +I) There are no restrictions on distributing unmodified copies of Vim except + that they must include this license text. You can also distribute + unmodified parts of Vim, likewise unrestricted except that they must + include this license text. You are also allowed to include executables + that you made from the unmodified Vim sources, plus your own usage + examples and Vim scripts. + +II) It is allowed to distribute a modified (or extended) version of Vim, + including executables and/or source code, when the following four + conditions are met: + 1) This license text must be included unmodified. + 2) The modified Vim must be distributed in one of the following five ways: + a) If you make changes to Vim yourself, you must clearly describe in + the distribution how to contact you. When the maintainer asks you + (in any way) for a copy of the modified Vim you distributed, you + must make your changes, including source code, available to the + maintainer without fee. The maintainer reserves the right to + include your changes in the official version of Vim. What the + maintainer will do with your changes and under what license they + will be distributed is negotiable. If there has been no negotiation + then this license, or a later version, also applies to your changes. + The current maintainer is Bram Moolenaar . If this + changes it will be announced in appropriate places (most likely + vim.sf.net, www.vim.org and/or comp.editors). When it is completely + impossible to contact the maintainer, the obligation to send him + your changes ceases. Once the maintainer has confirmed that he has + received your changes they will not have to be sent again. + b) If you have received a modified Vim that was distributed as + mentioned under a) you are allowed to further distribute it + unmodified, as mentioned at I). If you make additional changes the + text under a) applies to those changes. + c) Provide all the changes, including source code, with every copy of + the modified Vim you distribute. This may be done in the form of a + context diff. You can choose what license to use for new code you + add. The changes and their license must not restrict others from + making their own changes to the official version of Vim. + d) When you have a modified Vim which includes changes as mentioned + under c), you can distribute it without the source code for the + changes if the following three conditions are met: + - The license that applies to the changes permits you to distribute + the changes to the Vim maintainer without fee or restriction, and + permits the Vim maintainer to include the changes in the official + version of Vim without fee or restriction. + - You keep the changes for at least three years after last + distributing the corresponding modified Vim. When the maintainer + or someone who you distributed the modified Vim to asks you (in + any way) for the changes within this period, you must make them + available to him. + - You clearly describe in the distribution how to contact you. This + contact information must remain valid for at least three years + after last distributing the corresponding modified Vim, or as long + as possible. + e) When the GNU General Public License (GPL) applies to the changes, + you can distribute the modified Vim under the GNU GPL version 2 or + any later version. + 3) A message must be added, at least in the output of the ":version" + command and in the intro screen, such that the user of the modified Vim + is able to see that it was modified. When distributing as mentioned + under 2)e) adding the message is only required for as far as this does + not conflict with the license used for the changes. + 4) The contact information as required under 2)a) and 2)d) must not be + removed or changed, except that the person himself can make + corrections. + +III) If you distribute a modified version of Vim, you are encouraged to use + the Vim license for your changes and make them available to the + maintainer, including the source code. The preferred way to do this is + by e-mail or by uploading the files to a server and e-mailing the URL. + If the number of changes is small (e.g., a modified Makefile) e-mailing a + context diff will do. The e-mail address to be used is + + +IV) It is not allowed to remove this license from the distribution of the Vim + sources, parts of it or from a modified version. You may use this + license for previous Vim releases instead of the license that they came + with, at your option. diff --git a/bundle/vim-javacomplete2/README.md b/bundle/vim-javacomplete2/README.md new file mode 100644 index 000000000..f8dc5e030 --- /dev/null +++ b/bundle/vim-javacomplete2/README.md @@ -0,0 +1,318 @@ +# DEPRECATED in favor of [jc.nvim](https://github.com/artur-shaik/jc.nvim) + +# vim-javacomplete2 + +Updated version of the original [javacomplete plugin](http://www.vim.org/scripts/script.php?script_id=1785) for vim. + +## Demo + +![vim-javacomplete2](https://github.com/artur-shaik/vim-javacomplete2/raw/master/doc/demo.gif) + +Generics demo + +![vim-javacomplete2](https://github.com/artur-shaik/vim-javacomplete2/raw/master/doc/generics_demo.gif) + +## Intro + +This is vim-javacomplete2, an omni-completion plugin for [Java](http://www.oracle.com/technetwork/java/javase/downloads/index.html) requiring vim 7. + +It includes javacomplete.vim, java_parser.vim, javavi (reflecton and source parsing library), javavibridge.py, and the [javaparser](https://github.com/javaparser/javaparser) library. + +I have kept java_parser.vim for local (live) continuous parsing, because the javaparser library can't parse unfinished files. + +For now the main difference from the original plugin is the existence of a server-like java library, that allows communication over sockets. This speeds up reflection and parsing. + +One more issue I had with the original javacomplete plugin is losing my classpath and as a result, completion not working. +So now the javacomplete2 plugin detects the JRE library path, thus bringing standard java completion out of the box - no configuration required! +The plugin will scan child directory tree for `src` directory and add it to the sources path (For this, it is nice to have [vim-rooter](https://github.com/airblade/vim-rooter.git) plugin). + +For the first run the plugin will compile the Javavi library. + +## Features + +Features: +- Server side java reflection class loader and parsing library; +- Searches class files automatically, using `maven`, `gradle` or Eclipse's `.classpath` file to append completion classpath; +- Generics; +- Lambdas; +- Annotations completion; +- Nested classes; +- Adding imports automatically, includes `static` imports and imports of nested classes; +- Complete methods declaration after '@Override'; +- Jsp support, without taglibs; +- Cross-session cache; +- Auto insert methods that need to be implemented; +- `toString`, `equals`, `hashCode`, Constructors, Accessors generation; +- Class creation. + +Features (originally existed): +- 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. + +Features borrowed and ported to vimscript from vim-javacompleteex: +- Complete class name; +- Add import statement for a given class name. + +## Requirements + +- Vim version 7.4 or above with python support; +- JDK8. + +## Installation + +### pathogen +Run: + +````Shell +cd ~/.vim/bundle +git clone https://github.com/artur-shaik/vim-javacomplete2.git +```` + +### Vundle +Add to `.vimrc`: + +````vimL +Plugin 'artur-shaik/vim-javacomplete2' +```` + +### NeoBundle +Add to `.vimrc`: + +````vimL +NeoBundle 'artur-shaik/vim-javacomplete2' +```` + +### vim-plug +Add to `.vimrc`: +````vimL +Plug 'artur-shaik/vim-javacomplete2' +```` + +## Configuration + +### Required + +Add this to your `.vimrc` file: + +`autocmd FileType java setlocal omnifunc=javacomplete#Complete` + +To enable smart (trying to guess import option) inserting class imports with F4, add: + +`nmap (JavaComplete-Imports-AddSmart)` + +`imap (JavaComplete-Imports-AddSmart)` + +To enable usual (will ask for import option) inserting class imports with F5, add: + +`nmap (JavaComplete-Imports-Add)` + +`imap (JavaComplete-Imports-Add)` + +To add all missing imports with F6: + +`nmap (JavaComplete-Imports-AddMissing)` + +`imap (JavaComplete-Imports-AddMissing)` + +To remove all unused imports with F7: + +`nmap (JavaComplete-Imports-RemoveUnused)` + +`imap (JavaComplete-Imports-RemoveUnused)` + +Default mappings: + +``` + nmap jI (JavaComplete-Imports-AddMissing) + nmap jR (JavaComplete-Imports-RemoveUnused) + nmap ji (JavaComplete-Imports-AddSmart) + nmap jii (JavaComplete-Imports-Add) + + imap I (JavaComplete-Imports-AddMissing) + imap R (JavaComplete-Imports-RemoveUnused) + imap i (JavaComplete-Imports-AddSmart) + imap ii (JavaComplete-Imports-Add) + + nmap jM (JavaComplete-Generate-AbstractMethods) + + imap jM (JavaComplete-Generate-AbstractMethods) + + nmap jA (JavaComplete-Generate-Accessors) + nmap js (JavaComplete-Generate-AccessorSetter) + nmap jg (JavaComplete-Generate-AccessorGetter) + nmap ja (JavaComplete-Generate-AccessorSetterGetter) + nmap jts (JavaComplete-Generate-ToString) + nmap jeq (JavaComplete-Generate-EqualsAndHashCode) + nmap jc (JavaComplete-Generate-Constructor) + nmap jcc (JavaComplete-Generate-DefaultConstructor) + + imap s (JavaComplete-Generate-AccessorSetter) + imap g (JavaComplete-Generate-AccessorGetter) + imap a (JavaComplete-Generate-AccessorSetterGetter) + + vmap js (JavaComplete-Generate-AccessorSetter) + vmap jg (JavaComplete-Generate-AccessorGetter) + vmap ja (JavaComplete-Generate-AccessorSetterGetter) + + nmap jn (JavaComplete-Generate-NewClass) + nmap jN (JavaComplete-Generate-ClassInFile) +``` + +The default mappings could be disabled with following setting: + +```vim +let g:JavaComplete_EnableDefaultMappings = 0 +``` + +### Optional + +`g:JavaComplete_LibsPath` - path to additional jar files. This path appends with your libraries specified in `pom.xml`. Here you can add, for example, your glassfish libs directory or your project libs. It will be automatically append your JRE home path. + +`g:JavaComplete_SourcesPath` - path of additional sources. Don't try to add all sources you have, this will slow down the parsing process. Instead, add your project sources and necessary library sources. If you have compiled classes add them to the previous config (`g:JavaComplete_LibsPath`) instead. By default the plugin will search the `src` directory and add it automatically. + +`let g:JavaComplete_MavenRepositoryDisable = 1` - don't append classpath with libraries specified in `pom.xml` of your project. By default is `0`. + +`let g:JavaComplete_UseFQN = 1` - use full qualified name in completions description. By default is `0`. + +`let g:JavaComplete_PomPath = /path/to/pom.xml` - set path to `pom.xml` explicitly. It will be set automatically, if `pom.xml` is in underlying path. + +`let g:JavaComplete_ClosingBrace = 1` - add close brace automatically, when complete method declaration. Disable if it conflicts with another plugins. + +`let g:JavaComplete_JavaviLogDirectory = ''` - directory, where to write server logs. + +`let g:JavaComplete_JavaviLogLevel = 'debug'` - enables server side logging (log4j logging levels). + +`let g:JavaComplete_BaseDir = '~/.your_cache_dir'` - set the base cache directory of javacomplete2. By default it is `~/.cache`. + +`let g:JavaComplete_ImportDefault = 0` - the default selection of import options. By default it is 0, which means automatically select first one. To make nothing on default set `-1`. + +`let g:JavaComplete_GradleExecutable = 'gradle'` - use your own path to gradle executable file. + +`let g:JavaComplete_ImportSortType = 'jarName'` - imports sorting type. Sorting can be by jar archives `jarName` or by package names `packageName`. + +`let g:JavaComplete_StaticImportsAtTop = 1` - imports sorting with static imports at the top. By default this is `0`. + +`let g:JavaComplete_ImportOrder = ['java.', 'javax.', 'com.', 'org.', 'net.']` - Specifies the order of import groups, when use `packageName` sorting type. An import group is a list of individual import statements that all start with the same beginning of package name surrounded by blank lines above and below the group. A `*` indicates all packages not specified, for 'google style' import ordering, e.g. `let g:JavaComplete_ImportOrder = ['com.google.', *, 'java.', 'javax.']` + +`let g:JavaComplete_RegularClasses = ['java.lang.String', 'java.lang.Object']` - Regular class names that will be used automatically when you insert import. You can populate it with your custom classes, or it will be populated automatically when you choose any import option. List will be persisted, so it will be used next time you run the same project. + +`let g:JavaComplete_CustomTemplateDirectory = '~/jc_templates'` - set directory that contains custom templates, for class creation. By default this options is null. + +`let g:JavaComplete_AutoStartServer = 0` - Disable automatic startup of server. + +`let g:JavaComplete_CompletionResultSort = 1` - Sort completion results alphabetically. + +`let g:JavaComplete_IgnoreErrorMsg = 1` - When it is greater than 0, the error message will be ignored. By default it is 0. + +`let g:JavaComplete_CheckServerVersionAtStartup = 0` - Check server version on startup. Can be disabled on slow start, or infinite recompilation. By default it is 1. + +`let g:JavaComplete_ExcludeClassRegex = 'lombok\(\.experimental\)\?\.var'` - Exclude matching fully qualified class names from producing import statements. + +`let g:JavaComplete_SourceExclude = ['src/frontend']` - Exclude source directories. Accept absolute and relative values. + +## Commands + +`JCimportsAddMissing` - add all missing 'imports'; + +`JCimportsRemoveUnused` - remove all unsused 'imports'; + +`JCimportAdd` - add 'import' for classname that is under cursor, or before it; + +`JCimportAddSmart` - add 'import' for classname trying to guess variant without ask user to choose an option (it will ask on false guessing). + + +`JCgenerateAbtractMethods` - generate methods that need to be implemented; + +`JCgenerateAccessors` - generate getters and setters for all fields; + +`JCgenerateAccessorSetter` - generate setter for field under cursor; + +`JCgenerateAccessorGetter` - generate getter for field under cursor; + +`JCgenerateAccessorSetterGetter` - generate getter and setter for field under cursor; + +`JCgenerateToString` - generate `toString` method; + +`JCgenerateEqualsAndHashCode` - generate `equals` and `hashCode` methods; + +`JCgenerateConstructor` - generate constructor with chosen fields; + +`JCgenerateConstructorDefault` - generate default constructor; + + +`JCclassNew` - open prompt to enter class creation command; + +`JCclassInFile` - open prompt to choose template that will be used for creation class boilerplate in current empty file; + + +`JCserverShowPort` - show port, through which vim plugin communicates with server; + +`JCserverShowPID` - show server process identificator; + +`JCserverStart` - start server manually; + +`JCserverTerminate` - stop server manually; + +`JCserverCompile` - compile server manually; + + +`JCdebugEnableLogs` - enable logs; + +`JCdebugDisableLogs` - disable logs; + +`JCdebugGetLogContent` - get debug logs; + +`JCcacheClear` - clear cache manually. + +## Class creation + +Prompt scheme, for class creation: + + template:[subdirectory]:/package.ClassName extends SuperClass implements Interface(String str, public Integer i):contructor(*):toString(1) + +A: (optional) template - which will be used to create class boilerplate. Some existed templates: junit, interface, exception, servlet, etc; + +B: (optional) subdirectory in which class will be put. For example: test, androidTest; + +C: class name and package. With `/` will use backsearch for parent package to put in it. Without `/` put in relative package to current; + +D: (optional) extends and implements classes will be automatically imported; + +E: (optional) private str variable, and public i variable will be added to class; + +F: (optional) contructor using all fields and toString override method with only `str` field will be created. Also hashCode and equals can be used. + +There is autocompletion in command prompt that will try to help you. Your current opened file shouldn't have dirty changes or `hidden` should be set. + +## Limitations: + +- First run can be slow; +- The embedded parser works a bit slower than expected. + +## Todo + +- Add javadoc; +- ~~Lambda support~~; +- ~~Cross session cache~~; +- Most used (classes, methods, vars) at first place (smart suggestions); +- FXML support; +- ~~Check for jsp support~~; +- Refactoring support?; +- ~~Class creation helpers~~; +- ~~Generics~~; +- etc... + +## Thanks + +- Cheng Fang author of original javacomplete plugin; +- Zhang Li author of vim-javacompleteex plugin; +- http://github.com/javaparser/javaparser library. +- [vimdoc](https://github.com/google/vimdoc) generate `:h javacomplete` file + +## FeedBack + +Any problems, bugs or suggestions are welcome to send to ashaihullin@gmail.com diff --git a/bundle/vim-javacomplete2/Rakefile b/bundle/vim-javacomplete2/Rakefile new file mode 100644 index 000000000..f41b3a86e --- /dev/null +++ b/bundle/vim-javacomplete2/Rakefile @@ -0,0 +1,11 @@ +#!/usr/bin/env rake + +task :ci => [:dump, :test] + +task :dump do + sh 'vim --version' +end + +task :test do + sh 'bundle exec vim-flavor test' +end diff --git a/bundle/vim-javacomplete2/addon-info.json b/bundle/vim-javacomplete2/addon-info.json new file mode 100644 index 000000000..faeb46c03 --- /dev/null +++ b/bundle/vim-javacomplete2/addon-info.json @@ -0,0 +1,6 @@ +{ + "name": "javacomplete", + "description": "Updated version of the original javacomplete plugin", + "author": "artur shaik" +} + diff --git a/bundle/vim-javacomplete2/autoload/classpath.py b/bundle/vim-javacomplete2/autoload/classpath.py new file mode 100644 index 000000000..02a61e435 --- /dev/null +++ b/bundle/vim-javacomplete2/autoload/classpath.py @@ -0,0 +1,14 @@ +import os +from xml.etree.ElementTree import * + +def ReadClasspathFile(fn): + cp = [] + for a in parse(fn).findall('classpathentry'): + kind = a.get('kind') + if kind == 'src' and 'output' in a.keys(): + cp.append(os.path.abspath(a.get('output'))) + elif kind == 'lib' and 'path' in a.keys(): + cp.append(os.path.abspath(a.get('path'))) + elif kind == 'output' and 'path' in a.keys(): + cp.append(os.path.abspath(a.get('path'))) + return cp diff --git a/bundle/vim-javacomplete2/autoload/cm/sources/java.vim b/bundle/vim-javacomplete2/autoload/cm/sources/java.vim new file mode 100644 index 000000000..136f828e6 --- /dev/null +++ b/bundle/vim-javacomplete2/autoload/cm/sources/java.vim @@ -0,0 +1,11 @@ +func! cm#sources#java#register() + " the omnifunc pattern is PCRE + call cm#register_source({'name' : 'java', + \ 'priority': 9, + \ 'scopes': ['java'], + \ 'abbreviation': 'java', + \ 'cm_refresh_patterns':['\.', '::'], + \ 'cm_refresh': {'omnifunc': 'javacomplete#Complete' }, + \ }) + +endfunc diff --git a/bundle/vim-javacomplete2/autoload/java_parser.vim b/bundle/vim-javacomplete2/autoload/java_parser.vim new file mode 100644 index 000000000..bd03ae464 --- /dev/null +++ b/bundle/vim-javacomplete2/autoload/java_parser.vim @@ -0,0 +1,3599 @@ +" Vim autoload script for a JAVA PARSER and more. +" Language: Java +" Maintainer: artur shaik +" 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', '->': 'RARROW', '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 = '\%(@'. s:RE_IDENTIFIER. '\s*\)\=\(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*\%(@'. s:RE_IDENTIFIER. '\s*\)\=\(final\s*\)\=\('. s:RE_TYPE . '\)\s\+\(' . s:RE_IDENTIFIER . '\)' . s:RE_BRACKETS . '*' + +let s:RE_METHOD_ARGS = s:RE_IDENTIFIER . '\(\s*,\s*' . s:RE_IDENTIFIER . '\)*' + +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 s: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 + +fu! java_parser#SScope() + return s: +endfu + +fu! s:GetInnerText(left) + let line = b:line + let col = b:col + while b:lines[line][col] != a:left + if col == 0 + if line == 0 + return '' + endif + let line -= 1 + let col = len(b:lines[line]) + else + let col -= 1 + endif + endwhile + + let right = s:GetPair(a:left) + let startPos = java_parser#MakePos(line, col) + let endPos = 0 + let br = 0 + while b:lines[line][col] != right || br > 0 + let col += 1 + if col >= len(b:lines[line]) + let col = 0 + let line += 1 + if line >= len(b:lines) + return '' + endif + endif + + if b:lines[line][col] == a:left + let br += 1 + elseif b:lines[line][col] == right + if br > 0 + let br -= 1 + else + let endPos = java_parser#MakePos(line, col) + break + endif + endif + endwhile + let result = "" + while startPos <= endPos + let pos = java_parser#DecodePos(startPos) + let result .= b:lines[pos.line][pos.col] + let startPos += 1 + endwhile + return result +endfu + +fu! s:GetPair(left) + if a:left == '(' + return ')' + elseif a:left == '{' + return '}' + elseif a:left == '<' + return '>' + endif + return a:left +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' + let s = '' + for arg in get(t, 'arguments', []) + if type(arg) == type({}) && has_key(arg, 'tag') + if arg.tag == 'TYPEAPPLY' + let s .= java_parser#type2Str(arg). ',' + elseif arg.tag == 'TYPEARRAY' + let s .= java_parser#type2Str(arg). ',' + elseif has_key(arg, 'name') + let s .= arg.name. ',' + endif + endif + unlet arg + endfor + + if len(s) > 0 + return t.clazz.name . '<'. s[0:-2]. '>' + endif + + return get(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 + +" 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) + if exists('b:idxes') && len(b:idxes) >= a:line + return b:idxes[a:line] + a:col + endif + return 0 +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 : 0 +endfu + +fu! java_parser#GetLogContent() + new + put =s: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() + let log = type(a:key) == type("") ? a:key : string(a:key) + call add(s:log, log) + 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 =~# '^\(RARROW\|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:lambdaDeclaration(lhs) + if has_key(a:lhs, 'args') + let lambdadef = {'tag': 'LAMBDA', 'args': a:lhs.args, 'pos': a:lhs.pos} + else + let lambdadef = {'tag': 'LAMBDA', 'args': a:lhs} + endif + call s:nextToken() + if b:token == 'LBRACE' + let lambdadef.body = s:block() + else + let lambdadef.body = s:statement() + endif + + return lambdadef +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()} + + elseif b:token == 'RARROW' + return s:lambdaDeclaration(a:t) + + 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() + let nextText = s:GetInnerText("(") + if nextText =~ s:RE_FORMAL_PARAM_LIST + let args = s:formalParameters() + return {'tag': 'TYPEAPPLY', 'pos': b:pos, 'clazz': t, 'args': args} + elseif nextText =~ s:RE_METHOD_ARGS + let args = s:arguments() + return {'tag': 'TYPEAPPLY', 'pos': b:pos, 'clazz': t, 'args': args} + elseif s:modeAndEXPR() && b:token == 'LT' + call s:nextToken() + let b:mode = s:EXPR_OR_TYPE_OR_NOPARAMS + let t = s:term3() + + 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' + let idx = 0 + let arg = s:expression() + let arg.idx = idx + call add(args, arg) + while b:token == 'COMMA' + let idx += 1 + call s:nextToken() + let arg = s:expression() + let arg.idx = idx + call add(args, arg) + 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 = s: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 params = s:formalParameters() + 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, 'params': params, 'body': body, 'catchers': catchers, 'finalizer': finalizer} + + elseif b:token == 'RARROW' + let body = s:block() + let params = '' + return {'tag': 'RARROW', 'pos': pos, 'endpos': b:pos, 'params': params, 'body': body} + + 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, 'body': 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 = substitute(subs[1], ' ', '', 'g') + 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 + call s:Trace('[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/bundle/vim-javacomplete2/autoload/javacomplete.vim b/bundle/vim-javacomplete2/autoload/javacomplete.vim new file mode 100644 index 000000000..a752b9e1a --- /dev/null +++ b/bundle/vim-javacomplete2/autoload/javacomplete.vim @@ -0,0 +1,499 @@ +" Vim completion script for java +" Maintainer: artur shaik + + + + +"" +" @section Usage, usage +" You can use `vim-javacomplete2` just like other omni-completion plugin. +" Many samples of input context are gived in the following section. +" +" See |javacomplete-faq| in time if some problem occurs. +" When meeting other problems not described in FAQ, you can contact with +" the author by the following e-mail: ashaihullin@gmail.com +" + +"" +" @section Input contexts, contexts +" @parentsection usage +" 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| )" +" < +" + +"" +" @section Kind letter, kindletter +" @parentsection usage +" 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 +" < + +"" +" @section Class creation, classnew +" @parentsection usage +" Prompt scheme, for class creation: +" > +" template:[subdirectory]:/package.ClassName extends SuperClass implements Interface(String str, public Integer i):contructor(*):toString(1) +" < +" A: (optional) template - which will be used to create class boilerplate. Some existed templates: junit, interface, exception, servlet, etcl +" +" B: (optional) subdirectory in which class will be put. For example: test, androidTest; +" +" C: class name and package. With `/` will use backsearch for parent package to put in it. Without `/` put in relative package to current; +" +" D: (optional) extends and implements classes will be automatically imported; +" +" E: (optional) private str variable, and public i variable will be added to class; +" +" F: (optional) contructor using all fields and toString override method with only 'str' field will be created. Also hashCode and equals can be used. +" +" There is autocompletion in command prompt that will try to help you. Your current opened file shouldn't have dirty changes or 'hidden' should be set. + + +"" +" @section FAQ, faq +" 4.1 Why can not complete in gradle project? +" +" Check if 'gradle' is in your runtime path or './gradlew' (or +" '.\gradlew.bat' for Windows) is in your project's directory. +" +" 4.2 I have installed gradle, but why I can not complete R.class? +" +" In android project, many of the class contains a ton of +" innerclass, javacomplete2 could works better by reflection, so you need to +" compile you project, after use './gradlew build', R.java will be +" automatically generated and compiled. + +"" +" @section Todo, todo +" - Add javadoc +" - Cross session cache; +" - Most used (classes, methods, vars) at first place (smart suggestions); +" - FXML support; +" - JSP check support; +" - Refactoring support?; +" - Class creation helpers; +" - etc... + + +if exists('g:JavaComplete_Autoload') + finish +endif +let g:JavaComplete_Autoload = 1 + +" It doesn't make sense to do any work if vim doesn't support any Python since +" we relly on it to properly work. +if has('python3') + command! -nargs=1 JavacompletePy py3 + command! -nargs=1 JavacompletePyfile py3file +else + echoerr 'Javacomplete needs Python3 support to run!' + finish +endif + +function! s:Log(log) abort + let log = type(a:log) ==# type('') ? a:log : string(a:log) + call javacomplete#logger#Log('[javacomplete] '. a:log) +endfunction + +let g:J_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 g:J_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 g:J_PRIMITIVE_TYPE_INFO = {'tag': 'CLASSDEF', 'name': '!', 'fields': [{'n': 'class','m': '1','t': 'Class'}]} + +let g:J_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 g:J_PRIMITIVE_TYPES = ['boolean', 'byte', 'char', 'int', 'short', 'long', 'float', 'double'] +let g:J_KEYWORDS_MODS = ['public', 'private', 'protected', 'static', 'final', 'synchronized', 'volatile', 'transient', 'native', 'strictfp', 'abstract'] +let g:J_KEYWORDS_TYPE = ['class', 'interface', 'enum'] +let g:J_KEYWORDS = g:J_PRIMITIVE_TYPES + g:J_KEYWORDS_MODS + g:J_KEYWORDS_TYPE + ['super', 'this', 'void', 'var'] + ['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 g:JC_MODIFIER_PUBLIC = 1 +let g:JC_MODIFIER_PROTECTED = 3 +let g:JC_MODIFIER_FINAL = 5 +let g:JC_MODIFIER_NATIVE = 9 +let g:JC_MODIFIER_ABSTRACT = 11 + +let g:RE_BRACKETS = '\%(\s*\[\s*\]\)' +let g:RE_IDENTIFIER = '[a-zA-Z_$][a-zA-Z0-9_$]*' +let g:RE_ANNOTATION = '@[a-zA-Z_][a-zA-Z0-9_$]*' +let g:RE_QUALID = g:RE_IDENTIFIER. '\%(\s*\.\s*' .g:RE_IDENTIFIER. '\)*' + +let g:RE_REFERENCE_TYPE = g:RE_QUALID . g:RE_BRACKETS . '*' +let g:RE_TYPE = g:RE_REFERENCE_TYPE + +let g:RE_TYPE_ARGUMENT = '\%(?\s\+\%(extends\|super\)\s\+\)\=' . g:RE_TYPE +let g:RE_TYPE_ARGUMENT_EXTENDS = '\%(?\s\+\%(extends\|super\)\s\+\)' . g:RE_TYPE +let g:RE_TYPE_ARGUMENTS = '<' . g:RE_TYPE_ARGUMENT . '\%(\s*,\s*' . g:RE_TYPE_ARGUMENT . '\)*>' +let g:RE_TYPE_WITH_ARGUMENTS_I = g:RE_IDENTIFIER . '\s*' . g:RE_TYPE_ARGUMENTS +let g:RE_TYPE_WITH_ARGUMENTS = g:RE_TYPE_WITH_ARGUMENTS_I . '\%(\s*' . g:RE_TYPE_WITH_ARGUMENTS_I . '\)*' + +let g:RE_TYPE_MODS = '\%(public\|protected\|private\|abstract\|static\|final\|strictfp\)' +let g:RE_TYPE_DECL_HEAD = '\(class\|interface\|enum\)[ \t\n\r ]\+' +let g:RE_TYPE_DECL = '\<\C\(\%(' .g:RE_TYPE_MODS. '\s\+\)*\)' .g:RE_TYPE_DECL_HEAD. '\(' .g:RE_IDENTIFIER. '\)[{< \t\n\r ]' + +let g:RE_ARRAY_TYPE = '^\s*\(' .g:RE_QUALID . '\)\(' . g:RE_BRACKETS . '\+\)\s*$' +let g:RE_SELECT_OR_ACCESS = '^\s*\(' . g:RE_IDENTIFIER . '\)\s*\(\[.*\]\)\=\s*$' +let g:RE_ARRAY_ACCESS = '^\s*\(' . g:RE_IDENTIFIER . '\)\s*\(\[.*\]\)\+\s*$' +let g:RE_CASTING = '^\s*(\(' .g:RE_QUALID. '\))\s*\(' . g:RE_IDENTIFIER . '\)\>' + +let g:RE_KEYWORDS = '\<\%(' . join(g:J_KEYWORDS, '\|') . '\)\>' + +let g:JAVA_HOME = $JAVA_HOME + +let g:JavaComplete_Cache = {} " FQN -> member list, e.g. {'java.lang.StringBuffer': classinfo, 'java.util': packageinfo, '/dir/TopLevelClass.java': compilationUnit} +let g:JavaComplete_Files = {} " srouce file path -> properties, e.g. {filekey: {'unit': compilationUnit, 'changedtick': tick, }} + +let g:JavaComplete_ProjectKey = '' + +fu! SScope() abort + return s: +endfu + +function! javacomplete#Disable() abort + let g:JavaComplete_Disabled = 1 +endfunction + +function! javacomplete#Enable() abort + let g:JavaComplete_Disabled = 0 +endfunction + +function! javacomplete#ClearCache() abort + let g:JavaComplete_Cache = {} + let g:JavaComplete_Files = {} + + call javacomplete#util#RemoveFile(javacomplete#util#GetBase('cache'). g:FILE_SEP. 'class_packages_'. g:JavaComplete_ProjectKey. '.dat') + call javacomplete#server#Communicate('-collect-packages', '', 's:ClearCache') +endfunction + +function! javacomplete#Complete(findstart, base) abort + return javacomplete#complete#complete#Complete(a:findstart, a:base, 1) +endfunction + +" key of g:JavaComplete_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. +function! javacomplete#GetCurrentFileKey() abort + return s:GetCurrentFileKey() +endfunction + +function! s:GetCurrentFileKey() abort + return has('autocmd') ? s:curfilekey : empty(expand('%')) ? bufnr('%') : expand('%:p') +endfunction + +function! s:SetCurrentFileKey() abort + let s:curfilekey = empty(expand('%')) ? bufnr('%') : expand('%:p') +endfunction +call s:SetCurrentFileKey() + +function! s:HandleTextChangedI() abort + if get(g:, 'JC_ClassnameCompletedFlag', 0) && g:JavaComplete_InsertImports + let saveCursor = getcurpos() + let line = getline('.') + if empty(javacomplete#util#Trim(line)) + call cursor(line('.') - 1, 500) + let line = getline('.') + let offset = 1 + else + if line[col('.') - 2] !~# '\v(\s|\.|\(|\<)' + return + endif + let offset = 0 + endif + + let g:JC_ClassnameCompletedFlag = 0 + call javacomplete#imports#Add(1) + let saveCursor[1] = line('.') + offset + call setpos('.', saveCursor) + endif + + if get(g:, 'JC_DeclarationCompletedFlag', 0) + let line = getline('.') + if line[col('.') - 2] != ' ' + return + endif + + let g:JC_DeclarationCompletedFlag = 0 + + if line !~# '.*@Override.*' + let line = getline(line('.') - 1) + endif + + if line =~# '.*@Override\s\+\(\S\+\|\)\(\s\+\|\)$' + return + endif + + if !empty(javacomplete#util#Trim(getline('.'))) + call feedkeys("\b\r", 'n') + endif + if g:JavaComplete_ClosingBrace + call feedkeys("}\eO", 'n') + endif + endif +endfunction + +function! s:HandleInsertLeave() abort + if get(g:, 'JC_DeclarationCompletedFlag', 0) + let g:JC_DeclarationCompletedFlag = 0 + endif + if get(g:, 'JC_ClassnameCompletedFlag', 0) + let g:JC_ClassnameCompletedFlag = 0 + endif +endfunction + +function! javacomplete#UseFQN() abort + return g:JavaComplete_UseFQN +endfunction + +function! s:RemoveCurrentFromCache() abort + let package = javacomplete#complete#complete#GetPackageName() + let classname = split(expand('%:t'), '\.')[0] + let fqn = package. '.'. classname + if has_key(g:JavaComplete_Cache, fqn) + call remove(g:JavaComplete_Cache, fqn) + endif + call javacomplete#server#Communicate('-clear-from-cache', fqn, 's:RemoveCurrentFromCache') + call javacomplete#server#Communicate('-async -recompile-class', fqn, 's:RemoveCurrentFromCache') + + let arguments = '-source '. resolve(expand('%:p')) + let arguments .= ' -class '. classname + let arguments .= ' -package '. package + call javacomplete#server#Communicate('-async -add-source-to-cache', arguments, 's:RemoveCurrentFromCache') +endfunction + +function! s:DefaultMappings() abort + if g:JavaComplete_EnableDefaultMappings + return + endif + + nmap jI (JavaComplete-Imports-AddMissing) + nmap jR (JavaComplete-Imports-RemoveUnused) + nmap ji (JavaComplete-Imports-AddSmart) + nmap jii (JavaComplete-Imports-Add) + nmap jis (JavaComplete-Imports-SortImports) + + imap I (JavaComplete-Imports-AddMissing) + imap R (JavaComplete-Imports-RemoveUnused) + imap i (JavaComplete-Imports-AddSmart) + imap ii (JavaComplete-Imports-Add) + + nmap jM (JavaComplete-Generate-AbstractMethods) + + imap jM (JavaComplete-Generate-AbstractMethods) + + nmap jA (JavaComplete-Generate-Accessors) + nmap js (JavaComplete-Generate-AccessorSetter) + nmap jg (JavaComplete-Generate-AccessorGetter) + nmap ja (JavaComplete-Generate-AccessorSetterGetter) + nmap jts (JavaComplete-Generate-ToString) + nmap jeq (JavaComplete-Generate-EqualsAndHashCode) + nmap jc (JavaComplete-Generate-Constructor) + nmap jcc (JavaComplete-Generate-DefaultConstructor) + + imap s (JavaComplete-Generate-AccessorSetter) + imap g (JavaComplete-Generate-AccessorGetter) + imap a (JavaComplete-Generate-AccessorSetterGetter) + + vmap js (JavaComplete-Generate-AccessorSetter) + vmap jg (JavaComplete-Generate-AccessorGetter) + vmap ja (JavaComplete-Generate-AccessorSetterGetter) + + nmap jn (JavaComplete-Generate-NewClass) + nmap jN (JavaComplete-Generate-ClassInFile) +endfunction + +augroup javacomplete + autocmd! + autocmd BufEnter *.java,*.jsp call s:SetCurrentFileKey() + autocmd BufEnter *.java call s:DefaultMappings() + autocmd BufWritePost *.java call s:RemoveCurrentFromCache() + autocmd VimLeave * call javacomplete#server#Terminate() + + if v:version > 704 || v:version == 704 && has('patch143') + autocmd TextChangedI *.java,*.jsp call s:HandleTextChangedI() + else + echohl WarningMsg + echomsg 'JavaComplete2 : TextChangedI feature needs vim version >= 7.4.143' + echohl None + endif + autocmd InsertLeave *.java,*.jsp call s:HandleInsertLeave() +augroup END + +let g:JavaComplete_Home = fnamemodify(expand(''), ':p:h:h:gs?\\?'. g:FILE_SEP. '?') +let g:JavaComplete_JavaParserJar = fnamemodify(g:JavaComplete_Home. join(['', 'libs', 'javaparser-core-3.5.20.jar'], g:FILE_SEP), ':p') + +call s:Log('JavaComplete_Home: '. g:JavaComplete_Home) +"" +" path of your sources. Don't try to +" add all sources you have, this will slow down parsing process. +" Add you project sources and necessery library sources. If you +" have compiled classes add them to previous config instead. By +" default plugin will search `src` directory and add it +" automatically. +let g:JavaComplete_SourcesPath = get(g:, 'JavaComplete_SourcesPath', ''). g:PATH_SEP + \. join(filter(javacomplete#util#GlobPathList(getcwd(), 'src', 0, 3), "match(v:val, '.*build.*') < 0"), g:PATH_SEP) + +"" +" disable the maven repository. +" > +" let g:JavaComplete_MavenRepositoryDisabled = 1 +" < +" by default this option is disabled (0). +let g:JavaComplete_MavenRepositoryDisabled = 0 + +if filereadable(getcwd(). g:FILE_SEP. 'build.gradle') + let g:JavaComplete_SourcesPath = g:JavaComplete_SourcesPath + \. g:PATH_SEP + \. join(javacomplete#util#GlobPathList(getcwd() + \, join(['**', 'build', 'generated', 'source', '**', 'debug'], g:FILE_SEP), 0, 0) + \, g:PATH_SEP) +endif + +for source in get(g:, 'JavaComplete_SourceExclude', []) + let source = fnamemodify(source, ':p') + let idx = stridx(g:JavaComplete_SourcesPath, source) + while idx > 0 + let colon = stridx(g:JavaComplete_SourcesPath, ':', idx + 1) + let g:JavaComplete_SourcesPath = g:JavaComplete_SourcesPath[:idx - 1] . g:JavaComplete_SourcesPath[colon + 1:] + let idx = stridx(g:JavaComplete_SourcesPath, source) + endwhile +endfor + +call s:Log('Default sources: '. g:JavaComplete_SourcesPath) + +if exists('g:JavaComplete_LibsPath') + let g:JavaComplete_LibsPath .= g:PATH_SEP +else + "" + " path of you jar files. This path will + " always appended with '~/.m2/repository' directory. Here you can + " add your glassfish libs directory or your project libs. It will + " be automatically appended with you jre home path + let g:JavaComplete_LibsPath = '' +endif + +call javacomplete#classpath#classpath#BuildClassPath() + +function! javacomplete#Start() abort + call javacomplete#server#Start() +endfunction + +" vim:set fdm=marker sw=2 nowrap: diff --git a/bundle/vim-javacomplete2/autoload/javacomplete/classpath/ant.vim b/bundle/vim-javacomplete2/autoload/javacomplete/classpath/ant.vim new file mode 100644 index 000000000..8606b42d0 --- /dev/null +++ b/bundle/vim-javacomplete2/autoload/javacomplete/classpath/ant.vim @@ -0,0 +1,155 @@ +let s:antXmlTemplate = [ + \ ' ', + \ ' ', + \ ' ', + \ ' ', + \ ' ', + \ ' ', + \ ' ', + \ ' ', + \ ' ', + \ ' ', + \ ' ', + \ ' ', + \ ' ', + \ ' ', + \ ' ', + \ ' ', + \ ' ', + \ ' ', + \ ' ', + \ ' ', + \ ' ', + \ ' ', + \ ' '] + +function! s:Log(log) + let log = type(a:log) == type("") ? a:log : string(a:log) + call javacomplete#logger#Log("[classpath.ant] ". log) +endfunction + +function! javacomplete#classpath#ant#IfAnt() + if executable('ant') && g:JavaComplete_AntPath != "" + return 1 + endif + return 0 +endfunction + +function! javacomplete#classpath#ant#Generate(force) abort + let g:JavaComplete_ProjectKey = substitute(g:JavaComplete_AntPath, '[\\/:;.]', '_', 'g') + let path = javacomplete#util#GetBase("classpath". g:FILE_SEP). g:JavaComplete_ProjectKey + + if filereadable(path) + if a:force == 0 && getftime(path) >= getftime(g:JavaComplete_AntPath) + call s:Log("get libs from cache file") + return join(readfile(path), '') + endif + call javacomplete#util#RemoveFile(javacomplete#util#GetBase('cache'). g:FILE_SEP. 'class_packages_'. g:JavaComplete_ProjectKey. '.dat') + endif + + let s:antPath = path + let s:antOutput = [] + let cmd = "ant -projecthelp -v | grep '^ init\\>'" + call javacomplete#util#RunSystem( + \ cmd, "ant check 'init' target process", + \ "javacomplete#classpath#ant#CheckInitTargetHandler") + return '.' +endfunction + +function! javacomplete#classpath#ant#CheckInitTargetHandler(data, event) + if a:event == 'exit' + if a:data == "0" + let hasInitTarget = !empty(s:antOutput) + let s:antOutput = [] + call s:BuildAntClasspath(hasInitTarget) + else + echohl WarningMsg | echomsg "Failed to check 'init' target" | echohl None + endif + elseif a:event == 'stdout' + for data in filter(a:data,'v:val =~ "^ init\\>.*$"') + if g:JavaComplete_ShowExternalCommandsOutput + echomsg data + endif + if exists('s:antOutput') + call add(s:antOutput, data) + endif + endfor + elseif a:event == 'stderr' + for data in filter(a:data,'v:val !~ "^\\s*$"') + echoerr data + endfor + endif +endfunction + +function! s:BuildAntClasspath(hasInitTarget) + let tmpBuildFile = [] + for line in readfile(g:JavaComplete_AntPath) + if stridx(line, '') >= 0 + if a:hasInitTarget + let xmlTemplate = s:antXmlTemplate + let xmlTemplate[0] = xmlTemplate[0][:-2]. ' depends="init">' + call extend(tmpBuildFile, xmlTemplate) + else + call extend(tmpBuildFile, s:antXmlTemplate) + endif + endif + call add(tmpBuildFile, line) + endfor + let s:tmpAntFileName = "vjc-ant-build.xml" + call writefile(tmpBuildFile, s:tmpAntFileName) + + let s:antOutput = [] + let antCmd = ['ant', '-f', s:tmpAntFileName, '-q', 'vjc-printclasspath'] + call javacomplete#util#RunSystem( + \ antCmd, "ant classpath build process", + \ "javacomplete#classpath#ant#BuildClasspathHandler") +endfunction + +function! javacomplete#classpath#ant#BuildClasspathHandler(data, event) + if a:event == 'exit' + if a:data == "0" + for line in s:antOutput + let matches = matchlist(line, '\m^\s\+\[echo\]\s\+\(.*\)') + if !empty(matches) + let cp = matches[1] + break + endif + endfor + if cp != '.' + call writefile([cp], s:antPath) + endif + + let g:JavaComplete_LibsPath .= ':'. cp + + call javacomplete#util#RemoveFile(javacomplete#util#GetBase('cache'). g:FILE_SEP. 'class_packages_'. g:JavaComplete_ProjectKey. '.dat') + + call javacomplete#server#UnblockStart() + call javacomplete#server#Terminate() + call javacomplete#server#Start() + + echomsg "Ant classpath built successfully" + else + echohl WarningMsg | echomsg "Failed to build ant classpath" | echohl None + endif + + call delete(s:tmpAntFileName) + + unlet s:antOutput + unlet s:tmpAntFileName + + elseif a:event == 'stdout' + for data in filter(a:data,'v:val !~ "^\\s*$"') + if g:JavaComplete_ShowExternalCommandsOutput + echomsg data + endif + endfor + if exists('s:antOutput') + call extend(s:antOutput, a:data) + endif + elseif a:event == 'stderr' + for data in filter(a:data,'v:val !~ "^\\s*$"') + echoerr data + endfor + endif +endfunction +" vim:set fdm=marker sw=2 nowrap: diff --git a/bundle/vim-javacomplete2/autoload/javacomplete/classpath/classpath.vim b/bundle/vim-javacomplete2/autoload/javacomplete/classpath/classpath.vim new file mode 100644 index 000000000..8a5d1def0 --- /dev/null +++ b/bundle/vim-javacomplete2/autoload/javacomplete/classpath/classpath.vim @@ -0,0 +1,118 @@ +function! s:Log(log) + let log = type(a:log) == type("") ? a:log : string(a:log) + call javacomplete#logger#Log("[classpath] ". log) +endfunction + +function! javacomplete#classpath#classpath#BuildClassPath() + call s:BuildClassPath(0) +endfunction + +function! javacomplete#classpath#classpath#RebuildClassPath() + call s:BuildClassPath(1) +endfunction + +function! s:BuildClassPath(force) + if !g:JavaComplete_MavenRepositoryDisabled + if empty('g:JavaComplete_PomPath') + let g:JavaComplete_PomPath = javacomplete#util#FindFile('pom.xml') + if g:JavaComplete_PomPath != "" + let g:JavaComplete_PomPath = fnamemodify(g:JavaComplete_PomPath, ':p') + call s:Log("found maven file: ". g:JavaComplete_PomPath) + endif + endif + endif + + if !get(g:, 'JavaComplete_GradleRepositoryDisabled', 0) + if !exists('g:JavaComplete_GradlePath') + if filereadable(getcwd(). g:FILE_SEP. "build.gradle") + let g:JavaComplete_GradlePath = getcwd(). g:FILE_SEP. "build.gradle" + else + let g:JavaComplete_GradlePath = javacomplete#util#FindFile('build.gradle', '**3') + endif + if g:JavaComplete_GradlePath != "" + let g:JavaComplete_GradlePath = fnamemodify(g:JavaComplete_GradlePath, ':p') + call s:Log("found gradle file: ". g:JavaComplete_GradlePath) + endif + endif + endif + + if !get(g:, 'JavaComplete_AntRepositoryDisabled', 0) + if !exists('g:JavaComplete_AntPath') + if filereadable(getcwd(). g:FILE_SEP. "build.xml") + let g:JavaComplete_AntPath = getcwd(). g:FILE_SEP. "build.xml" + else + let g:JavaComplete_AntPath = javacomplete#util#FindFile('build.xml', '**3') + endif + if g:JavaComplete_AntPath != "" + let g:JavaComplete_AntPath = fnamemodify(g:JavaComplete_AntPath, ':p') + call s:Log("found ant file: ". g:JavaComplete_AntPath) + endif + endif + endif + + let g:JavaComplete_LibsPath .= s:FindClassPath(a:force) + + call s:Log("libs found: ". g:JavaComplete_LibsPath) +endfunction + +function! s:ReadClassPathFile(classpathFile) + let cp = '' + let file = g:JavaComplete_Home. join(['', 'autoload', 'classpath.py'], g:FILE_SEP) + execute "JavacompletePyfile" file + JavacompletePy import vim + JavacompletePy vim.command("let cp = '%s'" % os.pathsep.join(ReadClasspathFile(vim.eval('a:classpathFile'))).replace('\\', '/')) + return cp +endfunction + +function! s:UseEclipse(force) + if has('python') || has('python3') + let classpathFile = fnamemodify(findfile('.classpath', escape(expand('.'), '*[]?{}, ') . ';'), ':p') + if !empty(classpathFile) && filereadable(classpathFile) + return s:ReadClassPathFile(classpathFile) + endif + endif + + return "" +endf + +function! s:UseMaven(force) + if javacomplete#classpath#maven#IfMaven() + return javacomplete#classpath#maven#Generate(a:force) + endif + + return "" +endf + +function! s:UseGradle(force) + if javacomplete#classpath#gradle#IfGradle() + return javacomplete#classpath#gradle#Generate(a:force) + endif + + return "" +endf + +function! s:UseAnt(force) + if javacomplete#classpath#ant#IfAnt() + return javacomplete#classpath#ant#Generate(a:force) + endif + + return "" +endf + +function! s:FindClassPath(force) abort + for classpathSourceType in g:JavaComplete_ClasspathGenerationOrder + try + let cp = '' + exec "let cp .= s:Use". classpathSourceType. "(". a:force. ")" + if !empty(cp) + call s:Log("found ". classpathSourceType. " project") + return '.' . g:PATH_SEP . cp + endif + catch + endtry + endfor + + return '.' +endfunction + +" vim:set fdm=marker sw=2 nowrap: diff --git a/bundle/vim-javacomplete2/autoload/javacomplete/classpath/gradle.vim b/bundle/vim-javacomplete2/autoload/javacomplete/classpath/gradle.vim new file mode 100644 index 000000000..a6eaa9b07 --- /dev/null +++ b/bundle/vim-javacomplete2/autoload/javacomplete/classpath/gradle.vim @@ -0,0 +1,127 @@ +function! javacomplete#classpath#gradle#IfGradle() + if !empty(g:JavaComplete_GradleExecutable) + if executable(g:JavaComplete_GradleExecutable) && g:JavaComplete_GradlePath != "" + return 1 + else + return 0 + end + endif + + if g:JavaComplete_GradlePath != "" && s:IsGradleExecutable() && g:JavaComplete_GradlePath != "" + return 1 + endif + return 0 +endfunction + +function! s:IsGradleExecutable() + let osExec = javacomplete#util#IsWindows() ? '\gradlew.bat' : '/gradlew' + let path = fnamemodify(g:JavaComplete_GradlePath, ':p:h') + return executable('gradle') || executable(path. osExec) +endfunction + +function! javacomplete#classpath#gradle#BuildClasspathHandler(data, event) + if a:event == 'exit' + if a:data == "0" + let cp = '' + for i in range(len(s:gradleOutput)) + if s:gradleOutput[i] =~ '^CLASSPATH:' + let cp .= s:gradleOutput[i][10:] + for j in range(i, len(s:gradleOutput) - 1) + if s:gradleOutput[j] !~ '^END CLASSPATH GENERATION' + let cp .= s:gradleOutput[j] + else + break + endif + endfor + break + endif + endfor + let g:JavaComplete_LibsPath .= ':'. cp + + call writefile([cp], s:gradlePath) + + call javacomplete#util#RemoveFile(javacomplete#util#GetBase('cache'). g:FILE_SEP. 'class_packages_'. g:JavaComplete_ProjectKey. '.dat') + + call javacomplete#server#UnblockStart() + call javacomplete#server#Terminate() + call javacomplete#server#Start() + + echomsg "Gradle classpath built successfully" + else + echohl WarningMsg | echomsg "Failed to build gradle classpath" | echohl None + endif + + call delete(s:temporaryGradleFile) + + unlet s:temporaryGradleFile + unlet s:gradleOutput + unlet s:gradlePath + + elseif a:event == 'stdout' + for data in filter(a:data,'v:val !~ "^\\s*$"') + if g:JavaComplete_ShowExternalCommandsOutput + echomsg data + endif + endfor + if exists('s:gradleOutput') + call extend(s:gradleOutput, a:data) + endif + elseif a:event == 'stderr' + for data in filter(a:data,'v:val !~ "^\\s*$"') + echoerr data + endfor + endif +endfunction + +function! javacomplete#classpath#gradle#Generate(force) abort + let base = javacomplete#util#GetBase("classpath". g:FILE_SEP) + let g:JavaComplete_ProjectKey = substitute(g:JavaComplete_GradlePath, '[\\/:;.]', '_', 'g') + + let path = base . g:JavaComplete_ProjectKey + if filereadable(path) + if a:force == 0 && getftime(path) >= getftime(g:JavaComplete_GradlePath) + return join(readfile(path), '') + endif + call javacomplete#util#RemoveFile(javacomplete#util#GetBase('cache'). g:FILE_SEP. 'class_packages_'. g:JavaComplete_ProjectKey. '.dat') + endif + call s:GenerateClassPath(path) + return '' +endfunction + +function! s:GenerateClassPath(path) abort + let s:temporaryGradleFile = tempname() + let s:gradleOutput = [] + let s:gradlePath = a:path + if exists(g:JavaComplete_GradleExecutable) + let gradle = g:JavaComplete_GradleExecutable + else + let gradle = fnamemodify( + \ g:JavaComplete_GradlePath, ':p:h') + \ . (javacomplete#util#IsWindows() + \ ? + \ '\gradlew.bat' + \ : + \ '/gradlew') + if !executable(gradle) + let gradle = 'gradle' + endif + endif + call writefile( + \ ["rootProject{apply from: '" + \ . g:JavaComplete_Home. g:FILE_SEP. "classpath.gradle'}"], + \ s:temporaryGradleFile) + let cmd = [ + \ gradle, + \ '-p', + \ fnamemodify(g:JavaComplete_GradlePath, ':p:h'), + \ '-I', + \ s:temporaryGradleFile, + \ ':classpath'] + call javacomplete#server#BlockStart() + call javacomplete#util#RunSystem( + \ cmd, + \ 'gradle classpath build process', + \ 'javacomplete#classpath#gradle#BuildClasspathHandler') +endfunction + +" vim:set fdm=marker sw=2 nowrap: diff --git a/bundle/vim-javacomplete2/autoload/javacomplete/classpath/maven.vim b/bundle/vim-javacomplete2/autoload/javacomplete/classpath/maven.vim new file mode 100644 index 000000000..f6097102c --- /dev/null +++ b/bundle/vim-javacomplete2/autoload/javacomplete/classpath/maven.vim @@ -0,0 +1,122 @@ +let s:pomProperties={} "maven project properties +let s:pomTags = ['build', 'properties'] +let s:mavenErrors = [] + +function! javacomplete#classpath#maven#IfMaven() + if executable('mvn') && g:JavaComplete_PomPath != "" + return 1 + endif + return 0 +endfunction + +function! javacomplete#classpath#maven#Generate(force) abort + if a:force != 0 + let s:pomProperties = {} + endif + let g:JavaComplete_ProjectKey = substitute(g:JavaComplete_PomPath, '[\\/:;.]', '_', 'g') + let path = javacomplete#util#GetBase("classpath". g:FILE_SEP). g:JavaComplete_ProjectKey + + if filereadable(path) + if a:force == 0 && getftime(path) >= getftime(g:JavaComplete_PomPath) + return join(readfile(path), '') + endif + call javacomplete#util#RemoveFile(javacomplete#util#GetBase('cache'). g:FILE_SEP. 'class_packages_'. g:JavaComplete_ProjectKey. '.dat') + endif + + if !has_key(s:pomProperties, g:JavaComplete_PomPath) + let s:mavenPath = path + let s:mavenPom = g:JavaComplete_PomPath + let s:mavenSettingsOutput = [] + let mvnCmd = ['mvn', '-B', '--file', g:JavaComplete_PomPath, 'dependency:build-classpath', '-DincludeScope=test'] + call javacomplete#server#BlockStart() + call javacomplete#util#RunSystem(mvnCmd, 'maven classpath build process', 'javacomplete#classpath#maven#BuildClasspathHandler') + return "" + endif + + return s:GetMavenClasspath(path, g:JavaComplete_PomPath) +endfunction + +function! s:GetMavenClasspath(path, pom) + let mvnProperties = s:pomProperties[a:pom] + let cp = get(mvnProperties, 'project.dependencybuildclasspath', '.') + let cp .= g:PATH_SEP . get(mvnProperties, 'project.build.outputDirectory', join([fnamemodify(a:pom, ':h'), 'target', 'classes'], g:FILE_SEP)) + let cp .= g:PATH_SEP . get(mvnProperties, 'project.build.testOutputDirectory', join([fnamemodify(a:pom, ':h'), 'target', 'test-classes'], g:FILE_SEP)) + if cp != '.' + call writefile([cp], a:path) + endif + return cp +endfunction + +function! s:ParseMavenOutput() + let mvnProperties = {} + let mvnIsManagedTag = 1 + let currentPath = 'project' + for i in range(len(s:mavenSettingsOutput)) + if s:mavenSettingsOutput[i] =~ 'Dependencies classpath:' + let mvnProperties['project.dependencybuildclasspath'] = s:mavenSettingsOutput[i + 1] + let offset = 2 + while s:mavenSettingsOutput[i + offset] !~ '^[INFO.*' + let mvnProperties['project.dependencybuildclasspath'] .= s:mavenSettingsOutput[i + offset] + let offset += 1 + endwhile + endif + let matches = matchlist(s:mavenSettingsOutput[i], '\m^\s*<\([a-zA-Z0-9\-\.]\+\)>\s*$') + if mvnIsManagedTag && !empty(matches) + let mvnIsManagedTag = index(s:pomTags, matches[1]) >= 0 + let currentPath .= '.'. matches[1] + else + let matches = matchlist(s:mavenSettingsOutput[i], '\m^\s*\s*$') + if !empty(matches) + let mvnIsManagedTag = index(s:pomTags, matches[1]) < 0 + let currentPath = substitute(currentPath, '\m\.'. matches[1]. '$', '', '') + else + let matches = matchlist(s:mavenSettingsOutput[i], '\m^\s*<\([a-zA-Z0-9\-\.]\+\)>\(.\+\)\s*$') + if mvnIsManagedTag && !empty(matches) + let mvnProperties[currentPath. '.'. matches[1]] = matches[2] + endif + endif + endif + endfor + let s:pomProperties[s:mavenPom] = mvnProperties +endfunction + +function! javacomplete#classpath#maven#BuildClasspathHandler(data, event) + if a:event == 'exit' + if a:data == "0" + call s:ParseMavenOutput() + + let g:JavaComplete_LibsPath .= s:GetMavenClasspath(s:mavenPath, s:mavenPom) + + call javacomplete#util#RemoveFile(javacomplete#util#GetBase('cache'). g:FILE_SEP. 'class_packages_'. g:JavaComplete_ProjectKey. '.dat') + + call javacomplete#server#UnblockStart() + call javacomplete#server#Terminate() + call javacomplete#server#Start() + + echomsg "Maven classpath built successfully" + else + echoerr join(s:mavenErrors, "\n") + let s:mavenErrors = [] + echohl WarningMsg | echomsg "Failed to build maven classpath" | echohl None + endif + + unlet s:mavenPath + unlet s:mavenPom + unlet s:mavenSettingsOutput + elseif a:event == 'stdout' + for data in filter(a:data,'v:val !~ "^\\s*$"') + if g:JavaComplete_ShowExternalCommandsOutput + echomsg data + elseif data =~ '^\[ERROR\]\w*' || data =~ '^\[WARNING\]\w*' + echohl WarningMsg | echomsg data | echohl None + endif + endfor + call extend(s:mavenSettingsOutput, a:data) + elseif a:event == 'stderr' + for data in filter(a:data,'v:val !~ "^\\s*$"') + call add(s:mavenErrors, data) + endfor + endif +endfunction + +" vim:set fdm=marker sw=2 nowrap: diff --git a/bundle/vim-javacomplete2/autoload/javacomplete/collector.vim b/bundle/vim-javacomplete2/autoload/javacomplete/collector.vim new file mode 100644 index 000000000..d7f26490e --- /dev/null +++ b/bundle/vim-javacomplete2/autoload/javacomplete/collector.vim @@ -0,0 +1,686 @@ +" Vim completion script for java +" Maintainer: artur shaik +" +" This file contains everything related to collecting source data + +function! s:Log(log) + let log = type(a:log) == type("") ? a:log : string(a:log) + call javacomplete#logger#Log("[collector] ". log) +endfunction + +" a:1 - filepath +" a:2 - package name +function! javacomplete#collector#DoGetClassInfo(class, ...) + let class = type(a:class) == type({}) ? a:class.name : a:class + call s:Log("get class info. class: ". class) + + if class != 'this' && class != 'super' && has_key(g:JavaComplete_Cache, class) + call s:Log("class info from cache") + return g:JavaComplete_Cache[class] + endif + + " array type: TypeName[] or '[I' or '[[Ljava.lang.String;' + if class[-1:] == ']' || class[0] == '[' + return g:J_ARRAY_TYPE_INFO + endif + + let filekey = a:0 > 0 && len(a:1) > 0 ? a:1 : javacomplete#GetCurrentFileKey() + let packagename = a:0 > 1 && len(a:2) > 0 ? a:2 : javacomplete#collector#GetPackageName() + + let unit = javacomplete#parseradapter#Parse(filekey) + let pos = java_parser#MakePos(line('.') - 1, col('.') - 1) + let t = get(javacomplete#parseradapter#SearchTypeAt(unit, pos), -1, {}) + if has_key(t, 'extends') + if type(t.extends) == type([]) && len(t.extends) > 0 + if type(t.extends[0]) == type("") + let extends = t.extends[0] . '$'. class + elseif type(t.extends[0]) == type({}) + if has_key(t.extends[0], 'name') + let className = t.extends[0].name + elseif has_key(t.extends[0], 'clazz') + let className = t.extends[0].clazz.name + else + let className = '' + endif + if !empty(className) + let imports = javacomplete#imports#GetImports('imports_fqn', filekey) + let fqn = javacomplete#imports#SearchSingleTypeImport(className, imports) + let extends = fqn. '$'. a:class + endif + else + let extends = '' + endif + else + let extends = '' + endif + else + let extends = '' + endif + if class == 'this' || class == 'super' || (has_key(t, 'fqn') && t.fqn == packagename. '.'. class) + if &ft == 'jsp' + let ci = javacomplete#collector#FetchClassInfo('javax.servlet.jsp.HttpJspPage') + return ci + endif + + call s:Log('A0. ' . class) + if !empty(t) + return javacomplete#util#Sort(s:Tree2ClassInfo(t)) + else + return {} + endif + endif + for def in get(t, 'defs', []) + if get(def, 'tag', '') == 'CLASSDEF' && get(def, 'name', '') == class + return javacomplete#util#Sort(s:Tree2ClassInfo(def)) + endif + endfor + + let typename = class + + let typeArguments = '' + let splittedType = s:SplitTypeArguments(typename) + if type(splittedType) == type([]) + let typename = splittedType[0] + let typeArguments = splittedType[1] + endif + + if stridx(typename, '$') > 0 + let sc = split(typename, '\$') + let typename = sc[0] + let nested = '$'.sc[1] + else + let nested = '' + endif + + let hasKeyword = javacomplete#util#HasKeyword(typename) + if typename !~ '^\s*' . g:RE_QUALID . '\s*$' || hasKeyword + call s:Log("no qualid: ". typename) + return {} + endif + + let collectedArguments = s:CollectTypeArguments(typeArguments, packagename, filekey) + + let fqns = s:CollectFQNs(typename, packagename, filekey, extends) + for fqn in fqns + let fqn = fqn . nested . collectedArguments + let fqn = substitute(fqn, ' ', '', 'g') + call javacomplete#collector#FetchClassInfo(fqn) + + let key = s:KeyInCache(fqn) + if !empty(key) + return get(g:JavaComplete_Cache[key], 'tag', '') == 'CLASSDEF' ? g:JavaComplete_Cache[key] : {} + endif + endfor + + return {} +endfunction + +function! javacomplete#collector#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 +endfunction + +function! javacomplete#collector#FetchClassInfo(fqn) + call javacomplete#collector#FetchInfoFromServer(a:fqn, '-E') +endfunction + +function! javacomplete#collector#FetchInfoFromServer(class, option) + if has_key(g:JavaComplete_Cache, substitute(a:class, '\$', '.', 'g')) + return g:JavaComplete_Cache[substitute(a:class, '\$', '.', 'g')] + endif + + let res = javacomplete#server#Communicate(a:option, a:class, 'collector#FetchInfoFromServer') + if res =~ "^{'" + silent! let dict = eval(res) + if !empty(dict) && type(dict)==type({}) + for key in keys(dict) + if !has_key(g:JavaComplete_Cache, key) + if type(dict[key]) == type({}) + let g:JavaComplete_Cache[substitute(key, '\$', '.', '')] = javacomplete#util#Sort(dict[key]) + elseif type(dict[key]) == type([]) + let g:JavaComplete_Cache[substitute(key, '\$', '.', '')] = sort(dict[key]) + endif + endif + endfor + else + let b:errormsg = dict + endif + else + let b:errormsg = res + endif +endfunction + +function! s:SplitTypeArguments(typename) + if a:typename =~ g:RE_TYPE_WITH_ARGUMENTS + let lbridx = stridx(a:typename, '<') + let typeArguments = a:typename[lbridx + 1 : -2] + let typename = a:typename[0 : lbridx - 1] + return [typename, typeArguments] + endif + + let lbridx = stridx(a:typename, '<') + if lbridx > 0 + let typename = a:typename[0 : lbridx - 1] + return [typename, 0] + endif + + return a:typename +endfunction + +function! s:CollectTypeArguments(typeArguments, packagename, filekey) + let collectedArguments = '' + if !empty(a:typeArguments) + let typeArguments = a:typeArguments + let i = 0 + let lbr = 0 + while i < len(typeArguments) + let c = typeArguments[i] + if c == '<' + let lbr += 1 + elseif c == '>' + let lbr -= 1 + endif + + if c == ',' && lbr == 0 + let typeArguments = typeArguments[0 : i - 1] . "<_split_>". typeArguments[i + 1 : -1] + let i += 9 + else + let i += 1 + endif + endwhile + + for arg in split(typeArguments, "<_split_>") + let argTypeArguments = '' + if arg =~ g:RE_TYPE_WITH_ARGUMENTS + let lbridx = stridx(arg, '<') + let argTypeArguments = arg[lbridx : -1] + let arg = arg[0 : lbridx - 1] + endif + + if arg =~ g:RE_TYPE_ARGUMENT_EXTENDS + let i = matchend(arg, g:RE_TYPE) + let arg = arg[i+1 : -1] + endif + + let fqns = s:CollectFQNs(arg, a:packagename, a:filekey, '') + let collectedArguments .= '' + if len(fqns) > 1 + let collectedArguments .= '(' + endif + for fqn in fqns + if len(fqn) > 0 + let collectedArguments .= fqn. argTypeArguments. '|' + endif + endfor + if len(fqns) > 1 + let collectedArguments = collectedArguments[0:-2]. '),' + else + let collectedArguments = collectedArguments[0:-2]. ',' + endif + endfor + if !empty(collectedArguments) + let collectedArguments = '<'. collectedArguments[0:-2]. '>' + endif + endif + + return collectedArguments +endfunction + +function! 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 type(def) == type([]) && len(def) == 1 + let tmp = def[0] + unlet def + let def = tmp + unlet tmp + endif + let tag = get(def, 'tag', '') + if tag == 'METHODDEF' + call add(def.n == t.name ? t.ctors : t.methods, def) + elseif tag == 'VARDEF' + call add(t.fields, def) + elseif tag == 'CLASSDEF' + call add(t.classes, t.fqn . '.' . def.name) + endif + unlet def + endfor + + for line in reverse(getline(0, '.')) + let matches = matchlist(line, g:RE_TYPE_DECL_HEAD. t.name) + if len(matches) + if matches[1] == 'interface' + let t.interface = 1 + elseif matches[1] == 'enum' + let t.enum = 1 + endif + break + endif + endfor + + " convert type name in extends to fqn for class defined in source files + if has_key(a:t, 'filepath') && a:t.filepath != javacomplete#GetCurrentFileKey() + let filepath = a:t.filepath + let packagename = get(g:JavaComplete_Files[filepath].unit, 'package', '') + else + let filepath = expand('%:p') + let packagename = javacomplete#collector#GetPackageName() + endif + + if !has_key(a:t, 'extends') + let a:t.extends = ['java.lang.Object'] + endif + + let extends = a:t.extends + if has_key(a:t, 'implements') + let extends += a:t.implements + endif + + let i = 0 + while i < len(extends) + if type(extends[i]) == type("") && extends[i] == get(t, 'fqn', '') + let i += 1 + continue + elseif type(extends[i]) == type({}) && extends[i].tag == 'ERRONEOUS' + let i += 1 + continue + endif + let type2str = java_parser#type2Str(extends[i]) + let ci = javacomplete#collector#DoGetClassInfo(type2str, filepath, packagename) + if type(ci) == type([]) + let ci = [0] + endif + if has_key(ci, 'fqn') + let extends[i] = ci.fqn + endif + let i += 1 + endwhile + let t.extends = javacomplete#util#uniq(extends) + + return t +endfunction + +function! s:CollectFQNs(typename, packagename, filekey, extends) + if len(split(a:typename, '\.')) > 1 + return [a:typename] + endif + + let brackets = stridx(a:typename, '[') + let extra = '' + if brackets >= 0 + let typename = a:typename[0 : brackets - 1] + let extra = a:typename[brackets : -1] + else + let typename = a:typename + endif + + let imports = javacomplete#imports#GetImports('imports_fqn', a:filekey) + let directFqn = javacomplete#imports#SearchSingleTypeImport(typename, imports) + if !empty(directFqn) + return [directFqn. extra] + endif + + let fqns = [] + call add(fqns, empty(a:packagename) ? a:typename : a:packagename . '.' . a:typename) + let imports = javacomplete#imports#GetImports('imports_star', a:filekey) + for p in imports + call add(fqns, p . a:typename) + endfor + if !empty(a:extends) + call add(fqns, a:extends) + endif + if typename != 'Object' + call add(fqns, 'java.lang.Object') + endif + return fqns +endfunction + +function! s:KeyInCache(fqn) + let fqn = substitute(a:fqn, '<', '\\<', 'g') + let fqn = substitute(fqn, '>', '\\>', 'g') + let fqn = substitute(fqn, ']', '\\]', 'g') + let fqn = substitute(fqn, '[', '\\[', 'g') + let fqn = substitute(fqn, '\$', '.', 'g') + + let keys = keys(g:JavaComplete_Cache) + let idx = match(keys, '\v'. fqn. '$') + + if idx >= 0 + return keys[idx] + endif + + return '' +endfunction + +" a:1 - include related type +function! javacomplete#collector#GetDeclaredClassName(var, ...) + let var = javacomplete#util#Trim(a:var) + call s:Log('get declared class name for: "' . var . '"') + if var =~# '^\(this\|super\)$' + return var + endif + + " Special handling for objects in JSP + if &ft == 'jsp' + if get(g:J_JSP_BUILTIN_OBJECTS, a:var, '') != '' + return g:J_JSP_BUILTIN_OBJECTS[a:var] + endif + return s:FastBackwardDeclarationSearch(a:var) + endif + + let result = javacomplete#collector#SearchForName(var, 1, 1) + let variable = get(result[2], -1, {}) + if get(variable, 'tag', '') == 'VARDEF' + if has_key(variable, 't') + let splitted = split(variable.t, '\.') + + if len(splitted) == 1 + let rootClassName = s:SearchForRootClassName(variable) + if len(rootClassName) > 0 + call insert(splitted, rootClassName) + endif + endif + + if len(splitted) > 1 + let directFqn = javacomplete#imports#SearchSingleTypeImport(splitted[0], javacomplete#imports#GetImports('imports_fqn', javacomplete#GetCurrentFileKey())) + if empty(directFqn) + return variable.t + endif + else + return variable.t + endif + return substitute(join(splitted, '.'), '\.', '\$', 'g') + endif + return java_parser#type2Str(variable.vartype) + endif + + if has_key(variable, 't') + return variable.t + endif + + if a:0 > 0 + let class = get(result[0], -1, {}) + if get(class, 'tag', '') == 'CLASSDEF' + if has_key(class, 'name') + return class.name + endif + endif + endif + + return '' +endfunction + +function! s:FastBackwardDeclarationSearch(name) + let lines = reverse(getline(0, '.')) + for line in lines + let splittedLine = split(line, ';') + for l in splittedLine + let l = javacomplete#util#Trim(l) + let matches = matchlist(l, '^\('. g:RE_QUALID. '\)\s\+'. a:name) + if len(matches) > 0 + return matches[1] + endif + endfor + endfor + return '' +endfunction + +function! s:SearchForRootClassName(variable) + if has_key(a:variable, 'vartype') && type(a:variable.vartype) == type({}) + if has_key(a:variable.vartype, 'tag') && a:variable.vartype.tag == 'TYPEAPPLY' + if has_key(a:variable.vartype, 'clazz') && a:variable.vartype.clazz.tag == 'SELECT' + let clazz = a:variable.vartype.clazz + if has_key(clazz, 'selected') && has_key(clazz.selected, 'name') + return clazz.selected.name + endif + endif + endif + endif + + return "" +endfunction + +" first: return at once if found one. +" fullmatch: 1 - equal, 0 - match beginning +" return [types, methods, fields, vars] +function! javacomplete#collector#SearchForName(name, first, fullmatch) + let result = [[], [], [], []] + if javacomplete#util#IsKeyword(a:name) + return result + endif + + let unit = javacomplete#parseradapter#Parse() + let targetPos = java_parser#MakePos(line('.')-1, col('.')-1) + let trees = javacomplete#parseradapter#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) + elseif tree.tag == 'LAMBDA' + let t = s:DetermineLambdaArguments(unit, tree, a:name) + if !empty(t) + call add(result[2], t) + endif + endif + endfor + + if a:first && result != [[], [], [], []] | return result | endif + + " Accessible inherited members + let type = get(javacomplete#parseradapter#SearchTypeAt(unit, targetPos), -1, {}) + if !empty(type) + let members = javacomplete#complete#complete#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] + endif + + " static import + let si = javacomplete#imports#SearchStaticImports(a:name, a:fullmatch) + let result[0] += si[0] + let result[1] += si[1] + let result[2] += si[2] + + return result +endfunction + +function! s:DetermineLambdaArguments(unit, ti, name) + let nameInLambda = 0 + let argIdx = 0 " argument index in method declaration + let argPos = 0 + if type(a:ti.args) == type({}) + if a:name == a:ti.args.name + let nameInLambda = 1 + endif + elseif type(a:ti.args) == type([]) + for arg in a:ti.args + if arg.name == a:name + let nameInLambda = 1 + let argPos = arg.pos + break + endif + let argIdx += 1 + endfor + endif + + if !nameInLambda + return {} + endif + + let methods = [] + let t = a:ti + let type = '' + if has_key(t, 'meth') && !empty(t.meth) + let result = [] + while 1 + if has_key(t, 'meth') + let t = t.meth + elseif t.tag == 'SELECT' && has_key(t, 'selected') + call add(result, t.name. '()') + let t = t.selected + elseif t.tag == 'IDENT' + call add(result, t.name) + break + endif + endwhile + + let items = reverse(result) + let typename = javacomplete#collector#GetDeclaredClassName(items[0], 1) + let ti = {} + if (typename != '') + if typename[1] == '[' || typename[-1:] == ']' + let ti = g:J_ARRAY_TYPE_INFO + elseif typename != 'void' && !javacomplete#util#IsBuiltinType(typename) + let ti = javacomplete#collector#DoGetClassInfo(typename) + endif + else " it can be static request + let ti = javacomplete#collector#DoGetClassInfo(items[0]) + endif + + let ii = 1 + while !empty(ti) && ii < len(items) - 1 + " method invocation: "PrimaryExpr.method(parameters)[].|" + if items[ii] =~ '^\s*' . g:RE_IDENTIFIER . '\s*(' + let ti = javacomplete#collector#MethodInvocation(items[ii], ti, 0) + endif + let ii += 1 + endwhile + + if has_key(ti, 'methods') + let itemName = split(items[-1], '(')[0] + for m in ti.methods + if m.n == itemName + call add(methods, m) + endif + endfor + + endif + elseif has_key(t, 'stats') && !empty(t.stats) + if t.stats.tag == 'VARDEF' + let type = t.stats.t + elseif t.stats.tag == 'RETURN' + for ty in a:unit.types + for def in ty.defs + if def.tag == 'METHODDEF' + if t.stats.pos >= def.body.pos && t.stats.endpos <= def.body.endpos + let type = def.r + endif + endif + endfor + endfor + + endif + endif + + for method in methods + if a:ti.idx < len(method.p) + let type = method.p[a:ti.idx] + endif + let res = s:GetLambdaParameterType(type, a:name, argIdx, argPos) + if has_key(res, 'tag') + return res + endif + endfor + + return s:GetLambdaParameterType(type, a:name, argIdx, argPos) +endfunction + +" type should be FunctionInterface, and it contains only one abstract method +function! s:GetLambdaParameterType(type, name, argIdx, argPos) + let pType = '' + if !empty(a:type) + let matches = matchlist(a:type, '^java.util.function.Function<\(.*\)>') + if len(matches) > 0 + let types = split(matches[1], ',') + if !empty(types) + let type = javacomplete#scanner#ExtractCleanExpr(types[0]) + return {'tag': 'VARDEF', 'name': type, 'type': {'tag': 'IDENT', 'name': type}, 'vartype': {'tag': 'IDENT', 'name': type, 'pos': a:argPos}, 'pos': a:argPos} + endif + else + let functionalMembers = javacomplete#collector#DoGetClassInfo(a:type) + if has_key(functionalMembers, 'methods') + for m in functionalMembers.methods + if javacomplete#util#CheckModifier(m.m, g:JC_MODIFIER_ABSTRACT) + if a:argIdx < len(m.p) + let pType = m.p[a:argIdx] + break + endif + endif + endfor + + if !empty(pType) + return {'tag': 'VARDEF', 'name': a:name, 'type': {'tag': 'IDENT', 'name': pType}, 'vartype': {'tag': 'IDENT', 'name': pType, 'pos': a:argPos}, 'pos': a:argPos} + endif + endif + endif + endif + return {} +endfunction + +function! javacomplete#collector#MethodInvocation(expr, ti, itemkind) + let subs = split(substitute(a:expr, '\s*\(' . g:RE_IDENTIFIER . '\)\s*\((.*\)', '\1;\2', ''), ';') + + " all methods matched + if empty(a:ti) + let methods = javacomplete#collector#SearchForName(subs[0], 0, 1)[1] + elseif type(a:ti) == type({}) && get(a:ti, 'tag', '') == 'CLASSDEF' + let methods = javacomplete#complete#complete#SearchMember(a:ti, subs[0], 1, a:itemkind, 1, 0, a:itemkind == 2)[1] + else + let methods = [] + endif + + let method = s:DetermineMethod(methods, subs[1]) + if !empty(method) + return javacomplete#complete#complete#ArrayAccess(method.r, subs[0]) + endif + return {} +endfunction + +" determine overloaded method by parameters count +function! s:DetermineMethod(methods, parameters) + let parameters = substitute(a:parameters, '(\(.*\))', '\1', '') + let paramsCount = len(split(parameters, ',')) + for m in a:methods + if len(get(m, 'p', [])) == paramsCount + return m + endif + endfor + return get(a:methods, -1, {}) +endfunction + +function! javacomplete#collector#CurrentFileInfo() + let currentBuf = getline(1,'$') + let base64Content = javacomplete#util#Base64Encode(join(currentBuf, "\n")) + let ti = javacomplete#collector#DoGetClassInfo('this') + if has_key(ti, 'name') + let package = javacomplete#collector#GetPackageName(). '.'. ti.name + + call javacomplete#server#Communicate('-clear-from-cache', package, 's:CurrentFileInfo') + let response = javacomplete#server#Communicate('-class-info-by-content -target '. package. ' -content', base64Content, 'CurrentFileInfo') + if response =~ '^{' + return eval(response) + endif + else + call s:Log("`this` class parse error [CurrentFileInfo]") + endif + + return {} +endfunction + +" vim:set fdm=marker sw=2 nowrap: diff --git a/bundle/vim-javacomplete2/autoload/javacomplete/complete/complete.vim b/bundle/vim-javacomplete2/autoload/javacomplete/complete/complete.vim new file mode 100644 index 000000000..a295eac55 --- /dev/null +++ b/bundle/vim-javacomplete2/autoload/javacomplete/complete/complete.vim @@ -0,0 +1,966 @@ +" Vim completion script for java +" Maintainer: artur shaik +" +" This file contains everything related to completions + +let b:dotexpr = '' +let b:incomplete = '' +let b:errormsg = '' + +function! s:Log(log) + let log = type(a:log) == type("") ? a:log : string(a:log) + call javacomplete#logger#Log("[complete] ". log) +endfunction + +function! s:Init() + let g:JC_ClassnameCompletedFlag = 0 + let b:dotexpr = '' + let b:incomplete = '' + let b:context_type = 0 + + let s:et_whole = reltime() +endfunction + +function! javacomplete#complete#complete#Complete(findstart, base, is_filter) + if get(g:, 'JavaComplete_Disabled', 0) + return + endif + + call javacomplete#highlights#Drop() + + if a:findstart + call s:Init() + return javacomplete#complete#context#FindContext() + endif + + let base = (a:is_filter) ? a:base : + \ (a:base =~ '^@') ? a:base[:2] : a:base[:1] + let result = javacomplete#complete#context#ExecuteContext(base) + + if g:JavaComplete_CompletionResultSort + call sort(result) + endif + + if len(result) > 0 + " filter according to b:incomplete + if a:is_filter && b:incomplete != '' && 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 + + if type(result) == type([]) + call s:Log('finish completion' . reltimestr(reltime(s:et_whole)) . 's') + return result + endif + endif + + if len(get(b:, 'errormsg', '')) > 0 + call javacomplete#ClearCache() + + if get(g:, 'JavaComplete_IgnoreErrorMsg', 0) <= 0 + echom 'javacomplete error: ' . b:errormsg + let b:errormsg = '' + endif + endif + + return [] +endfunction + +function! javacomplete#complete#complete#CompleteAfterOverride() + call s:Log("complete after override") + + let ti = javacomplete#collector#DoGetClassInfo('this') + let s = '' + for i in get(ti, 'extends', []) + let parentInfo = javacomplete#collector#DoGetClassInfo(i) + let members = javacomplete#complete#complete#SearchMember(parentInfo, '', 1, 1, 1, 14, 0) + let s .= s:DoGetMethodList(members[1], 14, 0) + unlet i + endfor + let s = substitute(s, '\<\(abstract\|default\|native\)\s\+', '', 'g') + let s = javacomplete#util#CleanFQN(s) + let result = eval('[' . s . ']') + if !empty(result) + let g:JC_DeclarationCompletedFlag = 1 + endif + return result +endfunction + +function! javacomplete#complete#complete#CompleteSimilarClasses(base) + call s:Log("complete similar classes. base: ". a:base) + + let result = [] + if a:base =~ g:RE_ANNOTATION || a:base == '@' + let response = javacomplete#server#Communicate("-similar-annotations", a:base[1:], 'Filter packages by incomplete class name') + else + let b:incomplete = a:base + let response = javacomplete#server#Communicate("-similar-classes", a:base, 'Filter packages by incomplete class name') + endif + if response =~ '^[' + call extend(result, eval(response)) + endif + if !empty(result) + let g:JC_ClassnameCompletedFlag = 1 + endif + return result +endfunction + +function! javacomplete#complete#complete#CompleteSimilarClassesAndLocalMembers(base) + call s:Log("complete similar classes and local fields. base: ". a:base) + + let result = + \ javacomplete#complete#complete#CompleteSimilarClasses(a:base) + + \ s:DoGetMemberList(javacomplete#collector#DoGetClassInfo('this'), 7) + if !empty(result) + let g:JC_ClassnameCompletedFlag = 1 + endif + return result +endfunction + +function! javacomplete#complete#complete#CompleteAnnotationsParameters(name) + call s:Log("complete annotation parameters. name: ". a:name) + + let result = [] + let last = split(a:name, '@')[-1] + let identList = matchlist(last, '\('. g:RE_IDENTIFIER. '\)\((\|$\)') + if !empty(identList) + let name = identList[1] + let ti = javacomplete#collector#DoGetClassInfo(name) + if has_key(ti, 'methods') + let methods = [] + for m in ti.methods + if javacomplete#util#CheckModifier(m.m, g:JC_MODIFIER_ABSTRACT) && m.n !~ '^\(toString\|annotationType\|equals\|hashCode\)$' + call add(methods, m) + endif + endfor + call extend(result, eval('[' . s:DoGetMethodList(methods, 0, 2) . ']')) + endif + + endif + + return result +endfunction + +" Precondition: expr must end with '.' +" return members of the value of expression +function! javacomplete#complete#complete#CompleteAfterDot(expr) + call s:Log("complete after dot. expr: ". a:expr) + + let items = javacomplete#scanner#ParseExpr(a:expr) " TODO: return a dict containing more than items + if empty(items) + return [] + endif + + + " 0. String literal + if items[-1] =~ '\("\|"\.\)$' + call s:Log('P1. "str".|') + 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*' . g: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 javacomplete#util#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:Log('O1. ' . items[k] . ' ' . join(items[:k-1], '.')) + let ti = javacomplete#collector#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'), javacomplete#collector#GetPackageName()), ',') + call s:Log('O2. ' . fqn) + call javacomplete#collector#FetchClassInfo(fqn) + if get(get(g:JavaComplete_Cache, fqn, {}), 'tag', '') == 'CLASSDEF' + let ti = g:JavaComplete_Cache[fqn] + let itemkind = 11 + let ii = i + endif + endif + else + if items[0] =~ '^\s*' . g:RE_IDENTIFIER . '\s*(' + call insert(items, 'this', 0) + endif + endif + + " first item + if empty(ti) + if items[0] =~ '\("\|"\.\)$' + let items[0] = "new String()" + endif + + " 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*' . g:RE_IDENTIFIER . '\s*$' + let ident = substitute(items[0], '\s', '', 'g') + + if javacomplete#util#IsKeyword(ident) + " 1) + call s:Log('F1. "' . ident . '.|"') + if ident ==# 'void' || javacomplete#util#IsBuiltinType(ident) + let ti = g:J_PRIMITIVE_TYPE_INFO + let itemkind = 11 + + " 2) + call s:Log('F2. "' . ident . '.|"') + elseif ident ==# 'this' || ident ==# 'super' + let itemkind = ident ==# 'this' ? 1 : ident ==# 'super' ? 2 : 0 + let ti = javacomplete#collector#DoGetClassInfo(ident) + endif + + else + " 3) + let typename = javacomplete#collector#GetDeclaredClassName(ident) + call s:Log('F3. "' . ident . '.|" typename: "' . typename . '"') + if (typename != '') + if typename[0] == '[' || typename[-1:] == ']' + let ti = g:J_ARRAY_TYPE_INFO + elseif typename != 'void' && !javacomplete#util#IsBuiltinType(typename) + let ti = javacomplete#collector#DoGetClassInfo(typename) + endif + + else + " 4) + call s:Log('F4. "TypeName.|"') + let ti = javacomplete#collector#DoGetClassInfo(ident) + let itemkind = 11 + + if get(ti, 'tag', '') != 'CLASSDEF' || get(ti, 'name', '') == 'java.lang.Object' + let tib = ti + let ti = {} + endif + + " 5) + if empty(ti) + call s:Log('F5. "package.|"') + unlet ti + let ti = s:GetMembers(ident) " s:DoGetPackegInfo(ident) + if empty(ti) + unlet ti + let ti = tib + else + let itemkind = 20 + endif + endif + endif + endif + + " array type, return `class`: "int[] [].|", "java.lang.String[].|", "NestedClass[].|" + elseif items[0] =~# g:RE_ARRAY_TYPE + call s:Log('array type. "' . items[0] . '"') + let qid = substitute(items[0], g:RE_ARRAY_TYPE, '\1', '') + if javacomplete#util#IsBuiltinType(qid) || (!javacomplete#util#HasKeyword(qid) && !empty(javacomplete#collector#DoGetClassInfo(qid))) + let ti = g:J_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\+' + let joinedItems = join(items,'.') + call s:Log('creation expr. "' . joinedItems . '"') + let subs = split(substitute(joinedItems, '^\s*new\s\+\(' .g:RE_QUALID. '\)\s*\([<([]\|\)', '\1;\2', ''), ';') + if len(subs) == 1 + let ti = javacomplete#collector#DoGetClassInfo(subs[0]) + if get(ti, 'tag', '') == 'CLASSDEF' && get(ti, 'name', '') != 'java.lang.Object' + let members = javacomplete#complete#complete#SearchMember(ti, '', 1, itemkind, 1, 0) + return eval('['. s:DoGetNestedList(members[3]) . ']') + endif + return s:GetMembers(subs[0]) " may be a package + elseif subs[1][0] == '[' + let ti = g:J_ARRAY_TYPE_INFO + elseif subs[1][0] == '(' || subs[1] =~ '<>(.*' + let splitted = split(subs[0], '\.') + if len(splitted) > 1 + let directFqn = javacomplete#imports#SearchSingleTypeImport(splitted[0], javacomplete#imports#GetImports('imports_fqn', javacomplete#GetCurrentFileKey())) + if empty(directFqn) + let s = subs[0] + else + let s = substitute(subs[0], '\.', '\$', 'g') + endif + else + let s = subs[0] + endif + let ti = javacomplete#collector#DoGetClassInfo(s) + " 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] =~ g:RE_CASTING + call s:Log('Casting conversion. "' . items[0] . '"') + let subs = split(substitute(items[0], g:RE_CASTING, '\1;\2', ''), ';') + let ti = javacomplete#collector#DoGetClassInfo(subs[0]) + + " array access: "var[i][j].|" Note: "var[i][]" is incorrect + elseif items[0] =~# g:RE_ARRAY_ACCESS + let subs = split(substitute(items[0], g:RE_ARRAY_ACCESS, '\1;\2', ''), ';') + if get(subs, 1, '') !~ g:RE_BRACKETS + let typename = javacomplete#collector#GetDeclaredClassName(subs[0]) + if type(typename) == type([]) + let typename = typename[0] + endif + call s:Log('ArrayAccess. "' .items[0]. '.|" typename: "' . typename . '"') + if (typename != '') + let ti = javacomplete#complete#complete#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*' . g:RE_IDENTIFIER . '\s*(' + let tmp = ti + unlet ti + let ti = javacomplete#collector#MethodInvocation(items[ii], tmp, itemkind) + unlet tmp + let itemkind = 0 + let ii += 1 + continue + + + " expression of selection, field access, array access + elseif items[ii] =~ g:RE_SELECT_OR_ACCESS + let subs = split(substitute(items[ii], g:RE_SELECT_OR_ACCESS, '\1;\2', ''), ';') + let ident = subs[0] + let brackets = get(subs, 1, '') + + " package members + if itemkind/10 == 2 && empty(brackets) && !javacomplete#util#IsKeyword(ident) + let qn = join(items[:ii], '.') + call s:Log("package members: ". qn) + if type(ti) == type([]) + let idx = javacomplete#util#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 = javacomplete#collector#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' + call s:Log("type members: ". ident) + let ti = javacomplete#collector#DoGetClassInfo(ident == 'class' ? 'java.lang.Class' : join(items[:ii-1], '.')) + let itemkind = ident ==# 'this' ? 1 : ident ==# 'super' ? 2 : 0 + let ii += 1 + continue + + elseif !javacomplete#util#IsKeyword(ident) && type(ti) == type({}) && get(ti, 'tag', '') == 'CLASSDEF' + " accessible static field + call s:Log("static fields: ". ident) + let members = javacomplete#complete#complete#SearchMember(ti, ident, 1, itemkind, 1, 0) + if !empty(members[2]) + let ti = javacomplete#complete#complete#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 = javacomplete#collector#DoGetClassInfo(join(items[:ii], '.')) + let ii += 1 + continue + endif + + if !empty(members[3]) + if len(members[3]) > 0 + let found = 0 + for entry in members[3] + if has_key(entry, 'n') && entry.n == ident && has_key(entry, 'm') + let ti = javacomplete#collector#DoGetClassInfo(entry.m) + let ii += 1 + let found = 1 + break + endif + endfor + if found + continue + endif + endif + endif + endif + + + " instance members + elseif itemkind/10 == 0 && !javacomplete#util#IsKeyword(ident) + if type(ti) == type({}) && get(ti, 'tag', '') == 'CLASSDEF' + call s:Log("instance members") + let members = javacomplete#complete#complete#SearchMember(ti, ident, 1, itemkind, 1, 0) + let itemkind = 0 + if !empty(members[2]) + let ti = javacomplete#complete#complete#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 g:J_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 + +function! s:GetSourceDirs(filepath, ...) + call s:Log("get source dirs. filepath: ". a: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 : javacomplete#collector#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 +endfunction + +" return only classpath which are directories +function! s:GetClassDirs() + let dirs = [] + for path in split(javacomplete#server#GetClassPath(), g:PATH_SEP) + if isdirectory(path) + call add(dirs, fnamemodify(path, ':p:h')) + endif + endfor + return dirs +endfunction + +function! javacomplete#complete#complete#GetPackageName() + return javacomplete#collector#GetPackageName() +endfunction + +function! javacomplete#complete#complete#ArrayAccess(arraytype, expr) + call s:Log("array access. typename: ". a:arraytype. ", expr: ". a:expr) + + if a:expr =~ g:RE_BRACKETS | return {} | endif + let typename = a:arraytype + + let dims = 0 + if typename[0] == '[' || typename[-1:] == ']' || a:expr[-1:] == ']' + let dims = javacomplete#util#CountDims(a:expr) - javacomplete#util#CountDims(typename) + if dims == 0 + let typename = typename[0 : stridx(typename, '[') - 1] + elseif dims < 0 + return g:J_ARRAY_TYPE_INFO + else + "echoerr 'dims exceeds' + endif + endif + if dims == 0 + if typename != 'void' && !javacomplete#util#IsBuiltinType(typename) + return javacomplete#collector#DoGetClassInfo(typename) + endif + endif + return {} +endfunction + +function! s:CanAccess(mods, kind, outputkind, samePackage, isinterface) + if a:outputkind == 14 + return javacomplete#util#CheckModifier(a:mods, [g:JC_MODIFIER_PUBLIC, g:JC_MODIFIER_PROTECTED, g:JC_MODIFIER_ABSTRACT]) && !javacomplete#util#CheckModifier(a:mods, g:JC_MODIFIER_FINAL) + endif + if a:outputkind == 15 + return javacomplete#util#IsStatic(a:mods) + endif + 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:kind == 7 || a:samePackage)) + \ || (a:mods == 0 && (a:samePackage || a:isinterface)) + \ || (a:mods[-2:-2] && (a:kind == 1 || a:kind == 7))) +endfunction + +function! javacomplete#complete#complete#SearchMember(ci, name, fullmatch, kind, returnAll, outputkind, ...) + call s:Log("search member. name: ". a:name. ", kind: ". a:kind. ", outputkind: ". a:outputkind) + + let samePackage = javacomplete#complete#complete#GetPackageName() == + \ javacomplete#util#GetClassPackage(a:ci.name) + let result = [[], [], [], []] + + let isinterface = get(a:ci, 'interface', 0) + + if a:kind != 13 + if a:outputkind != 14 + for m in (a:0 > 0 && a:1 ? [] : get(a:ci, 'fields', [])) + ((a:kind == 1 || a:kind == 2 || a:kind == 7) ? 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, a:outputkind, samePackage, isinterface) + call add(result[2], m) + endif + endif + endfor + endif + + for m in (a:0 > 0 && a:1 ? [] : get(a:ci, 'methods', [])) + ((a:kind == 1 || a:kind == 2 || a:kind == 7) ? 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, a:outputkind, samePackage, isinterface) + call add(result[1], m) + endif + endif + endfor + endif + + for c in get(a:ci, 'nested', []) + let _c = substitute(c, '\$', '.', '') + if has_key(g:JavaComplete_Cache, _c) + let nestedClass = g:JavaComplete_Cache[_c] + if a:kind == 12 + if javacomplete#util#IsStatic(nestedClass.flags) + call add(result[3], {'n': split(c, '\$')[-1], 'm':c}) + endif + else + call add(result[3], {'n': split(c, '\$')[-1], 'm':c}) + endif + else + call add(result[3], {'n': split(c, '\$')[-1], 'm':c}) + endif + endfor + + 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(g:JavaComplete_Cache, t) || !has_key(g:JavaComplete_Cache[t], 'flags') || a:kind == 1 || g:JavaComplete_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', []) + if type(i) == type("") && i == get(a:ci, 'fqn', '') + continue + elseif type(i) == type({}) && i.tag == 'ERRONEOUS' + continue + endif + let ci = javacomplete#collector#DoGetClassInfo(java_parser#type2Str(i)) + if type(ci) == type([]) + let ci = ci[0] + endif + if a:outputkind == 15 + let outputkind = 11 + else + let outputkind = a:outputkind + endif + let members = javacomplete#complete#complete#SearchMember(ci, a:name, a:fullmatch, a:kind == 1 ? 2 : a:kind, a:returnAll, outputkind) + let result[0] += members[0] + let result[1] += members[1] + let result[2] += members[2] + unlet i + endfor + endif + return result +endfunction + +function! s:DoGetNestedList(classes) + let s = '' + let useFQN = javacomplete#UseFQN() + for class in a:classes + if !useFQN + let fieldType = javacomplete#util#CleanFQN(class.m) + else + let fieldType = class.m + endif + let s .= "{'kind':'C','word':'". class.n . "','menu':'". fieldType . "','dup':1}," + endfor + + return s +endfunction + +function! s:DoGetFieldList(fields) + let s = '' + let useFQN = javacomplete#UseFQN() + for field in a:fields + if !has_key(field, 't') + continue + endif + if type(field.t) == type([]) + let fieldType = field.t[0] + let args = '' + for arg in field.t[1] + let args .= arg. ',' + endfor + if len(args) > 0 + let fieldType .= '<'. args[0:-2]. '>' + endif + else + let fieldType = field.t + endif + if !useFQN + let fieldType = javacomplete#util#CleanFQN(fieldType) + endif + let s .= "{'kind':'" . (javacomplete#util#IsStatic(field.m) ? "F" : "f") . "','word':'" . field.n . "','menu':'" . fieldType . "','dup':1}," + endfor + return s +endfunction + +function! javacomplete#complete#complete#DoGetMethodList(methods, kind, ...) + return s:DoGetMethodList(a:methods, a:kind, a:000) +endfunction + +function! s:DoGetMethodList(methods, kind, ...) + let paren = a:0 == 0 || !a:1 ? '(' : (a:1 == 2) ? ' = ' : '' + + let abbrEnd = '' + if b:context_type != g:JC__CONTEXT_METHOD_REFERENCE + if a:0 == 0 || !a:1 + let abbrEnd = '()' + endif + endif + + let methodNames = map(copy(a:methods), 'v:val.n') + + let useFQN = javacomplete#UseFQN() + let s = '' + let origParen = paren + for method in a:methods + if !useFQN + let method.d = javacomplete#util#CleanFQN(method.d) + endif + let paren = origParen + if paren == '(' + if count(methodNames, method.n) == 1 + if !has_key(method, 'p') + let paren = '()' + endif + endif + endif + let s .= "{'kind':'" . (javacomplete#util#IsStatic(method.m) ? "M" : "m") . "','word':'" . s:GenWord(method, a:kind, paren) . "','abbr':'" . method.n . abbrEnd . "','menu':'" . method.d . "','info':'" . method.d ."','dup':'1'}," + endfor + + return s +endfunction + +function! s:GenWord(method, kind, paren) + if a:kind == 14 + + return javacomplete#util#GenMethodParamsDeclaration(a:method). ' {' + else + if b:context_type != g:JC__CONTEXT_METHOD_REFERENCE + if !empty(a:paren) + return a:method.n . a:paren + else + return a:method.n . '()' + endif + endif + + return a:method.n + endif +endfunction + +function! s:UniqDeclaration(members) + let declarations = {} + for m in a:members + let declarations[javacomplete#util#CleanFQN(m.d)] = m + endfor + let result = [] + for k in keys(declarations) + call add(result, declarations[k]) + endfor + return result +endfunction + +" kind: +" 0 - for instance, 1 - this, 2 - super, 3 - class, 4 - array, 5 - method result, 6 - primitive type, 7 - local fields +" 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 +" 14 - for public, protected methods of extends/implements. abstract first. +" 20 - for package +function! s:DoGetMemberList(ci, outputkind) + call s:Log("get member list. outputkind: ". a:outputkind) + + let kind = a:outputkind + let outputkind = a:outputkind + if type(a:ci) != type({}) || a:ci == {} + return [] + endif + + let s = '' + if b:context_type == g:JC__CONTEXT_METHOD_REFERENCE + let kind = 0 + if outputkind != 0 + let s = "{'kind': 'M', 'word': 'new', 'menu': 'new'}," + endif + endif + + if kind == 11 + let tmp = javacomplete#collector#DoGetClassInfo('this') + if tmp.name == get(a:ci, 'name', '') + let outputkind = 15 + endif + endif + + let members = javacomplete#complete#complete#SearchMember(a:ci, '', 1, kind, 1, outputkind, kind == 2) + let members[1] = s:UniqDeclaration(members[1]) + + let s .= kind == 11 ? "{'kind': 'C', 'word': 'class', 'menu': 'Class'}," : '' + + " add accessible member types + if 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(g:JavaComplete_Cache, class, {}) + if v == {} || v.flags[-1:] + let s .= "{'kind': 'C', 'word': '" . substitute(class, a:ci.name . '\.', '\1', '') . "','dup':1}," + endif + endfor + endif + + if kind != 13 + let fieldlist = [] + let sfieldlist = [] + for field in members[2] + "for field in get(a:ci, 'fields', []) + if javacomplete#util#IsStatic(field['m']) + if kind != 1 + call add(sfieldlist, field) + endif + elseif kind / 10 == 0 + call add(fieldlist, field) + endif + endfor + + let methodlist = [] + let smethodlist = [] + for method in members[1] + if javacomplete#util#IsStatic(method['m']) + if kind != 1 + call add(smethodlist, method) + endif + elseif kind / 10 == 0 + call add(methodlist, method) + endif + endfor + + if kind / 10 == 0 + let s .= s:DoGetFieldList(fieldlist) + let s .= s:DoGetMethodList(methodlist, outputkind) + endif + if b:context_type != g:JC__CONTEXT_METHOD_REFERENCE + let s .= s:DoGetFieldList(sfieldlist) + endif + + let s .= s:DoGetMethodList(smethodlist, outputkind, kind == 12) + let s .= s:DoGetNestedList(members[3]) + + let s = substitute(s, '\<' . a:ci.name . '\.', '', 'g') + let s = substitute(s, '\<\(public\|static\|synchronized\|transient\|volatile\|final\|strictfp\|serializable\|native\)\s\+', '', 'g') + else + let s .= s:DoGetNestedList(members[3]) + endif + return eval('[' . s . ']') +endfunction + +" interface {{{2 + +function! s:GetMemberList(class) + if javacomplete#util#IsBuiltinType(a:class) + return [] + endif + + return s:DoGetMemberList(javacomplete#collector#DoGetClassInfo(a:class), 0) +endfunction + +function! javacomplete#complete#complete#GetConstructorList(class) + let ci = javacomplete#collector#DoGetClassInfo(a:class) + if empty(ci) + return [] + endif + + let s = '' + for ctor in get(ci, 'ctors', []) + let s .= "{'kind': '+', 'word':'". a:class . "(','abbr':'" . ctor.d . "','dup':1}," + endfor + + let s = substitute(s, '\ 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 =~ '^' . g:RE_IDENTIFIER . '$' && isdirectory(f) && f !~# 'CVS$' + call add(list, {'kind': 'P', 'word': name}) + endif + endif + endfor + endfor + return list +endfunction + +" vim:set fdm=marker sw=2 nowrap: diff --git a/bundle/vim-javacomplete2/autoload/javacomplete/complete/context.vim b/bundle/vim-javacomplete2/autoload/javacomplete/complete/context.vim new file mode 100644 index 000000000..a141dd7f4 --- /dev/null +++ b/bundle/vim-javacomplete2/autoload/javacomplete/complete/context.vim @@ -0,0 +1,263 @@ +let g:JC__CONTEXT_AFTER_DOT = 1 +let g:JC__CONTEXT_METHOD_PARAM = 2 +let g:JC__CONTEXT_IMPORT = 3 +let g:JC__CONTEXT_IMPORT_STATIC = 4 +let g:JC__CONTEXT_PACKAGE_DECL = 6 +let g:JC__CONTEXT_COMPLETE_CLASSNAME = 7 +let g:JC__CONTEXT_COMPLETE_CLASSNAME_AND_LOCAL_MEMBERS = 8 +let g:JC__CONTEXT_METHOD_REFERENCE = 9 +let g:JC__CONTEXT_ANNOTATION_FIELDS = 10 +let g:JC__CONTEXT_COMPLETE_ON_OVERRIDE = 11 +let g:JC__CONTEXT_OTHER = 0 + +function! s:Log(log) + let log = type(a:log) == type("") ? a:log : string(a:log) + call javacomplete#logger#Log("[context] ". a:log) +endfunction + +function! s:ContextType2Str(type) + if a:type == g:JC__CONTEXT_COMPLETE_ON_OVERRIDE + return "CONTEXT_COMPLETE_ON_OVERRIDE" + elseif a:type == g:JC__CONTEXT_IMPORT + return "CONTEXT_IMPORT" + elseif a:type == g:JC__CONTEXT_AFTER_DOT + return "CONTEXT_AFTER_DOT" + elseif a:type == g:JC__CONTEXT_COMPLETE_CLASSNAME + return "CONTEXT_COMPLETE_CLASSNAME" + elseif a:type == g:JC__CONTEXT_COMPLETE_CLASSNAME_AND_LOCAL_MEMBERS + return "CONTEXT_COMPLETE_CLASSNAME_AND_LOCAL_MEMBERS" + elseif a:type == g:JC__CONTEXT_METHOD_PARAM + return "CONTEXT_METHOD_PARAM" + elseif a:type == g:JC__CONTEXT_PACKAGE_DECL + return "CONTEXT_PACKAGE_DECL" + elseif a:type == g:JC__CONTEXT_IMPORT_STATIC + return "CONTEXT_IMPORT_STATIC" + elseif a:type == g:JC__CONTEXT_METHOD_REFERENCE + return "CONTEXT_METHOD_REFERENCE" + elseif a:type == g:JC__CONTEXT_ANNOTATION_FIELDS + return "CONTEXT_ANNOTATION_FIELDS" + endif + return "CONTEXT_OTHER" +endfunction + +function! javacomplete#complete#context#FindContext() + let statement = javacomplete#scanner#GetStatement() + + let start = col('.') - 1 + + if statement =~ '^[@A-Z]\w*$' + let b:context_type = g:JC__CONTEXT_COMPLETE_CLASSNAME_AND_LOCAL_MEMBERS + + let curline = getline(".") + let start = col('.') - 1 + + while start > 0 && curline[start - 1] =~ '[@A-Za-z0-9_]' + let start -= 1 + if curline[start] == '@' + break + endif + endwhile + + return start + elseif 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 !~ '\C\<\(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 i = len(statement) + let quoteParity = 1 + while i >= 0 + let ch = statement[i] + if ch == '"' + let quoteParity = !quoteParity + endif + + let i -= 1 + endwhile + + if !quoteParity + let b:context_type = g:JC__CONTEXT_OTHER + return -1 + endif + + let b:context_type = g:JC__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 + let b:context_type = g:JC__CONTEXT_COMPLETE_ON_OVERRIDE + endif + endif + + let b:dotexpr = javacomplete#scanner#ExtractCleanExpr(statement) + if b:dotexpr =~ '.*::.*' + let b:context_type = g:JC__CONTEXT_METHOD_REFERENCE + let b:incomplete = strpart(b:dotexpr, stridx(b:dotexpr, ':') + 2) + let b:dotexpr = strpart(b:dotexpr, 0, strridx(b:dotexpr, ':') + 1) + return start - strlen(b:incomplete) + endif + + " all cases: " java.ut|" or " java.util.|" or "ja|" + let b:incomplete = strpart(b:dotexpr, strridx(b:dotexpr, '.')+1) + let b:dotexpr = strpart(b:dotexpr, 0, strridx(b:dotexpr, '.')+1) + return start - strlen(b:incomplete) + + elseif statement =~ '^@'. g:RE_IDENTIFIER + let b:context_type = g:JC__CONTEXT_ANNOTATION_FIELDS + let b:incomplete = substitute(statement, '\s*(\s*$', '', '') + + return start + + " method parameters, treat methodname or 'new' as an incomplete word + elseif statement =~ '(\s*$' + " TODO: Need to exclude method declaration? + let b:context_type = g:JC__CONTEXT_METHOD_PARAM + let pos = strridx(statement, '(') + let s:padding = strpart(statement, pos+1) + let start = start - (len(statement) - pos) + + let statement = substitute(statement, '\s*(\s*$', '', '') + + " new ClassName? + let str = matchstr(statement, '\]*::$' + let b:context_type = g:JC__CONTEXT_METHOD_REFERENCE + let b:dotexpr = javacomplete#scanner#ExtractCleanExpr(statement) + return start - strlen(b:incomplete) + endif + + return -1 +endfunction + +function! javacomplete#complete#context#ExecuteContext(base) + let result = [] + + call s:Log("context: ". s:ContextType2Str(b:context_type)) + if len(b:incomplete) > 0 + call s:Log("incomplete: ". b:incomplete) + endif + if len(b:dotexpr) > 0 + call s:Log("dot expression: ". b:dotexpr) + endif + + " Try to complete incomplete class name + if b:context_type == g:JC__CONTEXT_COMPLETE_CLASSNAME_AND_LOCAL_MEMBERS && a:base =~ '^[@A-Z]\([A-Za-z0-9_]*\|\)$' + let result = javacomplete#complete#complete#CompleteSimilarClassesAndLocalMembers(a:base) + elseif b:context_type == g:JC__CONTEXT_COMPLETE_CLASSNAME && a:base =~ '^[@A-Z]\([A-Za-z0-9_]*\|\)$' + let result = javacomplete#complete#complete#CompleteSimilarClasses(a:base) + elseif b:context_type == g:JC__CONTEXT_COMPLETE_ON_OVERRIDE + let result = javacomplete#complete#complete#CompleteAfterOverride() + endif + + if !empty(result) + return result + endif + + if b:dotexpr !~ '^\s*$' + if b:context_type == g:JC__CONTEXT_AFTER_DOT || b:context_type == g:JC__CONTEXT_METHOD_REFERENCE + let result = javacomplete#complete#complete#CompleteAfterDot(b:dotexpr) + elseif b:context_type == g:JC__CONTEXT_IMPORT || b:context_type == g:JC__CONTEXT_IMPORT_STATIC || b:context_type == g:JC__CONTEXT_PACKAGE_DECL + let result = javacomplete#complete#complete#GetMembers(b:dotexpr[:-2]) + elseif b:context_type == g:JC__CONTEXT_METHOD_PARAM + if b:incomplete == '+' + let result = javacomplete#complete#complete#GetConstructorList(b:dotexpr) + else + let result = javacomplete#complete#complete#CompleteAfterDot(b:dotexpr) + endif + endif + + " only incomplete word + elseif b:incomplete !~ '^\s*$' + " only need methods + if b:context_type == g:JC__CONTEXT_METHOD_PARAM + let methods = javacomplete#collector#SearchForName(b:incomplete, 0, 1)[1] + call extend(result, eval('[' . javacomplete#complete#complete#DoGetMethodList(methods, 0) . ']')) + elseif b:context_type == g:JC__CONTEXT_ANNOTATION_FIELDS + let result = javacomplete#complete#complete#CompleteAnnotationsParameters(b:incomplete) + else + let result = javacomplete#complete#complete#CompleteSimilarClassesAndLocalMembers(a:base) + endif + + " then no filter needed + let b:incomplete = '' + endif + + return result +endfunction + +" vim:set fdm=marker sw=2 nowrap: diff --git a/bundle/vim-javacomplete2/autoload/javacomplete/generators.vim b/bundle/vim-javacomplete2/autoload/javacomplete/generators.vim new file mode 100644 index 000000000..b790a8b0e --- /dev/null +++ b/bundle/vim-javacomplete2/autoload/javacomplete/generators.vim @@ -0,0 +1,634 @@ +" Vim completion script for java +" Maintainer: artur shaik +" +" Source code generators + +function! s:Log(log) + let log = type(a:log) == type("") ? a:log : string(a:log) + call javacomplete#logger#Log("[generators] ". log) +endfunction + +let g:JavaComplete_Templates = {} +let g:JavaComplete_Generators = {} + +let g:JavaComplete_Templates['setter'] = + \ "$modifiers void $funcname($type $varname) {\n" . + \ "$accessor.$varname = $varname;\n" . + \ "}" + +let g:JavaComplete_Templates['getter'] = + \ "$modifiers $type $funcname() {\n" . + \ "return $varname;\n" . + \ "}" + +let g:JavaComplete_Templates['abstractDeclaration'] = + \ "@Override\n" . + \ "$declaration {\n" . + \ "throw new UnsupportedOperationException();\n" . + \ "}" + +function! s:CollectVars() + let currentFileVars = [] + for d in s:ti.defs + if d.tag == 'VARDEF' + let var = s:GetVariable(s:ti.name, d) + call add(currentFileVars, var) + endif + endfor + return currentFileVars +endfunction + +function! javacomplete#generators#GenerateClass(options, ...) + let template = a:0 > 0 && !empty(a:1) ? '_'. a:1 : '' + let classCommand = {'template': 'class'. template, 'options': a:options, 'position_type' : 1} + call generateByTemplate(classCommand) +endfunction + +function! javacomplete#generators#GenerateConstructor(default) + let defaultConstructorCommand = {'key': '1', 'desc': 'generate default constructor', 'call': 'generateByTemplate', 'template': 'constructor', 'replace': {'type': 'same'}, 'options': {'default': 1}} + if a:default == 0 + let commands = [ + \ defaultConstructorCommand, + \ {'key': '2', 'desc': 'generate constructor', 'call': 'generateByTemplate', 'template': 'constructor', 'replace': {'type': 'same'}} + \ ] + call s:FieldsListBuffer(commands) + else + let s:ti = javacomplete#collector#DoGetClassInfo('this') + let s:savedCursorPosition = getpos('.') + call generateByTemplate(defaultConstructorCommand) + endif +endfunction + +function! javacomplete#generators#GenerateEqualsAndHashCode() + let commands = [ + \ {'key': '1', 'desc': 'generate `equals` method', 'call': 'generateByTemplate', 'template': 'equals', 'replace': {'type': 'similar'}}, + \ {'key': '2', 'desc': 'generate `hashCode` method', 'call': 'generateByTemplate', 'template': 'hashCode', 'replace': {'type': 'similar'}}, + \ {'key': '3', 'desc': 'generate `equals` and `hashCode` methods', 'call': 'generateByTemplate', 'template': ['hashCode', 'equals'], 'replace': {'type': 'similar'}} + \ ] + call s:FieldsListBuffer(commands) +endfunction + +function! javacomplete#generators#GenerateToString() + let commands = [ + \ {'key': '1', 'desc': 'generate `toString` method using concatination', 'call': 'generateByTemplate', 'template': 'toString_concat', 'replace': {'type': 'similar'}}, + \ {'key': '2', 'desc': 'generate `toString` method using StringBuilder', 'call': 'generateByTemplate', 'template': 'toString_StringBuilder', 'replace': {'type': 'similar'}} + \ ] + call s:FieldsListBuffer(commands) +endfunction + +function! s:FieldsListBuffer(commands) + let s:ti = javacomplete#collector#DoGetClassInfo('this') + let s:savedCursorPosition = getpos('.') + let contentLine = s:CreateBuffer("__FieldsListBuffer__", "remove unnecessary fields", a:commands) + + let b:currentFileVars = s:CollectVars() + + let lines = "" + let idx = 0 + while idx < len(b:currentFileVars) + let var = b:currentFileVars[idx] + let lines = lines. "\n". "f". idx. " --> ". var.type . " ". var.name + let idx += 1 + endwhile + silent put = lines + + call cursor(contentLine + 1, 0) +endfunction + +function! javacomplete#generators#GenerateByTemplate(command) + call generateByTemplate(a:command) +endfunction + +" a:1 - method declaration to replace +function! generateByTemplate(command) + let command = a:command + if !has_key(command, 'fields') + let command['fields'] = [] + endif + + if bufname('%') == "__FieldsListBuffer__" + call s:Log("generate method with template: ". string(command.template)) + + let currentBuf = getline(1,'$') + for line in currentBuf + if line =~ '^f[0-9]\+.*' + let cmd = line[0] + let idx = line[1:stridx(line, ' ')-1] + let var = b:currentFileVars[idx] + call add(command['fields'], var) + endif + endfor + + execute "bwipeout!" + endif + + let result = [] + let templates = type(command.template) != type([]) ? [command.template] : command.template + let class = {} + if has_key(s:, 'ti') + let class['name'] = s:ti.name + endif + let class['fields'] = command['fields'] + for template in templates + call s:CheckAndLoadTemplate(template) + if has_key(g:JavaComplete_Generators, template) + call s:Log(g:JavaComplete_Generators[template]['data']) + execute g:JavaComplete_Generators[template]['data'] + + let arguments = [class] + if has_key(command, 'options') + call add(arguments, command.options) + endif + let TemplateFunction = function('s:__'. template) + for line in split(call(TemplateFunction, arguments), '\n') + call add(result, line) + endfor + call add(result, '') + endif + endfor + + if len(result) > 0 + if has_key(command, 'replace') + let toReplace = [] + if command.replace.type == 'same' + let defs = s:GetNewMethodsDefinitions(result) + for def in defs + call add(toReplace, def.d) + endfor + elseif command.replace.type == 'similar' + let defs = s:GetNewMethodsDefinitions(result) + for def in defs + let m = s:FindMethod(s:ti.methods, def) + if !empty(m) + call add(toReplace, m.d) + endif + endfor + endif + + let idx = 0 + while idx < len(s:ti.defs) + let def = s:ti.defs[idx] + if get(def, 'tag', '') == 'METHODDEF' + \ && index(toReplace, get(def, 'd', '')) > -1 + \ && has_key(def, 'body') && has_key(def.body, 'endpos') + + let startline = java_parser#DecodePos(def.pos).line + if !empty(getline(startline)) + let startline += 1 + endif + let endline = java_parser#DecodePos(def.body.endpos).line + 1 + silent! execute startline.','.endline. 'delete _' + + call setpos('.', s:savedCursorPosition) + let s:ti = javacomplete#collector#DoGetClassInfo('this') + let idx = 0 + else + let idx += 1 + endif + endwhile + endif + if has_key(command, 'position_type') + call s:InsertResults(result, command['position_type']) + else + call s:InsertResults(result) + endif + if has_key(s:, 'savedCursorPosition') + call setpos('.', s:savedCursorPosition) + endif + endif +endfunction + +function! s:CheckAndLoadTemplate(template) + let filenames = [] + if isdirectory(g:JavaComplete_CustomTemplateDirectory) + call add(filenames, expand(g:JavaComplete_CustomTemplateDirectory). '/gen__'. a:template. '.tpl') + endif + call add(filenames, g:JavaComplete_Home. '/plugin/res/gen__'. a:template. '.tpl') + for filename in filenames + if filereadable(filename) + if has_key(g:JavaComplete_Generators, a:template) + if getftime(filename) > g:JavaComplete_Generators[a:template]['file_time'] + let g:JavaComplete_Generators[a:template]['data'] = join(readfile(filename), "\n") + let g:JavaComplete_Generators[a:template]['file_time'] = getftime(filename) + endif + else + let g:JavaComplete_Generators[a:template] = {} + let g:JavaComplete_Generators[a:template]['data'] = join(readfile(filename), "\n") + let g:JavaComplete_Generators[a:template]['file_time'] = getftime(filename) + endif + break + endif + endfor +endfunction + +function! javacomplete#generators#AbstractDeclaration() + let s:ti = javacomplete#collector#DoGetClassInfo('this') + if get(s:ti, 'interface', 0) == 1 + return + endif + let s = '' + let abstractMethods = [] + let implementedMethods = [] + for i in get(s:ti, 'extends', []) + let parentInfo = javacomplete#collector#DoGetClassInfo(i) + let members = javacomplete#complete#complete#SearchMember(parentInfo, '', 1, 1, 1, 14, 0) + for m in members[1] + if javacomplete#util#CheckModifier(m.m, [g:JC_MODIFIER_ABSTRACT]) + call add(abstractMethods, m) + elseif javacomplete#util#CheckModifier(m.m, [g:JC_MODIFIER_PUBLIC]) + call add(implementedMethods, m) + endif + endfor + unlet i + endfor + + let result = [] + let method = g:JavaComplete_Templates['abstractDeclaration'] + for m in abstractMethods + if !empty(s:CheckImplementationExistense(s:ti, implementedMethods, m)) + continue + endif + let declaration = javacomplete#util#GenMethodParamsDeclaration(m) + let declaration = substitute(declaration, '\<\(abstract\|default\|native\)\s\+', '', 'g') + let declaration = javacomplete#util#CleanFQN(declaration) + + call add(result, '') + for line in split(substitute(method, '$declaration', declaration, 'g'), '\n') + call add(result, line) + endfor + + call add(implementedMethods, m) + endfor + + call s:InsertResults(result) +endfunction + +" ti - this class info +" implementedMethods - implemented methods from parent class +" method - method to check +function! s:CheckImplementationExistense(ti, implementedMethods, method) + let methods = a:ti.methods + call extend(methods, a:implementedMethods) + return s:FindMethod(methods, a:method) +endfunction + +function! s:GetParams(params) + let params = [] + for param in a:params + if type(param) == type({}) && has_key(param, 'type') + if has_key(param.type, 'name') + call add(params, javacomplete#util#CleanFQN(param.type.name)) + elseif has_key(param.type, 'clazz') && has_key(param.type.clazz, 'name') + let name = javacomplete#util#CleanFQN(param.type.clazz.name) + if has_key(param.type, 'arguments') + let args = [] + for arg in param.type.arguments + if type(arg) == type({}) + if len(arg.name) == 1 + call add(params, '\('. g:RE_TYPE_ARGUMENT_EXTENDS. '\|'. g:RE_TYPE. '\)') + else + call add(params, arg.name) + endif + else + call add(params, '?') + endif + endfor + let name .= '<'. join(args, ',\s*'). '>' + endif + call add(params, name) + elseif has_key(param.type, 'typetag') + call add(params, param.type.typetag) + elseif has_key(param.type, 'tag') && param.type.tag == 'TYPEARRAY' + if has_key(param.type, 'elementtype') && has_key(param.type.elementtype, 'name') + call add(params, param.type.elementtype.name . '[]') + endif + endif + endif + endfor + return params +endfunction + +function! s:FindMethod(methods, method) + let searchMethodParamList = [] + if has_key(a:method, 'p') + for p in a:method.p + call add(searchMethodParamList, javacomplete#util#CleanFQN(p)) + endfor + elseif has_key(a:method, 'params') + call extend(searchMethodParamList, s:GetParams(a:method.params)) + endif + + let methodDeclaration = javacomplete#util#CleanFQN(a:method.r . ' '. a:method.n) + for method in a:methods + if methodDeclaration ==# javacomplete#util#CleanFQN(method.r . ' '. method.n) + let methodParamList = [] + if has_key(method, 'params') + call extend(methodParamList, s:GetParams(method.params)) + elseif has_key(method, 'p') + for param in method.p + if type(param) == type("") + call add(methodParamList, javacomplete#util#CleanFQN(param)) + endif + endfor + endif + + " compare parameters need to be done with regexp because of separator of + " arguments if paramater has generic arguments + let i = 0 + let _continue = 0 + for p in searchMethodParamList + if i < len(methodParamList) + if p !~ methodParamList[i] + let _continue = 1 + break + endif + else + let _continue = 1 + break + endif + let i += 1 + endfor + if _continue == 1 + continue + endif + return method + endif + endfor + return {} +endfunction + +function! s:CreateBuffer(name, title, commands) + let n = bufwinnr(a:name) + if n != -1 + execute "bwipeout!" + endif + exec 'silent! split '. a:name + + " Mark the buffer as scratch + setlocal buftype=nofile + setlocal bufhidden=wipe + setlocal noswapfile + setlocal nowrap + setlocal nobuflisted + + nnoremap q :bwipeout! + + syn match Comment "^\".*" + put = '\"-----------------------------------------------------' + put = '\" '. a:title + put = '\" ' + put = '\" q - close this window' + for command in a:commands + put = '\" '. command.key . ' - '. command.desc + if has_key(command, 'call') + exec "nnoremap ". command.key . " :call ". command.call . "(". string(command). ")" + endif + endfor + put = '\"-----------------------------------------------------' + + return line(".") + 1 +endfunction + +function! javacomplete#generators#Accessors() + let s:ti = javacomplete#collector#DoGetClassInfo('this') + + let commands = [{'key': 's', 'desc': 'generate accessors', 'call': 'generateAccessors'}] + let contentLine = s:CreateBuffer("__AccessorsBuffer__", "remove unnecessary accessors", commands) + + let b:currentFileVars = s:CollectVars() + + let lines = "" + let idx = 0 + while idx < len(b:currentFileVars) + let var = b:currentFileVars[idx] + let varName = toupper(var.name[0]). var.name[1:] + let lines = lines. "\n". "g". idx. " --> ". var.type . " get". varName . "()" + if !var.final + let lines = lines. "\n". "s". idx. " --> ". "set". varName . "(". var.type . " ". var.name. ")" + endif + let lines = lines. "\n" + + let idx += 1 + endwhile + silent put = lines + + call cursor(contentLine + 1, 0) + +endfunction + +function! javacomplete#generators#Accessor(...) + let s:ti = javacomplete#collector#DoGetClassInfo('this') + call generateAccessors(a:000) +endfunction + +function! s:AddAccessor(map, result, var, declaration, type) + let method = g:JavaComplete_Templates[a:type] + + let mods = "public" + if a:var.static + let mods = mods . " static" + let accessor = a:var.className + else + let accessor = 'this' + endif + + let method = substitute(method, '$type', a:var.type, 'g') + let method = substitute(method, '$varname', a:var.name, 'g') + let method = substitute(method, '$funcname', a:declaration, 'g') + let method = substitute(method, '$modifiers', mods, 'g') + let method = substitute(method, '$accessor', accessor, 'g') + + let begin = len(a:result) + call add(a:result, '') + for line in split(method, '\n') + call add(a:result, line) + endfor + let end = len(a:result) + call add(a:map, [begin, end]) +endfunction + +function! s:GetVariable(className, def) + let var = { + \ 'name': a:def.name, + \ 'type': a:def.t, + \ 'className': a:className, + \ 'static': javacomplete#util#IsStatic(a:def.m), + \ 'final': javacomplete#util#CheckModifier(a:def.m, g:JC_MODIFIER_FINAL), + \ 'isArray': a:def.t =~# g:RE_ARRAY_TYPE} + + let varName = toupper(var.name[0]). var.name[1:] + for def in get(s:ti, 'defs', []) + if get(def, 'tag', '') == 'METHODDEF' + if stridx(get(def, 'd', ''), var.type. ' get'. varName. '()') > -1 + let var.getter = 'get'. varName. '()' + break + endif + endif + endfor + return var +endfunction + +function! s:CreateAccessors(map, result, var, cmd) + let varName = toupper(a:var.name[0]). a:var.name[1:] + if !a:var.final && stridx(a:cmd, 's') > -1 + call s:AddAccessor(a:map, a:result, a:var, "set". varName, 'setter') + endif + if stridx(a:cmd, 'g') > -1 + call s:AddAccessor(a:map, a:result, a:var, "get". varName, 'getter') + endif +endfunction + +function! generateAccessors(...) + let result = [] + let locationMap = [] + if bufname('%') == "__AccessorsBuffer__" + call s:Log("generate accessor for selected fields") + let currentBuf = getline(1,'$') + for line in currentBuf + if line =~ '^\(g\|s\)[0-9]\+.*' + let cmd = line[0] + let idx = line[1:stridx(line, ' ')-1] + let var = b:currentFileVars[idx] + call s:CreateAccessors(locationMap, result, var, cmd) + endif + endfor + + execute "bwipeout!" + else + call s:Log("generate accessor for fields under cursor") + if mode() == 'n' + let currentLines = [line('.') - 1] + elseif mode() == 'v' + let [lnum1, col1] = getpos("'<")[1:2] + let [lnum2, col2] = getpos("'>")[1:2] + let currentLines = range(lnum1 - 1, lnum2 - 1) + else + let currentLines = [] + endif + for d in get(s:ti, 'defs', []) + if get(d, 'tag', '') == 'VARDEF' + let line = java_parser#DecodePos(d.pos).line + if has_key(d, 'endpos') + let endline = java_parser#DecodePos(d.endpos).line + else + let endline = line + endif + for l in currentLines + if l >= line && l <= endline + let cmd = len(a:1) > 0 ? a:1[0] : 'sg' + let var = s:GetVariable(s:ti.name, d) + call s:CreateAccessors(locationMap, result, var, cmd) + endif + endfor + endif + endfor + + endif + + call s:InsertResults(s:FilterExistedMethods(locationMap, result)) +endfunction + +function! s:FilterExistedMethods(locationMap, result) + let resultMethods = [] + for def in s:GetNewMethodsDefinitions(a:result) + if !empty(s:CheckImplementationExistense(s:ti, [], def)) + continue + endif + for m in a:locationMap + if m[0] <= def.beginline && m[1] >= def.endline + call extend(resultMethods, a:result[m[0] : m[1] -1]) + break + endif + endfor + endfor + + return resultMethods +endfunction + +" create temporary buffer with class declaration, then parse it to get new +" methods definitions. +function! s:GetNewMethodsDefinitions(declarations) + let n = bufwinnr("__tmp_buffer__") + if n != -1 + execute "bwipeout!" + endif + silent! split __tmp_buffer__ + let result = ['class Tmp {'] + call extend(result, a:declarations) + call add(result, '}') + call append(0, result) + let tmpClassInfo = javacomplete#collector#DoGetClassInfo('this', '__tmp_buffer__') + let defs = [] + for def in get(tmpClassInfo, 'defs', []) + if get(def, 'tag', '') == 'METHODDEF' + let def.beginline = java_parser#DecodePos(def.pos).line + let def.endline = java_parser#DecodePos(def.body.endpos).line + call add(defs, def) + endif + endfor + execute "bwipeout!" + + return defs +endfunction + +function! s:InsertResults(result, ...) + if len(a:result) > 0 + let positionType = a:0 > 0 && len(a:1) > 0 ? a:1 : 'END' + if positionType == 'END' + call s:InsertAtTheEndOfTheClass(a:result) + return + endif + + call s:Log(a:result) + call append(0, a:result) + silent execute "normal! dd" + silent execute "normal! =G" + endif +endfunction + +function! s:InsertAtTheEndOfTheClass(result) + let result = a:result + let t = javacomplete#collector#CurrentFileInfo() + let contentLine = line('.') + let currentCol = col('.') + let posResult = {} + for clazz in values(t) + if contentLine > clazz.pos[0] && contentLine <= clazz.endpos[0] + let posResult[clazz.endpos[0] - clazz.pos[0]] = clazz.endpos + endif + endfor + + let saveCursor = getpos('.') + call s:Log(posResult) + if len(posResult) > 0 + let pos = posResult[min(keys(posResult))] + let endline = pos[0] + if pos[1] > 1 && !empty(javacomplete#util#Trim(getline(pos[0])[:pos[1] - 2])) + let endline += 1 + call cursor(pos[0], pos[1]) + execute "normal! i\r" + endif + elseif has_key(s:, 'ti') && has_key(s:ti, 'endpos') + let endline = java_parser#DecodePos(s:ti.endpos).line + else + call s:Log("cannot find `endpos` [InsertResult]") + return + endif + + if empty(javacomplete#util#Trim(getline(endline - 1))) + if empty(result[0]) + let result = result[1:] + endif + elseif !empty(result[0]) + call insert(result, '', 0) + endif + + call append(endline - 1, result) + call cursor(endline - 1, 1) + silent execute "normal! =G" + if has('saveCursor') + call setpos('.', saveCursor) + endif +endfunction + +" vim:set fdm=marker sw=2 nowrap: diff --git a/bundle/vim-javacomplete2/autoload/javacomplete/highlights.vim b/bundle/vim-javacomplete2/autoload/javacomplete/highlights.vim new file mode 100644 index 000000000..cde96c04a --- /dev/null +++ b/bundle/vim-javacomplete2/autoload/javacomplete/highlights.vim @@ -0,0 +1,44 @@ +" Vim completion script for java +" Maintainer: artur shaik +" +" Work with attention highlights + +let s:matchesCount = 0 +let s:signId = 271992 +sign define jc2signparseproblem text=-> + +function! s:Log(log) + let log = type(a:log) == type("") ? a:log : string(a:log) + call javacomplete#logger#Log("[highlights] ". log) +endfunction + +function! javacomplete#highlights#Drop() + if s:matchesCount > 0 && !empty(getmatches()) + lclose + exe "sign unplace * file=". expand("%:p") + call clearmatches() + call setloclist(0, [], 'f') + let s:matchesCount = len(getmatches()) + endif +endfunction + +function! javacomplete#highlights#ShowProblems(problems) + let loclist = [] + let matchposlist = [] + for problem in a:problems + call extend(loclist,[{ + \ 'bufnr':bufnr('%'), + \ 'lnum': problem['lnum'], + \ 'col': problem['col'], + \ 'text': problem['message']}]) + call add(matchposlist,[problem['lnum'], problem['col']]) + exe ":sign place ".s:signId." line=".problem['lnum']. + \ " name=jc2signparseproblem file=" . expand("%:p") + endfor + if !empty(matchposlist) + let s:matchesCount = len(matchposlist) + call setloclist(0, loclist, 'r') + call matchaddpos("SpellBad", matchposlist) + lopen + endif +endfunction diff --git a/bundle/vim-javacomplete2/autoload/javacomplete/imports.vim b/bundle/vim-javacomplete2/autoload/javacomplete/imports.vim new file mode 100644 index 000000000..444a13a97 --- /dev/null +++ b/bundle/vim-javacomplete2/autoload/javacomplete/imports.vim @@ -0,0 +1,576 @@ +" Vim completion script for java +" Maintainer: artur shaik +" +" Everything to work with imports + +function! s:Log(log) + let log = type(a:log) == type("") ? a:log : string(a:log) + call javacomplete#logger#Log("[imports] ". log) +endfunction + +" Similar with filter(), but returns a new list instead of operating in-place. +" `item` has the value of the current item. +function! 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 + +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 stat = matchlist(str, '.*import\s*=\s*[''"]\([a-zA-Z0-9_$.*, \t]\+\)[''"].*') + if !empty(stat) + for item in stat[1:] + if !empty(item) + for i in split(item, ',') + call add(imports, [substitute(i, '\s', '', 'g'), lnum]) + endfor + endif + endfor + endif + endif + call cursor(lnum + 1, 1) + endwhile + else + while 1 + let lnum = search('\', 'Wc') + if (lnum == 0) + break + elseif !javacomplete#util#InComment(line("."), col(".")-1) + call search(' \S', 'e') + " TODO: search semicolon or import keyword, excluding comment + let stat = matchstr(getline(lnum)[col('.')-1:], '\(static\s\+\)\?\(' .g:RE_QUALID. '\%(\s*\.\s*\*\)\?\)\s*;') + if !empty(stat) + call add(imports, [stat[:-2], lnum]) + endif + else + let curPos = getpos('.') + call cursor(curPos[1] + 1, curPos[2]) + endif + endwhile + endif + + call cursor(lnum_old, col_old) + return imports +endfunction + +function! javacomplete#imports#GetImports(kind, ...) + let filekey = a:0 > 0 && !empty(a:1) ? a:1 : javacomplete#GetCurrentFileKey() + let props = get(g:JavaComplete_Files, filekey, {}) + let props['imports'] = filekey == javacomplete#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 = matchlist(import[0], '^\s*\(static\s\+\)\?\(' .g:RE_QUALID. '\%(\s*\.\s*\*\)\?\)\s*$') + if !empty(subs) + let qid = substitute(subs[2] , '\s', '', 'g') + if !empty(subs[1]) + if qid[-1:] == '*' + call add(props.imports_static, qid[:-2]) + else + call add(props.imports_static, qid) + call add(props.imports_fqn, qid) + endif + elseif qid[-1:] == '*' + call add(props.imports_star, qid[:-2]) + else + call add(props.imports_fqn, qid) + endif + endif + endfor + let g:JavaComplete_Files[filekey] = props + return get(props, a:kind, []) +endfu + +" search for name in +" return the fqn matched +function! javacomplete#imports#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] +function! javacomplete#imports#SearchStaticImports(name, fullmatch) + let result = [[], [], []] + let candidates = [] " list of the canonical name + for item in javacomplete#imports#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, '.') - 1]) + 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(g:JavaComplete_Cache, typename) + let res = javacomplete#server#Communicate('-E', typename, 's:SearchStaticImports') + if res =~ "^{'" + let dict = eval(res) + for key in keys(dict) + let g:JavaComplete_Cache[key] = javacomplete#util#Sort(dict[key]) + endfor + endif + endif + endfor + + " search in all candidates + for typename in candidates + let ti = get(g:JavaComplete_Cache, typename, 0) + if type(ti) == type({}) && get(ti, 'tag', '') == 'CLASSDEF' + let members = javacomplete#complete#complete#SearchMember(ti, a:name, a:fullmatch, 12, 1, 0) + if !empty(members[1]) || !empty(members[2]) + call add(result[0], ti) + endif + let result[1] += members[1] + let result[2] += members[2] + else + " TODO: mark the wrong import declaration. + endif + endfor + return result +endfu + +function! javacomplete#imports#SortImports() + let imports = javacomplete#imports#GetImports('imports') + if (len(imports) > 0) + let beginLine = imports[0][1] + let lastLine = imports[len(imports) - 1][1] + let importsList = [] + for import in imports + call add(importsList, import[0]) + endfor + + call sort(importsList) + let importsListSorted = s:SortImportsList(importsList) + + if g:JavaComplete_StaticImportsAtTop + let importsListSorted = s:StaticImportsFirst(importsListSorted) + endif + + let saveCursor = getpos('.') + silent execute beginLine.','.lastLine. 'delete _' + for imp in importsListSorted + if imp != '' + if &ft == 'jsp' + call append(beginLine - 1, '<%@ page import = "'. imp. '" %>') + else + call append(beginLine - 1, 'import '. imp. ';') + endif + else + call append(beginLine - 1, '') + endif + let beginLine += 1 + endfor + let saveCursor[1] += beginLine - lastLine - 1 + call setpos('.', saveCursor) + endif +endfunction + +function! s:AddImport(import) + if exists('g:JavaComplete_ExcludeClassRegex') + if a:import =~ get(g:, 'JavaComplete_ExcludeClassRegex') + return + endif + endif + + let importPackage = a:import[0:strridx(a:import, '.') - 1] + if importPackage == javacomplete#collector#GetPackageName() + return + endif + + let isStaticImport = a:import =~ "^static.*" ? 1 : 0 + let import = substitute(a:import, "\\$", ".", "g") + if !isStaticImport + let importsFqn = javacomplete#imports#GetImports('imports_fqn') + let importsStar = javacomplete#imports#GetImports('imports_star') + else + let importsStar = javacomplete#imports#GetImports('imports_static') + let importsFqn = importsStar + let import = import[stridx(import, " ") + 1:] + endif + + for imp in importsFqn + if imp == import + redraw + echom 'JavaComplete: import for '. import. ' already exists' + return + endif + endfor + + let splittedImport = split(import, '\.') + let className = splittedImport[-1] + call remove(splittedImport, len(splittedImport) - 1) + let importPath = join(splittedImport, '.') + for imp in importsStar + if imp == importPath. '.' + redraw + echom 'JavaComplete: import for '. import. ' already exists' + return + endif + endfor + + if className != '*' + if has_key(g:JavaComplete_Cache, className) + call remove(g:JavaComplete_Cache, className) + endif + endif + + let imports = javacomplete#imports#GetImports('imports') + if empty(imports) + for i in range(line('$')) + if getline(i) =~ '^package\s\+.*\;$' + let insertline = i + 2 + call append(i, '') + break + endif + endfor + if !exists('insertline') + let insertline = 1 + endif + let linesCount = line('$') + while (javacomplete#util#Trim(getline(insertline)) == '' && insertline < linesCount) + silent execute insertline. 'delete _' + endwhile + + let insertline = insertline - 1 + let newline = 1 + else + let replaceIdx = -1 + let idx = 0 + for i in imports + if split(i[0], '\.')[-1] == className + let replaceIdx = idx + break + endif + let idx += 1 + endfor + let insertline = imports[len(imports) - 1][1] - 1 + let newline = 0 + if replaceIdx >= 0 + let saveCursor = getcurpos() + silent execute imports[replaceIdx][1]. 'normal! dd' + call remove(imports, replaceIdx) + let saveCursor[1] -= 1 + call setpos('.', saveCursor) + endif + endif + + if &ft == 'jsp' + call append(insertline, '<%@ page import = "'. import. '" %>') + else + if isStaticImport + call append(insertline, 'import static '. import. ';') + else + call append(insertline, 'import '. import. ';') + endif + endif + + if newline + call append(insertline + 1, '') + endif + +endfunction + +function! s:StaticImportsFirst(importsList) + let staticImportsList = [] + let l_a = copy(a:importsList) + for imp in l_a + if imp =~ '^static' + call remove(a:importsList, index(a:importsList, imp)) + call add(staticImportsList, imp) + endif + endfor + if len(staticImportsList) > 0 + call add(staticImportsList, '') + endif + return staticImportsList + a:importsList +endfunction + +function! s:SortImportsList(importsList, ...) + let sortType = a:0 > 0 ? a:1 : g:JavaComplete_ImportSortType + let importsListSorted = [] + if sortType == 'packageName' + let beforeWildcardSorted = [] + let afterWildcardSorted = [''] + let wildcardSeen = 0 + for a in g:JavaComplete_ImportOrder + if a ==? '*' + let wildcardSeen = 1 + continue + endif + let l_a = filter(copy(a:importsList),"v:val =~? '^" . substitute(a, '\.', '\\.', 'g') . "'") + if len(l_a) > 0 + for imp in l_a + call remove(a:importsList, index(a:importsList, imp)) + if wildcardSeen == 0 + call add(beforeWildcardSorted, imp) + else + call add(afterWildcardSorted, imp) + endif + endfor + if wildcardSeen == 0 + call add(beforeWildcardSorted, '') + else + call add(afterWildcardSorted, '') + endif + endif + endfor + let importsListSorted = beforeWildcardSorted + a:importsList + afterWildcardSorted + else + let response = javacomplete#server#Communicate("-fetch-class-archives", join(a:importsList, ","), "Fetch imports jar archives") + if response =~ '^[' + let result = sort(eval(response), 's:_SortArchivesByFirstClassName') + for jar in result + for classFqn in sort(jar[1]) + let idx = index(a:importsList, classFqn) + let cf = a:importsList[idx] + call remove(a:importsList, idx) + call add(importsListSorted, cf) + endfor + call add(importsListSorted, '') + endfor + endif + if len(a:importsList) > 0 + for imp in a:importsList + call add(importsListSorted, imp) + endfor + endif + endif + while (len(importsListSorted) > 0) && (importsListSorted[-1] ==? '') + call remove(importsListSorted, -1) + endwhile + return importsListSorted +endfunction + +function! s:_SortArchivesByFirstClassName(i1, i2) + return a:i1[1][0] > a:i2[1][0] +endfunction + +function! s:_SortStaticToEnd(i1, i2) + if stridx(a:i1, '$') >= 0 && stridx(a:i2, '$') < 0 + return 1 + elseif stridx(a:i2, '$') >= 0 && stridx(a:i1, '$') < 0 + return -1 + else + return a:i1 > a:i2 + endif +endfunction + +" a:1 - use smart import if True +function! javacomplete#imports#Add(...) + call javacomplete#server#Start() + + let i = 0 + let classname = '' + while empty(classname) + let offset = col('.') - i + if offset <= 0 + return + endif + let classname = javacomplete#util#GetClassNameWithScope(offset) + let i += 1 + endwhile + + if classname =~ '^@.*' + let classname = classname[1:] + endif + if index(g:J_KEYWORDS, classname) >= 0 + return + endif + if a:0 > 0 && a:1 && index(keys(javacomplete#util#GetRegularClassesDict()), classname) >= 0 + call s:AddImport(javacomplete#util#GetRegularClassesDict()[classname]) + call javacomplete#imports#SortImports() + else + let response = javacomplete#server#Communicate("-class-packages", classname, 'Filter packages to add import') + if response =~ '^[' + let result = eval(response) + let import = s:ChooseImportOption(result, classname) + + if !empty(import) + call s:AddImport(import) + call javacomplete#imports#SortImports() + endif + endif + endif +endfunction + +function! javacomplete#imports#getType(...) + call javacomplete#server#Start() + + let i = 0 + let classname = '' + while empty(classname) + let offset = col('.') - i + if offset <= 0 + return + endif + let classname = javacomplete#util#GetClassNameWithScope(offset) + let i += 1 + endwhile + + if classname =~ '^@.*' + let classname = classname[1:] + endif + if index(keys(javacomplete#util#GetRegularClassesDict()), classname) != -1 + echo javacomplete#util#GetRegularClassesDict()[classname] + else + endif +endfunction + +function! s:ChooseImportOption(options, classname) + let import = '' + let options = a:options + if len(options) == 0 + echo "JavaComplete: classname '". a:classname. "' not found in any scope." + + elseif len(options) == 1 + let import = options[0] + + else + call sort(options, 's:_SortStaticToEnd') + let options = s:SortImportsList(options, 'packageName') + let index = 0 + let message = '' + for imp in options + if len(imp) == 0 + let message .= "\n" + else + let message .= "candidate [". index. "]: ". imp. "\n" + endif + let index += 1 + endfor + let message .= "\nselect one candidate [". g:JavaComplete_ImportDefault."]: " + let userinput = input(message, '') + if empty(userinput) + let userinput = g:JavaComplete_ImportDefault + elseif userinput =~ '^[0-9]*$' + let userinput = str2nr(userinput) + else + let userinput = -1 + endif + redraw! + + if userinput < 0 || userinput >= len(options) + echo "JavaComplete: wrong input" + else + let import = options[userinput] + call s:PopulateRegularClasses(a:classname, import) + endif + endif + return import +endfunction + +function! s:PopulateRegularClasses(classname, import) + let s:RegularClassesDict = javacomplete#util#GetRegularClassesDict() + let s:RegularClassesDict[a:classname] = a:import + call javacomplete#util#SaveRegularClassesList(s:RegularClassesDict) +endfunction + +function! javacomplete#imports#RemoveUnused() + call javacomplete#highlights#Drop() + + let currentBuf = getline(1,'$') + let base64Content = javacomplete#util#Base64Encode(join(currentBuf, "\n")) + + let response = javacomplete#server#Communicate('-unused-imports -content', base64Content, 'RemoveUnusedImports') + if response =~ '^{' + let response = eval(response) + if has_key(response, 'imports') + let saveCursor = getpos('.') + let unusedImports = response['imports'] + for unusedImport in unusedImports + let imports = javacomplete#imports#GetImports('imports') + if stridx(unusedImport, '$') != -1 + let unusedImport = 'static '. substitute(unusedImport, "\\$", ".", "") + endif + for import in imports + if import[0] == unusedImport + silent execute import[1]. 'delete _' + endif + endfor + endfor + let saveCursor[1] = saveCursor[1] - len(unusedImports) + call setpos('.', saveCursor) + elseif has_key(response, 'parse-problems') + call javacomplete#highlights#ShowProblems(response['parse-problems']) + endif + endif +endfunction + +function! javacomplete#imports#AddMissing() + call javacomplete#highlights#Drop() + + let currentBuf = getline(1,'$') + let base64Content = javacomplete#util#Base64Encode(join(currentBuf, "\n")) + + let response = javacomplete#server#Communicate('-missing-imports -content', base64Content, 'AddMissingImports') + if response =~ '^{' + let response = eval(response) + if has_key(response, 'imports') + for import in response['imports'] + let classname = split(import[0], '\(\.\|\$\)')[-1] + if index(keys(javacomplete#util#GetRegularClassesDict()), classname) < 0 + let result = s:ChooseImportOption(import, classname) + if !empty(result) + call s:AddImport(result) + endif + else + call s:AddImport(javacomplete#util#GetRegularClassesDict()[classname]) + endif + endfor + call javacomplete#imports#SortImports() + elseif has_key(response, 'parse-problems') + call javacomplete#highlights#ShowProblems(response['parse-problems']) + endif + else + echo response + endif +endfunction + +" vim:set fdm=marker sw=2 nowrap: diff --git a/bundle/vim-javacomplete2/autoload/javacomplete/logger.vim b/bundle/vim-javacomplete2/autoload/javacomplete/logger.vim new file mode 100644 index 000000000..73ec89cab --- /dev/null +++ b/bundle/vim-javacomplete2/autoload/javacomplete/logger.vim @@ -0,0 +1,35 @@ +" Vim completion script for java +" Maintainer: artur shaik +" +" Debug methods + +let s:log = [] +let s:loglevel = 1 +if !exists('s:startupDate') + let s:startupDate = reltime() +endif + +function! javacomplete#logger#Enable() + let s:loglevel = 0 +endfunction + +function! javacomplete#logger#Disable() + let s:loglevel = 1 +endfunction + +function! javacomplete#logger#GetContent() + new + set modifiable + put =s:log + set nomodifiable + set nomodified +endfunction + +function! javacomplete#logger#Log(log) + if 0 >= s:loglevel + let log = type(a:log) == type("") ? a:log : string(a:log) + call add(s:log, reltimestr(reltime(s:startupDate)). " ". log) + endif +endfunction + +" vim:set fdm=marker sw=2 nowrap: diff --git a/bundle/vim-javacomplete2/autoload/javacomplete/newclass.vim b/bundle/vim-javacomplete2/autoload/javacomplete/newclass.vim new file mode 100644 index 000000000..b5e2b9d47 --- /dev/null +++ b/bundle/vim-javacomplete2/autoload/javacomplete/newclass.vim @@ -0,0 +1,491 @@ +" Vim completion script for java +" Maintainer: artur shaik +" +" Classes generator + +function! s:Log(log) + let log = type(a:log) == type("") ? a:log : string(a:log) + call javacomplete#logger#Log("[newclass] ". log) +endfunction + +function! javacomplete#newclass#TemplatesCompletion(argLead, command, cursorPos) + call s:Log("arglead:[".a:argLead ."] cmdline:[" .a:command ."] cursorpos:[" .a:cursorPos ."]") + let result = [] + let commandTokens = split(a:command, ':', 1) + let command = len(commandTokens) >= 1 ? commandTokens[-1] : a:command + call extend(result, s:FetchTemplatesByPrefix(command, 0)) + return result +endfunction + +function! javacomplete#newclass#Completion(argLead, command, cursorPos) + call s:Log("arglead:[".a:argLead ."] cmdline:[" .a:command ."] cursorpos:[" .a:cursorPos ."]") + let result = [] + let commandTokens = split(a:command, ':', 1) + let command = len(commandTokens) >= 1 ? commandTokens[-1] : a:command + if command[0] == '/' + call extend(result, s:ClassnameCompletions(command[1:], s:GetCompleted(commandTokens), 0)) + elseif command[0] == '[' + call extend(result, s:FetchAvailableSubDirectories(command[1:], s:GetCompleted(commandTokens))) + elseif len(commandTokens) == 1 + call extend(result, s:FetchTemplatesByPrefix(command, 1)) + call extend(result, s:ClassnameCompletions(command, s:GetCompleted(commandTokens), 1)) + elseif len(commandTokens) == 2 + call extend(result, s:ClassnameCompletions(command, s:GetCompleted(commandTokens), 1)) + call extend(result, s:ClassMethods(command, s:GetCompleted(commandTokens))) + elseif len(commandTokens) == 3 + if commandTokens[1] =~ '[\[\]]' + call extend(result, s:ClassnameCompletions(command, s:GetCompleted(commandTokens), 1)) + else + call extend(result, s:ClassMethods(command, s:GetCompleted(commandTokens))) + endif + else + call extend(result, s:ClassMethods(command, s:GetCompleted(commandTokens))) + endif + return result +endfunction + +function! s:FetchTemplatesByPrefix(command, addSeparator) + let result = s:FetchTemplatesByPath( + \ g:JavaComplete_Home. '/plugin/res/gen__class_', + \ a:command, + \ a:addSeparator) + if isdirectory(g:JavaComplete_CustomTemplateDirectory) + call extend(result, + \ s:FetchTemplatesByPath(expand(g:JavaComplete_CustomTemplateDirectory). '/gen__class_', + \ a:command, + \ a:addSeparator)) + endif + return result +endfunction + +function! s:FetchTemplatesByPath(path, command, addSeparator) + let result = [] + let cutLength = len(a:path) + for template in glob(a:path. a:command. '*.tpl', 0, 1) + call add(result, template[cutLength:-5]. (a:addSeparator == 1 ? ':' : '')) + endfor + return result +endfunction + +function! s:ClassnameCompletions(command, completed, isRelative) + if stridx(a:command, ' ') < 0 + return s:FetchAvailablePackages(a:command, a:completed, a:isRelative) + else + return s:FetchKeywords(a:command, a:completed, a:isRelative) + endif +endfunction + +function! s:FetchAvailablePackages(command, completed, isRelative) + let result = [] + let currentPath = split(expand('%:p:h'), g:FILE_SEP) + if a:isRelative == 0 + let currentPackage = split(javacomplete#collector#GetPackageName(), '\.') + let sameSubpackageIdx = index(currentPath, currentPackage[0]) + if sameSubpackageIdx >= 0 + let currentPath = currentPath[:sameSubpackageIdx - 1] + if empty(a:command) + for p in currentPackage + call add(result, a:completed. '/'. p) + endfor + endif + endif + endif + let command = substitute(a:command, '\.', g:FILE_SEP, 'g') + let cutLength = len(join(currentPath, g:FILE_SEP)) + 2 + for path in glob(g:FILE_SEP. join(currentPath, g:FILE_SEP). g:FILE_SEP. '**'. g:FILE_SEP. command. '*'. g:FILE_SEP, 1, 1) + let p = substitute(path[cutLength:], g:FILE_SEP, '.', 'g') + if a:isRelative == 0 + let p = '/'. p + endif + call add(result, a:completed. p) + endfor + return result +endfunction + +function! s:FetchKeywords(command, completed, isRelative) + let keywords = ['extends', 'implements'] + let tokens = split(a:command, ' ', 1) + if len(tokens) > 1 && index(keywords, tokens[-2]) >= 0 + return [] + endif + let completed = a:completed. (a:isRelative == 0 ? '/' : '') + let completed = completed. join(tokens[:-2], ' ') + let result = [] + for kw in keywords + if a:command =~ '\<'. kw. '\>' + \ || kw !~ '\<'. tokens[-1]. '*' + continue + endif + call add(result, completed. ' '. kw) + endfor + return result +endfunction + +function! s:FetchAvailableSubDirectories(command, completed) + let result = [] + let currentPath = split(expand('%:p:h'), g:FILE_SEP) + let currentPath = currentPath[:index(currentPath, 'src')] + let prePath = g:FILE_SEP. join(currentPath, g:FILE_SEP). g:FILE_SEP + let cutLength = len(prePath) + for path in glob(prePath. a:command. '*'. g:FILE_SEP, 0, 1) + call add(result, a:completed. '['. path[cutLength:-2]. ']') + endfor + return result +endfunction + +function! s:ClassMethods(command, completed) + let keywords = ['constructor(', 'toString(', 'hashCode(', 'equals('] + let result = [] + for kw in keywords + if kw !~ '\<'. a:command. '*' + continue + endif + call add(result, a:completed. kw) + endfor + return result +endfunction + +function! s:GetCompleted(commandTokens) + let completed = join(a:commandTokens[:-2], ':') + if !empty(completed) + let completed = completed. ':' + endif + return completed +endfunction + +function! javacomplete#newclass#CreateInFile() + let templates = s:FetchTemplatesByPrefix('', 0) + let message = join(templates, ', ') + let message .= "\nenter template name [default]: " + let userinput = input(message, '', 'customlist,javacomplete#newclass#TemplatesCompletion') + call s:Log("input: ". userinput) + + let currentPath = split(expand('%:p:h'), g:FILE_SEP) + call filter(currentPath, 'empty(v:val) == 0') + if has('win32') && currentPath[0][-1:] ==':' + let currentPath = currentPath[1:] + endif + + let data = {} + let data['path'] = '' + let data['current_path'] = g:FILE_SEP. join(currentPath, g:FILE_SEP) + let data['class'] = expand('%:t:r') + let data['package'] = s:DeterminePackage(currentPath) + if !empty(userinput) + let data['template'] = userinput + endif + call s:Log(data) + call s:CreateClass(data) +endfunction + +function! s:DeterminePackage(currentPath) + let i = 0 + while i < len(a:currentPath) + if a:currentPath[i] == 'java' + break + endif + let i += 1 + endwhile + if i < len(a:currentPath) + let package = a:currentPath[i + 1:] + else + let rootPackage = input("\nenter your root package: ") + if empty(rootPackage) + return '' + endif + let i = 0 + while i < len(a:currentPath) + if a:currentPath[i] == rootPackage + break + endif + let i += 1 + endwhile + if i < len(a:currentPath) + let package = a:currentPath[i:] + else + return '' + endif + endif + return join(package, '.') +endfunction + +function! javacomplete#newclass#CreateClass() + call javacomplete#Enable() + call javacomplete#Start() + let message = "enter new class name: " + let userinput = input(message, '', 'customlist,javacomplete#newclass#Completion') + if empty(userinput) + return + endif + call s:Log("input: ". userinput) + + let currentPackage = split(javacomplete#collector#GetPackageName(), '\.') + let currentPath = split(expand('%:p:h'), g:FILE_SEP) + call filter(currentPath, 'empty(v:val) == 0') + if has('win32') && currentPath[0][-1:] ==':' + let currentPath = currentPath[1:] + endif + let data = s:ParseInput( + \ userinput, reverse(copy(currentPath)), currentPackage) + if type(data) != type({}) + echom "\n" + echoerr "Error: could not parse input line" + return + endif + let data['current_path'] = g:FILE_SEP. join(currentPath, g:FILE_SEP). g:FILE_SEP + call s:CreateClass(data) +endfunction + +function! s:CreateClass(data) + call s:Log("create class: ". string(a:data)) + + let path = a:data['current_path'] + \ . g:FILE_SEP + \ . a:data['path'] + if filewritable(path) != 2 + call mkdir(path, 'p') + endif + let fileName = fnamemodify(path. g:FILE_SEP. a:data['class'], ":p") + let bufname = bufname('') + if getbufvar(bufname, "&mod") == 1 && getbufvar(bufname, "&hidden") == 0 + execute ':vs' + endif + execute ':e '. fileName. '.java' + let fileSize = getfsize(fileName. '.java') + if (fileSize <= 0 && fileSize > -2) || (line('$') == 1 && getline(1) == '') + let options = { + \ 'name' : a:data['class'], + \ 'package' : a:data['package'] + \ } + if has_key(a:data, 'fields') + let options['fields'] = a:data['fields'] + endif + if has_key(a:data, 'extends') + let options['extends'] = a:data['extends'] + endif + if has_key(a:data, 'implements') + let options['implements'] = a:data['implements'] + endif + let isInterfaceTemplate = 0 + if has_key(a:data, 'template') + if a:data['template'] == 'interface' + let isInterfaceTemplate = 1 + endif + call javacomplete#generators#GenerateClass(options, a:data['template']) + else + call javacomplete#generators#GenerateClass(options) + endif + silent execute "normal! gg=G" + call search(a:data['class']) + silent execute "normal! j" + call javacomplete#imports#AddMissing() + if !isInterfaceTemplate + call javacomplete#generators#AbstractDeclaration() + endif + if has_key(a:data, 'methods') + let methods = a:data['methods'] + let vars = s:GetVariables(get(a:data, 'fields', {})) + if has_key(methods, 'constructor') + let command = {'template': 'constructor', 'replace': {'type': 'same'}, 'fields' : []} + call s:InsertVars(command, methods['constructor'], vars) + call javacomplete#generators#GenerateByTemplate(command) + endif + if has_key(methods, 'toString') + let command = {'template': 'toString_StringBuilder', 'replace': {'type': 'similar'}, 'fields' : []} + if empty(methods['toString']) + call add(methods['toString'], '*') + endif + call s:InsertVars(command, methods['toString'], vars) + call javacomplete#generators#GenerateByTemplate(command) + endif + if has_key(methods, 'equals') + let command = {'template': 'equals', 'replace': {'type': 'similar'}, 'fields' : []} + if empty(methods['equals']) + call add(methods['equals'], '*') + endif + call s:InsertVars(command, methods['equals'], vars) + call javacomplete#generators#GenerateByTemplate(command) + endif + if has_key(methods, 'hashCode') + let command = {'template': 'hashCode', 'replace': {'type': 'similar'}, 'fields' : []} + if empty(methods['hashCode']) + call add(methods['hashCode'], '*') + endif + call s:InsertVars(command, methods['hashCode'], vars) + call javacomplete#generators#GenerateByTemplate(command) + endif + endif + if !isInterfaceTemplate + if has_key(a:data, 'fields') + call javacomplete#generators#Accessors() + endif + endif + endif +endfunction + +function! s:InsertVars(command, method, vars) + for arg in a:method + if arg == '*' + let a:command['fields'] = values(a:vars) + break + endif + + call add(a:command['fields'], a:vars[arg]) + endfor +endfunction + +function! s:GetVariables(fields) + let result = {} + for fieldIdx in keys(a:fields) + let field = a:fields[fieldIdx] + let var = { + \ 'name' : field['name'], + \ 'type' : field['type'], + \ 'static' : field['mod'] =~ '.*\.*', + \ 'final' : field['mod'] =~ '.*\.*', + \ 'isArray' : field['type'] =~# g:RE_ARRAY_TYPE + \ } + let result[fieldIdx] = var + endfor + return result +endfunction + +function! s:ParseInput(userinput, currentPath, currentPackage) + let submatch = matchlist(a:userinput, '^\([A-Za-z0-9_]*:\)\=\(\[.\{-}\]:\)\=\(\%(\/\|\/\.\|\)'. g:RE_TYPE. '\)\(\s\+extends\s\+'. g:RE_TYPE. '\)\=\(\s\+implements\s\+'. g:RE_TYPE. '\)\=\((.\{-})\|\)\(:.*\)\=$') + if !empty(submatch) + let path = split(submatch[3], '\.') + let subdir = !empty(submatch[2]) ? submatch[2][1:-3] : '' + let classData = s:BuildPathData(path, subdir, a:currentPath, a:currentPackage) + if !empty(submatch[1]) + let classData['template'] = submatch[1][:-2] + endif + if !empty(submatch[4]) + let m = matchlist(submatch[4], '.*extends\s\+\('. g:RE_TYPE. '\)') + if !empty(m) + let classData['extends'] = m[1] + endif + endif + if !empty(submatch[5]) + let m = matchlist(submatch[5], '.*implements\s\+\('. g:RE_TYPE. '\)') + if !empty(m) + let classData['implements'] = m[1] + endif + endif + if !empty(submatch[6]) + let fieldsMap = s:ParseFields(submatch[6]) + if type(fieldsMap) == type({}) + let classData['fields'] = fieldsMap + endif + endif + if !empty(submatch[7]) + let methodsMap = s:ParseMethods(submatch[7]) + if !empty(methodsMap) + let classData['methods'] = methodsMap + endif + endif + return classData + endif +endfunction + +function! s:ParseMethods(methods) + let methodsMap = {} + let methods = split(a:methods[1:], ':') + for method in methods + let bracketsIdx = stridx(method, '(') + if bracketsIdx > 0 + let methodName = method[:bracketsIdx - 1] + let methodsMap[methodName] = [] + let args = split(method[bracketsIdx + 1:-2], ',') + for arg in args + if arg != '*' + let arg = arg*1 + endif + call add(methodsMap[methodName], arg) + endfor + else + let methodsMap[method] = [] + endif + endfor + return methodsMap +endfunction + +function! s:ParseFields(fields) + let fields = javacomplete#util#Trim(a:fields[1:-2]) + if !empty(fields) + let fieldsList = split(fields, ',') + let fieldsMap = {} + let idx = 1 + for field in fieldsList + let fieldMatch = matchlist(field, '^\s*\(\%('. g:RE_TYPE_MODS. '\s\+\)\+\)\=\('. g:RE_TYPE. '\)\s\+\('. g:RE_IDENTIFIER. '\).*$') + if !empty(fieldMatch) + let fieldMap = {} + let fieldMap['mod'] = empty(fieldMatch[1]) ? + \ 'private' : javacomplete#util#Trim(fieldMatch[1]) + let fieldMap['type'] = fieldMatch[2] + let fieldMap['name'] = fieldMatch[3] + let fieldsMap[string(idx)] = fieldMap + let idx += 1 + endif + endfor + return fieldsMap + endif + return 0 +endfunction + +function! s:BuildPathData(path, subdir, currentPath, currentPackage) + if !empty(a:subdir) + let idx = index(a:currentPath, 'src') + let newPath = repeat('..'. g:FILE_SEP, idx) + let newPath .= a:subdir. g:FILE_SEP. 'java'. g:FILE_SEP + let newPath .= join(a:currentPackage, g:FILE_SEP). g:FILE_SEP + else + let newPath = '' + endif + + let path = a:path + if path[0] == '/' || path[0][0] == '/' + if path[0] == '/' + let path = path[1:] + else + let path[0] = path[0][1:] + endif + let sameSubpackageIdx = index(a:currentPath, a:currentPackage[0]) + if sameSubpackageIdx < 0 + return s:RelativePath(path, newPath, a:currentPath, a:currentPackage) + endif + let currentPath = a:currentPath[:sameSubpackageIdx] + let idx = index(currentPath, path[0]) + if idx < 0 + let newPath .= repeat('..'. g:FILE_SEP, len(currentPath)) + let newPath .= join(path[:-2], g:FILE_SEP) + let newPackage = path[:-2] + else + let newPath .= idx > 0 ? + \ repeat('..'. g:FILE_SEP, + \ len(currentPath[:idx-1])) + \ : + \ '' + let newPath .= join(path[1:-2], g:FILE_SEP) + let newPackage = path[1:-2] + call extend(newPackage, reverse(currentPath)[:-idx-1], 0) + endif + return { + \ 'path' : newPath, + \ 'class' : path[-1], + \ 'package' : join(newPackage, '.') + \ } + else + return s:RelativePath(path, newPath, a:currentPath, a:currentPackage) + endif +endfunction + +function! s:RelativePath(path, newPath, currentPath, currentPackage) + let newPackage = join(a:currentPackage + a:path[:-2], '.') + return { + \ 'path' : a:newPath. join(a:path[:-2], g:FILE_SEP), + \ 'class' : a:path[-1], + \ 'package' : newPackage + \ } +endfunction + +" vim:set fdm=marker sw=2 nowrap: diff --git a/bundle/vim-javacomplete2/autoload/javacomplete/parseradapter.vim b/bundle/vim-javacomplete2/autoload/javacomplete/parseradapter.vim new file mode 100644 index 000000000..11a39a9c4 --- /dev/null +++ b/bundle/vim-javacomplete2/autoload/javacomplete/parseradapter.vim @@ -0,0 +1,151 @@ +" Vim completion script for java +" Maintainer: artur shaik +" +" Methods that calling internal parser + +function! javacomplete#parseradapter#Parse(...) + let filename = a:0 == 0 ? '%' : a:1 + let currentFilename = javacomplete#GetCurrentFileKey() + + let changed = 0 + if filename == '%' || currentFilename == filename + let props = get(g:JavaComplete_Files, currentFilename, {}) + if get(props, 'changedtick', -1) != b:changedtick + let changed = 1 + let props.changedtick = b:changedtick + let lines = getline('^', '$') + endif + else + let props = get(g:JavaComplete_Files, filename, {}) + if get(props, 'modifiedtime', 0) != getftime(filename) + let changed = 1 + let props.modifiedtime = getftime(filename) + if filename =~ '^__' + let lines = getline('^', '$') + else + let lines = readfile(filename) + endif + endif + endif + + if changed + call java_parser#InitParser(lines) + call java_parser#SetLogLevel(0) + let props.unit = java_parser#compilationUnit() + + if &ft == 'jsp' + return props + endif + + let package = has_key(props.unit, 'package') ? props.unit.package . '.' : '' + call s:UpdateFQN(props.unit, package) + endif + if filename !~ '^__' + let g:JavaComplete_Files[filename] = props + endif + return props.unit +endfunction + +" update fqn for toplevel types or nested types. +" not for local type or anonymous type +function! 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 type(def) != type([]) + unlet def + continue + endif + if def.tag == 'CLASSDEF' + call s:UpdateFQN(def, a:tree.fqn . '.') + endif + endfor + endif +endfunction + +" TreeVisitor {{{2 +" parent argument exist for lambdas only +function! s:visitTree(tree, param, parent) 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, a:parent) + unlet tree + endfor + endif +endfunction + +" we need to return to parent leafs to get lambda's arguments declaration +function! s:lambdaMeth(tree) + if a:tree.tag == 'APPLY' + return {'meth': a:tree.meth, 'stats': {}} + elseif a:tree.tag == 'VARDEF' + return {'stats': {'tag': a:tree.tag, 't': a:tree.t, 'name': a:tree.name, 'endpos': a:tree.endpos, 'n': a:tree.n, 'pos': a:tree.pos, 'm': a:tree.m}, 'meth': {}} + elseif a:tree.tag == 'RETURN' + return {'stats': {'tag': a:tree.tag, 'endpos': a:tree.endpos, 'pos': a:tree.pos}, 'meth': {}} + endif + return {'meth': {}, 'stats': {}} +endfunction + +let s:TreeVisitor = {'visit': function('s:visitTree'), + \ 'lambdameth': function('s:lambdaMeth'), + \ 'TOPLEVEL' : 'call self.visit(a:tree.types, a:param, a:tree)', + \ '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, a:tree)', + \ 'DOLOOP' : 'call self.visit(a:tree.body, a:param, a:tree) | call self.visit(a:tree.cond, a:param, a:tree)', + \ 'WHILELOOP' : 'call self.visit(a:tree.cond, a:param, a:tree) | call self.visit(a:tree.body, a:param, a:tree)', + \ 'FORLOOP' : 'call self.visit(a:tree.init, a:param, a:tree) | call self.visit(a:tree.cond, a:param, a:tree) | call self.visit(a:tree.step, a:param, a:tree) | call self.visit(a:tree.body, a:param, a:tree)', + \ 'FOREACHLOOP' : 'call self.visit(a:tree.var, a:param, a:tree) | call self.visit(a:tree.expr, a:param, a:tree) | call self.visit(a:tree.body, a:param, a:tree)', + \ 'LABELLED' : 'call self.visit(a:tree.body, a:param, a:tree)', + \ 'SWITCH' : 'call self.visit(a:tree.selector, a:param, a:tree) | call self.visit(a:tree.cases, a:param, a:tree)', + \ 'CASE' : 'call self.visit(a:tree.pat, a:param, a:tree) | call self.visit(a:tree.stats, a:param, a:tree)', + \ 'SYNCHRONIZED': 'call self.visit(a:tree.lock, a:param, a:tree) | call self.visit(a:tree.body, a:param, a:tree)', + \ 'TRY' : 'call self.visit(a:tree.params, a:param, a:tree) | call self.visit(a:tree.body, a:param, a:tree) | call self.visit(a:tree.catchers, a:param, a:tree) | call self.visit(a:tree.finalizer, a:param, a:tree) ', + \ 'RARROW' : 'call self.visit(a:tree.body, a:param, a:tree)', + \ 'CATCH' : 'call self.visit(a:tree.param,a:param, a:tree) | call self.visit(a:tree.body, a:param, a:tree)', + \ 'CONDEXPR' : 'call self.visit(a:tree.cond, a:param, a:tree) | call self.visit(a:tree.truepart, a:param, a:tree) | call self.visit(a:tree.falsepart, a:param, a:tree)', + \ 'IF' : 'call self.visit(a:tree.cond, a:param, a:tree) | call self.visit(a:tree.thenpart, a:param, a:tree) | if has_key(a:tree, "elsepart") | call self.visit(a:tree.elsepart, a:param, a:tree) | endif', + \ 'EXEC' : 'call self.visit(a:tree.expr, a:param, a:tree)', + \ 'APPLY' : 'call self.visit(a:tree.meth, a:param, a:tree) | call self.visit(a:tree.args, a:param, a:tree)', + \ 'NEWCLASS' : 'call self.visit(a:tree.def, a:param, a:tree)', + \ 'RETURN' : 'if has_key(a:tree, "expr") | call self.visit(a:tree.expr, a:param, a:tree) | endif', + \ 'LAMBDA' : 'call extend(a:tree, self.lambdameth(a:parent)) | call self.visit(a:tree.meth, a:param, a:tree) | call self.visit(a:tree.stats, a:param, a:tree) | call self.visit(a:tree.args, a:param, a:tree) | call self.visit(a:tree.body, a:param, a:tree)' + \} + +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. +function! javacomplete#parseradapter#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, a:tree) | endif' + let s:TreeVisitor.METHODDEF = 'if ' . s:TV_CMP_POS_BODY . ' | call self.visit(a:tree.body, a:param, a:tree) | 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, a:tree) | endif' + let s:TreeVisitor.IDENT = '' + + let result = [] + call s:TreeVisitor.visit(a:tree, {'result': result, 'pos': a:targetPos, 'allNonLocal': a:0 == 0 ? 0 : 1}, {}) + return result +endfunction + +" a:1 match beginning +" return a stack of matching name +function! javacomplete#parseradapter#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 cmdPos = 'if a:tree.name ' .comparator. ' a:param.name && a:tree.pos <= a:param.pos | 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, a:tree) | endif' + let s:TreeVisitor.METHODDEF = cmd . ' | if ' . s:TV_CMP_POS_BODY . ' | call self.visit(a:tree.params, a:param, a:tree) | call self.visit(a:tree.body, a:param, a:tree) | endif' + let s:TreeVisitor.VARDEF = cmdPos . ' | if has_key(a:tree, "init") && ' . s:TV_CMP_POS . ' | call self.visit(a:tree.init, a:param, a:tree) | endif' + let s:TreeVisitor.IDENT = 'if a:parent.tag == "LAMBDA" && a:parent.body.pos <= a:param.pos | call add(a:param.result, a:parent) | endif' + + let result = [] + call s:TreeVisitor.visit(a:tree, {'result': result, 'pos': a:targetPos, 'name': a:name}, {}) + return result +endfunction + +" vim:set fdm=marker sw=2 nowrap: diff --git a/bundle/vim-javacomplete2/autoload/javacomplete/scanner.vim b/bundle/vim-javacomplete2/autoload/javacomplete/scanner.vim new file mode 100644 index 000000000..bb0998d73 --- /dev/null +++ b/bundle/vim-javacomplete2/autoload/javacomplete/scanner.vim @@ -0,0 +1,231 @@ +" Vim completion script for java +" Maintainer: artur shaik +" +" Simple parsing functions + +" 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! javacomplete#scanner#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('.') + + call s:SkipBlock() + + while 1 + if search('[{};]\|<%\|<%!', 'bW') == 0 + let lnum = 1 + let col = 1 + else + if javacomplete#util#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 + +function! s:SkipBlock() + let pos = line('.') + 1 + let clbracket = 0 + let quoteFlag = 0 + while pos > 0 + let pos -= 1 + if pos == 0 + break + endif + + let line = getline(pos) + let cursor = len(line) + while cursor > 0 + if line[cursor] == '"' + if quoteFlag == 0 + let quoteFlag = 1 + else + let quoteFlag = 0 + endif + endif + + if quoteFlag + let line = line[0 : cursor - 1]. line[cursor + 1 : -1] + let cursor -= 1 + continue + endif + + if line[cursor] == '}' + let clbracket += 1 + elseif line[cursor] == '(' && clbracket == 0 + call cursor(pos, cursor) + break + elseif line[cursor] == '{' + if clbracket > 0 + let clbracket -= 1 + else + break + endif + endif + let cursor -= 1 + endwhile + if clbracket == 0 + break + endif + endwhile +endfunction + +function! 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 = javacomplete#util#Prune(strpart(getline(lnum), a:col-1)) + let lnum += 1 + while lnum < a:lnum_old + let str .= javacomplete#util#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 .= javacomplete#util#Prune(lastline, col) + let str = javacomplete#util#RemoveBlockComments(str) + " generic in JAVA 5+ + while match(str, g:RE_TYPE_ARGUMENTS) != -1 + let str = substitute(str, '\(' . g:RE_TYPE_ARGUMENTS . '\)', '\=repeat("", len(submatch(1)))', 'g') + endwhile + let str = substitute(str, '\s\s\+', ' ', 'g') + if str !~ '.*'. g:RE_KEYWORDS. '.*' + let str = substitute(str, '\([.()]\)[ \t]\+', '\1', 'g') + let str = substitute(str, '[ \t]\+\([.()]\)', '\1', 'g') + endif + return javacomplete#util#Trim(str) . matchstr(lastline, '\s*$') +endfunction + +" Extract a clean expr, removing some non-necessary characters. +function! javacomplete#scanner#ExtractCleanExpr(expr) + if a:expr !~ '.*'. g:RE_KEYWORDS. '.*' + let cmd = substitute(a:expr, '[ \t\r\n ]\+\([.()[\]]\)', '\1', 'g') + let cmd = substitute(cmd, '\([.()[\]]\)[ \t\r\n ]\+', '\1', 'g') + else + let cmd = a:expr + endif + + let pos = strlen(cmd)-1 + while pos >= 0 && cmd[pos] =~ '[a-zA-Z0-9_.)\]:<>]' + if cmd[pos] == ')' + let pos = javacomplete#util#SearchPairBackward(cmd, pos, '(', ')') + elseif cmd[pos] == ']' + let pos = javacomplete#util#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] == '.' || a:expr[e] == ':' + let subexpr = strpart(a:expr, s, e-s) + call extend(items, isparen ? s:ProcessParentheses(subexpr) : [subexpr]) + let isparen = 0 + if a:expr[e] == ':' && a:expr[e+1] == ':' + let s = e + 2 + else + let s = e + 1 + endif + elseif a:expr[e] == '(' + let e = javacomplete#util#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 = javacomplete#util#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 +endfunction + +" 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 = javacomplete#util#GetMatchedIndexEx(a:expr, s-1, '(', ')') + if e >= 0 + let tail = strpart(a:expr, e+1) + if tail[-1:] == '.' + return [tail[0:-2]] + endif + 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 javacomplete#scanner#ParseExpr(a:expr) + endif + return [a:expr] +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! javacomplete#scanner#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 cursor(lnum_old, col_old) + return s:MergeLines(lnum, col, lnum_old, col_old) +endfunction + +" vim:set fdm=marker sw=2 nowrap: diff --git a/bundle/vim-javacomplete2/autoload/javacomplete/server.vim b/bundle/vim-javacomplete2/autoload/javacomplete/server.vim new file mode 100644 index 000000000..c7f771575 --- /dev/null +++ b/bundle/vim-javacomplete2/autoload/javacomplete/server.vim @@ -0,0 +1,425 @@ +" Vim completion script for java +" Maintainer: artur shaik +" +" Java server bridge initiator and caller + +let s:serverStartBlocked = 0 +let s:autoRecompileCheckFlag = 0 + +function! s:Log(log) + let log = type(a:log) == type("") ? a:log : string(a:log) + call javacomplete#logger#Log("[server] ". log) +endfunction + +function! javacomplete#server#BlockStart() + let s:serverStartBlocked = 1 +endfunction + +function! javacomplete#server#UnblockStart() + let s:serverStartBlocked = 0 +endfunction + +function! s:System(cmd, caller) + let t = reltime() + let res = system(a:cmd) + call s:Log(reltimestr(reltime(t)) . 's to exec "' . a:cmd . '" by ' . a:caller) + return res +endfunction + +function! s:Poll() + let value = 0 +JavacompletePy << EOPC +try: + vim.command("let value = '%d'" % bridgeState.poll()) +except: + # we'll get here if the bridgeState variable was not defined or if it's None. + # In this case we stop the processing and return the default 0 value. + pass +EOPC + return value +endfunction + +function! javacomplete#server#Terminate() + if s:Poll() + JavacompletePy bridgeState.terminateServer() + + let pid = 0 + JavacompletePy vim.command('let pid = %d' % bridgeState.pid()) + if pid > 1 + if g:JavaComplete_IsWindows + call system('taskkill /t /pid '. pid) + else + call system('kill '. (pid + 1)) + endif + endif + endif +endfunction + +function! s:ControlServerAppVersion() + let classpath = + \ s:GetJavaviClassPath(). g:PATH_SEP. + \ s:GetJavaviDeps(). g:PATH_SEP + let s:serverVersionOutput = [] + call javacomplete#util#RunSystem(join( + \ [ + \ javacomplete#server#GetJVMLauncher(), '-cp', classpath, + \ 'kg.ash.javavi.Javavi -version' + \ ]), + \ 'Javavi server version check', + \ 'javacomplete#server#CheckServerAccordance') +endfunction + +function! javacomplete#server#CheckServerAccordance(data, event) + if a:event == 'exit' + if a:data == '0' + let serverVersion = join(s:serverVersionOutput) + if !javacomplete#version#CheckServerCompatibility(serverVersion) + call s:Log("server ". serverVersion. " is outdated, recompile") + call javacomplete#server#Compile() + endif + endif + + unlet s:serverVersionOutput + elseif a:event == 'stdout' + call extend(s:serverVersionOutput, a:data) + endif +endfunction + +function! javacomplete#server#Start() + if s:Poll() == 0 && s:serverStartBlocked == 0 + if get(g:, 'JavaComplete_CheckServerVersionAtStartup', 1) + call s:ControlServerAppVersion() + endif + + JavacompletePy import vim + let file = g:JavaComplete_Home. g:FILE_SEP. "autoload". g:FILE_SEP. "javavibridge.py" + call s:Log("executing python file: " . file) + execute "JavacompletePyfile ". file + + let javaProps = [] + if exists('g:JavaComplete_JavaviLogLevel') + call add(javaProps, '-Dlog.level='. g:JavaComplete_JavaviLogLevel) + endif + if !empty(g:JavaComplete_JavaviLogDirectory) + call add(javaProps, '-Dlog.directory='. g:JavaComplete_JavaviLogDirectory) + endif + JavacompletePy vim.command('let port = "%s"' % SERVER[1]) + call add(javaProps, '-Ddaemon.port='. port) + let log4j2Config = join([g:JavaComplete_Home,'libs', 'javavi', 'target', 'classes', 'log4j2.xml'], g:FILE_SEP) + call add(javaProps, '-Dlog4j.configurationFile='. log4j2Config) + + let classpath = substitute(javacomplete#server#GetClassPath(), '\\', '\\\\', 'g') + let sources = [] + if exists('g:JavaComplete_SourcesPath') + let sources += ['-sources', s:ExpandAllPaths(g:JavaComplete_SourcesPath)] + endif + + let args = javaProps + ['kg.ash.javavi.Javavi'] + sources + if g:JavaComplete_ServerAutoShutdownTime > 0 + let args += ['-t', g:JavaComplete_ServerAutoShutdownTime] + endif + let args += ['-base', javacomplete#util#GetBase('')] + let args += ['-compiler', javacomplete#server#GetCompiler()] + if !empty(g:JavaComplete_ProjectKey) + let args += ['-project', g:JavaComplete_ProjectKey] + endif + + call s:Log("server classpath: -cp ". classpath) + call s:Log("server arguments:". join(args, ' ')) + + JavacompletePy bridgeState = JavaviBridge() + JavacompletePy bridgeState.setupServer(vim.eval('javacomplete#server#GetJVMLauncher()'), vim.eval('args'), vim.eval('classpath')) + + endif +endfunction + +function! javacomplete#server#ShowPort() + if s:Poll() + JavacompletePy vim.command('echo "Javavi port: %d"' % bridgeState.port()) + endif +endfunction + +function! javacomplete#server#ShowPID() + if s:Poll() + JavacompletePy vim.command('echo "Javavi pid: %d"' % bridgeState.pid()) + endif +endfunction + +function! javacomplete#server#GetCompiler() + return exists('g:JavaComplete_JavaCompiler') && g:JavaComplete_JavaCompiler !~ '^\s*$' ? g:JavaComplete_JavaCompiler : 'javac' +endfunction + +function! javacomplete#server#SetCompiler(compiler) + let g:JavaComplete_JavaCompiler = a:compiler +endfunction + +function! javacomplete#server#GetJVMLauncher() + return exists('g:JavaComplete_JvmLauncher') && g:JavaComplete_JvmLauncher !~ '^\s*$' ? g:JavaComplete_JvmLauncher : 'java' +endfunction + +function! javacomplete#server#SetJVMLauncher(interpreter) + if javacomplete#server#GetJVMLauncher() != a:interpreter + let g:JavaComplete_Cache = {} + endif + let g:JavaComplete_JvmLauncher = a:interpreter +endfunction + +function! javacomplete#server#CompilationJobHandler(data, event) + if a:event == 'exit' + if a:data == "0" + JCserverStart + echo 'Javavi compilation finished ' + else + echo 'Failed to compile javavi server' + endif + let s:compilationIsRunning = 0 + elseif a:event == 'stderr' + echomsg join(a:data) + elseif a:event == 'stdout' + if g:JavaComplete_ShowExternalCommandsOutput + echomsg join(a:data) + endif + endif +endfunction + +function! javacomplete#server#Compile() + call javacomplete#server#Terminate() + + let javaviDir = g:JavaComplete_Home. g:FILE_SEP. join(['libs', 'javavi'], g:FILE_SEP). g:FILE_SEP + if isdirectory(javaviDir. join(['target', 'classes'], g:FILE_SEP)) + call javacomplete#util#RemoveFile(javaviDir.join(['target', 'classes'], g:FILE_SEP)) + endif + + let s:compilationIsRunning = 1 + if executable('mvn') + let command = ['mvn', '-B', '-f', javaviDir. g:FILE_SEP. 'pom.xml', 'compile'] + else + call mkdir(javaviDir. join(['target', 'classes'], g:FILE_SEP), "p") + let deps = s:GetJavaviDeps() + let command = javacomplete#server#GetCompiler() + let command .= ' -d '. javaviDir. 'target'. g:FILE_SEP. 'classes -classpath '. javaviDir. 'target'. g:FILE_SEP. 'classes'. g:PATH_SEP. deps. ' -sourcepath '. javaviDir. 'src'. g:FILE_SEP. 'main'. g:FILE_SEP. 'java -g -nowarn -target 1.8 -source 1.8 -encoding UTF-8 '. javaviDir. join(['src', 'main', 'java', 'kg', 'ash', 'javavi', 'Javavi.java'], g:FILE_SEP) + endif + call javacomplete#util#RunSystem(command, "server compilation", "javacomplete#server#CompilationJobHandler") +endfunction + +" Check if Javavi classes exists and return classpath directory. +" If not found, build Javavi library classes with maven or javac. +fu! s:GetJavaviClassPath() + let javaviDir = g:JavaComplete_Home. join(['', 'libs', 'javavi', ''], g:FILE_SEP) + if !isdirectory(javaviDir. "target". g:FILE_SEP. "classes") + call javacomplete#server#Compile() + endif + + if !empty(javacomplete#util#GlobPathList(javaviDir. 'target'. g:FILE_SEP. 'classes', '**'. g:FILE_SEP. '*.class', 1, 0)) + return javaviDir. "target". g:FILE_SEP. "classes" + else + if !get(s:, 'compilationIsRunning', 0) + echo "No Javavi library classes found, it means that we couldn't compile it. Do you have JDK8+ installed?" + endif + endif +endfu + +" Function for server communication {{{2 +function! javacomplete#server#Communicate(option, args, log) + if !s:Poll() + call javacomplete#server#Start() + endif + + if s:Poll() + if !empty(a:args) + let args = ' "'. substitute(a:args, '"', '\\"', 'g'). '"' + else + let args = '' + endif + let cmd = a:option. args + call s:Log("communicate: ". cmd. " [". a:log. "]") + let result = "" +JavacompletePy << EOPC +vim.command('let result = "%s"' % bridgeState.send(vim.eval("cmd")).replace('"', '\\"')) +EOPC + + call s:Log(result) + if result =~ '^message:' + echom result + return "[]" + endif + + return result + endif + + return "" +endfunction + +function! javacomplete#server#GetClassPath() + let jars = s:GetExtraPath() + let path = s:GetJavaviClassPath() . g:PATH_SEP. s:GetJavaviDeps(). g:PATH_SEP + let path = path . join(jars, g:PATH_SEP) . g:PATH_SEP + + if &ft == 'jsp' + let path .= s:GetClassPathOfJsp() + endif + + if exists('b:classpath') && b:classpath !~ '^\s*$' + call s:Log(b:classpath) + return path . b:classpath + endif + + if exists('s:classpath') + call s:Log(s:classpath) + return path . javacomplete#GetClassPath() + endif + + if exists('g:java_classpath') && g:java_classpath !~ '^\s*$' + call s:Log(g:java_classpath) + return path . g:java_classpath + endif + + if empty($CLASSPATH) + if g:JAVA_HOME == '' + let java = javacomplete#server#GetJVMLauncher() + let javaSettings = split(s:System(java. " -XshowSettings", "Get java settings"), '\n') + for line in javaSettings + if line =~ 'java\.home' + let g:JAVA_HOME = split(line, ' = ')[1] + endif + endfor + endif + return path. g:JAVA_HOME. g:FILE_SEP. 'lib' + endif + + return path . $CLASSPATH +endfunction + +function! s:ExpandAllPaths(path) + let result = '' + let list = javacomplete#util#uniq(sort(split(a:path, g:PATH_SEP))) + for l in list + let result = result. substitute(expand(l), '\\', '/', 'g') . g:PATH_SEP + endfor + return result +endfunction + +function! s:GetJavaviDeps() + let deps = [] + call add(deps, fnamemodify(g:JavaComplete_Home. join(['', 'libs', 'javaparser-core-3.5.20.jar'], g:FILE_SEP), ":p")) + call add(deps, fnamemodify(g:JavaComplete_Home. join(['', 'libs', 'javavi_log4j-api.jar'], g:FILE_SEP), ":p")) + call add(deps, fnamemodify(g:JavaComplete_Home. join(['', 'libs', 'javavi_log4j-core.jar'], g:FILE_SEP), ":p")) + let path = join(deps, g:PATH_SEP) + if exists('b:classpath') && b:classpath !~ '^\s*$' + return path . b:classpath + endif + + if exists('s:classpath') + return path . s:GetClassPath() + endif + + if exists('g:java_classpath') && g:java_classpath !~ '^\s*$' + return path . g:java_classpath + endif + + return path +endfunction + +function! s:GetExtraPath() + let jars = [] + let extrapath = '' + if exists('g:JavaComplete_LibsPath') + let paths = split(g:JavaComplete_LibsPath, g:PATH_SEP) + for path in paths + let exp = s:ExpandPathToJars(path) + if empty(exp) + " ex: target/classes + call extend(jars, [path]) + else + call extend(jars, exp) + endif + endfor + endif + + return jars +endfunction + +function! s:ExpandPathToJars(path, ...) + if isdirectory(a:path) + return javacomplete#util#GlobPathList(a:path, "**5/*.jar", 1, 0) + \ + javacomplete#util#GlobPathList(a:path, "**5/*.zip", 1, 0) + elseif index(['zip', 'jar'], fnamemodify(a:path, ':e')) != -1 + return [a:path] + endif + return [] +endfunction + +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 .= g:PATH_SEP . path . '/WEB-INF/classes' + endif + if isdirectory(path . '/WEB-INF/lib') + let b:classpath_jsp .= g:PATH_SEP . path . '/WEB-INF/lib/*.jar' + 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 + +function! s:GetClassPath() + return exists('s:classpath') ? join(s:classpath, g:PATH_SEP) : '' +endfu + +function! s:GetDebugLogPath() + return javacomplete#server#Communicate('-get-debug-log-path', '', '') +endfunction + +function! javacomplete#server#EnableDebug() + let g:JavaComplete_JavaviLogLevel = "debug" + if s:Poll() + JCserverTerminate + JCserverStart + endif +endfunction + +function! javacomplete#server#EnableTraceDebug() + let g:JavaComplete_JavaviLogLevel = "trace" + if s:Poll() + JCserverTerminate + JCserverStart + endif +endfunction + +function! javacomplete#server#GetLogContent() + let bufferName = "__JCServer_Log_Buffer__" + let n = bufnr(bufferName) + if n != -1 + execute "bwipeout! ". n + endif + let curWin = winnr("#") + execute 'silent! split '. bufferName + set modifiable + setlocal buftype=nofile + setlocal bufhidden=wipe + setlocal noswapfile + setlocal nowrap + setlocal nobuflisted + execute '.-1read '. s:GetDebugLogPath() + execute "normal! G" + set nomodified + nnoremap q :bwipeout! + execute curWin. 'wincmd w' +endfunction + +" vim:set fdm=marker sw=2 nowrap: diff --git a/bundle/vim-javacomplete2/autoload/javacomplete/util.vim b/bundle/vim-javacomplete2/autoload/javacomplete/util.vim new file mode 100644 index 000000000..759f8b3d0 --- /dev/null +++ b/bundle/vim-javacomplete2/autoload/javacomplete/util.vim @@ -0,0 +1,453 @@ +" Vim completion script for java +" Maintainer: artur shaik +" +" Utility functions + +function! s:Log(log) + let log = type(a:log) == type("") ? a:log : string(a:log) + call javacomplete#logger#Log("[util] ". log) +endfunction + +" TODO: search pair used in string, like +" 'create(ao.fox("("), new String).foo().' +function! javacomplete#util#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 = javacomplete#util#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 + +" set string literal empty, remove comments, trim begining or ending spaces +" test case: ' sb. /* block comment*/ append( "stringliteral" ) // comment ' +function! javacomplete#util#Prune(str, ...) + if a:str =~ '^\s*$' | return '' | endif + + let str = substitute(a:str, '"\(\\\(["\\''ntbrf]\)\|[^"]\)*"', '""', 'g') + let str = substitute(str, '\/\/.*', '', 'g') + let str = javacomplete#util#RemoveBlockComments(str) + let str = javacomplete#util#Trim(str) + return a:0 > 0 ? str : str . ' ' +endfunction + +" Given argument, replace block comments with spaces of same number +function! javacomplete#util#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 +endfunction + +function! javacomplete#util#Trim(str) + let str = substitute(a:str, '^\s*', '', '') + return substitute(str, '\s*$', '', '') +endfunction + +fu! javacomplete#util#SplitAt(str, index) + return [strpart(a:str, 0, a:index+1), strpart(a:str, a:index+1)] +endfu + +function! javacomplete#util#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 + +function! javacomplete#util#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 = javacomplete#util#SearchPairBackward(a:str, idx, '[', ']')-1 + endwhile + endif + return dims +endfu + +function! javacomplete#util#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 +endfunction + +function! javacomplete#util#KeepCursor(cmd) + let lnum_old = line('.') + let col_old = col('.') + exe a:cmd + call cursor(lnum_old, col_old) +endfunction + +function! javacomplete#util#InCommentOrLiteral(line, col) + if has("syntax") && &ft != 'jsp' + return synIDattr(synID(a:line, a:col, 1), "name") =~? '\(Comment\|String\|Character\)' + endif +endfunction + +function! javacomplete#util#InComment(line, col) + if has("syntax") && &ft != 'jsp' + return synIDattr(synID(a:line, a:col, 1), "name") =~? 'comment' + endif +endfunction + +fu! javacomplete#util#GotoUpperBracket() + let searched = 0 + while (!searched) + call search('[{}]', 'bW') + if getline('.')[col('.')-1] == '}' + normal! % + else + let searched = 1 + endif + endwhile +endfu + +function! javacomplete#util#GetClassNameWithScope(...) + let offset = a:0 > 0 ? a:1 : col('.') + let curline = getline('.') + let word_l = offset - 1 + while curline[word_l - 1] =~ '[\.:@A-Za-z0-9_]' + let word_l -= 1 + if curline[word_l] == '@' + break + endif + endwhile + let word_r = word_l + while curline[word_r] =~ '[@A-Za-z0-9_]' + let word_r += 1 + endwhile + + return curline[word_l : word_r - 1] +endfunction + +function! s:MemberCompare(m1, m2) + return a:m1['n'] == a:m2['n'] ? 0 : a:m1['n'] > a:m2['n'] ? 1 : -1 +endfunction + +function! javacomplete#util#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 +endfunction + +function! javacomplete#util#CleanFQN(fqnDeclaration) + let start = 0 + let fqnDeclaration = a:fqnDeclaration + let result = matchlist(fqnDeclaration, '\<'. g:RE_IDENTIFIER. '\%(\s*\.\s*\('. g:RE_IDENTIFIER. '\)\)*', start) + while !empty(result) + + if len(result[1]) > 0 + if result[0][-1:-1] == '$' + let result[0] = result[0][:-2]. '\$' + endif + let fqnDeclaration = substitute(fqnDeclaration, result[0], result[1], '') + let shift = result[1] + else + let shift = result[0] + endif + if shift[-1:-1] == '$' + let shift = shift[:-2]. '\$' + endif + let start = match(fqnDeclaration, shift, start) + len(shift) + + let result = matchlist(fqnDeclaration, '\<'. g:RE_IDENTIFIER. '\%(\s*\.\s*\('. g:RE_IDENTIFIER. '\)\)*', start) + endwhile + + return fqnDeclaration +endfunction + +function! javacomplete#util#FindFile(what, ...) abort + let direction = a:0 > 0 ? a:1 : ';' + let old_suffixesadd = &suffixesadd + try + let &suffixesadd = '' + return findfile(a:what, escape(expand('.'), '*[]?{}, ') . direction) + finally + let &suffixesadd = old_suffixesadd + endtry +endfunction + +function! javacomplete#util#GlobPathList(path, pattern, suf, depth) + if v:version > 704 || v:version == 704 && has('patch279') + let pathList = globpath(a:path, a:pattern, a:suf, 1) + else + let pathList = split(globpath(a:path, a:pattern, a:suf), "\n") + endif + if a:depth > 0 + let depths = [] + for i in range(1, a:depth) + call add(depths, repeat("*".g:FILE_SEP, i)) + endfor + for i in depths + call extend(pathList, javacomplete#util#GlobPathList(a:path, i. a:pattern, 0, 0)) + endfor + endif + return pathList +endfunction + +function! javacomplete#util#IsWindows() abort + return has("win32") || has("win64") || has("win16") || has("dos32") || has("dos16") +endfunction + +function! s:JobVimOnCloseHandler(channel) + let job = s:asyncJobs[s:ChannelId(a:channel)] + let info = job_info(job['job']) + let Handler = function(job['handler']) + call call(Handler, [info['exitval'], 'exit']) +endfunction + +function! s:JobVimOnErrorHandler(channel, text) + let job = s:asyncJobs[s:ChannelId(a:channel)] + let Handler = function(job['handler']) + call call(Handler, [[a:text], 'stderr']) +endfunction + +function! s:JobVimOnCallbackHandler(channel, text) + let job = s:asyncJobs[s:ChannelId(a:channel)] + let Handler = function(job['handler']) + call call(Handler, [[a:text], 'stdout']) +endfunction + +function! s:JobNeoVimResponseHandler(jobId, data, event) + let job = s:asyncJobs[a:jobId] + let Handler = function(job['handler']) + call call(Handler, [a:data, a:event]) +endfunction + +function! s:ChannelId(channel) + return matchstr(a:channel, '\d\+') +endfunction + +function! s:NewJob(id, handler) + let s:asyncJobs = get(s:, 'asyncJobs', {}) + let s:asyncJobs[a:id] = {} + let s:asyncJobs[a:id]['handler'] = a:handler +endfunction + +function! javacomplete#util#RunSystem(command, shellName, handler) + call s:Log("running command: ". string(a:command)) + if has('nvim') + if exists('*jobstart') + let callbacks = { + \ 'on_stdout': function('s:JobNeoVimResponseHandler'), + \ 'on_stderr': function('s:JobNeoVimResponseHandler'), + \ 'on_exit': function('s:JobNeoVimResponseHandler') + \ } + let jobId = jobstart(a:command, extend({'shell': a:shellName}, callbacks)) + call s:NewJob(jobId, a:handler) + return + endif + elseif exists('*job_start') + let options = { + \ 'out_cb' : function('s:JobVimOnCallbackHandler'), + \ 'err_cb' : function('s:JobVimOnErrorHandler'), + \ 'close_cb' : function('s:JobVimOnCloseHandler') + \ } + if has('win32') && type(a:command) == 3 + let a:command[0] = exepath(a:command[0]) + endif + let job = job_start(a:command, options) + let jobId = s:ChannelId(job_getchannel(job)) + call s:NewJob(jobId, a:handler) + let s:asyncJobs[jobId]['job'] = job + return + endif + + if type(a:command) == type([]) + let ret = system(join(a:command, " ")) + else + let ret = system(a:command) + endif + for l in split(ret, "\n") + call call(a:handler, [[l], "stdout"]) + endfor + call call(a:handler, ["0", "exit"]) +endfunction + +function! javacomplete#util#Base64Encode(str) + JavacompletePy import base64 + JavacompletePy import vim + JavacompletePy content = vim.eval('a:str') if sys.version_info.major == 2 else bytes(vim.eval('a:str'), 'utf-8') + JavacompletePy b64 = base64.b64encode(content) + JavacompletePy vim.command("let base64 = '%s'" % (b64 if sys.version_info.major == 2 else b64.decode('utf-8'))) + return base64 +endfunction + +function! javacomplete#util#RemoveFile(file) + if filewritable(a:file) + if g:JavaComplete_IsWindows + silent exe '!rmdir /s /q "'. a:file. '"' + else + silent exe '!rm -r "'. a:file. '"' + endif + silent redraw! + endif +endfunction + +if exists('*uniq') + function! javacomplete#util#uniq(list) abort + return uniq(a:list) + endfunction +else + function! javacomplete#util#uniq(list) abort + let i = len(a:list) - 1 + while 0 < i + if a:list[i] ==# a:list[i - 1] + call remove(a:list, i) + let i -= 2 + else + let i -= 1 + endif + endwhile + return a:list + endfunction +endif + +function! javacomplete#util#GetBase(extra) + let base = expand(g:JavaComplete_BaseDir. g:FILE_SEP. "javacomplete2". g:FILE_SEP. a:extra) + if !isdirectory(base) + call mkdir(base, "p") + endif + + return base +endfunction + +function! javacomplete#util#RemoveEmptyClasses(classes) + return filter(a:classes, 'v:val !~ "^$"') +endfunction + +function! javacomplete#util#GetRegularClassesDict() + if exists('s:RegularClassesDict') + return s:RegularClassesDict + endif + let path = javacomplete#util#GetBase('cache'). g:FILE_SEP. 'regular_classes_'. g:JavaComplete_ProjectKey. '.dat' + if filereadable(path) + let classes = readfile(path) + else + let classes = [] + endif + let classes = javacomplete#util#RemoveEmptyClasses(javacomplete#util#uniq(sort(extend(classes, g:JavaComplete_RegularClasses)))) + let dict = {} + for class in classes + call extend(dict, {split(class,'\.')[-1] : class}) + endfor + let s:RegularClassesDict = dict + return s:RegularClassesDict +endfunction + +function! javacomplete#util#SaveRegularClassesList(classesDict) + let path = javacomplete#util#GetBase('cache'). g:FILE_SEP. 'regular_classes_'. g:JavaComplete_ProjectKey. '.dat' + call writefile(values(a:classesDict), path) + unlet s:RegularClassesDict +endfunction + +function! javacomplete#util#IsStatic(modifier) + return a:modifier[strlen(a:modifier)-4] +endfunction + +function! javacomplete#util#IsBuiltinType(name) + return index(g:J_PRIMITIVE_TYPES, a:name) >= 0 +endfunction + +function! javacomplete#util#IsKeyword(name) + return index(g:J_KEYWORDS, a:name) >= 0 +endfunction + +function! javacomplete#util#HasKeyword(name) + return a:name =~# g:RE_KEYWORDS +endfunction + +function! javacomplete#util#CheckModifier(modifier, condition) + if type(a:condition) == type([]) + for condition in a:condition + if condition <= len(a:modifier) + if a:modifier[-condition : -condition] == '1' + return 1 + endif + endif + endfor + return 0 + else + if a:condition <= len(a:modifier) + return a:modifier[-a:condition : -a:condition] == '1' + endif + return 0 + endif +endfunction + +function! javacomplete#util#GenMethodParamsDeclaration(method) + if has_key(a:method, 'p') + let match = matchlist(a:method.d, '^\(.*(\)') + if len(match) > 0 + let d = match[1] + let match = matchlist(a:method.d, '.*)\(.*\)$') + let throws = len(match) > 0 ? substitute(match[1], ',', ', ', 'g') : '' + + let ds = [] + let paramNames = [] + for p in a:method.p + let repeats = count(a:method.p, p) > 1 ? 1 : 0 + if index(g:J_PRIMITIVE_TYPES, p) >= 0 + let var = p[0] + else + let p = javacomplete#util#CleanFQN(p) + let var = tolower(p[0]). p[1:] + endif + let match = matchlist(var, '^\([a-zA-Z0-9]\+\)\A*') + let countVar = count(paramNames, match[1]) + repeats + call add(paramNames, match[1]) + call add(ds, p. ' '. match[1]. (countVar > 0 ? countVar : "")) + endfor + return d. join(ds, ', '). ')'. throws + endif + endif + return a:method.d +endfunction + +function! javacomplete#util#GetClassPackage(class) + let lastDot = strridx(a:class, '.') + if lastDot > 0 + return a:class[0:lastDot - 1] + endif + return a:class +endfunction + +" vim:set fdm=marker sw=2 nowrap: diff --git a/bundle/vim-javacomplete2/autoload/javacomplete/version.vim b/bundle/vim-javacomplete2/autoload/javacomplete/version.vim new file mode 100644 index 000000000..beaaad847 --- /dev/null +++ b/bundle/vim-javacomplete2/autoload/javacomplete/version.vim @@ -0,0 +1,44 @@ +" Vim completion script for java +" Maintainer: artur shaik +" +" Version control + +let g:JavaComplete_ServerCompatibilityVersion = "2.4.1" + +function! javacomplete#version#GetCompatibilityVerison() + return g:JavaComplete_ServerCompatibilityVersion +endfunction + +function! javacomplete#version#CompareVersions(scriptVersion, serverVersion) + let scriptVersion = split(a:scriptVersion, '\.') + let serverVersion = split(a:serverVersion, '\.') + while len(scriptVersion) < len(serverVersion) + call add(scriptVersion, '0') + endwhile + while len(serverVersion) < len(scriptVersion) + call add(serverVersion, '0') + endwhile + let i = 0 + while i < len(scriptVersion) + if i < len(serverVersion) + if str2nr(scriptVersion[i]) < str2nr(serverVersion[i]) + return -1 + elseif str2nr(scriptVersion[i]) > str2nr(serverVersion[i]) + return 1 + endif + else + return 1 + endif + let i += 1 + endwhile + return 0 +endfunction + +function! javacomplete#version#CheckServerCompatibility(serverVersion) + return + \ javacomplete#version#CompareVersions( + \ g:JavaComplete_ServerCompatibilityVersion, + \ a:serverVersion) <= 0 +endfunction + +" vim:set fdm=marker sw=2 nowrap: diff --git a/bundle/vim-javacomplete2/autoload/javavibridge.py b/bundle/vim-javacomplete2/autoload/javavibridge.py new file mode 100644 index 000000000..1196a86bc --- /dev/null +++ b/bundle/vim-javacomplete2/autoload/javavibridge.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python +# encoding: utf-8 + +import socket +import sys +import tempfile +import time +import subprocess +import os + +# function to get free port from ycmd +def GetUnusedLocalhostPort(): + sock = socket.socket() + # This tells the OS to give us any free port in the range [1024 - 65535] + sock.bind(('', 0)) + port = sock.getsockname()[1] + sock.close() + return port + +SERVER = ('127.0.0.1', GetUnusedLocalhostPort()) + +# A wrapper for subprocess.Popen that works around a Popen bug on Windows. +def SafePopen(args, **kwargs): + if kwargs.get('stdin') is None: + kwargs['stdin'] = subprocess.PIPE if sys.platform == 'win32' else None + + return subprocess.Popen(args, **kwargs) + +class JavaviBridge(): + + pythonVersion = sys.version_info.major + sock = None + popen = None + logfile = None + + def setupServer(self, javabin, args, classpath): + is_win = sys.platform == 'win32' + separator = (';' if is_win else ':') + fileSeparator = ('\\' if is_win else '/') + + classpathset = set(classpath.split(separator)) + + environ = os.environ.copy() + if 'CLASSPATH' in environ: + classpathset.union(environ['CLASSPATH'].split(separator)) + + environ['CLASSPATH'] = separator.join(classpathset) + + if vim.eval('get(g:, "JavaComplete_JavaviLogLevel", 0)') != 0: + defaulttmp = tempfile.gettempdir() + fileSeparator + 'javavi_log' + logdir = vim.eval( + "empty(g:JavaComplete_JavaviLogDirectory) ? '%s' : g:JavaComplete_JavaviLogDirectory" + % defaulttmp) + if not os.path.isdir(logdir): + os.mkdir(logdir) + self.logfile = open("%s%s%s" % ( + logdir, fileSeparator, "javavi_stdout.log"), + "a") + output = self.logfile + else: + output = subprocess.PIPE + + args = [javabin] + args + ['-D', str(SERVER[1])] + if is_win and vim.eval('has("gui_running")'): + info = subprocess.STARTUPINFO() + info.dwFlags = 1 + info.wShowWindow = 0 + self.popen = SafePopen(args, env=environ, stdout = output, stderr = output, startupinfo = info) + else: + self.popen = SafePopen(args, env=environ, stdout = output, stderr = output) + + def pid(self): + return self.popen.pid + + def port(self): + return SERVER[1] + + def poll(self): + if self.popen: + return self.popen.poll() is None + else: + return 0 + + def terminateServer(self): + if self.popen: + self.popen.terminate() + self.popen.wait() + + if self.logfile: + self.logfile.close() + + def makeSocket(self): + try: + self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + except socket.error as msg: + self.sock = None + + try: + self.sock.connect(SERVER) + time.sleep(.1) + except socket.error as msg: + self.sock.close() + self.sock = None + + if self.sock is None: + print('could not open socket, try again') + return + + self.sock.setblocking(0) + + + def send(self, data): + if self.sock is None: + self.makeSocket() + if self.sock is None: + return '' + + if self.pythonVersion == 3: + self.sock.sendall((str(data) + '\n').encode('UTF-8')) + else: + self.sock.sendall((data.decode('UTF-8') + '\n').encode('UTF-8')) + + totalData = [] + while 1: + try: + data = self.sock.recv(4096) + if not data or len(data) == 0: + break + + totalData.append(data.decode('UTF-8')) + time.sleep(0.0001) + except: + if totalData: break + + self.sock.close() + self.sock = None + return ''.join(totalData) diff --git a/bundle/vim-javacomplete2/classpath.gradle b/bundle/vim-javacomplete2/classpath.gradle new file mode 100644 index 000000000..d15cedd69 --- /dev/null +++ b/bundle/vim-javacomplete2/classpath.gradle @@ -0,0 +1,60 @@ +task classpath { + doLast { + HashSet classpathFiles = new HashSet() + for (project in allprojects) { + if (project.hasProperty('android')) { + project.android.getBootClasspath().each { + classpathFiles += it + } + if (project.android.hasProperty('applicationVariants')) { + project.android.applicationVariants.all { variant -> + + def variantBase = variant.baseName.replaceAll("-", File.separator) + + def buildClasses = project.getBuildDir().absolutePath + + File.separator + "intermediates" + + File.separator + "classes" + + File.separator + variantBase + + classpathFiles += buildClasses + + def userClasses = project.getBuildDir().absolutePath + + File.separator + "intermediates" + + File.separator + "javac" + + File.separator + variant.baseName.replaceAll("-", File.separator) + + File.separator + "compile" + variantBase.capitalize() + "JavaWithJavac" + File.separator + "classes" + + classpathFiles += userClasses + + variant.getCompileClasspath().each { + classpathFiles += it + } + } + } + } else { + // Print the list of all dependencies jar files. + project.configurations.findAll { + it.metaClass.respondsTo(it, "isCanBeResolved") ? it.isCanBeResolved() : false + }.each { + it.resolve().each { + if (it.inspect().endsWith("jar")) { + classpathFiles += it + } else if (it.inspect().endsWith("aar")) { + // If the dependency is an AAR file we try to determine the location + // of the classes.jar file in the exploded aar folder. + def splitted = it.inspect().split("/") + def namespace = splitted[-5] + def name = splitted[-4] + def version = splitted[-3] + def explodedPath = "$project.buildDir/intermediates/exploded-aar/$namespace/$name/$version/jars/classes.jar" + classpathFiles += explodedPath + } + } + } + } + } + def classpath = classpathFiles.join(File.pathSeparator) + println "CLASSPATH:" + classpath + println "END CLASSPATH GENERATION" + } +} diff --git a/bundle/vim-javacomplete2/doc/demo.gif b/bundle/vim-javacomplete2/doc/demo.gif new file mode 100644 index 000000000..a424e7eb3 Binary files /dev/null and b/bundle/vim-javacomplete2/doc/demo.gif differ diff --git a/bundle/vim-javacomplete2/doc/generics_demo.gif b/bundle/vim-javacomplete2/doc/generics_demo.gif new file mode 100644 index 000000000..e9bf31648 Binary files /dev/null and b/bundle/vim-javacomplete2/doc/generics_demo.gif differ diff --git a/bundle/vim-javacomplete2/doc/javacomplete.txt b/bundle/vim-javacomplete2/doc/javacomplete.txt new file mode 100644 index 000000000..17b9bdee3 --- /dev/null +++ b/bundle/vim-javacomplete2/doc/javacomplete.txt @@ -0,0 +1,821 @@ +*javacomplete.txt* Updated version of the original javacomplete plugin +artur shaik *javacomplete* + +============================================================================== +CONTENTS *javacomplete-contents* + 1. Overview..........................................|javacomplete-overview| + 1. Download......................................|javacomplete-download| + 2. Features......................................|javacomplete-features| + 3. Install........................................|javacomplete-install| + 4. Requirements..............................|javacomplete-requirements| + 2. Usage................................................|javacomplete-usage| + 1. Class creation................................|javacomplete-classnew| + 2. Commands......................................|javacomplete-commands| + 3. Input contexts................................|javacomplete-contexts| + 4. Kind letter.................................|javacomplete-kindletter| + 5. Options.........................................|javacomplete-config| + 3. FAQ....................................................|javacomplete-faq| + 4. History............................................|javacomplete-history| + 5. Todo..................................................|javacomplete-todo| + 6. Thanks..............................................|javacomplete-thanks| + +============================================================================== +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, javavi library, +javaparser library and javacomplete.txt. + +============================================================================== +DOWNLOAD *javacomplete-download* + +You can download the lastest version from this url: +https://github.com/artur-shaik/vim-javacomplete2 + +============================================================================== +FEATURES *javacomplete-features* + + 1. List members of a class, including (static) fields, (static) methods and + ctors; + 2. List classes or subpackages of a package; + 3. Provide parameters information of a method, list all overload methods; + 4. Complete an incomplete word; + 5. Provide a complete JAVA parser written in Vim script language; + 6. Use the JVM to obtain most information; + 7. Use the embedded parser to obtain the class information from source + files; + 8. JSP is supported, Builtin objects such as request, session can be + recognized; + 9. The classes and jar files in the WEB-INF will be appended automatically + to the classpath; + 10. Server side java reflection class loader and parsing library; + 11. Search class files automatically; + 12. Complete class name; + 13. Add import statement for a given class name; + 14. Complete methods declaration after '@Override'; + 15. Support for maven, gradle and Eclipse's '.classpath'; + 16. Cross-session cache; + 17. Auto insert methods that need to be implemented; + 18. `toString`, `equals`, `hashCode`, Accessors generation. + +============================================================================== +INSTALL *javacomplete-install* + + 1. This assumes you are using `Vundle`. +Adapt for your plugin manager of choice. Put this into your `.vimrc`. +> + " Java completion plugin. + Plugin 'artur-shaik/vim-javacomplete2' +< + 2. Set 'omnifunc' option. e.g. + +> + autocmd Filetype java setlocal omnifunc=javacomplete#Complete +< + 3. Map keys you prefer: +For smart (trying to guess import option) insert class import with : +> + nmap (JavaComplete-Imports-AddSmart) + imap (JavaComplete-Imports-AddSmart) +< +For usual (will ask for import option) insert class import with : +> + nmap (JavaComplete-Imports-Add) + imap (JavaComplete-Imports-Add) +< +For add all missing imports with : +> + nmap (JavaComplete-Imports-AddMissing) + imap (JavaComplete-Imports-AddMissing) +< +For remove all missing imports with : +> + nmap (JavaComplete-Imports-RemoveUnused) + imap (JavaComplete-Imports-RemoveUnused) +< +For sorting all imports with : +> + nmap (JavaComplete-Imports-SortImports) + imap (JavaComplete-Imports-SortImports) +< + +Default mappings: +> + nmap jI (JavaComplete-Imports-AddMissing) + nmap jR (JavaComplete-Imports-RemoveUnused) + nmap ji (JavaComplete-Imports-AddSmart) + nmap jii (JavaComplete-Imports-Add) + nmap jis (JavaComplete-Imports-SortImports) + + imap I (JavaComplete-Imports-AddMissing) + imap R (JavaComplete-Imports-RemoveUnused) + imap i (JavaComplete-Imports-AddSmart) + imap ii (JavaComplete-Imports-Add) + + nmap jM (JavaComplete-Generate-AbstractMethods) + + imap jM (JavaComplete-Generate-AbstractMethods) + + nmap jA (JavaComplete-Generate-Accessors) + nmap js (JavaComplete-Generate-AccessorSetter) + nmap jg (JavaComplete-Generate-AccessorGetter) + nmap ja (JavaComplete-Generate-AccessorSetterGetter) + nmap jts (JavaComplete-Generate-ToString) + nmap jeq (JavaComplete-Generate-EqualsAndHashCode) + nmap jc (JavaComplete-Generate-Constructor) + nmap jcc (JavaComplete-Generate-DefaultConstructor) + + imap s (JavaComplete-Generate-AccessorSetter) + imap g (JavaComplete-Generate-AccessorGetter) + imap a (JavaComplete-Generate-AccessorSetterGetter) + + vmap js (JavaComplete-Generate-AccessorSetter) + vmap jg (JavaComplete-Generate-AccessorGetter) + vmap ja (JavaComplete-Generate-AccessorSetterGetter) +< + + 4. Javavi library will be automatcally compiled when you +use first time. If no libs/javavi/target is generated, check that you have +the write permission and jdk installed. + +============================================================================== +REQUIREMENTS *javacomplete-requirements* + + + 1. Vim version 7.4 and above with python support; + 2. JDK8. + + +============================================================================== +USAGE *javacomplete-usage* + +You can use `vim-javacomplete2` just like other omni-completion plugin. Many +samples of input context are gived in the following section. + +See |javacomplete-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: ashaihullin@gmail.com + + +============================================================================== +CLASS CREATION *javacomplete-classnew* + +Prompt scheme, for class creation: +> + template:[subdirectory]:/package.ClassName extends SuperClass implements + Interface(String str, public Integer i):contructor(*):toString(1) +< +A: (optional) template - which will be used to create class boilerplate. Some +existed templates: junit, interface, exception, servlet, etcl + +B: (optional) subdirectory in which class will be put. For example: test, +androidTest; + +C: class name and package. With `/` will use backsearch for parent package to +put in it. Without `/` put in relative package to current; + +D: (optional) extends and implements classes will be automatically imported; + +E: (optional) private str variable, and public i variable will be added to +class; + +F: (optional) contructor using all fields and toString override method with +only 'str' field will be created. Also hashCode and equals can be used. + +There is autocompletion in command prompt that will try to help you. Your +current opened file shouldn't have dirty changes or 'hidden' should be set. + +============================================================================== +COMMANDS *javacomplete-commands* + +All these commands are supported when encoding with java project. + +:JCimportsAddMissing *:JCimportsAddMissing* + add all missing 'imports' + +:JCimportsRemoveUnused *:JCimportsRemoveUnused* + remove all unsused 'imports' + +:JCimportAdd *:JCimportAdd* + add 'import' for classname that is under cursor, or before it + +:JCimportAddSmart *:JCimportAddSmart* + add 'import' for classname trying to guess variant without ask user to + choose an option (it will ask on false guessing) + +:JCimportsSort *:JCimportsSort* + sort all 'imports' + +:JCserverShowPort *:JCserverShowPort* + show port, through which vim plugin communicates with server; + +:JCserverShowPID *:JCserverShowPID* + show server process identificator; + +:JCserverStart *:JCserverStart* + start server manually; + +:JCserverTerminate *:JCserverTerminate* + stop server manually; + +:JCserverCompile *:JCserverCompile* + compile server manually; + +:JCdebugEnableLogs *:JCdebugEnableLogs* + enable logs; + +:JCdebugDisableLogs *:JCdebugDisableLogs* + disable logs; + +:JCdebugGetLogContent *:JCdebugGetLogContent* + get debug logs; + +:JCcacheClear *:JCcacheClear* + clear cache manually. + +:JCgenerateAbstractMethods *:JCgenerateAbstractMethods* + generate methods that need to be implemented + +:JCgenerateAccessors *:JCgenerateAccessors* + generate getters and setters for all fields; + +:JCgenerateAccessorSetter *:JCgenerateAccessorSetter* + generate setter for field under cursor; + +:JCgenerateAccessorGetter *:JCgenerateAccessorGetter* + generate getter for field under cursor; + +:JCgenerateAccessorSetterGetter *:JCgenerateAccessorSetterGetter* + generate getter and setter for field under cursor; + +:JCgenerateToString *:JCgenerateToString* + generate 'toString' method; + +:JCgenerateEqualsAndHashCode *:JCgenerateEqualsAndHashCode* + generate 'equals' and 'hashCode' methods; + +:JCgenerateConstructor *:JCgenerateConstructor* + generate constructor with chosen fields; + +:JCgenerateConstructorDefault *:JCgenerateConstructorDefault* + generate default constructor; + +:JCclassNew *:JCclassNew* + open prompt to enter class creation command; + +:JCclassInFile *:JCclassInFile* + open prompt to choose template that will be used for creation class + boilerplate in current empty file; + +============================================================================== +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| )" +< + + +============================================================================== +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 +< + +============================================================================== +OPTIONS *javacomplete-config* + +All these options are supported when encoding with java project. + + *g:JavaComplete_SourcesPath* +path of your sources. Don't try to add all sources you have, this will slow +down parsing process. Add you project sources and necessery library sources. +If you have compiled classes add them to previous config instead. By default +plugin will search `src` directory and add it automatically. + + *g:JavaComplete_MavenRepositoryDisabled* +disable the maven repository. +> + let g:JavaComplete_MavenRepositoryDisabled = 1 +< +by default this option is disabled (0). + + *g:JavaComplete_LibsPath* +path of you jar files. This path will always appended with '~/.m2/repository' +directory. Here you can add your glassfish libs directory or your project +libs. It will be automatically appended with you jre home path + + *g:JavaComplete_BaseDir* +Base cache directory of javacomplete2 (default is ~/.cache): +> + let g:JavaComplete_BaseDir = '~/.your_cache_dir' +< + + *g:JavaComplete_ImportDefault* +In the import selection the default behavior is to use the first option +available: +> + let g:JavaComplete_ImportDefault = 0 +< +To avoid this behavior use: +> + let g:JavaComplete_ImportDefault = -1 +< + + *g:JavaComplete_InsertImports* +Import selection is activated automatically when completing new class name. +This can be avoided by setting: +> + let g:JavaComplete_InsertImports = 0 +< + + *g:JavaComplete_GradleExecutable* +Set the path of gradle executable file. by default it is empty string. + + *g:JavaComplete_ServerAutoShutdownTime* +The Java daemon should kill itself when Vim stops. Also its possible to +configure the timeout, so if there is no request during this time the daemon +will stop. To configure the timemout use the following (in seconds). By +default this option is 0. + + *g:JavaComplete_ImportSortType* +Sorting can by jar archives `jarName` or by package names `packageName`. This +option is to set the imports sorting type. By default this option is +`jarName`: +> + let g:JavaComplete_ImportSortType = 'jarName' +< + + + *g:JavaComplete_ImportOrder* +Specifies the order of import groups, when use `packageName` sorting type, for +example: +> + let g:JavaComplete_ImportOrder = ['java.', 'javax.', 'com.', 'org.', 'net.'] +< +An import group is a list of individual import statements that all start with +the same beginning of package name surrounded by blank lines above and below +the group. + + *g:JavaComplete_RegularClasses* +Regular class names that will be used automatically when you insert import: +> + let g:JavaComplete_RegularClasses = ['java.lang.String', 'java.lang.Object'] +< +You can populate it with your custom classes, or it will be populated +automatically when you choose any import option. List will be persisted, so it +will be used next time you run the same project. + + *g:JavaComplete_AutoStartServer* +Disable automatic startup of server: +> + let g:JavaComplete_AutoStartServer = 0 +< +By default this option is disabled (1). + + *g:JavaComplete_UseFQN* +Use fully qualified name in description: +> + let g:JavaComplete_UseFQN = 1 +< +By default this option is disabled (0). + + + *g:JavaComplete_EnableDefaultMappings* +Enable or disable default key mappings, by default this option is 1, and +default mappings are defined. To disable default mappings, set this option to +1. +> + let g:JavaComplete_EnableDefaultMappings = 1 +< + + *g:JavaComplete_PomPath* +Set pom.xml path explicitly: +> + let g:JavaComplete_PomPath = /path/to/pom.xml +< +It will be set automatically, if pom.xml is in underlying path. + + *g:JavaComplete_ClosingBrace* +Close brace on method declaration completion: +> + let g:JavaComplete_ClosingBrace = 1 +< +Add close brace automatically, when complete method declaration. By default +this option is enabled (1). Disable if it conflicts with another plugins. + + *g:JavaComplete_JavaviLogDirectory* +Set the directory where to write server logs. By default this option is empty. + + *g:JavaComplete_CustomTemplateDirectory* +Set directory that contains custom templates for class creation, for example: +> + let g:JavaComplete_CustomTemplateDirectory = '~/jc_templates' +< +By default this options is empty string. + +============================================================================== +FAQ *javacomplete-faq* + +4.1 Why can not complete in gradle project? + + Check if 'gradle' is in your runtime path or './gradlew' (or +'.\gradlew.bat' for Windows) is in your project's directory. + +4.2 I have installed gradle, but why I can not complete R.class? + + In android project, many of the class contains a ton of innerclass, +javacomplete2 could works better by reflection, so you need to compile you +project, after use './gradlew build', R.java will be automatically +generated and compiled. + +============================================================================== +HISTORY *javacomplete-history* + + +This section document the history of `vim-javacomplete2`. + +v2.3.4 2015-12-14 + + Use maven, gradle, or Eclipse's 'classpath` file to generate classpath + + Complete methods declaration on '@Override'. + +v2.3.3 2015-10-08 + + Jsp files support, no taglibs yet. + + Vimscript refactored. + + Read eclipse ".classpath" file. + + Option to set jvm launcher and compiler for javavi server. + + Using mappings. + + Bug fixes. + +v2.3.2 2015-09-18 + + Nested classes. + + Vimscript refactored. + +v2.3.1 2015-09-07 + + Better experience with imports. + + Commands added. + +v2.3 2015-07-29 + + Annotations completion support. + + Option to swtich use of FQN in completion suggestions. + + Check python support before start. + +v2.2 2015-07-08 + + Lambda expressions parsing. + +v2.1 2015-06-12 + + Generics completion. Bug fixes. + + Added g:JavaComplete_MavenRepositoryDisable option. + +v2.0 2015-05-26 + + Writed new library for reflection and parsing. Parsing make by + + third party library. Library run in server like way. + + Added class name completion and insertion of class import. + + Added auto classpath searcher. + +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 + + Added GetCurrentFileKey() avoid empty key of s:files for current buffer. + + Use a new strategy for searching inherited members. + + Supported new contexts "jav|", "var|", just after an incomplete word. + + Supported new context "abs(|)", a imported static method. + + Improved FoundClassDeclaration() + + Fixed bug calling cursor(0, 0) + + Rewrote DoGetClassInfo(), GetFQN() and IsFQN() + + Fixed a bug when merging superclass's members + + Improved s:MergeLines() and s:ExtractCleanExpr(). + + Rewrote CompleteAfterDot(). Added ParseExpr(). Removed +s:GetNextSubexprType() + + Supported accessible static imported members. + + Supported accessible inherited members. + + Used b:changedtick and getftime() to check buffer (or other file) for +changing. + + 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. + + 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 + + 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. + + Support accessible nested classes. + + Support import static members and import accessible nested classes. + + 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. + + 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. + +============================================================================== +TODO *javacomplete-todo* + +Add javadoc +Cross session cache; +Most used (classes, methods, vars) at first place (smart suggestions); +FXML support; +JSP check support; +Refactoring support?; +Class creation helpers; +etc... + +============================================================================== +THANKS *javacomplete-thanks* + + * Cheng Fang author of original javacomplete plugin; + * Zhang Li author of vim-javacompleteex plugin; + * http://github.com/javaparser/javaparser library. + +FeedBack: Any problem, bug or suggest are welcome to send to +ashaihullin@gmail.com + + +vim:tw=78:ts=8:ft=help:norl: diff --git a/bundle/vim-javacomplete2/libs/javavi/pom.xml b/bundle/vim-javacomplete2/libs/javavi/pom.xml new file mode 100644 index 000000000..e218544dd --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/pom.xml @@ -0,0 +1,105 @@ + + 4.0.0 + + kg.ash.javavi + javavi + 1.0-SNAPSHOT + jar + + javavi + http://maven.apache.org + + + UTF-8 + + + + + com.github.javaparser + javaparser-core + 3.5.20 + + + kg.ash.javavi.apache.logging.log4j + log4j-api + 2.7 + system + ${basedir}/../javavi_log4j-api.jar + + + kg.ash.javavi.apache.logging.log4j + log4j-core + 2.7 + system + ${basedir}/../javavi_log4j-core.jar + + + junit + junit + 4.13.1 + test + + + org.json + json + 20150729 + test + + + org.jmockit + jmockit + 1.20 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 1.8 + 1.8 + + + + org.codehaus.mojo + exec-maven-plugin + 1.3.2 + + java + + -Xms512m + -Xmx512m + -XX:NewRatio=3 + -XX:+PrintGCTimeStamps + -XX:+PrintGCDetails + -Xloggc:gc.log + -classpath + + kg.ash.javavi.Javavi + + + + + + org.apache.maven.plugins + maven-jar-plugin + 2.4 + + + + true + lib/ + kg.ash.javavi.Javavi + + + + + + + diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/Daemon.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/Daemon.java new file mode 100644 index 000000000..954117d22 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/Daemon.java @@ -0,0 +1,144 @@ +package kg.ash.javavi; + +import kg.ash.javavi.apache.logging.log4j.LogManager; +import kg.ash.javavi.apache.logging.log4j.Logger; +import kg.ash.javavi.cache.Cache; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.LinkedList; +import java.util.List; +import java.util.Timer; +import java.util.TimerTask; + +public class Daemon extends Thread { + + public static final Logger logger = LogManager.getLogger(); + + private int port; + private int timeoutSeconds; + private Timer timeoutTimer = new Timer(); + private TimerTask timeoutTask; + + public Daemon(int port, int timeoutSeconds) { + this.port = port; + this.timeoutSeconds = timeoutSeconds; + } + + public void run() { + ServerSocket echoServer = null; + Cache.getInstance().collectPackages(); + + while (true) { + if (timeoutSeconds > 0) { + timeoutTask = new TimeoutTask(); + timeoutTimer.schedule(timeoutTask, timeoutSeconds * 1000); + } + + try { + if (echoServer == null) { + echoServer = new ServerSocket(port); + } + } catch (IOException e) { + logger.warn(e); + break; + } + + try (Socket clientSocket = echoServer.accept()) { + if (timeoutTask != null) { + timeoutTask.cancel(); + } + + try (BufferedReader is = new BufferedReader( + new InputStreamReader(clientSocket.getInputStream())); + PrintStream os = new PrintStream(clientSocket.getOutputStream())) { + while (true) { + String[] request = parseRequest(is.readLine()); + if (request != null) { + os.print(Javavi.makeResponse(request)); + } + break; + } + } catch (Throwable e) { + logger.error(e, e); + } + } catch (IOException e) { + logger.error(e); + break; + } + } + } + + public String[] parseRequest(String request) { + if (request == null) { + return null; + } + + List args = new LinkedList<>(); + + StringBuilder buff = new StringBuilder(); + boolean quoteFlag = false; + boolean slashFlag = false; + for (char ch : request.toCharArray()) { + if (quoteFlag) { + if (ch == '\\') { + if (slashFlag) { + buff.append("\\"); + slashFlag = false; + } else { + slashFlag = true; + } + continue; + } + if (ch == '"' && !slashFlag) { + if (buff.length() == 0) { + args.add(""); + } + quoteFlag = false; + continue; + } + } + + if (ch == '"' && !slashFlag) { + quoteFlag = true; + } + + if (!quoteFlag) { + if (ch == ' ') { + if (buff.length() > 0) { + args.add(buff.toString()); + buff = new StringBuilder(); + } + continue; + } + } + + if ((ch != '"' && !slashFlag) || ((ch == '"' || ch == 'n') && slashFlag)) { + if (slashFlag && ch != '"') { + buff.append('\\'); + } + buff.append(ch); + } + + if (slashFlag) { + slashFlag = false; + } + } + if (buff.length() > 0) { + args.add(buff.toString()); + } + + return args.toArray(new String[0]); + } + + class TimeoutTask extends TimerTask { + public void run() { + logger.info("Shutdown by timeout timer."); + System.exit(0); + } + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/Javavi.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/Javavi.java new file mode 100644 index 000000000..2bd15a8f4 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/Javavi.java @@ -0,0 +1,115 @@ +package kg.ash.javavi; + +import kg.ash.javavi.actions.Action; +import kg.ash.javavi.actions.ActionFactory; +import kg.ash.javavi.apache.logging.log4j.LogManager; +import kg.ash.javavi.apache.logging.log4j.Logger; +import kg.ash.javavi.searchers.ClasspathCollector; + +import java.util.HashMap; + +public class Javavi { + + public static final String VERSION = "2.4.3"; + + public static String NEWLINE = ""; + + public static final Logger logger = LogManager.getLogger(); + + static void output(String s) { + System.out.print(s); + } + + private static void usage() { + version(); + System.out.println( + " java [-classpath] kg.ash.javavi.Javavi [-sources sourceDirs] [-h] [-v] [-d] [-D " + + "port] [action]"); + System.out.println("Options:"); + System.out.println(" -h help"); + System.out.println(" -v version"); + System.out.println(" -sources sources directory"); + System.out.println(" -d enable debug mode"); + System.out.println(" -D port start daemon on specified port"); + } + + private static void version() { + System.out.println("Reflection and parsing for javavi " + "vim plugin (" + VERSION + ")"); + } + + public static HashMap system = new HashMap<>(); + public static Daemon daemon = null; + + public static void main(String[] args) { + logger.info("starting javavi server on port: {}", System.getProperty("daemon.port", "0")); + + if (logger.isTraceEnabled()) { + logger.trace("output included libraries"); + new ClasspathCollector().collectClassPath().forEach(logger::trace); + } + + String response = makeResponse(args); + + output(response); + } + + public static String makeResponse(String[] args) { + + long ms = System.currentTimeMillis(); + Action action = null; + boolean asyncRun = false; + for (int i = 0; i < args.length; i++) { + String arg = args[i]; + logger.debug("argument: {}", arg); + switch (arg) { + case "-h": + usage(); + return ""; + case "-v": + version(); + return ""; + case "-sources": + system.put("sources", args[++i]); + break; + case "-n": + NEWLINE = "\n"; + break; + case "-base": + system.put("base", args[++i]); + break; + case "-project": + system.put("project", args[++i]); + break; + case "-compiler": + system.put("compiler", args[++i]); + break; + case "-async": + asyncRun = true; + break; + default: + if (action == null) { + action = ActionFactory.get(arg); + } + } + + if (action != null) { + break; + } + } + + String result = ""; + if (action != null) { + logger.debug("new {} action: \"{}\"", asyncRun ? "async" : "", + action.getClass().getSimpleName()); + + if (asyncRun) { + final Action a = action; + new Thread(() -> a.perform(args)).start(); + } else { + result = action.perform(args); + } + } + logger.debug("action time: {}ms", (System.currentTimeMillis() - ms)); + return result; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/TargetParser.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/TargetParser.java new file mode 100644 index 000000000..e832f457d --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/TargetParser.java @@ -0,0 +1,114 @@ +package kg.ash.javavi; + +import kg.ash.javavi.searchers.ClassSearcher; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class TargetParser { + + private final Pattern pattern = Pattern.compile("^(.*?)<(.*)>$"); + private final Pattern extendsPattern = Pattern.compile("^?[\\s]+(super|extends)\\s+(.*)$"); + private final ClassSearcher seacher = new ClassSearcher(); + + private final List typeArguments = new ArrayList<>(); + private String sources; + + public TargetParser(String sources) { + this.sources = sources; + } + + public String parse(String target) { + typeArguments.clear(); + + Matcher matcher = pattern.matcher(target); + if (matcher.find()) { + target = matcher.group(1); + String ta = markSplits(matcher.group(2)); + for (String arguments : ta.split("<_split_>")) { + parseArguments(arguments); + } + } + + return target; + } + + private void parseArguments(String arguments) { + arguments = arguments.replaceAll("(\\(|\\))", ""); + String[] argumentVariants = arguments.split("\\|"); + boolean added = false; + for (String arg : argumentVariants) { + Matcher argMatcher = pattern.matcher(arg); + boolean matchResult = argMatcher.find(); + if (matchResult) { + arg = argMatcher.group(1); + } + String name = getExactName(arg); + if (seacher.find(name.replaceAll("(\\[|\\])", ""), sources)) { + if (matchResult && !argMatcher.group(2).equals("?")) { + typeArguments.add(String.format("%s<%s>", name, argMatcher.group(2))); + } else { + typeArguments.add(name); + } + added = true; + break; + } + } + + if (!added) { + typeArguments.add("java.lang.Object"); + } + } + + private String getExactName(String name) { + Matcher matcher = extendsPattern.matcher(name); + if (matcher.find()) { + name = matcher.group(2); + } + + return name; + } + + private String markSplits(String ta) { + int i = 0; + int lbr = 0; + while (i < ta.length()) { + char c = ta.charAt(i); + if (c == '<') { + lbr++; + } else if (c == '>') { + lbr--; + } else if (c == ',' && lbr == 0) { + ta = ta.substring(0, i) + "<_split_>" + ta.substring(i + 1, ta.length()); + i += 9; + } + + i++; + } + + return ta; + } + + public List getTypeArguments() { + return typeArguments; + } + + public String getTypeArgumentsString() { + return getTypeArgumentsString(this.typeArguments); + } + + public static String getTypeArgumentsString(List typeArguments) { + if (typeArguments.isEmpty()) { + return ""; + } + + StringBuilder builder = new StringBuilder("<"); + for (String arg : typeArguments) { + builder.append(arg).append(","); + } + builder.setCharAt(builder.length() - 1, '>'); + return builder.toString(); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/Action.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/Action.java new file mode 100644 index 000000000..6417fe42c --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/Action.java @@ -0,0 +1,5 @@ +package kg.ash.javavi.actions; + +public interface Action { + String perform(String[] args); +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/ActionFactory.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/ActionFactory.java new file mode 100644 index 000000000..1521fa83d --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/ActionFactory.java @@ -0,0 +1,59 @@ +package kg.ash.javavi.actions; + +import java.util.Map; + +public class ActionFactory { + public static Action get(String action) { + switch (action) { + case "-E": + return new GetClassInfoAction(); + case "-p": + return new GetPackageInfoAction(); + case "-s": + return new GetClassInfoFromSourceAction(); + case "-class-packages": + return new GetClassPackagesAction(); + case "-similar-classes": + return new FilterSimilarClassesAction(); + case "-similar-annotations": + return new FilterSimilarAnnotationsAction(); + case "-D": + return new ExecuteDaemonAction(); + case "-unused-imports": + return new GetUnusedImportsAction(); + case "-missing-imports": + return new GetMissingImportsAction(); + case "-clear-from-cache": + return new RemoveClassInfoFromCache(); + case "-recompile-class": + return new ClassRecompileAction(); + case "-collect-packages": + return new CollectPackagesAction(); + case "-fetch-class-archives": + return new GetClassesArchiveNamesAction(); + case "-class-info-by-content": + return new ParseByContentAction(); + case "-get-debug-log-path": + return new GetDebugLogPath(); + case "-version": + return new GetAppVersion(); + case "-add-source-to-cache": + return new AddClassToCacheAction(); + } + return null; + } + + public static String getJavaViSources(Map javaViSystem) { + String sources = javaViSystem.get("sources"); + return sources != null ? sources.replace('\\', '/') : ""; + } + + public static String getArgWithName(String[] args, String name) { + for (int i = 0; i < args.length; i++) { + if (args[i].equals(name)) { + return args[i + 1]; + } + } + return ""; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/ActionWithTarget.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/ActionWithTarget.java new file mode 100644 index 000000000..2fc0be783 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/ActionWithTarget.java @@ -0,0 +1,24 @@ +package kg.ash.javavi.actions; + +import static kg.ash.javavi.actions.ActionFactory.getJavaViSources; + +import kg.ash.javavi.Javavi; +import kg.ash.javavi.TargetParser; + +public abstract class ActionWithTarget implements Action { + + protected TargetParser targetParser; + protected String sources; + + public ActionWithTarget() { + sources = getJavaViSources(Javavi.system); + targetParser = new TargetParser(sources); + } + + protected String parseTarget(String[] args) { + if (args.length > 0) { + return targetParser.parse(args[args.length - 1]); + } + return ""; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/AddClassToCacheAction.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/AddClassToCacheAction.java new file mode 100644 index 000000000..ee94206b1 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/AddClassToCacheAction.java @@ -0,0 +1,33 @@ +package kg.ash.javavi.actions; + +import static kg.ash.javavi.actions.ActionFactory.getArgWithName; + +import kg.ash.javavi.apache.logging.log4j.LogManager; +import kg.ash.javavi.apache.logging.log4j.Logger; +import kg.ash.javavi.cache.Cache; +import kg.ash.javavi.searchers.ClassNameMap; +import kg.ash.javavi.searchers.JavaClassMap; + +public class AddClassToCacheAction implements Action { + + public static final Logger logger = LogManager.getLogger(); + + @Override + public String perform(String[] args) { + String sourceFileArg = getArgWithName(args, "-source"); + String classNameArg = getArgWithName(args, "-class"); + String packageNameArg = getArgWithName(args, "-package"); + + ClassNameMap cnm = (ClassNameMap) Cache.getInstance().getClassPackages().get(classNameArg); + if (cnm == null) { + cnm = new ClassNameMap(classNameArg); + } + + cnm.setJavaFile(sourceFileArg); + cnm.add(packageNameArg, JavaClassMap.SOURCETYPE_SOURCES, JavaClassMap.TYPE_SUBPACKAGE, + null); + + Cache.getInstance().getClassPackages().put(classNameArg, cnm); + return ""; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/ClassRecompileAction.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/ClassRecompileAction.java new file mode 100644 index 000000000..46e7dac3f --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/ClassRecompileAction.java @@ -0,0 +1,75 @@ +package kg.ash.javavi.actions; + +import kg.ash.javavi.Javavi; +import kg.ash.javavi.apache.logging.log4j.LogManager; +import kg.ash.javavi.apache.logging.log4j.Logger; +import kg.ash.javavi.cache.Cache; +import kg.ash.javavi.searchers.ClassNameMap; +import kg.ash.javavi.searchers.JavaClassMap; + +import java.io.File; + +public class ClassRecompileAction extends ActionWithTarget { + + public static final Logger logger = LogManager.getLogger(); + + @Override + public String perform(String[] args) { + String target = parseTarget(args); + String[] splitted = target.split("\\."); + + ClassNameMap classMap = findClass(splitted[splitted.length - 1]); + if (classMap != null && classMap.getClassFile() != null && classMap.getJavaFile() != null) { + String classFile = classMap.getClassFile(); + String sourceFile = classMap.getJavaFile(); + String classDir = classFile.substring(0, classFile.lastIndexOf(File.separator)); + + int offset = findOffset(sourceFile.substring(0, sourceFile.lastIndexOf(File.separator)), + classDir); + classDir = classFile.substring(0, offset); + + if (classDir.isEmpty()) { + return ""; + } + + String compiler = Javavi.system.get("compiler"); + String classPath = System.getProperty("java.class.path"); + + execute(String.format("%s -cp %s -d %s %s", compiler, classPath, classDir, sourceFile)); + } + + return ""; + } + + private void execute(String command) { + try { + Process p = Runtime.getRuntime().exec(command); + p.waitFor(); + } catch (Exception e) { + logger.error(e); + } + } + + private int findOffset(String sourceFile, String classDir) { + int offset = 0; + while (!sourceFile.endsWith(classDir)) { + int index = classDir.indexOf(File.separator, 2); + if (index == 0) { + return 0; + } + + classDir = classDir.substring(index); + offset += index; + } + return offset; + } + + private ClassNameMap findClass(String name) { + JavaClassMap classMap = Cache.getInstance().getClassPackages().get(name); + if (classMap != null && classMap instanceof ClassNameMap) { + return (ClassNameMap) classMap; + } + return null; + } + +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/CollectPackagesAction.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/CollectPackagesAction.java new file mode 100644 index 000000000..90686ff67 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/CollectPackagesAction.java @@ -0,0 +1,14 @@ +package kg.ash.javavi.actions; + +import kg.ash.javavi.cache.Cache; + +public class CollectPackagesAction implements Action { + + @Override + public String perform(String[] args) { + Cache.getInstance().getClassPackages().clear(); + Cache.getInstance().collectPackages(); + return ""; + } + +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/ExecuteDaemonAction.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/ExecuteDaemonAction.java new file mode 100644 index 000000000..c737f231d --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/ExecuteDaemonAction.java @@ -0,0 +1,29 @@ +package kg.ash.javavi.actions; + +import kg.ash.javavi.Daemon; +import kg.ash.javavi.Javavi; +import kg.ash.javavi.apache.logging.log4j.LogManager; +import kg.ash.javavi.apache.logging.log4j.Logger; + +public class ExecuteDaemonAction implements Action { + + public static final Logger logger = LogManager.getLogger(); + + @Override + public String perform(String[] args) { + if (Javavi.daemon != null) { + return ""; + } + + Integer daemonPort = Integer.valueOf(System.getProperty("daemon.port", "0")); + if (daemonPort == 0) { + return "Error: daemonPort is null"; + } + + logger.debug("starting daemon mode"); + Javavi.daemon = new Daemon(daemonPort, -1); + Javavi.daemon.start(); + + return ""; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/FilterSimilarAnnotationsAction.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/FilterSimilarAnnotationsAction.java new file mode 100644 index 000000000..4da073418 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/FilterSimilarAnnotationsAction.java @@ -0,0 +1,13 @@ +package kg.ash.javavi.actions; + +import kg.ash.javavi.cache.Cache; +import kg.ash.javavi.output.OutputSimilarAnnotations; + +public class FilterSimilarAnnotationsAction extends ActionWithTarget { + + @Override + public String perform(String[] args) { + return new OutputSimilarAnnotations(Cache.getInstance().getClassPackages()).get( + parseTarget(args)); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/FilterSimilarClassesAction.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/FilterSimilarClassesAction.java new file mode 100644 index 000000000..a17b62f2f --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/FilterSimilarClassesAction.java @@ -0,0 +1,13 @@ +package kg.ash.javavi.actions; + +import kg.ash.javavi.cache.Cache; +import kg.ash.javavi.output.OutputSimilarClasses; + +public class FilterSimilarClassesAction extends ActionWithTarget { + + @Override + public String perform(String[] args) { + return new OutputSimilarClasses(Cache.getInstance().getClassPackages()).get( + parseTarget(args)); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetAppVersion.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetAppVersion.java new file mode 100644 index 000000000..145df9f3f --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetAppVersion.java @@ -0,0 +1,11 @@ +package kg.ash.javavi.actions; + +import kg.ash.javavi.Javavi; + +public class GetAppVersion implements Action { + + @Override + public String perform(String[] args) { + return Javavi.VERSION; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetClassInfoAction.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetClassInfoAction.java new file mode 100644 index 000000000..3058632d9 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetClassInfoAction.java @@ -0,0 +1,34 @@ +package kg.ash.javavi.actions; + +import kg.ash.javavi.clazz.SourceClass; +import kg.ash.javavi.output.OutputClassInfo; +import kg.ash.javavi.readers.ClassReader; +import kg.ash.javavi.searchers.ClassSearcher; + +public class GetClassInfoAction extends ActionWithTarget { + + @Override + public String perform(String[] args) { + String target = parseTarget(args); + + ClassSearcher searcher = new ClassSearcher(); + boolean found = true; + while (!searcher.find(target, sources)) { + if (!target.contains(".")) { + found = false; + break; + } + target = target.substring(0, target.lastIndexOf(".")); + } + + if (found) { + ClassReader reader = searcher.getReader(); + reader.setTypeArguments(targetParser.getTypeArguments()); + SourceClass clazz = reader.read(target); + if (clazz != null) { + return new OutputClassInfo().get(clazz); + } + } + return ""; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetClassInfoFromSourceAction.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetClassInfoFromSourceAction.java new file mode 100644 index 000000000..984eb3137 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetClassInfoFromSourceAction.java @@ -0,0 +1,13 @@ +package kg.ash.javavi.actions; + +import kg.ash.javavi.output.OutputClassInfo; +import kg.ash.javavi.readers.Parser; + +public class GetClassInfoFromSourceAction extends ActionWithTarget { + + @Override + public String perform(String[] args) { + Parser parser = new Parser(sources, parseTarget(args)); + return new OutputClassInfo().get(parser.read(null)); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetClassPackagesAction.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetClassPackagesAction.java new file mode 100644 index 000000000..a5adf758a --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetClassPackagesAction.java @@ -0,0 +1,13 @@ +package kg.ash.javavi.actions; + +import kg.ash.javavi.cache.Cache; +import kg.ash.javavi.output.OutputClassPackages; + +public class GetClassPackagesAction extends ActionWithTarget { + + @Override + public String perform(String[] args) { + return new OutputClassPackages(Cache.getInstance().getClassPackages()).get( + parseTarget(args)); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetClassesArchiveNamesAction.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetClassesArchiveNamesAction.java new file mode 100644 index 000000000..e6f70fd53 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetClassesArchiveNamesAction.java @@ -0,0 +1,79 @@ +package kg.ash.javavi.actions; + +import kg.ash.javavi.apache.logging.log4j.LogManager; +import kg.ash.javavi.apache.logging.log4j.Logger; +import kg.ash.javavi.cache.Cache; +import kg.ash.javavi.searchers.JavaClassMap; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class GetClassesArchiveNamesAction extends ActionWithTarget { + + public static final Logger logger = LogManager.getLogger(); + + @Override + public String perform(String[] args) { + String classes = parseTarget(args); + + Map> result = new HashMap<>(); + for (String _classFqn : classes.split(",")) { + final String classFqn = removeStaticKeyword(_classFqn); + + String[] classFqnArray = classFqn.split("\\."); + String className = classFqnArray[classFqnArray.length - 1]; + + logger.debug("class name: {}", className); + + HashMap classPackages = getClassPackages(); + if (classPackages.containsKey(className)) { + String classPackage = removeLastElementAndJoin(classFqnArray); + + logger.debug("class name: {}", className); + + JavaClassMap cm = classPackages.get(className); + Arrays.stream(new String[] { "", "$" }).forEach(s -> { + if (cm.getSubpackages().get(classPackage + s) != null) { + String fileName = cm.getSubpackages().get(classPackage + s); + if (result.containsKey(fileName)) { + result.get(fileName).add(classFqn); + } else { + result.put(fileName, new ArrayList<>(Arrays.asList(classFqn))); + } + } + }); + } + } + return String.format("[%s]", buildResult(result)); + } + + private String removeStaticKeyword(String classFqn) { + if (classFqn.contains(" ")) { + return classFqn.split(" ")[1]; + } + return classFqn; + } + + private HashMap getClassPackages() { + return Cache.getInstance().getClassPackages(); + } + + private String removeLastElementAndJoin(String[] array) { + String[] newArray = new String[array.length - 1]; + System.arraycopy(array, 0, newArray, 0, newArray.length); + return String.join(".", newArray); + } + + private StringBuilder buildResult(Map> result) { + StringBuilder builder = new StringBuilder(); + result.forEach((s, l) -> { + builder.append("['").append(s).append("',["); + l.forEach(classFqn -> builder.append("'").append(classFqn).append("',")); + builder.append("]],"); + }); + return builder; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetDebugLogPath.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetDebugLogPath.java new file mode 100644 index 000000000..d54d174d1 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetDebugLogPath.java @@ -0,0 +1,18 @@ +package kg.ash.javavi.actions; + +import kg.ash.javavi.apache.logging.log4j.LogManager; +import kg.ash.javavi.apache.logging.log4j.core.LoggerContext; +import kg.ash.javavi.apache.logging.log4j.core.lookup.StrLookup; + +public class GetDebugLogPath implements Action { + + @Override + public String perform(String[] args) { + LoggerContext ctx = (LoggerContext) LogManager.getContext(); + StrLookup lookup = (ctx.getConfiguration()).getStrSubstitutor().getVariableResolver(); + String timeId = lookup.lookup("log.time_id"); + String logsDirectory = System.getProperty("log.directory", lookup.lookup("log.directory")); + String daemonPort = System.getProperty("daemon.port", lookup.lookup("daemon.port")); + return String.format("%s/%s-%s.log", logsDirectory, timeId, daemonPort); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetMissingImportsAction.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetMissingImportsAction.java new file mode 100644 index 000000000..ce37f6538 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetMissingImportsAction.java @@ -0,0 +1,95 @@ +package kg.ash.javavi.actions; + +import com.github.javaparser.ast.ImportDeclaration; +import com.github.javaparser.ast.Node; +import com.github.javaparser.printer.PrettyPrinter; +import com.github.javaparser.printer.PrettyPrinterConfiguration; +import kg.ash.javavi.clazz.ClassImport; + +import java.util.ArrayList; +import java.util.List; + +public class GetMissingImportsAction extends ImportsAction { + + // TODO(joshleeb): Move this somewhere nice. + private static String removeComments(Node n) { + PrettyPrinterConfiguration config = new PrettyPrinterConfiguration(); + config.setPrintComments(false); + return new PrettyPrinter(config).print(n); + } + + @Override + public String action() { + List importTails = new ArrayList<>(); + List asteriskImports = new ArrayList<>(); + if (compilationUnit.getImports() != null) { + for (ImportDeclaration importDeclaration : + compilationUnit.getImports()) { + ClassImport classImport = new ClassImport( + removeComments( + importDeclaration.getName()), + importDeclaration.isStatic(), + importDeclaration.isAsterisk()); + if (classImport.isAsterisk()) { + asteriskImports.add(classImport.getName()); + } else { + importTails.add(classImport.getTail()); + } + } + } + + if (compilationUnit.getPackageDeclaration().isPresent()) { + asteriskImports.add( + removeComments( + compilationUnit.getPackageDeclaration(). + get().getName())); + } + + StringBuilder result = new StringBuilder("{'imports':["); + for (String classname : classnames) { + if (!importTails.contains(classname)) { + GetClassPackagesAction getPackagesAction = + new GetClassPackagesAction(); + String packages = getPackagesAction.perform( + new String[] { classname }); + + if (packages.startsWith("message:")) { + return packages; + } else if (packages.length() == 2) { + continue; + } + + String[] splitted = packages.substring( + 1, packages.length() - 1).split(","); + boolean found = false; + for (String foundPackage : splitted) { + if (foundPackage.trim().isEmpty()) { + continue; + } + + for (String asteriskImport : asteriskImports) { + if (isolatePackage(foundPackage) + .equals(asteriskImport)) { + found = true; + break; + } + } + } + if (!found) { + if (declarations.contains(classname)) { + found = true; + } + } + if (!found) { + result.append(packages).append(","); + } + } + } + return result.append("]}").toString(); + } + + private static String isolatePackage(String pkg) { + pkg = pkg.trim().substring(1, pkg.length() - 1); + return pkg.substring(0, pkg.lastIndexOf(".")); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetPackageInfoAction.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetPackageInfoAction.java new file mode 100644 index 000000000..4483f6fb2 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetPackageInfoAction.java @@ -0,0 +1,12 @@ +package kg.ash.javavi.actions; + +import kg.ash.javavi.cache.Cache; +import kg.ash.javavi.output.OutputPackageInfo; + +public class GetPackageInfoAction extends ActionWithTarget { + + @Override + public String perform(String[] args) { + return new OutputPackageInfo(Cache.getInstance().getClassPackages()).get(parseTarget(args)); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetUnusedImportsAction.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetUnusedImportsAction.java new file mode 100644 index 000000000..2f0270804 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/GetUnusedImportsAction.java @@ -0,0 +1,43 @@ +package kg.ash.javavi.actions; + +import com.github.javaparser.ast.ImportDeclaration; +import com.github.javaparser.ast.Node; +import com.github.javaparser.printer.PrettyPrinter; +import com.github.javaparser.printer.PrettyPrinterConfiguration; +import kg.ash.javavi.clazz.ClassImport; + +public class GetUnusedImportsAction extends ImportsAction { + + @Override + public String action() { + StringBuilder result = new StringBuilder("{'imports':["); + for (ImportDeclaration importDeclaration : + compilationUnit.getImports()) { + if (importDeclaration.isAsterisk()) { + continue; + } + + ClassImport classImport = new ClassImport( + removeComments(importDeclaration.getName()), + importDeclaration.isStatic(), + importDeclaration.isAsterisk()); + + String classname = classImport.getTail(); + if (!classnames.contains(classname)) { + result.append("'") + .append(classImport.getHead()) + .append(classImport.isStatic() ? "$" : ".") + .append(classname) + .append("',"); + } + } + return result.append("]}").toString(); + } + + private static String removeComments(Node n) { + PrettyPrinterConfiguration config = + new PrettyPrinterConfiguration(); + config.setPrintComments(false); + return new PrettyPrinter(config).print(n); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/ImportsAction.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/ImportsAction.java new file mode 100644 index 000000000..31c7ffe3a --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/ImportsAction.java @@ -0,0 +1,75 @@ +package kg.ash.javavi.actions; + +import com.github.javaparser.Range; +import com.github.javaparser.ast.CompilationUnit; + +import java.io.UnsupportedEncodingException; +import java.util.Base64; +import java.util.HashSet; +import java.util.Optional; +import java.util.Set; + +import kg.ash.javavi.readers.source.ClassNamesFetcher; +import kg.ash.javavi.readers.source.CompilationUnitCreator; +import kg.ash.javavi.readers.source.CompilationUnitResult; + +public abstract class ImportsAction implements Action { + + protected Set classnames; + protected Set declarations; + protected CompilationUnit compilationUnit; + + @Override + public String perform(String[] args) { + try { + String base64Content = getContent(args); + String content = new String( + Base64.getDecoder().decode(base64Content), "UTF-8"); + + CompilationUnitResult compilationUnitResult = + CompilationUnitCreator.createFromContent(content); + if (compilationUnitResult == null) { + return "Couldn't parse file"; + } else if (compilationUnitResult.getProblems() != null) { + StringBuilder result = + new StringBuilder("{'parse-problems':["); + Set ranges = new HashSet<>(); + compilationUnitResult.getProblems().stream().forEach(p -> { + p.getLocation().get().getBegin().getRange() + .filter(range -> !ranges.contains(range)) + .ifPresent(range -> { + result.append("{'message':'").append(p.getMessage()).append("'"); + result.append(",'lnum':'") + .append(range.begin.line).append("'"); + result.append(",'col':'") + .append(range.begin.column).append("'},"); + ranges.add(range); + }); + }); + return result.append("]}").toString(); + } else { + compilationUnit = compilationUnitResult.getCompilationUnit(); + } + + ClassNamesFetcher classnamesFetcher = + new ClassNamesFetcher(compilationUnit); + classnames = classnamesFetcher.getNames(); + declarations = classnamesFetcher.getDeclarationList(); + + return action(); + } catch (UnsupportedEncodingException ex) { + return ex.getMessage(); + } + } + + private String getContent(String[] args) { + for (int i = 0; i < args.length; i++) { + if (args[i].equals("-content")) { + return args[i + 1]; + } + } + return ""; + } + + public abstract String action(); +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/ParseByContentAction.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/ParseByContentAction.java new file mode 100644 index 000000000..89df16b45 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/ParseByContentAction.java @@ -0,0 +1,30 @@ +package kg.ash.javavi.actions; + +import static kg.ash.javavi.actions.ActionFactory.getArgWithName; +import static kg.ash.javavi.actions.ActionFactory.getJavaViSources; + +import kg.ash.javavi.Javavi; +import kg.ash.javavi.output.OutputClassInfo; +import kg.ash.javavi.readers.Parser; + +import java.io.UnsupportedEncodingException; +import java.util.Base64; + +public class ParseByContentAction implements Action { + + @Override + public String perform(String[] args) { + try { + String targetClass = getArgWithName(args, "-target"); + String base64Content = getArgWithName(args, "-content"); + + String content = new String(Base64.getDecoder().decode(base64Content), "UTF-8"); + Parser parser = new Parser(getJavaViSources(Javavi.system)); + parser.setSourceContent(content); + + return new OutputClassInfo().get(parser.read(targetClass)); + } catch (UnsupportedEncodingException ex) { + return ex.getMessage(); + } + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/RemoveClassInfoFromCache.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/RemoveClassInfoFromCache.java new file mode 100644 index 000000000..046a3545b --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/actions/RemoveClassInfoFromCache.java @@ -0,0 +1,15 @@ +package kg.ash.javavi.actions; + +import kg.ash.javavi.cache.Cache; + +public class RemoveClassInfoFromCache extends ActionWithTarget { + + @Override + public String perform(String[] args) { + String target = parseTarget(args); + if (Cache.getInstance().getClasses().containsKey(target)) { + Cache.getInstance().getClasses().remove(target); + } + return ""; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/cache/Cache.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/cache/Cache.java new file mode 100644 index 000000000..b0fe28a32 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/cache/Cache.java @@ -0,0 +1,108 @@ +package kg.ash.javavi.cache; + +import kg.ash.javavi.Javavi; +import kg.ash.javavi.apache.logging.log4j.LogManager; +import kg.ash.javavi.apache.logging.log4j.Logger; +import kg.ash.javavi.clazz.SourceClass; +import kg.ash.javavi.searchers.JavaClassMap; +import kg.ash.javavi.searchers.PackagesLoader; + +import java.util.HashMap; +import java.util.Timer; +import java.util.TimerTask; + +public class Cache { + + public static final Logger logger = LogManager.getLogger(); + public static final String PACKAGES_EMPTY_ERROR + = "message: packages still empty, try later. indexing..."; + + private CacheSerializator serializator = new CacheSerializator(); + private HashMap classPackages = new HashMap<>(); + private HashMap classes = new HashMap<>(); + private Timer autosaveCacheTimer = new Timer(); + private boolean collectIsRunning = false; + private int autosavePeriod = 60; + private int cacheCode; + + private static Cache instance; + + public static Cache getInstance() { + if (instance == null) { + instance = new Cache(); + } + return instance; + } + + public synchronized void collectPackages() { + if (collectIsRunning) { + return; + } + + collectIsRunning = true; + new Thread(() -> { + logger.info("start collecting cache"); + loadCache(); + + if (classPackages.isEmpty()) { + logger.info("collecting empty cache"); + + HashMap classPackagesTemp = new HashMap<>(); + new PackagesLoader(Javavi.system.get("sources")).collectPackages(classPackagesTemp); + classPackages.putAll(classPackagesTemp); + + saveCache(); + } + + cacheCode = getClassPackages().hashCode(); + collectIsRunning = false; + + autosaveCacheTimer.schedule(new AutosaveTask(this), autosavePeriod * 1000); + }).start(); + } + + @SuppressWarnings("unchecked") + public void loadCache() { + Object o = serializator.loadCache("class_packages"); + if (o != null) { + try { + classPackages = (HashMap) o; + } catch (ClassCastException e) { + logger.warn("Couldn't load cache"); + } + } + } + + public void saveCache() { + serializator.saveCache("class_packages", classPackages); + } + + public HashMap getClassPackages() { + if (classPackages.isEmpty()) { + collectPackages(); + } + return classPackages; + } + + public HashMap getClasses() { + return classes; + } + + class AutosaveTask extends TimerTask { + private final Cache cache; + + public AutosaveTask(Cache cache) { + this.cache = cache; + } + + public void run() { + int newCode = cache.getClassPackages().hashCode(); + if (newCode != cache.cacheCode) { + logger.info("autosave cache: {} != {}", newCode, cache.cacheCode); + cache.saveCache(); + cache.cacheCode = cache.getClassPackages().hashCode(); + } + cache.autosaveCacheTimer.schedule(new AutosaveTask(cache), cache.autosavePeriod * 1000); + } + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/cache/CacheSerializator.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/cache/CacheSerializator.java new file mode 100644 index 000000000..5a3e43aa8 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/cache/CacheSerializator.java @@ -0,0 +1,75 @@ +package kg.ash.javavi.cache; + +import kg.ash.javavi.Javavi; +import kg.ash.javavi.apache.logging.log4j.LogManager; +import kg.ash.javavi.apache.logging.log4j.Logger; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.Map; + +public class CacheSerializator { + + public static final Logger logger = LogManager.getLogger(); + + private String base = null; + private String project = null; + + public CacheSerializator() { + if (Javavi.system.containsKey("base")) { + Map system = Javavi.system; + + base = system.get("base"); + if (system.containsKey("project")) { + project = Javavi.system.get("project"); + } + + File cacheFile = new File(base + File.separator + "cache"); + if (!cacheFile.exists()) { + cacheFile.mkdir(); + } + } + } + + public void saveCache(String name, Object data) { + if (base != null && project != null) { + String filename = String.format( + "%s%scache%s%s_%s.dat", + base, + File.separator, + File.separator, + name, + project); + logger.info("saving cache {} to file {}", name, filename); + try (FileOutputStream fout = new FileOutputStream(filename); + ObjectOutputStream oos = new ObjectOutputStream(fout)) { + oos.writeObject(data); + } catch (Throwable e) { + logger.error(e); + } + } + } + + public Object loadCache(String name) { + if (base != null && project != null) { + String filename = String.format( + "%s%scache%s%s_%s.dat", + base, + File.separator, + File.separator, + name, + project); + logger.info("loading cache {} from file {}", name, filename); + try (FileInputStream fin = new FileInputStream(filename); + ObjectInputStream ois = new ObjectInputStream(fin)) { + return ois.readObject(); + } catch (Throwable e) { + logger.warn("Couldn't load cache"); + } + } + return null; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/clazz/ClassConstructor.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/clazz/ClassConstructor.java new file mode 100644 index 000000000..f3fe54ace --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/clazz/ClassConstructor.java @@ -0,0 +1,64 @@ +package kg.ash.javavi.clazz; + +import com.github.javaparser.ast.Modifier; + +import java.util.EnumSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; + +public class ClassConstructor { + + private String declaration = ""; + private EnumSet modifiers; + private List typeParameters = new LinkedList<>(); + + public void setDeclaration(String declaration) { + this.declaration = declaration; + } + + public String getDeclaration() { + return declaration; + } + + public void setModifiers(EnumSet modifiers) { + this.modifiers = modifiers; + } + + public EnumSet getModifiers() { + return modifiers; + } + + public void addTypeParameter(ClassTypeParameter parameter) { + typeParameters.add(parameter); + } + + public List getTypeParameters() { + return typeParameters; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + + final ClassConstructor other = (ClassConstructor) obj; + if (!Objects.equals(this.declaration, other.declaration) && (this.declaration == null + || !this.declaration.equals(other.declaration))) { + return false; + } + return this.modifiers == other.modifiers; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 17 * hash + (modifiers.hashCode()); + hash = 17 * hash + (this.declaration != null ? this.declaration.hashCode() : 0); + return hash; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/clazz/ClassField.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/clazz/ClassField.java new file mode 100644 index 000000000..15b330384 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/clazz/ClassField.java @@ -0,0 +1,63 @@ +package kg.ash.javavi.clazz; + +import com.github.javaparser.ast.Modifier; + +import java.util.EnumSet; +import java.util.Objects; + +public class ClassField { + + private String name; + private EnumSet modifiers; + private String typeName; + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setModifiers(EnumSet modifiers) { + this.modifiers = modifiers; + } + + public EnumSet getModifiers() { + return modifiers; + } + + public void setTypeName(String typeName) { + this.typeName = typeName; + } + + public String getTypeName() { + return typeName; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + + final ClassField other = (ClassField) obj; + if (!Objects.equals(this.name, other.name) && (this.name == null || !this.name.equals( + other.name))) { + return false; + } + return this.typeName == other.typeName || (this.typeName != null && this.typeName.equals( + other.typeName)); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 17 * hash + (this.name != null ? this.name.hashCode() : 0); + hash = 17 * hash + (this.typeName != null ? this.typeName.hashCode() : 0); + return hash; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/clazz/ClassImport.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/clazz/ClassImport.java new file mode 100644 index 000000000..6cec05b6d --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/clazz/ClassImport.java @@ -0,0 +1,45 @@ +package kg.ash.javavi.clazz; + +public class ClassImport { + private String name; + private boolean isStatic; + private boolean isAsterisk; + + public ClassImport(String name, boolean isStatic, boolean isAsterisk) { + this.name = name; + this.isStatic = isStatic; + this.isAsterisk = isAsterisk; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public boolean isStatic() { + return isStatic; + } + + public boolean isAsterisk() { + return isAsterisk; + } + + public String getHead() { + return name.substring(0, name.lastIndexOf(".")); + } + + public String getTail() { + if (name.contains(".")) { + String[] splitted = name.split("\\."); + return splitted[splitted.length - 1]; + } + return name; + } + + public String toString() { + return String.format("%s, isStatic: %b", name, isStatic); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/clazz/ClassMethod.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/clazz/ClassMethod.java new file mode 100644 index 000000000..1a81b0288 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/clazz/ClassMethod.java @@ -0,0 +1,102 @@ +package kg.ash.javavi.clazz; + +import com.github.javaparser.ast.Modifier; + +import java.util.EnumSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; + +public class ClassMethod { + + private String name; + private EnumSet modifiers; + private String declaration; + private String typeName; + private List typeParameters = new LinkedList<>(); + private boolean deprecated = false; + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setModifiers(EnumSet modifiers) { + this.modifiers = modifiers; + } + + public EnumSet getModifiers() { + return modifiers; + } + + public void setDeclaration(String declaration) { + this.declaration = declaration; + } + + public String getDeclaration() { + return declaration; + } + + public void setTypeName(String typeName) { + this.typeName = typeName; + } + + public String getTypeName() { + return typeName; + } + + public void addTypeParameter(ClassTypeParameter typeParameter) { + typeParameters.add(typeParameter); + } + + public List getTypeParameters() { + return typeParameters; + } + + public void setDeprecated(boolean deprecated) { + this.deprecated = deprecated; + } + + public boolean getDeprecated() { + return deprecated; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + + final ClassMethod other = (ClassMethod) obj; + if (!Objects.equals(this.name, other.name) && (this.name == null || !this.name.equals( + other.name))) { + return false; + } + if (!Objects.equals(this.typeName, other.typeName) && (this.typeName == null + || !this.typeName.equals(other.typeName))) { + return false; + } + return this.declaration == other.declaration || (this.declaration != null + && this.declaration.equals(other.declaration)); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 17 * hash + (this.name != null ? this.name.hashCode() : 0); + hash = 17 * hash + (this.typeName != null ? this.typeName.hashCode() : 0); + hash = 17 * hash + (this.declaration != null ? this.declaration.hashCode() : 0); + return hash; + } + + @Override + public String toString() { + return name; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/clazz/ClassTypeParameter.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/clazz/ClassTypeParameter.java new file mode 100644 index 000000000..3e5ba36c5 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/clazz/ClassTypeParameter.java @@ -0,0 +1,18 @@ +package kg.ash.javavi.clazz; + +public class ClassTypeParameter { + + private String name; + + public ClassTypeParameter(String name) { + this.name = name; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/clazz/CodeRegion.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/clazz/CodeRegion.java new file mode 100644 index 000000000..c5b87998e --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/clazz/CodeRegion.java @@ -0,0 +1,36 @@ +package kg.ash.javavi.clazz; + +public class CodeRegion { + + private int beginLine = -1; + private int beginColumn = -1; + private int endLine = -1; + private int endColumn = -1; + + public CodeRegion() { + } + + public CodeRegion(int beginLine, int beginColumn, int endLine, int endColumn) { + this.beginLine = beginLine; + this.beginColumn = beginColumn; + this.endLine = endLine; + this.endColumn = endColumn; + } + + public int getBeginLine() { + return beginLine; + } + + public int getBeginColumn() { + return beginColumn; + } + + public int getEndLine() { + return endLine; + } + + public int getEndColumn() { + return endColumn; + } + +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/clazz/SourceClass.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/clazz/SourceClass.java new file mode 100644 index 000000000..c30217866 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/clazz/SourceClass.java @@ -0,0 +1,161 @@ +package kg.ash.javavi.clazz; + +import com.github.javaparser.ast.Modifier; +import kg.ash.javavi.TargetParser; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; + +public class SourceClass { + + private String pkg = null; + private String name = null; + private EnumSet modifiers; + private boolean isInterface = false; + private List constructors = new ArrayList<>(); + private List methods = new ArrayList<>(); + private List fields = new ArrayList<>(); + private List imports = new ArrayList<>(); + + private String superclass = null; + private List interfaces = new ArrayList<>(); + + private List linkedClasses = new ArrayList<>(); + private List typeArguments = new ArrayList<>(); + + private List nestedClasses = new ArrayList<>(); + + private CodeRegion region = new CodeRegion(); + + public String getName() { + StringBuilder sb = new StringBuilder(); + + if (pkg != null) { + sb.append(pkg).append("."); + } + sb.append(name).append(TargetParser.getTypeArgumentsString(typeArguments)); + + return sb.toString(); + } + + public void setName(String name) { + this.name = name; + } + + public String getSimpleName() { + return name; + } + + public List getConstructors() { + return constructors; + } + + public void addConstructor(ClassConstructor constructor) { + if (constructor != null && !constructors.contains(constructor)) { + constructors.add(constructor); + } + } + + public List getMethods() { + return methods; + } + + public void addMethod(ClassMethod method) { + if (method != null && !methods.contains(method)) { + methods.add(method); + } + } + + public List getFields() { + return fields; + } + + public void addField(ClassField field) { + if (field != null && !fields.contains(field)) { + fields.add(field); + } + } + + public EnumSet getModifiers() { + return modifiers; + } + + public void setModifiers(EnumSet modifiers) { + this.modifiers = modifiers; + } + + public String getPackage() { + return pkg; + } + + public void setPackage(String pakage) { + this.pkg = pakage; + } + + public void setSuperclass(String superclass) { + this.superclass = superclass; + } + + public String getSuperclass() { + return superclass; + } + + public void addImport(ClassImport classImport) { + imports.add(classImport); + } + + public List getImports() { + return imports; + } + + public void addInterface(String interfaceName) { + interfaces.add(interfaceName); + } + + public List getInterfaces() { + return interfaces; + } + + public void setIsInterface(boolean isInterface) { + this.isInterface = isInterface; + } + + public boolean isInterface() { + return isInterface; + } + + public void addLinkedClass(SourceClass clazz) { + linkedClasses.add(clazz); + methods.addAll(clazz.getMethods()); + fields.addAll(clazz.getFields()); + } + + public List getLinkedClasses() { + return linkedClasses; + } + + public void addTypeArgument(String type) { + typeArguments.add(type); + } + + public void addNestedClass(String cls) { + nestedClasses.add(cls); + } + + public List getNestedClasses() { + return nestedClasses; + } + + public void setRegion(int beginLine, int beginColumn, int endLine, int endColumn) { + setRegion(new CodeRegion(beginLine, beginColumn, endLine, endColumn)); + } + + public void setRegion(CodeRegion region) { + this.region = region; + } + + public CodeRegion getRegion() { + return region; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/output/OutputClassInfo.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/output/OutputClassInfo.java new file mode 100644 index 000000000..9f39c3502 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/output/OutputClassInfo.java @@ -0,0 +1,229 @@ +package kg.ash.javavi.output; + +import kg.ash.javavi.Javavi; +import kg.ash.javavi.clazz.ClassConstructor; +import kg.ash.javavi.clazz.ClassField; +import kg.ash.javavi.clazz.ClassMethod; +import kg.ash.javavi.clazz.ClassTypeParameter; +import kg.ash.javavi.clazz.SourceClass; +import kg.ash.javavi.readers.Reflection; + +import java.util.HashMap; +import java.util.List; + +public class OutputClassInfo { + + public static final String KEY_NAME = "'n':"; + public static final String KEY_TYPE = "'t':"; + public static final String KEY_MODIFIER = "'m':"; + public static final String KEY_PARAMETERTYPES = "'p':"; + public static final String KEY_RETURNTYPE = "'r':"; + public static final String KEY_DESCRIPTION = "'d':"; + public static final String KEY_DECLARING_CLASS = "'c':"; + + public String get(SourceClass clazz) { + HashMap classMap = new HashMap<>(); + putClassInfo(classMap, clazz); + + StringBuilder sb = new StringBuilder(); + if (classMap.size() > 0) { + sb.append("{"); + for (String s : classMap.keySet()) { + sb.append("'").append(s).append("':") + .append(classMap.get(s)).append(","); + } + sb.append("}"); + } + + return sb.toString(); + } + + private void putClassInfo( + HashMap map, SourceClass clazz) { + if (clazz == null || map.containsKey(clazz.getName())) { + return; + } + + StringBuilder sb = init(clazz); + isIntefaceOrClass(sb, clazz); + hasNested(sb, clazz); + hasConstructors(sb, clazz); + hasFields(sb, clazz); + hasMethods(sb, clazz); + finish(sb, clazz, map); + } + + private StringBuilder init(SourceClass clazz) { + StringBuilder sb = new StringBuilder(); + sb.append("{") + .append("'tag':'CLASSDEF',") + .append(Javavi.NEWLINE) + .append("'flags':'") + .append(Integer.toString( + Reflection.EnumSetModifierToInt( + clazz.getModifiers()), 2)) + .append("',") + .append(Javavi.NEWLINE) + .append("'name':'") + .append(clazz.getName()) + .append("',") + .append(Javavi.NEWLINE) + .append("'classpath':'1',") + .append(Javavi.NEWLINE) + .append("'pos':[") + .append(clazz.getRegion().getBeginLine()) + .append(",") + .append(clazz.getRegion().getBeginColumn()) + .append("],") + .append("'endpos':[") + .append(clazz.getRegion().getEndLine()) + .append(",") + .append(clazz.getRegion().getEndColumn()) + .append("],") + .append("'fqn':'") + .append(clazz.getName()) + .append("',") + .append(Javavi.NEWLINE); + + return sb; + } + + private void isIntefaceOrClass(StringBuilder sb, SourceClass clazz) { + if (clazz.isInterface()) { + sb.append("'interface':'1','extends':["); + } else { + String superclass = clazz.getSuperclass(); + if (superclass != null + && !"java.lang.Object".equals(superclass)) { + sb.append("'extends':['").append(superclass) + .append("'],").append(Javavi.NEWLINE); + } + sb.append("'implements':["); + } + + clazz.getInterfaces().forEach( + iface -> sb.append("'").append(iface).append("',")); + sb.append("],").append(Javavi.NEWLINE); + } + + private void hasNested(StringBuilder sb, SourceClass clazz) { + sb.append("'nested':["); + clazz.getNestedClasses().forEach( + nested -> sb.append("'").append(nested).append("',")); + sb.append("],").append(Javavi.NEWLINE); + } + + private void hasConstructors(StringBuilder sb, SourceClass clazz) { + sb.append("'ctors':["); + for (ClassConstructor ctor : clazz.getConstructors()) { + sb.append("{"); + + appendModifier(sb, + Reflection.EnumSetModifierToInt( + ctor.getModifiers())); + appendParameterTypes(sb, ctor.getTypeParameters()); + + sb.append(KEY_DESCRIPTION).append("'") + .append(ctor.getDeclaration()).append("'"); + + sb.append("},").append(Javavi.NEWLINE); + } + + sb.append("],").append(Javavi.NEWLINE); + } + + + private void hasFields(StringBuilder sb, SourceClass clazz) { + sb.append("'fields':["); + for (ClassField field : clazz.getFields()) { + sb.append("{"); + sb.append(KEY_NAME).append("'") + .append(field.getName()).append("',"); + + if (!field.getTypeName().equals(clazz.getName())) { + sb.append(KEY_DECLARING_CLASS).append("'") + .append(field.getTypeName()).append("',"); + } + + appendModifier(sb, Reflection.EnumSetModifierToInt( + field.getModifiers())); + sb.append(KEY_TYPE) + .append("'") + .append(field.getTypeName()) + .append("'") + .append("},") + .append(Javavi.NEWLINE); + } + + sb.append("],").append(Javavi.NEWLINE); + } + + private void hasMethods(StringBuilder sb, SourceClass clazz) { + sb.append("'methods':["); + for (ClassMethod method : clazz.getMethods()) { + sb.append("{"); + + sb.append(KEY_NAME).append("'") + .append(method.getName()).append("',"); + + if (!method.getTypeName().equals(clazz.getName())) { + sb.append(KEY_DECLARING_CLASS) + .append("'") + .append(method.getTypeName()) + .append("',"); + } + + appendModifier(sb, Reflection.EnumSetModifierToInt( + method.getModifiers())); + + sb.append(KEY_RETURNTYPE).append("'") + .append(method.getTypeName()).append("',"); + + appendParameterTypes(sb, method.getTypeParameters()); + + sb.append(KEY_DESCRIPTION) + .append("'"); + if (method.getDeprecated()) { + sb.append("@Deprecated "); + } + sb.append(method.getDeclaration()) + .append("'") + .append("},") + .append(Javavi.NEWLINE); + } + sb.append("],").append(Javavi.NEWLINE); + } + + private void finish( + StringBuilder sb, SourceClass clazz, + HashMap map) { + + sb.append("}"); + + map.put(clazz.getName(), sb.toString()); + + for (SourceClass sourceClass : clazz.getLinkedClasses()) { + putClassInfo(map, sourceClass); + } + } + + private void appendModifier(StringBuilder sb, int modifier) { + sb.append(KEY_MODIFIER).append("'") + .append(Integer.toString(modifier, 2)).append("',"); + } + + private void appendParameterTypes( + StringBuilder sb, List paramTypes) { + if (paramTypes == null || paramTypes.isEmpty()) { + return; + } + + sb.append(KEY_PARAMETERTYPES).append("["); + + paramTypes.forEach( + parameter -> sb.append("'").append( + parameter.getName()).append("',")); + + sb.append("],"); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/output/OutputClassPackages.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/output/OutputClassPackages.java new file mode 100644 index 000000000..89cabb579 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/output/OutputClassPackages.java @@ -0,0 +1,56 @@ +package kg.ash.javavi.output; + +import com.github.javaparser.ast.Modifier; +import kg.ash.javavi.Javavi; +import kg.ash.javavi.cache.Cache; +import kg.ash.javavi.clazz.SourceClass; +import kg.ash.javavi.readers.ClassReader; +import kg.ash.javavi.searchers.ClassSearcher; +import kg.ash.javavi.searchers.JavaClassMap; + +import java.util.HashMap; + +public class OutputClassPackages { + + private HashMap classPackages; + private String sources = Javavi.system.get("sources").replace('\\', '/'); + + public OutputClassPackages(HashMap classPackages) { + this.classPackages = classPackages; + } + + public String get(String targetClass) { + if (classPackages == null || classPackages.isEmpty()) { + return Cache.PACKAGES_EMPTY_ERROR; + } + + StringBuilder builder = new StringBuilder(""); + if (classPackages.containsKey(targetClass)) { + JavaClassMap cm = classPackages.get(targetClass); + if (cm.getType() == JavaClassMap.TYPE_CLASS) { + cm.getSubpackages().keySet().forEach((String scope) -> { + if (scope.endsWith("$")) { + String target = scope + targetClass; + ClassSearcher seacher = new ClassSearcher(); + if (seacher.find(target, sources)) { + ClassReader reader = seacher.getReader(); + SourceClass clazz = reader.read(target); + if (clazz != null && clazz.getModifiers().contains(Modifier.STATIC)) { + scope = "static " + scope; + } + } + } + builder.append("'") + .append(scope) + .append(scope.endsWith("$") ? "" : ".") + .append(targetClass) + .append("',") + .append(Javavi.NEWLINE); + }); + } + } + + return String.format("[%s]", builder); + } + +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/output/OutputPackageInfo.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/output/OutputPackageInfo.java new file mode 100644 index 000000000..67ebcc9ec --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/output/OutputPackageInfo.java @@ -0,0 +1,41 @@ +package kg.ash.javavi.output; + +import kg.ash.javavi.cache.Cache; +import kg.ash.javavi.searchers.JavaClassMap; + +import java.util.HashMap; + +public class OutputPackageInfo { + + private HashMap classPackages; + + public OutputPackageInfo(HashMap classPackages) { + this.classPackages = classPackages; + } + + public String get(String targetPackage) { + if (classPackages == null || classPackages.isEmpty()) { + return Cache.PACKAGES_EMPTY_ERROR; + } + + StringBuilder sb = new StringBuilder(); + if (classPackages.containsKey(targetPackage)) { + JavaClassMap classMap = classPackages.get(targetPackage); + + sb.append("'") + .append(targetPackage) + .append("':") + .append("{'tag':'PACKAGE'") + .append(",'subpackages':[") + .append(classMap.getCachedSubpackages()) + .append("]") + .append(",'classes':[") + .append(classMap.getCachedClasses().toString()) + .append("]") + .append("},"); + } + + return String.format("{%s}", sb); + } + +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/output/OutputSimilar.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/output/OutputSimilar.java new file mode 100644 index 000000000..633663825 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/output/OutputSimilar.java @@ -0,0 +1,51 @@ +package kg.ash.javavi.output; + +import kg.ash.javavi.Javavi; +import kg.ash.javavi.cache.Cache; +import kg.ash.javavi.searchers.JavaClassMap; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +public abstract class OutputSimilar { + + protected String wordPrefix = ""; + + protected HashMap classPackages; + + public OutputSimilar(HashMap classPackages) { + this.classPackages = classPackages; + } + + public String get(String target) { + if (target == null) { + target = ""; + } + + if (classPackages == null || classPackages.isEmpty()) { + return Cache.PACKAGES_EMPTY_ERROR; + } + + List keys = getKeys(target); + Collections.sort(keys); + + StringBuilder builder = new StringBuilder(); + for (String key : keys) { + classPackages.get(key) + .getPaths() + .forEach(scope -> builder.append("{") + .append("'word':'") + .append(wordPrefix) + .append(key) + .append("', 'menu':'") + .append(scope) + .append("', 'type': 'c'},") + .append(Javavi.NEWLINE)); + } + return String.format("[%s]", builder); + } + + protected abstract List getKeys(String target); + +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/output/OutputSimilarAnnotations.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/output/OutputSimilarAnnotations.java new file mode 100644 index 000000000..ae4b9dbf5 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/output/OutputSimilarAnnotations.java @@ -0,0 +1,48 @@ +package kg.ash.javavi.output; + +import kg.ash.javavi.searchers.JavaClassMap; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.function.Predicate; + +public class OutputSimilarAnnotations extends OutputSimilar { + + public OutputSimilarAnnotations(HashMap classPackages) { + super(classPackages); + wordPrefix = "@"; + } + + @Override + protected List getKeys(String target) { + List keysResult = new ArrayList<>(); + + classPackages.forEach((key, value) -> { + if (target.isEmpty() || key.startsWith(target)) { + value.getPaths() + .stream() + .filter(isFromClasspath(value)) + .forEach(fqn -> addIfAnnotation(keysResult, fqn, key)); + } + }); + + return keysResult; + } + + private void addIfAnnotation(List keys, String fqn, String key) { + try { + String fullFqn = String.format("%s.%s", fqn, key); + ClassLoader loader = getClass().getClassLoader(); + Class cls = Class.forName(fullFqn, false, loader); + if (cls.isAnnotation()) { + keys.add(key); + } + } catch (NoClassDefFoundError | ClassNotFoundException ex) { + } + } + + private Predicate isFromClasspath(JavaClassMap map) { + return s -> map.getSourceType(s) == JavaClassMap.SOURCETYPE_CLASSPATH; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/output/OutputSimilarClasses.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/output/OutputSimilarClasses.java new file mode 100644 index 000000000..b58dce84d --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/output/OutputSimilarClasses.java @@ -0,0 +1,27 @@ +package kg.ash.javavi.output; + +import kg.ash.javavi.searchers.JavaClassMap; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.stream.Collectors; + +public class OutputSimilarClasses extends OutputSimilar { + + public OutputSimilarClasses(HashMap classPackages) { + super(classPackages); + } + + @Override + protected List getKeys(String target) { + if (target.isEmpty()) { + return new ArrayList<>(); + } + return classPackages.keySet() + .stream() + .filter(k -> k.startsWith(target)) + .collect(Collectors.toList()); + } + +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/readers/ClassReader.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/readers/ClassReader.java new file mode 100644 index 000000000..6132d8923 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/readers/ClassReader.java @@ -0,0 +1,13 @@ +package kg.ash.javavi.readers; + +import kg.ash.javavi.clazz.SourceClass; + +import java.util.List; + +public interface ClassReader { + SourceClass read(String fqn); + + ClassReader setTypeArguments(List typeArguments); + + ClassReader addKnown(List knownClasses); +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/readers/FileClassLoader.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/readers/FileClassLoader.java new file mode 100644 index 000000000..90f5805c1 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/readers/FileClassLoader.java @@ -0,0 +1,48 @@ +package kg.ash.javavi.readers; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; + +public class FileClassLoader extends ClassLoader { + + private String classFile; + + public FileClassLoader(ClassLoader parent, String classFile) { + super(parent); + this.classFile = classFile; + } + + // TODO: Handle throwables. + public Class loadClass(String name) { + try { + if (name.startsWith("java.")) { + return Class.forName(name); + } + + File file = new File(classFile); + if (file.exists()) { + FileInputStream fileInputStream = new FileInputStream(file); + ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + int data = fileInputStream.read(); + + while (data != -1) { + buffer.write(data); + data = fileInputStream.read(); + } + + fileInputStream.close(); + + byte[] classData = buffer.toByteArray(); + return defineClass(name, classData, 0, classData.length); + } + } catch (Throwable t) { + try { + return Class.forName(name); + } catch (Throwable t2) { + } + } + + return null; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/readers/Parser.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/readers/Parser.java new file mode 100644 index 000000000..d71767e4b --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/readers/Parser.java @@ -0,0 +1,365 @@ +package kg.ash.javavi.readers; + +import com.github.javaparser.Position; +import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.ImportDeclaration; +import com.github.javaparser.ast.Modifier; +import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.NodeList; +import com.github.javaparser.ast.PackageDeclaration; +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; +import com.github.javaparser.ast.body.ConstructorDeclaration; +import com.github.javaparser.ast.body.FieldDeclaration; +import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.body.Parameter; +import com.github.javaparser.ast.body.VariableDeclarator; +import com.github.javaparser.ast.type.ClassOrInterfaceType; +import com.github.javaparser.ast.type.TypeParameter; +import com.github.javaparser.ast.visitor.VoidVisitorAdapter; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.Optional; + +import kg.ash.javavi.apache.logging.log4j.LogManager; +import kg.ash.javavi.apache.logging.log4j.Logger; + +import kg.ash.javavi.cache.Cache; +import kg.ash.javavi.clazz.ClassConstructor; +import kg.ash.javavi.clazz.ClassField; +import kg.ash.javavi.clazz.ClassImport; +import kg.ash.javavi.clazz.ClassMethod; +import kg.ash.javavi.clazz.ClassTypeParameter; +import kg.ash.javavi.clazz.SourceClass; +import kg.ash.javavi.readers.source.ClassNamesFetcher; +import kg.ash.javavi.readers.source.CompilationUnitCreator; +import kg.ash.javavi.readers.source.CompilationUnitResult; +import kg.ash.javavi.searchers.ClassSearcher; +import kg.ash.javavi.searchers.FqnSearcher; + +public class Parser implements ClassReader { + + public static final Logger logger = LogManager.getLogger(); + + private String sources; + private String sourceFile = null; + private String sourceContent = null; + private ClassOrInterfaceDeclaration parentClass = null; + + public Parser(String sources) { + this.sources = sources.replace('\\', '/'); + } + + public Parser(String sources, String sourceFile) { + this.sources = sources.replace('\\', '/'); + this.sourceFile = sourceFile.replace('\\', '/'); + } + + public void setSourceContent(String sourceContent) { + this.sourceContent = sourceContent; + } + + @Override + public ClassReader setTypeArguments(List typeArguments) { + // Not supported yet. + return this; + } + + @Override + public ClassReader addKnown(List list) { + return this; + } + + @Override + public SourceClass read(String targetClass) { + if ((sourceFile == null || sourceFile.isEmpty()) + && (sourceContent == null || sourceContent.isEmpty())) { + return null; + } + + logger.debug("read class from sources: {}", targetClass); + + if (targetClass.contains("$")) { + targetClass = targetClass.split("\\$")[0]; + } + + if (Cache.getInstance().getClasses().containsKey(targetClass)) { + return Cache.getInstance().getClasses().get(targetClass); + } + + CompilationUnitResult cur = sourceFile != null + ? CompilationUnitCreator.createFromFile(sourceFile) + : CompilationUnitCreator.createFromContent(sourceContent); + + CompilationUnit cu; + if (cur == null) { + return null; + } else if (cur.getProblems() != null) { + return null; + } else { + cu = cur.getCompilationUnit(); + } + + SourceClass clazz = new SourceClass(); + Cache.getInstance().getClasses().put(targetClass, clazz); + + Optional packageDeclaration = + cu.getPackageDeclaration(); + if (packageDeclaration.isPresent()) { + clazz.setPackage(packageDeclaration.get().getNameAsString()); + } + + Optional beginning = cu.getBegin(); + Optional ending = cu.getEnd(); + if (beginning.isPresent() && ending.isPresent()) { + clazz.setRegion( + beginning.get().line, + beginning.get().column, + ending.get().line, + ending.get().column); + } + + if (cu.getImports() != null) { + for (ImportDeclaration id : cu.getImports()) { + clazz.addImport( + new ClassImport( + id.getName().toString(), + id.isStatic(), + id.isAsterisk())); + } + } + + ClassOrInterfaceVisitor coiVisitor = + new ClassOrInterfaceVisitor(clazz); + coiVisitor.visit(cu, null); + clazz = coiVisitor.getClazz(); + + ClassVisitor visitor = new ClassVisitor(clazz); + visitChildren(parentClass.getChildNodes(), visitor); + clazz = visitor.getClazz(); + + List impls = new ArrayList<>(); + if (clazz.getSuperclass() != null) { + impls.add(clazz.getSuperclass()); + } + + impls.addAll(clazz.getInterfaces()); + for (String impl : impls) { + ClassSearcher seacher = new ClassSearcher(); + if (seacher.find(impl, sources)) { + SourceClass implClass = seacher.getReader().read(impl); + if (implClass != null) { + clazz.addLinkedClass(implClass); + for (ClassConstructor c : implClass.getConstructors()) { + + if (implClass.getName().equals("java.lang.Object")) { + continue; + } + c.setDeclaration( + c.getDeclaration().replace( + implClass.getName(), clazz.getName())); + c.setDeclaration(c.getDeclaration() + .replace( + implClass.getSimpleName(), + clazz.getSimpleName())); + clazz.addConstructor(c); + } + for (ClassMethod method : implClass.getMethods()) { + clazz.addMethod(method); + } + for (ClassField field : implClass.getFields()) { + clazz.addField(field); + } + } + } + } + + return clazz; + } + + private void visitChildren(List nodes, ClassVisitor visitor) { + for (Node n : nodes) { + if (n instanceof FieldDeclaration) { + visitor.visit((FieldDeclaration) n, null); + } else if (n instanceof MethodDeclaration) { + visitor.visit((MethodDeclaration) n, null); + } else if (n instanceof ConstructorDeclaration) { + visitor.visit((ConstructorDeclaration) n, null); + } else if (n instanceof ClassOrInterfaceDeclaration) { + visitor.visit((ClassOrInterfaceDeclaration) n, null); + } + } + } + + public void setExtendedAndInterfaceTypes( + SourceClass clazz, ClassOrInterfaceDeclaration n) { + NodeList extendedTypes = n.getExtendedTypes(); + if (extendedTypes != null && extendedTypes.size() > 0) { + String className = extendedTypes.get(0).getNameAsString(); + clazz.setSuperclass( + new FqnSearcher(sources).getFqn(clazz, className)); + } else { + clazz.setSuperclass("java.lang.Object"); + addConstructorIfNotEmpty(clazz); + } + + NodeList implementedTypes = + n.getImplementedTypes(); + if (implementedTypes != null) { + for (ClassOrInterfaceType iface : implementedTypes) { + clazz.addInterface( + new FqnSearcher(sources).getFqn( + clazz, iface.getNameAsString())); + } + } + } + + private class ClassOrInterfaceVisitor extends VoidVisitorAdapter { + + private SourceClass clazz; + + public ClassOrInterfaceVisitor(SourceClass clazz) { + this.clazz = clazz; + } + + public SourceClass getClazz() { + return clazz; + } + + @Override + public void visit(ClassOrInterfaceDeclaration n, Object arg) { + parentClass = n; + clazz.setName(n.getNameAsString()); + clazz.setModifiers(n.getModifiers()); + clazz.setIsInterface(n.isInterface()); + + Optional beginning = n.getBegin(); + Optional ending = n.getEnd(); + if (beginning.isPresent() && ending.isPresent()) { + clazz.setRegion( + beginning.get().line, + beginning.get().column, + ending.get().line, + ending.get().column); + } + + setExtendedAndInterfaceTypes(clazz, n); + } + } + + private void addConstructorIfNotEmpty(SourceClass clazz) { + if (clazz.getConstructors().isEmpty()) { + ClassConstructor ctor = new ClassConstructor(); + ctor.setDeclaration( + String.format("public %s()", clazz.getName())); + ctor.setModifiers(EnumSet.of(Modifier.PUBLIC)); + clazz.addConstructor(ctor); + } + } + + private class ClassVisitor extends VoidVisitorAdapter { + + private SourceClass clazz; + + public ClassVisitor(SourceClass clazz) { + this.clazz = clazz; + } + + public SourceClass getClazz() { + return clazz; + } + + @Override + public void visit(ConstructorDeclaration n, Object arg) { + ClassConstructor constructor = new ClassConstructor(); + constructor.setDeclaration(n.getDeclarationAsString()); + constructor.setModifiers(n.getModifiers()); + if (n.getTypeParameters() != null) { + for (TypeParameter parameter : n.getTypeParameters()) { + constructor.addTypeParameter( + new ClassTypeParameter(parameter.getNameAsString())); + } + } + clazz.addConstructor(constructor); + } + + @Override + public void visit(MethodDeclaration n, Object arg) { + ClassMethod method = new ClassMethod(); + method.setName(n.getNameAsString()); + method.setModifiers(n.getModifiers()); + method.setDeclaration(n.getDeclarationAsString()); + + n.getAnnotationByClass(Deprecated.class) + .ifPresent(c -> method.setDeprecated(true)); + + String className = n.getType().toString(); + method.setTypeName( + new FqnSearcher(sources).getFqn(clazz, className)); + + if (n.getTypeParameters() != null) { + for (TypeParameter parameter : n.getTypeParameters()) { + method.addTypeParameter( + new ClassTypeParameter( + parameter.getNameAsString())); + } + } + + if (n.getParameters() != null) { + for (Parameter parameter : n.getParameters()) { + method.addTypeParameter( + new ClassTypeParameter( + parameter.getType().toString( + ClassNamesFetcher.withoutComments()))); + } + } + clazz.addMethod(method); + } + + @Override + public void visit(FieldDeclaration n, Object arg) { + for (VariableDeclarator v : n.getVariables()) { + ClassField field = new ClassField(); + field.setName(v.getNameAsString()); + field.setModifiers(n.getModifiers()); + + String className = n.getElementType().asString(); + field.setTypeName( + new FqnSearcher(sources).getFqn(clazz, className)); + + clazz.addField(field); + } + } + + @Override + public void visit(ClassOrInterfaceDeclaration n, Object arg) { + SourceClass clazz = new SourceClass(); + clazz.setName(this.clazz.getSimpleName() + "$" + n.getName()); + clazz.setModifiers(n.getModifiers()); + clazz.setIsInterface(n.isInterface()); + + Optional beginning = n.getBegin(); + Optional ending = n.getEnd(); + if (beginning.isPresent() && ending.isPresent()) { + clazz.setRegion( + beginning.get().line, + beginning.get().column, + ending.get().line, + ending.get().column); + } + + setExtendedAndInterfaceTypes(clazz, n); + + clazz.setPackage(this.clazz.getPackage()); + + ClassVisitor visitor = new ClassVisitor(clazz); + visitChildren(n.getChildNodes(), visitor); + this.clazz.addNestedClass(clazz.getName()); + this.clazz.addLinkedClass(clazz); + + Cache.getInstance().getClasses().put(clazz.getName(), clazz); + } + + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/readers/Reflection.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/readers/Reflection.java new file mode 100644 index 000000000..bfce0a603 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/readers/Reflection.java @@ -0,0 +1,391 @@ +package kg.ash.javavi.readers; + +import com.github.javaparser.ast.Modifier; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.Set; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.stream.Stream; + +import kg.ash.javavi.apache.logging.log4j.LogManager; +import kg.ash.javavi.apache.logging.log4j.Logger; + +import kg.ash.javavi.TargetParser; +import kg.ash.javavi.cache.Cache; +import kg.ash.javavi.clazz.ClassConstructor; +import kg.ash.javavi.clazz.ClassField; +import kg.ash.javavi.clazz.ClassMethod; +import kg.ash.javavi.clazz.ClassTypeParameter; +import kg.ash.javavi.clazz.SourceClass; +import kg.ash.javavi.searchers.ClassNameMap; +import kg.ash.javavi.searchers.ClassSearcher; + +public class Reflection implements ClassReader { + + public static final Logger logger = LogManager.getLogger(); + + private String sources; + private List typeArguments = null; + + private List knownClasses = new ArrayList<>(); + + @Override + public ClassReader setTypeArguments(List typeArguments) { + this.typeArguments = typeArguments; + return this; + } + + @Override + public ClassReader addKnown(List knownClasses) { + this.knownClasses.addAll(knownClasses); + return this; + } + + public Reflection(String sources) { + this.sources = sources; + } + + public static boolean exist(String name) { + try { + Class.forName(name); + return true; + } catch (Exception | NoClassDefFoundError ex) { + return false; + } + } + + private String getNameWithArguments(String name) { + if (typeArguments != null && !typeArguments.isEmpty()) { + name += "<" + String.join(",", typeArguments) + ">"; + } + return name; + } + + @Override + public SourceClass read(String name) { + String nameWithArguments = getNameWithArguments(name); + + logger.debug("read class with reflections: {}", nameWithArguments); + + HashMap cachedClasses = + Cache.getInstance().getClasses(); + if (cachedClasses.containsKey(nameWithArguments)) { + return cachedClasses.get(nameWithArguments); + } + + SourceClass result = loadFromCompiledSource(name); + if (result == null) { + result = lookup(name); + } + if (result == null && !name.startsWith("java.lang.")) { + result = lookup("java.lang." + name); + } + + String binaryName = name; + while (result == null) { + int lastDotPos = binaryName.lastIndexOf('.'); + if (lastDotPos == -1) { + break; + } + + binaryName = String.format("%s$%s", + binaryName.substring(0, lastDotPos), + binaryName.substring( + lastDotPos + 1, binaryName.length())); + + result = lookup(binaryName); + } + + return result; + } + + private SourceClass loadFromCompiledSource(String name) { + String last = name.substring(name.lastIndexOf(".") + 1); + ClassNameMap classMap = (ClassNameMap) Cache.getInstance() + .getClassPackages() + .get(getNameWithArguments(last)); + if (classMap != null) { + if (classMap.getClassFile() != null + && classMap.getJavaFile() != null) { + logger.debug( + "loading class from compiled source: {}", classMap); + + ClassLoader parentClassLoader = + FileClassLoader.class.getClassLoader(); + FileClassLoader fileClassLoader = + new FileClassLoader(parentClassLoader, + classMap.getClassFile()); + Class clazz = fileClassLoader.loadClass(name); + if (clazz != null) { + try { + return getSourceClass(clazz); + } catch (Throwable t) { + logger.error(t, t); + } + } + } + } + + return null; + } + + private SourceClass lookup(String className) { + try { + logger.debug("lookup class name: {}", className); + + Class clazz = Class.forName(className); + return getSourceClass(clazz); + } catch (Exception ex) { + logger.debug(String.format("error lookup %s", className), ex); + return null; + } + } + + private String popTypeArgument(List arguments) { + if (!arguments.isEmpty()) { + String argument = arguments.get(0); + arguments.remove(0); + return argument; + } + + return "java.lang.Object"; + } + + private String getGenericName( + TreeMap taa, String genericName) { + + if (typeArguments == null || typeArguments.isEmpty()) { + return genericName; + } + for (Entry kv : taa.entrySet()) { + genericName = genericName.replaceAll( + String.format("\\b%s\\b", kv.getKey()), + Matcher.quoteReplacement(kv.getValue())); + } + return genericName; + } + + @SuppressWarnings("unchecked") + public SourceClass getSourceClass(Class cls) { + logger.debug("class loaded: {}", cls.getName()); + + String name = cls.getName(); + knownClasses.add(name); + if (name.contains(".")) { + name = name.substring(name.lastIndexOf(".") + 1); + } + + SourceClass clazz = new SourceClass(); + clazz.setName(name); + clazz.setModifiers(EnumSetModifierFromInt(cls.getModifiers())); + clazz.setIsInterface(cls.isInterface()); + if (cls.getPackage() != null) { + clazz.setPackage(cls.getPackage().getName()); + } + + TreeMap typeArgumentsMap = new TreeMap<>(); + if (typeArguments != null && !typeArguments.isEmpty()) { + List arguments = new ArrayList<>(typeArguments); + Stream.of(cls.getTypeParameters()).forEachOrdered(type -> { + typeArgumentsMap.put( + type.getTypeName(), popTypeArgument(arguments)); + clazz.addTypeArgument( + typeArgumentsMap.get(type.getTypeName())); + }); + } + + List linked = new ArrayList<>(); + Stream.of(cls.getDeclaredClasses()).forEach(c -> { + linked.add(c); + clazz.addNestedClass(c.getName()); + }); + + Optional.ofNullable(cls.getSuperclass()).ifPresent(c -> { + linked.add(c); + clazz.setSuperclass(c.getName()); + }); + + Stream.of(cls.getGenericInterfaces()) + .map(i -> getGenericName( + typeArgumentsMap, i.getTypeName())) + .forEach(clazz::addInterface); + + ClassSearcher seacher = new ClassSearcher(); + clazz.getInterfaces().stream().forEach(i -> { + TargetParser parser = new TargetParser(sources); + String ifaceClassName = parser.parse(i); + if (!knownClasses.contains(ifaceClassName) && seacher.find(ifaceClassName, sources)) { + clazz.addLinkedClass( + seacher.getReader().addKnown(knownClasses).setTypeArguments( + parser.getTypeArguments()).read(ifaceClassName)); + } + }); + + linked.stream() + .filter(c -> !knownClasses.contains(c.getName())) + .map(this::getSourceClass) + .forEach(clazz::addLinkedClass); + + Stream.of(cls.getConstructors()).forEachOrdered(ctor -> { + ClassConstructor constructor = new ClassConstructor(); + + String genericDeclaration = + getGenericName(typeArgumentsMap, + ctor.toGenericString()); + constructor.setDeclaration(genericDeclaration); + constructor.setModifiers( + EnumSetModifierFromInt(ctor.getModifiers())); + + Stream.of(ctor.getGenericParameterTypes()) + .map(t -> getGenericName( + typeArgumentsMap, t.getTypeName())) + .forEach(t -> constructor.addTypeParameter( + new ClassTypeParameter(t))); + + clazz.addConstructor(constructor); + }); + + Set fieldsSet = new HashSet<>(); + fieldsSet.addAll(Arrays.asList(cls.getDeclaredFields())); + fieldsSet.addAll(Arrays.asList(cls.getFields())); + fieldsSet.forEach(f -> { + ClassField field = new ClassField(); + field.setName(f.getName()); + field.setModifiers(EnumSetModifierFromInt(f.getModifiers())); + + String genericType = getGenericName(typeArgumentsMap, + f.getGenericType().getTypeName()); + field.setTypeName(genericType); + + clazz.addField(field); + }); + + Set methodsSet = new HashSet<>(); + methodsSet.addAll(Arrays.asList(cls.getDeclaredMethods())); + methodsSet.addAll(Arrays.asList(cls.getMethods())); + methodsSet.forEach(m -> { + if (!m.getDeclaringClass().getName().equals(cls.getName())) { + return; + } + ClassMethod method = new ClassMethod(); + if (m.getAnnotationsByType(Deprecated.class).length > 0) { + method.setDeprecated(true); + } + method.setName(m.getName()); + method.setModifiers(EnumSetModifierFromInt(m.getModifiers())); + + String genericDeclaration = getGenericName( + typeArgumentsMap, m.toGenericString()); + method.setDeclaration(genericDeclaration); + + String genericReturnType = getGenericName( + typeArgumentsMap, + m.getGenericReturnType().getTypeName()); + method.setTypeName(genericReturnType); + + Type[] parameterTypes = m.getGenericParameterTypes(); + Stream.of(parameterTypes) + .map(t -> getGenericName(typeArgumentsMap, t.getTypeName())) + .forEachOrdered( + t -> method.addTypeParameter( + new ClassTypeParameter(t))); + + clazz.addMethod(method); + }); + + Cache.getInstance().getClasses().put( + getNameWithArguments(cls.getName()), clazz); + return clazz; + } + + public static EnumSet EnumSetModifierFromInt(int i) { + EnumSet set = EnumSet.noneOf(Modifier.class); + + if (java.lang.reflect.Modifier.isPublic(i)) { + set.add(Modifier.PUBLIC); + } + if (java.lang.reflect.Modifier.isPrivate(i)) { + set.add(Modifier.PRIVATE); + } + if (java.lang.reflect.Modifier.isProtected(i)) { + set.add(Modifier.PROTECTED); + } + if (java.lang.reflect.Modifier.isStatic(i)) { + set.add(Modifier.STATIC); + } + if (java.lang.reflect.Modifier.isFinal(i)) { + set.add(Modifier.FINAL); + } + if (java.lang.reflect.Modifier.isSynchronized(i)) { + set.add(Modifier.SYNCHRONIZED); + } + if (java.lang.reflect.Modifier.isVolatile(i)) { + set.add(Modifier.VOLATILE); + } + if (java.lang.reflect.Modifier.isTransient(i)) { + set.add(Modifier.TRANSIENT); + } + if (java.lang.reflect.Modifier.isNative(i)) { + set.add(Modifier.NATIVE); + } + if (java.lang.reflect.Modifier.isAbstract(i)) { + set.add(Modifier.ABSTRACT); + } + if (java.lang.reflect.Modifier.isStrict(i)) { + set.add(Modifier.STRICTFP); + } + + return set; + } + + public static int EnumSetModifierToInt(EnumSet set) { + int mod = 0; + + if (set.contains(Modifier.PUBLIC)) { + mod |= java.lang.reflect.Modifier.PUBLIC; + } + if (set.contains(Modifier.PRIVATE)) { + mod |= java.lang.reflect.Modifier.PRIVATE; + } + if (set.contains(Modifier.PROTECTED)) { + mod |= java.lang.reflect.Modifier.PROTECTED; + } + if (set.contains(Modifier.STATIC)) { + mod |= java.lang.reflect.Modifier.STATIC; + } + if (set.contains(Modifier.FINAL)) { + mod |= java.lang.reflect.Modifier.FINAL; + } + if (set.contains(Modifier.SYNCHRONIZED)) { + mod |= java.lang.reflect.Modifier.SYNCHRONIZED; + } + if (set.contains(Modifier.VOLATILE)) { + mod |= java.lang.reflect.Modifier.VOLATILE; + } + if (set.contains(Modifier.TRANSIENT)) { + mod |= java.lang.reflect.Modifier.TRANSIENT; + } + if (set.contains(Modifier.NATIVE)) { + mod |= java.lang.reflect.Modifier.NATIVE; + } + if (set.contains(Modifier.ABSTRACT)) { + mod |= java.lang.reflect.Modifier.ABSTRACT; + } + if (set.contains(Modifier.STRICTFP)) { + mod |= java.lang.reflect.Modifier.STRICT; + } + + return mod; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/readers/source/ClassNamesFetcher.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/readers/source/ClassNamesFetcher.java new file mode 100644 index 000000000..60fff24bb --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/readers/source/ClassNamesFetcher.java @@ -0,0 +1,250 @@ +package kg.ash.javavi.readers.source; + +import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.ImportDeclaration; +import com.github.javaparser.ast.Node; +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; +import com.github.javaparser.ast.body.ConstructorDeclaration; +import com.github.javaparser.ast.body.FieldDeclaration; +import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.body.Parameter; +import com.github.javaparser.ast.expr.AnnotationExpr; +import com.github.javaparser.ast.expr.Expression; +import com.github.javaparser.ast.expr.FieldAccessExpr; +import com.github.javaparser.ast.expr.MethodCallExpr; +import com.github.javaparser.ast.expr.Name; +import com.github.javaparser.ast.expr.NameExpr; +import com.github.javaparser.ast.stmt.BlockStmt; +import com.github.javaparser.ast.type.ClassOrInterfaceType; +import com.github.javaparser.ast.type.Type; +import com.github.javaparser.ast.type.UnionType; +import com.github.javaparser.ast.visitor.TreeVisitor; +import com.github.javaparser.ast.visitor.VoidVisitorAdapter; +import com.github.javaparser.printer.PrettyPrinterConfiguration; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class ClassNamesFetcher { + + private final CompilationUnit compilationUnit; + private final Set resultList = new HashSet<>(); + private final Set declarationList = new HashSet<>(); + private List staticImportsList = new ArrayList<>(); + + public static PrettyPrinterConfiguration withoutComments() { + PrettyPrinterConfiguration pp = new PrettyPrinterConfiguration(); + pp.setPrintComments(false); + return pp; + } + + public ClassNamesFetcher(CompilationUnit compilationUnit) { + this.compilationUnit = compilationUnit; + for (ImportDeclaration id : compilationUnit.getImports()) { + if (id.isStatic()) { + String name = id.getName().toString(); + staticImportsList.add( + name.substring( + name.lastIndexOf(".") + 1, name.length())); + } + } + } + + @SuppressWarnings("unchecked") + public Set getNames() { + List adapters = new ArrayList<>(); + adapters.add(new ClassTypeVisitor()); + adapters.add(new TypesVisitor()); + adapters.add(new AnnotationsVisitor()); + adapters.forEach(a -> a.visit(compilationUnit, null)); + + return resultList; + } + + public Set getDeclarationList() { + if (resultList.isEmpty()) { + getNames(); + } + return declarationList; + } + + private class ClassTypeVisitor extends VoidVisitorAdapter { + + @Override + public void visit(ClassOrInterfaceDeclaration type, Object arg) { + new DeepVisitor(this, arg).visitBreadthFirst(type); + if (type.getAnnotations() != null) { + for (AnnotationExpr expr : type.getAnnotations()) { + resultList.add(expr.getNameAsString()); + List children = expr.getChildNodes(); + for (Node node : children.subList(1, children.size())) { + new DeepVisitor(this, arg).visitBreadthFirst(node); + } + } + } + } + } + + private class AnnotationsVisitor extends VoidVisitorAdapter { + + private void addAnnotations( + List annotations, Object arg) { + if (annotations != null) { + for (AnnotationExpr expr : annotations) { + resultList.add(expr.getNameAsString()); + List children = expr.getChildNodes(); + for (Node node : children.subList(1, children.size())) { + new DeepVisitor(this, arg).visitBreadthFirst(node); + } + } + } + } + + @Override + public void visit(ConstructorDeclaration type, Object arg) { + addAnnotations(type.getAnnotations(), arg); + } + + @Override + public void visit(FieldDeclaration type, Object arg) { + addAnnotations(type.getAnnotations(), arg); + } + + @Override + public void visit(MethodDeclaration type, Object arg) { + addAnnotations(type.getAnnotations(), arg); + if (type.getParameters() != null) { + for (Parameter param : type.getParameters()) { + addAnnotations(param.getAnnotations(), arg); + } + } + + if (type.getThrownExceptions() != null) { + for (Type expr : type.getThrownExceptions()) { + resultList.add(expr.toString(withoutComments())); + } + } + } + } + + private class TypesVisitor extends VoidVisitorAdapter { + + @Override + public void visit(BlockStmt type, Object arg) { + new DeepVisitor(this, arg).visitBreadthFirst(type); + } + + @Override + public void visit(FieldAccessExpr type, Object arg) { + addStatic(type); + } + + @Override + public void visit(MethodCallExpr type, Object arg) { + addStatic(type); + } + + private void addStatic(Expression type) { + if (type.getChildNodes() != null && + type.getChildNodes().size() > 0) { + String name = type.getChildNodes(). + get(0).toString(withoutComments()); + if (!name.contains(".")) { + resultList.add(name); + } + } + } + + @Override + public void visit(ClassOrInterfaceType type, Object arg) { + String name = type.getNameAsString(); + String fullName = type.toString(withoutComments()); + if (!fullName.startsWith(name)) { + if (!type.getChildNodes().isEmpty()) { + name = type.getChildNodes(). + get(0).toString(withoutComments()); + } + } + if (name.contains(".")) { + name = name.split("\\.")[0]; + } + resultList.add(name); + if (type.getTypeArguments().isPresent()) { + for (Type t : type.getTypeArguments().get()) { + String typeName = t.toString(withoutComments()); + if (typeName.contains(".")) { + typeName = typeName.split("\\.")[0]; + } + resultList.add(typeName); + } + } + } + } + + private class DeepVisitor extends TreeVisitor { + + private VoidVisitorAdapter adapter; + private Object arg; + + public DeepVisitor(VoidVisitorAdapter adapter, Object arg) { + this.adapter = adapter; + this.arg = arg; + } + + public void process(Node node) { + if (node instanceof ClassOrInterfaceType) { + adapter.visit((ClassOrInterfaceType) node, arg); + } else if (node instanceof UnionType) { + ((UnionType) node).getElements() + .forEach( + t -> resultList.add( + t.toString(withoutComments()))); + } else if (node instanceof MethodCallExpr) { + MethodCallExpr methodCall = ((MethodCallExpr) node); + String name = methodCall.getNameAsString(); + if (staticImportsList.contains(name)) { + resultList.add(name); + } + } else if (node instanceof NameExpr) { + // javaparser has no difference on 'method call' expression, + // so class name with static method call look the same as + // object method call. that's why we check here for usual + // class name type with upper case letter at the beginning. + // it can miss some unusual class names with lower case at + // the beginning. + NameExpr nameExpr = (NameExpr) node; + String parent = ""; + if (nameExpr.getParentNode().isPresent()) { + parent = nameExpr.getParentNode(). + get().toString(withoutComments()); + } + String name = nameExpr.getNameAsString(); + if (name != null) { + if (!parent.startsWith("@") && + !parent.equals(name) && + parent.endsWith(name)) { + return; + } + + if (name.matches("^[A-Z][A-Za-z0-9_]*")) { + resultList.add(name); + } + } + } else if (node instanceof Name) { + String name = node.toString(withoutComments()); + + if (name.matches("^[A-Z][A-Za-z0-9_]*")) { + resultList.add(name); + } + } else if (node instanceof ClassOrInterfaceDeclaration) { + ClassOrInterfaceDeclaration declaration = + (ClassOrInterfaceDeclaration) node; + declarationList.add( + declaration.getName().toString( + withoutComments())); + } + } + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/readers/source/CompilationUnitCreator.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/readers/source/CompilationUnitCreator.java new file mode 100644 index 000000000..72f5cccf2 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/readers/source/CompilationUnitCreator.java @@ -0,0 +1,47 @@ +package kg.ash.javavi.readers.source; + +import com.github.javaparser.JavaParser; +import com.github.javaparser.ParseProblemException; +import com.github.javaparser.TokenMgrException; +import com.github.javaparser.ast.CompilationUnit; + +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.StringReader; + +import kg.ash.javavi.apache.logging.log4j.LogManager; +import kg.ash.javavi.apache.logging.log4j.Logger; + +public class CompilationUnitCreator { + + public static final Logger logger = LogManager.getLogger(); + + public static CompilationUnitResult createFromFile(String fileName) { + try { + return new CompilationUnitResult( + JavaParser.parse(new FileReader(fileName))); + } catch (TokenMgrException | FileNotFoundException e) { + logger.error(e, e); + return null; + } catch (ParseProblemException ex) { + logger.debug("parse error", ex); + return new CompilationUnitResult( + ex.getProblems()); + } + } + + public static CompilationUnitResult createFromContent(String content) { + try { + return new CompilationUnitResult( + JavaParser.parse(new StringReader(content))); + } catch (TokenMgrException ex) { + logger.error(ex, ex); + return null; + } catch (ParseProblemException ex) { + logger.debug("parse error", ex); + return new CompilationUnitResult( + ex.getProblems()); + } + } + +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/readers/source/CompilationUnitResult.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/readers/source/CompilationUnitResult.java new file mode 100644 index 000000000..6e7239258 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/readers/source/CompilationUnitResult.java @@ -0,0 +1,36 @@ +package kg.ash.javavi.readers.source; + +import com.github.javaparser.Problem; +import com.github.javaparser.ast.CompilationUnit; + +import java.util.List; + +public class CompilationUnitResult { + + private CompilationUnit compilationUnit; + private List problems; + + public CompilationUnitResult(CompilationUnit compilationUnit) { + this.compilationUnit = compilationUnit; + } + + public CompilationUnitResult(List problems) { + this.problems = problems; + } + + public CompilationUnit getCompilationUnit() { + return compilationUnit; + } + + public void setCompilationUnit(CompilationUnit compilationUnit) { + this.compilationUnit = compilationUnit; + } + + public List getProblems() { + return problems; + } + + public void setProblems(List problems) { + this.problems = problems; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/ByExtensionVisitor.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/ByExtensionVisitor.java new file mode 100644 index 000000000..c018c16ec --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/ByExtensionVisitor.java @@ -0,0 +1,60 @@ +package kg.ash.javavi.searchers; + +import java.io.IOException; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitResult; +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class ByExtensionVisitor extends SimpleFileVisitor { + + private final List matchers = new ArrayList<>(); + private List resultList = new ArrayList<>(); + + public ByExtensionVisitor(List patterns) { + matchers.addAll(getMatchers(patterns)); + } + + private List getMatchers(List patterns) { + return patterns.stream() + .map(p -> FileSystems.getDefault().getPathMatcher("glob:" + p)) + .collect(Collectors.toList()); + } + + public List getResultList() { + return resultList; + } + + private void find(Path file) { + Path name = file.getFileName(); + if (name != null) { + for (PathMatcher matcher : matchers) { + if (matcher.matches(name)) { + resultList.add(file.toFile().getPath().replace('\\', '/')); + return; + } + } + } + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + find(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) { + return FileVisitResult.CONTINUE; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/ClassNameMap.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/ClassNameMap.java new file mode 100644 index 000000000..bd1bb85da --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/ClassNameMap.java @@ -0,0 +1,38 @@ +package kg.ash.javavi.searchers; + +public class ClassNameMap extends JavaClassMap { + + public String javaFile = null; + public String classFile = null; + + public ClassNameMap(String name) { + super(name); + } + + @Override + public int getType() { + return JavaClassMap.TYPE_CLASS; + } + + public void setJavaFile(String javaFile) { + this.javaFile = javaFile; + } + + public String getJavaFile() { + return javaFile; + } + + public void setClassFile(String classFile) { + this.classFile = classFile; + } + + public String getClassFile() { + return classFile; + } + + @Override + public String toString() { + return String.format("name = %s, type = %d, javaFile = %s, classFile = %s", name, getType(), + javaFile, classFile); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/ClassSearcher.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/ClassSearcher.java new file mode 100644 index 000000000..b584bb1a3 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/ClassSearcher.java @@ -0,0 +1,68 @@ +package kg.ash.javavi.searchers; + +import kg.ash.javavi.apache.logging.log4j.LogManager; +import kg.ash.javavi.apache.logging.log4j.Logger; +import kg.ash.javavi.readers.ClassReader; +import kg.ash.javavi.readers.Parser; +import kg.ash.javavi.readers.Reflection; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +public class ClassSearcher { + + public static final Logger logger = LogManager.getLogger(); + + private boolean isReflected = false; + private String sources; + private String sourceFile = null; + + public boolean find(String targetClass, String sources) { + logger.debug("executing search of \"{}\"", targetClass); + + this.sources = sources; + if (Reflection.exist(targetClass) || Reflection.exist("java.lang." + targetClass)) { + isReflected = true; + return true; + } else { + String[] sourcesArray = sources.split(File.pathSeparator); + for (String sourceDir : sourcesArray) { + if (targetClass.contains("$")) { + targetClass = targetClass.split("\\$")[0]; + } + targetClass = targetClass.replaceAll("[\\[\\]]", ""); + SourceFileVisitor visitor = new SourceFileVisitor(targetClass); + try { + Files.walkFileTree(Paths.get(sourceDir), visitor); + + if (visitor.getTargetFile() != null) { + sourceFile = visitor.getTargetFile().replace('\\', '/'); + return true; + } + } catch (IOException e) { + logger.error(e, e); + } + } + } + + return false; + } + + public ClassReader getReader() { + if (isReflected()) { + return new Reflection(sources); + } else { + return new Parser(sources, getSourceFile()); + } + } + + public boolean isReflected() { + return isReflected; + } + + public String getSourceFile() { + return sourceFile; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/ClasspathCollector.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/ClasspathCollector.java new file mode 100644 index 000000000..a68b1b2b5 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/ClasspathCollector.java @@ -0,0 +1,69 @@ +package kg.ash.javavi.searchers; + +import kg.ash.javavi.apache.logging.log4j.LogManager; +import kg.ash.javavi.apache.logging.log4j.Logger; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Stream; + +public class ClasspathCollector { + + public static final Logger logger = LogManager.getLogger(); + + private ByExtensionVisitor finder = new ByExtensionVisitor( + Arrays.asList("*.jar", "*.JAR", "*.zip", "*.ZIP", "*.class", + "*.jmod", "classlist")); + + private String pSep = File.pathSeparator; + + public List collectClassPath() { + List result = new ArrayList<>(); + + String extdirs = System.getProperty("java.ext.dirs"); + if (extdirs != null) { + Stream.of(extdirs.split(pSep)) + .map(path -> addPathFromDir(path + File.separator)) + .forEach(result::addAll); + } + + result.addAll(addPathFromDir(System.getProperty("java.home"))); + + String classPath = System.getProperty("java.class.path"); + Stream.of(classPath.split(pSep)) + .filter(p -> p.length() >= 4).forEach(path -> { + if (path.contains("vim-javacomplete2/libs/")) { + return; + } + String ext = path.substring(path.length() - 4) + .toLowerCase(); + if (ext.endsWith(".jar") || ext.endsWith(".zip")) { + result.add(path); + } else { + result.addAll(addPathFromDir(path)); + } + }); + + return result; + } + + private List addPathFromDir(String dirpath) { + List result = new ArrayList<>(); + File dir = new File(dirpath); + if (dir.isDirectory()) { + try { + Files.walkFileTree(Paths.get(dir.getPath()), finder); + result.addAll(finder.getResultList()); + } catch (IOException e) { + logger.error(e, e); + } + } + + return result; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/ClasspathPackageSearcher.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/ClasspathPackageSearcher.java new file mode 100644 index 000000000..13d2d2fa1 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/ClasspathPackageSearcher.java @@ -0,0 +1,118 @@ +package kg.ash.javavi.searchers; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; +import java.util.zip.ZipFile; + +import kg.ash.javavi.apache.logging.log4j.LogManager; +import kg.ash.javavi.apache.logging.log4j.Logger; + +public class ClasspathPackageSearcher implements PackageSeacherIFace { + + public static final Logger logger = LogManager.getLogger(); + + public List loadEntries() { + List result = new ArrayList<>(); + + List knownPaths = new ArrayList<>(); + new ClasspathCollector().collectClassPath() + .stream() + .forEach(filePath -> { + if (filePath.toLowerCase().endsWith(".class")) { + String path = filePath.substring( + 0, filePath.length() - 6) + .replaceAll("/", "."); + String newPath = path.substring( + 0, path.lastIndexOf(".")); + String fileName = path.substring( + path.lastIndexOf(".") + 1, path.length()); + Optional kp = knownPaths.parallelStream() + .filter(s -> newPath.endsWith(s)) + .findFirst() + .map(p -> p + File.separator + fileName + ".class") + .map(p -> new PackageEntry( + p, + JavaClassMap.SOURCETYPE_CLASSPATH, + filePath, + PackageEntry.FILETYPE_CLASS)); + if (kp.isPresent()) { + result.add(kp.get()); + return; + } + + String[] split = path.split("\\."); + int j = split.length - 2; + while (j > 0) { + path = ""; + for (int i = j; i <= split.length - 2; i++) { + path += split[i] + "."; + } + String pkg = getPackageByFile(path + fileName); + if (pkg != null) { + result.add( + new PackageEntry( + pkg + File.separator + + fileName + ".class", + JavaClassMap.SOURCETYPE_CLASSPATH, + filePath, + PackageEntry.FILETYPE_CLASS)); + knownPaths.add(pkg); + break; + } else { + j--; + } + } + } else if (filePath.endsWith("classlist")) { + try (Stream stream = + Files.lines(Paths.get(filePath))) { + stream.forEach(l -> { + result.add( + new PackageEntry(l + ".class", + JavaClassMap.SOURCETYPE_CLASSPATH, + filePath)); + }); + } catch (IOException ex) { + logger.warn("error read classlist file", ex); + } + } else { + try { + for (Enumeration entries = + new ZipFile(filePath).entries(); + entries.hasMoreElements(); ) { + String entry = entries.nextElement().toString(); + if (filePath.endsWith(".jmod") + && entry.startsWith("classes/")) { + entry = entry.substring(8); + } + result.add( + new PackageEntry(entry, + JavaClassMap.SOURCETYPE_CLASSPATH, + filePath)); + } + } catch (Exception e) { + logger.error(e, e); + } + } + }); + + return result; + } + + private String getPackageByFile(String path) { + try { + Class clazz = Class.forName(path); + return clazz.getPackage().getName(); + } catch (ExceptionInInitializerError | + ClassNotFoundException | + NoClassDefFoundError ex) { + return null; + } + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/FqnSearcher.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/FqnSearcher.java new file mode 100644 index 000000000..38fae1ec5 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/FqnSearcher.java @@ -0,0 +1,86 @@ +package kg.ash.javavi.searchers; + +import kg.ash.javavi.TargetParser; +import kg.ash.javavi.clazz.ClassImport; +import kg.ash.javavi.clazz.SourceClass; + +import java.util.ArrayList; +import java.util.List; + +public class FqnSearcher { + + private String sources; + + public FqnSearcher(String sources) { + this.sources = sources; + if (this.sources != null) { + this.sources = this.sources.replace('\\', '/'); + } + } + + public String getFqn(SourceClass clazz, String name) { + TargetParser targetParser = new TargetParser(sources); + name = targetParser.parse(name); + + List fqns = new ArrayList<>(); + for (ClassImport ci : clazz.getImports()) { + if (!ci.isAsterisk()) { + if (ci.getTail().equals(name)) { + fqns.add(ci.getName()); + break; + } + } else { + fqns.add(replaceAsterisk(ci.getName()) + name); + } + } + + if (clazz.getPackage() != null) { + fqns.add(clazz.getPackage().concat(".").concat(name)); + } + + String result = searchForRealClass(fqns); + if (result == null) { + result = name; + } + + return result.concat(searchForTypeArguments(clazz, targetParser.getTypeArguments())); + } + + private String searchForRealClass(List fqns) { + ClassSearcher seacher = new ClassSearcher(); + for (String fqn : fqns) { + if (seacher.find(fqn, sources)) { + return fqn; + } + } + + return null; + } + + private String searchForTypeArguments(SourceClass clazz, List typeArguments) { + if (typeArguments.isEmpty()) { + return ""; + } + + StringBuilder arguments = new StringBuilder("<"); + for (String arg : typeArguments) { + String fqn = getFqn(clazz, arg); + arguments.append(fqn).append(","); + } + arguments.setCharAt(arguments.length() - 1, '>'); + + return arguments.toString(); + } + + private String replaceAsterisk(String asteriskImport) { + String[] splitted = asteriskImport.split("\\."); + String importName = ""; + for (String s : splitted) { + if (!s.equals("*")) { + importName += s.concat("."); + } + } + + return importName; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/JavaClassMap.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/JavaClassMap.java new file mode 100644 index 000000000..1f20d1478 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/JavaClassMap.java @@ -0,0 +1,86 @@ +package kg.ash.javavi.searchers; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public abstract class JavaClassMap implements Serializable { + + public static final int SOURCETYPE_CLASSPATH = 0; + public static final int SOURCETYPE_SOURCES = 1; + + public static final int TYPE_CLASS = 0; + public static final int TYPE_SUBPACKAGE = 1; + + protected String name = null; + protected HashMap pathsMap = new HashMap<>(); + protected List classes = new ArrayList<>(); + protected Map subpackages = new HashMap<>(); + + public JavaClassMap(String name) { + setName(name); + } + + public boolean contains(String path) { + return pathsMap.containsKey(path); + } + + public Set getPaths() { + return pathsMap.keySet(); + } + + public int getSourceType(String path) { + return pathsMap.get(path); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public void add(String path, int source, int type, String filename) { + if (!contains(path)) { + pathsMap.put(path, source); + if (type == TYPE_CLASS) { + classes.add(path); + } else { + subpackages.put(path, filename); + } + } + } + + public StringBuilder getCachedClasses() { + StringBuilder cachedClasses = new StringBuilder(); + classes.stream() + .sorted() + .forEach(path -> cachedClasses.append("'").append(path).append("',")); + return cachedClasses; + } + + public StringBuilder getCachedSubpackages() { + StringBuilder cachedSubpackages = new StringBuilder(); + subpackages.keySet() + .stream() + .sorted() + .forEach(path -> cachedSubpackages.append("'").append(path).append("',")); + return cachedSubpackages; + } + + + public Map getSubpackages() { + return subpackages; + } + + public abstract int getType(); + + @Override + public String toString() { + return String.format("name: %s, type: %d", name, getType()); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/PackageEntry.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/PackageEntry.java new file mode 100644 index 000000000..3729c9967 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/PackageEntry.java @@ -0,0 +1,60 @@ +package kg.ash.javavi.searchers; + +public class PackageEntry { + + public final static int FILETYPE_JAVA = 0; + public final static int FILETYPE_CLASS = 1; + + private String entry; + private int source; + private String javaFile = null; + private String classFile = null; + private String archiveName = null; + + public PackageEntry(String entry, int source) { + this.entry = entry; + this.source = source; + } + + public PackageEntry(String entry, int source, String archiveName) { + this.entry = entry; + this.source = source; + this.archiveName = archiveName; + } + + public PackageEntry(String entry, int source, String filePath, int fileType) { + this.entry = entry; + this.source = source; + if (fileType == FILETYPE_JAVA) { + this.javaFile = filePath; + } else { + this.classFile = filePath; + } + } + + public String getEntry() { + return entry; + } + + public int getSource() { + return source; + } + + public String getJavaFile() { + return javaFile; + } + + public String getClassFile() { + return classFile; + } + + public String getArchiveName() { + return archiveName; + } + + @Override + public String toString() { + return String.format("{%s, %d, %s, %s}", entry, source, javaFile, classFile); + } + +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/PackageNameMap.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/PackageNameMap.java new file mode 100644 index 000000000..d47b18e42 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/PackageNameMap.java @@ -0,0 +1,13 @@ +package kg.ash.javavi.searchers; + +public class PackageNameMap extends JavaClassMap { + + public PackageNameMap(String name) { + super(name); + } + + @Override + public int getType() { + return JavaClassMap.TYPE_SUBPACKAGE; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/PackageSeacherIFace.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/PackageSeacherIFace.java new file mode 100644 index 000000000..6a9642c6a --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/PackageSeacherIFace.java @@ -0,0 +1,7 @@ +package kg.ash.javavi.searchers; + +import java.util.List; + +public interface PackageSeacherIFace { + List loadEntries(); +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/PackagesLoader.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/PackagesLoader.java new file mode 100644 index 000000000..01cc1ef8d --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/PackagesLoader.java @@ -0,0 +1,114 @@ +package kg.ash.javavi.searchers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class PackagesLoader { + + private HashMap classPackages; + private List searchers = new ArrayList<>(); + + public PackagesLoader(String sourceDirectories) { + searchers.add(new ClasspathPackageSearcher()); + searchers.add(new SourcePackageSearcher(sourceDirectories)); + } + + public void collectPackages(HashMap classPackages) { + this.classPackages = classPackages; + + List entries = new ArrayList<>(); + searchers.parallelStream().forEach(s -> entries.addAll(s.loadEntries())); + + entries.forEach(entry -> appendEntry(entry)); + } + + public void setSearchers(List searchers) { + this.searchers = searchers; + } + + private void appendEntry(PackageEntry entry) { + String name = entry.getEntry(); + if (isClassFile(name)) { + int seppos = name.lastIndexOf('$'); + if (seppos < 0) { + seppos = name.replace('\\', '/').lastIndexOf('/'); + } + if (seppos != -1) { + processClass(entry, seppos); + } + } + } + + private JavaClassMap getClassMap(String name, int type) { + if (classPackages.containsKey(name) && classPackages.get(name).getType() == type) { + return classPackages.get(name); + } + + JavaClassMap jcm; + if (type == JavaClassMap.TYPE_CLASS) { + jcm = new ClassNameMap(name); + } else { + jcm = new PackageNameMap(name); + } + + classPackages.put(name, jcm); + return jcm; + } + + private void processClass(PackageEntry entry, int seppos) { + String name = entry.getEntry(); + String parent = name.substring(0, seppos); + String child = name.substring(seppos + 1, name.length() - 6); + + boolean nested = false; + String parentDots = makeDots(parent); + if (name.contains("$")) { + nested = true; + parentDots += "$"; + } + + int source = entry.getSource(); + + if (!child.isEmpty() && !parentDots.isEmpty()) { + ClassNameMap classMap = (ClassNameMap) getClassMap(child, JavaClassMap.TYPE_CLASS); + classMap.add(parentDots, source, JavaClassMap.TYPE_SUBPACKAGE, entry.getArchiveName()); + if (entry.getJavaFile() != null) { + classMap.setJavaFile(entry.getJavaFile()); + } + if (entry.getClassFile() != null) { + classMap.setClassFile(entry.getClassFile()); + } + + if (!nested) { + getClassMap(parentDots, JavaClassMap.TYPE_SUBPACKAGE).add(child, source, + JavaClassMap.TYPE_CLASS, null); + } + + addToParent(parent, source); + } + } + + private boolean isClassFile(String name) { + return name.endsWith(".class"); + } + + private void addToParent(String name, int source) { + int seppos = name.replace('\\', '/').lastIndexOf('/'); + if (seppos == -1) { + return; + } + + String parent = name.substring(0, seppos); + String child = name.substring(seppos + 1); + + getClassMap(makeDots(parent), JavaClassMap.TYPE_SUBPACKAGE).add(child, source, + JavaClassMap.TYPE_SUBPACKAGE, null); + + addToParent(parent, source); + } + + private String makeDots(String name) { + return name.replaceAll("/", ".").replaceAll("[.]{2,}", ""); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/SourceFileVisitor.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/SourceFileVisitor.java new file mode 100644 index 000000000..b1f7138c2 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/SourceFileVisitor.java @@ -0,0 +1,94 @@ +package kg.ash.javavi.searchers; + +import kg.ash.javavi.apache.logging.log4j.LogManager; +import kg.ash.javavi.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitResult; +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; + +public class SourceFileVisitor extends SimpleFileVisitor { + + public static final Logger logger = LogManager.getLogger(); + + private final PathMatcher matcher; + private String targetFile; + private String pattern; + + public SourceFileVisitor(String pattern) { + String path; + pattern = pattern != null ? pattern : ""; + + if (pattern.contains(".")) { + String[] splitted = pattern.split("\\."); + path = splitted[splitted.length - 1]; + } else { + path = pattern; + } + + logger.info("visit source: {}", path); + matcher = FileSystems.getDefault().getPathMatcher(String.format("glob:%s.java", path)); + + this.pattern = pattern; + } + + public String getTargetFile() { + return targetFile; + } + + private boolean find(Path file) { + Path name = file.getFileName(); + + if (name != null && matcher.matches(name)) { + if (pattern.contains(".")) { + return checkPattern(file); + } + return true; + } + return false; + } + + private boolean checkPattern(Path file) { + String[] splitted = pattern.split("\\."); + for (int i = splitted.length - 2; i >= 0; i--) { + file = file.getParent(); + if (file != null) { + String filename = file.getFileName().toString(); + if (!filename.equals(splitted[i])) { + return false; + } + } else { + return false; + } + } + return true; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + if (!find(file)) { + return FileVisitResult.CONTINUE; + } + + targetFile = file.toFile().getPath().replace('\\', '/'); + return FileVisitResult.TERMINATE; + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { + if (!find(dir)) { + return FileVisitResult.CONTINUE; + } + + return FileVisitResult.TERMINATE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) { + return FileVisitResult.CONTINUE; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/SourcePackageSearcher.java b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/SourcePackageSearcher.java new file mode 100644 index 000000000..759b01672 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/java/kg/ash/javavi/searchers/SourcePackageSearcher.java @@ -0,0 +1,88 @@ +package kg.ash.javavi.searchers; + +import com.github.javaparser.JavaParser; +import com.github.javaparser.ast.CompilationUnit; +import kg.ash.javavi.apache.logging.log4j.LogManager; +import kg.ash.javavi.apache.logging.log4j.Logger; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class SourcePackageSearcher implements PackageSeacherIFace { + + public static final Logger logger = LogManager.getLogger(); + + private String sourceDirectories = ""; + private ByExtensionVisitor finder = new ByExtensionVisitor(Arrays.asList("*.java")); + + public SourcePackageSearcher(String sourceDirectories) { + if (sourceDirectories != null) { + this.sourceDirectories = sourceDirectories; + } + } + + public List loadEntries() { + List result = new ArrayList<>(); + for (String directory : getExistDirectories()) { + try { + logger.debug("search source files"); + + Files.walkFileTree(Paths.get(directory), finder); + for (String path : finder.getResultList()) { + String packagePath = fetchPackagePath(path); + if (packagePath != null) { + logger.trace(path); + + packagePath = packagePath.substring(0, packagePath.length() - 4) + "class"; + result.add( + new PackageEntry(packagePath, JavaClassMap.SOURCETYPE_SOURCES, path, + PackageEntry.FILETYPE_JAVA)); + } + } + } catch (IOException e) { + logger.error(e, e); + } + } + + return result; + } + + private List getExistDirectories() { + String[] splitted = sourceDirectories.split(File.pathSeparator); + return Arrays.asList(splitted) + .stream() + .filter(d -> new File(d).isDirectory()) + .collect(Collectors.toList()); + } + + private String fetchPackagePath(String sourcePath) { + CompilationUnit cu; + try (FileInputStream in = new FileInputStream(sourcePath)) { + cu = JavaParser.parse(in); + } catch (Exception ex) { + return null; + } + + if (cu.getPackageDeclaration().isPresent()) { + int lastslash = sourcePath.replace('\\', '/').lastIndexOf('/'); + if (lastslash >= 0) { + String className = sourcePath.substring(lastslash + 1); + String path = cu.getPackageDeclaration() + .get() + .getNameAsString() + .replace(".", File.separator); + return path + File.separator + className; + } + } + + return null; + } +} + diff --git a/bundle/vim-javacomplete2/libs/javavi/src/main/resources/log4j2.xml b/bundle/vim-javacomplete2/libs/javavi/src/main/resources/log4j2.xml new file mode 100644 index 000000000..5a6e8204c --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/main/resources/log4j2.xml @@ -0,0 +1,27 @@ + + + + 0 + off + /tmp/javavi_log + ${date:yyMMddHHmm} + + + + + + + + + %d{MMdd HH:mm:ss.SSS} %p{length=1} [%t] %m [%c{-3}]%n + + + + + + + + + + diff --git a/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/DaemonTest.java b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/DaemonTest.java new file mode 100644 index 000000000..79f99d17a --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/DaemonTest.java @@ -0,0 +1,34 @@ +package kg.ash.javavi; + +import org.junit.Assert; +import org.junit.Test; + +public class DaemonTest { + + private Daemon daemon = new Daemon(0, -1); + + @Test + public void testParseLine() { + Assert.assertArrayEquals(new String[] { "-v" }, daemon.parseRequest("-v")); + Assert.assertArrayEquals(new String[] { "-E", "java.util.List" }, + daemon.parseRequest("-E \"java.util.List\"")); + Assert.assertArrayEquals(new String[] { "-E", "java.util.List" }, + daemon.parseRequest("-E java.util.List")); + Assert.assertArrayEquals(new String[] { "-E", "java.util.List>" }, + daemon.parseRequest("-E java.util.List>")); + Assert.assertArrayEquals(new String[0], daemon.parseRequest("")); + Assert.assertArrayEquals(new String[] { "-E", "" }, daemon.parseRequest("-E \"\"")); + Assert.assertEquals("\\n", daemon.parseRequest("\"\\\\n\"")[0]); + } + + @Test + public void testParseEmptyValue() { + Assert.assertEquals(new String[] { "-sources", "" }, daemon.parseRequest("-sources \"\"")); + } + + @Test + public void testWindowsDirectoryValue() { + Assert.assertEquals(new String[] { "-base", "C:\\Documents and Settings\\directory\\" }, + daemon.parseRequest("-base \"C:\\\\Documents and Settings\\\\directory\\\\\"")); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/TargetParserTest.java b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/TargetParserTest.java new file mode 100644 index 000000000..16e77d93a --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/TargetParserTest.java @@ -0,0 +1,68 @@ +package kg.ash.javavi; + +import org.junit.Assert; +import org.junit.Test; + +public class TargetParserTest { + + @Test + public void testParse() { + TargetParser parser = new TargetParser(""); + Assert.assertEquals("", parser.parse("")); + Assert.assertEquals("java.util.List", parser.parse("java.util.List")); + Assert.assertEquals(0, parser.getTypeArguments().size()); + + Assert.assertEquals("java.util.List", + parser.parse("java.util.List>>")); + Assert.assertEquals(1, parser.getTypeArguments().size()); + Assert.assertEquals("java.util.List>", + parser.getTypeArguments().get(0)); + + Assert.assertEquals("HashMap", parser.parse("HashMap>")); + + Assert.assertEquals("java.util.HashMap", parser.parse( + "java.util.HashMap<(kg.ash.demo.String|java.lang.String),java.math.BigDecimal>")); + Assert.assertEquals(2, parser.getTypeArguments().size()); + Assert.assertEquals("java.lang.String", parser.getTypeArguments().get(0)); + Assert.assertEquals("java.math.BigDecimal", parser.getTypeArguments().get(1)); + + Assert.assertEquals("java.util.List", parser.parse("java.util.List")); + Assert.assertEquals("Integer", parser.getTypeArguments().get(0)); + + Assert.assertEquals("java.util.List", parser.parse("java.util.List")); + Assert.assertEquals("Integer[]", parser.getTypeArguments().get(0)); + + Assert.assertEquals("java.util.List", parser.parse("java.util.List")); + Assert.assertEquals("Integer", parser.getTypeArguments().get(0)); + + Assert.assertEquals("java.lang.Class", parser.parse("java.lang.Class")); + + Assert.assertEquals("java.util.HashMap$KeySet", parser.parse( + "java.util.HashMap$KeySet<(kg.ash.demo.String|java.lang.String),java.math" + + ".BigDecimal>")); + Assert.assertEquals(2, parser.getTypeArguments().size()); + Assert.assertEquals("java.lang.String", parser.getTypeArguments().get(0)); + Assert.assertEquals("java.math.BigDecimal", parser.getTypeArguments().get(1)); + + Assert.assertEquals("java.util.List", + parser.parse("java.util.List")); + Assert.assertEquals("java.util.List", parser.parse( + "java.util.List<(kg.test.Object|java.lang.Object),(kg.test.Object|java.lang.Object)," + + "(kg.test.String|java.lang.String|java.lang.Object)>")); + } + + @Test + public void testTypeArgumentsToString() { + TargetParser parser = new TargetParser(""); + parser.parse("java.util.List"); + Assert.assertEquals("", parser.getTypeArgumentsString()); + parser.parse("java.util.List"); + Assert.assertEquals("", parser.getTypeArgumentsString()); + parser.parse("java.util.List"); + Assert.assertEquals("", parser.getTypeArgumentsString()); + parser.parse( + "java.util.HashMap<(kg.ash.demo.String|java.lang.String),java.math.BigDecimal>"); + Assert.assertEquals("", + parser.getTypeArgumentsString()); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/actions/ClassRecompileActionTest.java b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/actions/ClassRecompileActionTest.java new file mode 100644 index 000000000..9320cdbd7 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/actions/ClassRecompileActionTest.java @@ -0,0 +1,40 @@ +package kg.ash.javavi.actions; + +import kg.ash.javavi.Javavi; +import kg.ash.javavi.searchers.ClassNameMap; +import mockit.integration.junit4.JMockit; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(JMockit.class) +public class ClassRecompileActionTest { + + @Test + public void testRecompilationCommandBuild() { + ClassNameMap classMap = new ClassNameMap("foo.bar.Test"); + classMap.setJavaFile("/path/to/src/foo/bar/Test.java"); + classMap.setClassFile("/another/path/target/foo/bar/Test.class"); + + Javavi.system.put("compiler", "javac"); + new ClassRecompileAction().perform(new String[] { classMap.getName() }); + } + + @Test + public void testBadTargetClass() { + ClassNameMap classMap = new ClassNameMap("foo.bar.Test"); + classMap.setJavaFile("/path/to/src/foo/bar/Test.java"); + classMap.setClassFile("/bar/Test.class"); + + new ClassRecompileAction().perform(new String[] { classMap.getName() }); + + } + + @Test + public void testBadSrcFile() { + ClassNameMap classMap = new ClassNameMap("baz.Test"); + classMap.setJavaFile("/bar/baz/Test.java"); + classMap.setClassFile("/another/path/target/foo/bar/Test.class"); + + new ClassRecompileAction().perform(new String[] { classMap.getName() }); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/actions/GetClassInfoActionTest.java b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/actions/GetClassInfoActionTest.java new file mode 100644 index 000000000..f174713d2 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/actions/GetClassInfoActionTest.java @@ -0,0 +1,46 @@ +package kg.ash.javavi.actions; + +import kg.ash.javavi.Javavi; +import org.json.JSONObject; +import org.junit.Assert; +import org.junit.Ignore; +import org.junit.Test; + +public class GetClassInfoActionTest { + + @Ignore + @Test + public void testCorrect() { + Javavi.system.put("sources", ""); + GetClassInfoAction cia = new GetClassInfoAction(); + + JSONObject json = new JSONObject( + "{'java.lang.Object':{'implements':[],'fqn':'java.lang.Object','classpath':'1'," + + "'ctors':[{'d':'public java.lang.Object()','m':'1'}],'methods':[{'r':'void'," + + "'c':'void','d':'protected void java.lang.Object.finalize() throws java.lang" + + ".Throwable','m':'100','n':'finalize'},{'p':['long','int'],'r':'void'," + + "'c':'void','d':'public final void java.lang.Object.wait(long,int) throws java" + + ".lang.InterruptedException','m':'10001','n':'wait'},{'p':['long'],'r':'void'," + + "'c':'void','d':'public final native void java.lang.Object.wait(long) throws " + + "java.lang.InterruptedException','m':'100010001','n':'wait'},{'r':'void'," + + "'c':'void','d':'public final void java.lang.Object.wait() throws java.lang" + + ".InterruptedException','m':'10001','n':'wait'},{'p':['java.lang.Object']," + + "'r':'boolean','c':'boolean','d':'public boolean java.lang.Object.equals(java" + + ".lang.Object)','m':'1','n':'equals'},{'r':'java.lang.String','c':'java.lang" + + ".String','d':'public java.lang.String java.lang.Object.toString()','m':'1'," + + "'n':'toString'},{'r':'int','c':'int','d':'public native int java.lang.Object" + + ".hashCode()','m':'100000001','n':'hashCode'},{'r':'java.lang.Class'," + + "'c':'java.lang.Class','d':'public final native java.lang.Class java.lang" + + ".Object.getClass()','m':'100010001','n':'getClass'},{'r':'java.lang.Object'," + + "'d':'protected native java.lang.Object java.lang.Object.clone() throws java" + + ".lang.CloneNotSupportedException','m':'100000100','n':'clone'},{'r':'void'," + + "'c':'void','d':'private static native void java.lang.Object.registerNatives()" + + "','m':'100001010','n':'registerNatives'},{'r':'void','c':'void','d':'public " + + "final native void java.lang.Object.notify()','m':'100010001','n':'notify'}," + + "{'r':'void','c':'void','d':'public final native void java.lang.Object" + + ".notifyAll()','m':'100010001','n':'notifyAll'}],'flags':'1','name':'java.lang" + + ".Object','tag':'CLASSDEF','fields':[],'nested':[]}}"); + Assert.assertTrue( + json.similar(new JSONObject(cia.perform(new String[] { "-E", "java.lang.Object" })))); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/actions/GetClassesArchiveNamesActionTest.java b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/actions/GetClassesArchiveNamesActionTest.java new file mode 100644 index 000000000..b8f19fcb9 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/actions/GetClassesArchiveNamesActionTest.java @@ -0,0 +1,61 @@ +package kg.ash.javavi.actions; + +import kg.ash.javavi.searchers.ClassNameMap; +import kg.ash.javavi.searchers.JavaClassMap; +import mockit.Mock; +import mockit.MockUp; +import mockit.integration.junit4.JMockit; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.HashMap; + +@RunWith(JMockit.class) +public class GetClassesArchiveNamesActionTest { + + @Test + public void testArchiveNamesFetch() { + new MockUp() { + @Mock + private HashMap getClassPackages() { + HashMap map = new HashMap<>(); + + JavaClassMap jc = new ClassNameMap("List"); + jc.add("java.util", JavaClassMap.SOURCETYPE_CLASSPATH, JavaClassMap.TYPE_SUBPACKAGE, + "/dir/lib.jar"); + map.put("List", jc); + + jc = new ClassNameMap("HashMap"); + jc.add("java.util", JavaClassMap.SOURCETYPE_CLASSPATH, JavaClassMap.TYPE_SUBPACKAGE, + "/dir/lib.jar"); + map.put("HashMap", jc); + + return map; + } + }; + + GetClassesArchiveNamesAction action = new GetClassesArchiveNamesAction(); + String result = action.perform(new String[] { "java.util.List,java.util.HashMap" }); + Assert.assertEquals("[['/dir/lib.jar',['java.util.List','java.util.HashMap',]],]", result); + } + + @Test + public void testNoResult() { + GetClassesArchiveNamesAction action = new GetClassesArchiveNamesAction(); + Assert.assertEquals("[]", + action.perform(new String[] { "java.util.List, java.util.HashMap" })); + } + + @Test + public void testEmptyRequest() { + GetClassesArchiveNamesAction action = new GetClassesArchiveNamesAction(); + Assert.assertEquals("[]", action.perform(new String[] { "" })); + } + + @Test + public void testNoArgs() { + GetClassesArchiveNamesAction action = new GetClassesArchiveNamesAction(); + Assert.assertEquals("[]", action.perform(new String[0])); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/output/OutputClassInfoTest.java b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/output/OutputClassInfoTest.java new file mode 100644 index 000000000..f82ad02ce --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/output/OutputClassInfoTest.java @@ -0,0 +1,227 @@ +package kg.ash.javavi.output; + +import com.github.javaparser.ast.Modifier; +import kg.ash.javavi.clazz.ClassConstructor; +import kg.ash.javavi.clazz.ClassField; +import kg.ash.javavi.clazz.ClassMethod; +import kg.ash.javavi.clazz.ClassTypeParameter; +import kg.ash.javavi.clazz.SourceClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.EnumSet; + +public class OutputClassInfoTest { + + private SourceClass clazz; + private OutputClassInfo oci; + + @Before + public void Init() { + oci = new OutputClassInfo(); + + clazz = new SourceClass(); + clazz.setName("Bar"); + clazz.setPackage("foo.bar"); + clazz.setIsInterface(false); + clazz.setModifiers(EnumSet.of(Modifier.PUBLIC)); + } + + @Test + public void testEmptyClass() { + Assert.assertEquals( + "{'foo.bar.Bar':{'tag':'CLASSDEF','flags':'1','name':'foo.bar.Bar','classpath':'1'," + + "'pos':[-1,-1],'endpos':[-1,-1],'fqn':'foo.bar.Bar','implements':[]," + + "'nested':[],'ctors':[],'fields':[],'methods':[],},}", oci.get(clazz)); + } + + @Test + public void testConstructors() { + ClassConstructor cc = new ClassConstructor(); + cc.setDeclaration("public Bar(Foo foo)"); + cc.setModifiers(EnumSet.of(Modifier.PUBLIC)); + ClassTypeParameter ctp = new ClassTypeParameter("Foo"); + cc.addTypeParameter(ctp); + + clazz.addConstructor(cc); + + Assert.assertEquals( + "{'foo.bar.Bar':{'tag':'CLASSDEF','flags':'1','name':'foo.bar.Bar','classpath':'1'," + + "'pos':[-1,-1],'endpos':[-1,-1],'fqn':'foo.bar.Bar','implements':[]," + + "'nested':[],'ctors':[{'m':'1','p':['Foo',],'d':'public Bar(Foo foo)'},]," + + "'fields':[],'methods':[],},}", oci.get(clazz)); + } + + @Test + public void testTwoConstructors() { + ClassConstructor cc = new ClassConstructor(); + cc.setModifiers(EnumSet.of(Modifier.PUBLIC)); + cc.setDeclaration("public Bar()"); + + clazz.addConstructor(cc); + + cc = new ClassConstructor(); + cc.setDeclaration("public Bar(Foo foo)"); + cc.setModifiers(EnumSet.of(Modifier.PUBLIC)); + ClassTypeParameter ctp = new ClassTypeParameter("Foo"); + cc.addTypeParameter(ctp); + + clazz.addConstructor(cc); + + Assert.assertEquals( + "{'foo.bar.Bar':{'tag':'CLASSDEF','flags':'1','name':'foo.bar.Bar','classpath':'1'," + + "'pos':[-1,-1],'endpos':[-1,-1],'fqn':'foo.bar.Bar','implements':[]," + + "'nested':[],'ctors':[{'m':'1','d':'public Bar()'},{'m':'1','p':['Foo',]," + + "'d':'public Bar(Foo foo)'},],'fields':[],'methods':[],},}", oci.get(clazz)); + } + + @Test + public void testFields() { + ClassField field = new ClassField(); + field.setTypeName("Foo"); + field.setName("foo"); + field.setModifiers(EnumSet.of(Modifier.PUBLIC)); + + clazz.addField(field); + + field = new ClassField(); + field.setTypeName("Bar"); + field.setName("bar"); + field.setModifiers(EnumSet.of(Modifier.PRIVATE)); + + clazz.addField(field); + + Assert.assertEquals( + "{'foo.bar.Bar':{'tag':'CLASSDEF','flags':'1','name':'foo.bar.Bar','classpath':'1'," + + "'pos':[-1,-1],'endpos':[-1,-1],'fqn':'foo.bar.Bar','implements':[]," + + "'nested':[],'ctors':[],'fields':[{'n':'foo','c':'Foo','m':'1','t':'Foo'}," + + "{'n':'bar','c':'Bar','m':'10','t':'Bar'},],'methods':[],},}", oci.get(clazz)); + } + + @Test + public void testMethods() { + ClassMethod method = new ClassMethod(); + method.setTypeName("Foo"); + method.setName("foo"); + method.setModifiers(EnumSet.of(Modifier.PUBLIC)); + method.setDeclaration("public Foo foo()"); + + clazz.addMethod(method); + + method = new ClassMethod(); + method.setTypeName("Bar"); + method.setName("bar"); + method.setModifiers(EnumSet.of(Modifier.PRIVATE)); + method.setDeclaration("private Bar bar()"); + + clazz.addMethod(method); + + Assert.assertEquals( + "{'foo.bar.Bar':{'tag':'CLASSDEF','flags':'1','name':'foo.bar.Bar','classpath':'1'," + + "'pos':[-1,-1],'endpos':[-1,-1],'fqn':'foo.bar.Bar','implements':[]," + + "'nested':[],'ctors':[],'fields':[],'methods':[{'n':'foo','c':'Foo','m':'1'," + + "'r':'Foo','d':'public Foo foo()'},{'n':'bar','c':'Bar','m':'10','r':'Bar'," + + "'d':'private Bar bar()'},],},}", oci.get(clazz)); + } + + @Test + public void testExtends() { + clazz.setSuperclass("foo.baz.Baz"); + Assert.assertEquals( + "{'foo.bar.Bar':{'tag':'CLASSDEF','flags':'1','name':'foo.bar.Bar','classpath':'1'," + + "'pos':[-1,-1],'endpos':[-1,-1],'fqn':'foo.bar.Bar','extends':['foo.baz.Baz']," + + "'implements':[],'nested':[],'ctors':[],'fields':[],'methods':[],},}", + oci.get(clazz)); + } + + @Test + public void testImplements() { + clazz.addInterface("foo.baz.Baz"); + clazz.addInterface("foo.bas.Bas"); + Assert.assertEquals( + "{'foo.bar.Bar':{'tag':'CLASSDEF','flags':'1','name':'foo.bar.Bar','classpath':'1'," + + "'pos':[-1,-1],'endpos':[-1,-1],'fqn':'foo.bar.Bar','implements':['foo.baz" + + ".Baz','foo.bas.Bas',],'nested':[],'ctors':[],'fields':[],'methods':[],},}", + oci.get(clazz)); + } + + @Test + public void testTypeArguments() { + clazz.addTypeArgument("A"); + clazz.addTypeArgument("B"); + Assert.assertEquals( + "{'foo.bar.Bar':{'tag':'CLASSDEF','flags':'1','name':'foo.bar.Bar'," + + "'classpath':'1','pos':[-1,-1],'endpos':[-1,-1],'fqn':'foo.bar.Bar'," + + "'implements':[],'nested':[],'ctors':[],'fields':[],'methods':[],},}", + oci.get(clazz)); + } + + @Test + public void testCompleteClass() { + ClassConstructor cc = new ClassConstructor(); + cc.setDeclaration("public Bar(Foo foo)"); + cc.setModifiers(EnumSet.of(Modifier.PUBLIC)); + ClassTypeParameter ctp = new ClassTypeParameter("Foo"); + cc.addTypeParameter(ctp); + clazz.addConstructor(cc); + + ClassField field = new ClassField(); + field.setTypeName("Foo"); + field.setName("foo"); + field.setModifiers(EnumSet.of(Modifier.PUBLIC)); + clazz.addField(field); + + ClassMethod method = new ClassMethod(); + method.setTypeName("Foo"); + method.setName("foo"); + method.setModifiers(EnumSet.of(Modifier.PUBLIC)); + method.setDeclaration("public Foo foo()"); + clazz.addMethod(method); + + clazz.addInterface("foo.baz.Baz"); + clazz.addInterface("foo.bas.Bas"); + clazz.setSuperclass("foo.baz.Baz"); + + Assert.assertEquals( + "{'foo.bar.Bar':{'tag':'CLASSDEF','flags':'1','name':'foo.bar.Bar','classpath':'1'," + + "'pos':[-1,-1],'endpos':[-1,-1],'fqn':'foo.bar.Bar','extends':['foo.baz.Baz']," + + "'implements':['foo.baz.Baz','foo.bas.Bas',],'nested':[],'ctors':[{'m':'1'," + + "'p':['Foo',],'d':'public Bar(Foo foo)'},],'fields':[{'n':'foo','c':'Foo'," + + "'m':'1','t':'Foo'},],'methods':[{'n':'foo','c':'Foo','m':'1','r':'Foo'," + + "'d':'public Foo foo()'},],},}", oci.get(clazz)); + } + + @Test + public void testNullRequest() { + Assert.assertEquals("", oci.get(null)); + } + + @Test + public void testNullConstructor() { + clazz.addConstructor(null); + Assert.assertEquals( + "{'foo.bar.Bar':{'tag':'CLASSDEF','flags':'1','name':'foo.bar.Bar','classpath':'1'," + + "'pos':[-1,-1],'endpos':[-1,-1],'fqn':'foo.bar.Bar','implements':[]," + + "'nested':[],'ctors':[],'fields':[],'methods':[],},}", oci.get(clazz)); + } + + @Test + public void testNullField() { + clazz.addField(null); + Assert.assertEquals( + "{'foo.bar.Bar':{'tag':'CLASSDEF','flags':'1','name':'foo.bar.Bar','classpath':'1'," + + "'pos':[-1,-1],'endpos':[-1,-1],'fqn':'foo.bar.Bar','implements':[]," + + "'nested':[],'ctors':[],'fields':[],'methods':[],},}", oci.get(clazz)); + } + + @Test + public void testNullMethod() { + clazz.addMethod(null); + Assert.assertEquals( + "{'foo.bar.Bar':{'tag':'CLASSDEF','flags':'1','name':'foo.bar.Bar','classpath':'1'," + + "'pos':[-1,-1],'endpos':[-1,-1],'fqn':'foo.bar.Bar','implements':[]," + + "'nested':[],'ctors':[],'fields':[],'methods':[],},}", + oci.get(clazz)); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/output/OutputClassPackagesTest.java b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/output/OutputClassPackagesTest.java new file mode 100644 index 000000000..2a54c775d --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/output/OutputClassPackagesTest.java @@ -0,0 +1,51 @@ +package kg.ash.javavi.output; + +import kg.ash.javavi.Javavi; +import kg.ash.javavi.cache.Cache; +import kg.ash.javavi.searchers.ClassNameMap; +import kg.ash.javavi.searchers.JavaClassMap; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; + +public class OutputClassPackagesTest { + + private String target = "Bar"; + private HashMap classPackages; + + @Before + public void Init() { + Javavi.system.put("sources", ""); + JavaClassMap classMap = new ClassNameMap(target); + classMap.add("bar.baz", JavaClassMap.SOURCETYPE_CLASSPATH, JavaClassMap.TYPE_SUBPACKAGE, + null); + classMap.add("foo.bar", JavaClassMap.SOURCETYPE_CLASSPATH, JavaClassMap.TYPE_SUBPACKAGE, + null); + + classPackages = new HashMap<>(); + classPackages.put(target, classMap); + } + + @Test + public void testCorrect() { + Assert.assertEquals("['foo.bar.Bar','bar.baz.Bar',]", + new OutputClassPackages(classPackages).get(target)); + } + + @Test + public void testCorrectUknownTarget() { + Assert.assertEquals("[]", new OutputClassPackages(classPackages).get("Baz")); + } + + @Test + public void testNullTarget() { + Assert.assertEquals("[]", new OutputClassPackages(classPackages).get(null)); + } + + @Test + public void testNullPackages() { + Assert.assertEquals(Cache.PACKAGES_EMPTY_ERROR, new OutputClassPackages(null).get(target)); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/output/OutputPackageInfoTest.java b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/output/OutputPackageInfoTest.java new file mode 100644 index 000000000..2409242da --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/output/OutputPackageInfoTest.java @@ -0,0 +1,52 @@ +package kg.ash.javavi.output; + +import kg.ash.javavi.cache.Cache; +import kg.ash.javavi.searchers.ClassNameMap; +import kg.ash.javavi.searchers.JavaClassMap; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; + +public class OutputPackageInfoTest { + + private String target = "foo.bar"; + private HashMap classPackages; + + @Before + public void Init() { + JavaClassMap classMap = new ClassNameMap(target); + classMap.add("baz", JavaClassMap.SOURCETYPE_CLASSPATH, JavaClassMap.TYPE_SUBPACKAGE, null); + classMap.add("bax", JavaClassMap.SOURCETYPE_CLASSPATH, JavaClassMap.TYPE_SUBPACKAGE, null); + classMap.add("Bat", JavaClassMap.SOURCETYPE_CLASSPATH, JavaClassMap.TYPE_CLASS, null); + + classPackages = new HashMap<>(); + classPackages.put(target, classMap); + } + + @Test + public void testCorrect() { + OutputPackageInfo opi = new OutputPackageInfo(classPackages); + String result = opi.get(target); + + Assert.assertEquals(String.format( + "{'%s':{'tag':'PACKAGE','subpackages':['bax','baz',],'classes':['Bat',]},}", target), + result); + } + + @Test + public void testCorrectUknownTarget() { + Assert.assertEquals("{}", new OutputPackageInfo(classPackages).get("foo.baa")); + } + + @Test + public void testNullTarget() { + Assert.assertEquals("{}", new OutputPackageInfo(classPackages).get(null)); + } + + @Test + public void testNullPackages() { + Assert.assertEquals(Cache.PACKAGES_EMPTY_ERROR, new OutputPackageInfo(null).get(target)); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/output/OutputSimilarClassesTest.java b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/output/OutputSimilarClassesTest.java new file mode 100644 index 000000000..1806f2124 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/output/OutputSimilarClassesTest.java @@ -0,0 +1,64 @@ +package kg.ash.javavi.output; + +import kg.ash.javavi.cache.Cache; +import kg.ash.javavi.searchers.ClassNameMap; +import kg.ash.javavi.searchers.JavaClassMap; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; + +public class OutputSimilarClassesTest { + + private String target = "Bar"; + private HashMap classPackages; + + @Before + public void Init() { + classPackages = new HashMap<>(); + + JavaClassMap classMap = new ClassNameMap("Barabaz"); + classMap.add("bar", JavaClassMap.SOURCETYPE_CLASSPATH, JavaClassMap.TYPE_SUBPACKAGE, null); + classPackages.put("Barabaz", classMap); + + classMap = new ClassNameMap("Bara"); + classMap.add("bar.bara", JavaClassMap.SOURCETYPE_CLASSPATH, JavaClassMap.TYPE_SUBPACKAGE, + null); + classPackages.put("Bara", classMap); + + classMap = new ClassNameMap("Bazaraz"); + classMap.add("bar.baz", JavaClassMap.SOURCETYPE_CLASSPATH, JavaClassMap.TYPE_SUBPACKAGE, + null); + classPackages.put("Bazaraz", classMap); + + classMap = new ClassNameMap("Foobar"); + classMap.add("bar.bas", JavaClassMap.SOURCETYPE_CLASSPATH, JavaClassMap.TYPE_SUBPACKAGE, + null); + classPackages.put("Foobar", classMap); + } + + @Test + public void testCorrect() { + String result = new OutputSimilarClasses(classPackages).get(target); + + Assert.assertEquals( + "[{'word':'Bara', 'menu':'bar.bara', 'type': 'c'},{'word':'Barabaz', 'menu':'bar', " + + "'type': 'c'},]", result); + } + + @Test + public void testCorrectUknownTarget() { + Assert.assertEquals("[]", new OutputSimilarClasses(classPackages).get("Tar")); + } + + @Test + public void testNullTarget() { + Assert.assertEquals("[]", new OutputSimilarClasses(classPackages).get(null)); + } + + @Test + public void testNullPackages() { + Assert.assertEquals(Cache.PACKAGES_EMPTY_ERROR, new OutputSimilarClasses(null).get(target)); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/readers/source/ClassNamesFetcherTest.java b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/readers/source/ClassNamesFetcherTest.java new file mode 100644 index 000000000..4959900b9 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/readers/source/ClassNamesFetcherTest.java @@ -0,0 +1,58 @@ +package kg.ash.javavi.readers.source; + +import com.github.javaparser.ast.CompilationUnit; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +public class ClassNamesFetcherTest { + + + @Test + public void testClassnamesFetch() { + String testClassDeclarationPath = + "src/test/resources/kg/ash/javavi/ClassWithClasses.java"; + + CompilationUnitResult cur = CompilationUnitCreator.createFromFile( + testClassDeclarationPath); + Assert.assertNull(cur.getProblems()); + CompilationUnit cu = cur.getCompilationUnit(); + ClassNamesFetcher parser = new ClassNamesFetcher(cu); + Set result = parser.getNames(); + + Assert.assertTrue(result.contains("BigDecimal")); + Assert.assertTrue(result.contains("String")); + Assert.assertTrue(result.contains("List")); + Assert.assertTrue(result.contains("ArrayList")); + Assert.assertTrue(result.contains("LinkedList")); + Assert.assertEquals(5, result.size()); + } + + @Test + public void testClassnamesFetchComplex() { + String fetcherTestClassDeclarationPath + = "src/test/resources/kg/ash/javavi/ResourceClassForClassFetcherTest.java"; + String waitFor = "UserTransaction, TestException, WebService, " + + "HashMap, TestResponse, Resource, TestClass, String, " + + "Logger, WebMethod, TestClassForbiddenException, Long, " + + "EJB, BeanClass1, InterceptorRefs, InterceptorRef, BeanClass2, " + + "WebParam, HashSet, Set, List, Map, Attr, ArrayList, HashLine, " + + "SomeClass, unusualClassName, FakeAttr, StaticClassName, " + + "AnotherStatic, ParentAnnotation, ChildAnnotation, format, " + + "AnnotationForConstractor"; + Set waitForList = new HashSet<>(); + waitForList.addAll(Arrays.asList(waitFor.split(", "))); + + CompilationUnitResult cur = CompilationUnitCreator.createFromFile( + fetcherTestClassDeclarationPath); + Assert.assertNull(cur.getProblems()); + CompilationUnit cu = cur.getCompilationUnit(); + ClassNamesFetcher parser = new ClassNamesFetcher(cu); + Set result = parser.getNames(); + + Assert.assertEquals(waitForList, result); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/searchers/ClasspathPackageSearcherTest.java b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/searchers/ClasspathPackageSearcherTest.java new file mode 100644 index 000000000..018c11f19 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/searchers/ClasspathPackageSearcherTest.java @@ -0,0 +1,41 @@ +package kg.ash.javavi.searchers; + +import mockit.Mock; +import mockit.MockUp; +import mockit.integration.junit4.JMockit; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Arrays; +import java.util.List; + +@RunWith(JMockit.class) +public class ClasspathPackageSearcherTest { + + @Test + public void testLoadClassFileEntries() { + new MockUp() { + @Mock + private List collectClassPath() { + return Arrays.asList("/directory/foo/bar/Classname.class", + "/directory/foo/bar/Classname2.class", "/directory/foo/baz/Classname.class", + ""); + } + + }; + + new MockUp() { + @Mock + private String getPackageByFile(String path) { + if (path.split("\\.").length > 2) { + return path.substring(0, path.lastIndexOf('.')); + } + return null; + } + }; + + List entries = new ClasspathPackageSearcher().loadEntries(); + Assert.assertEquals(3, entries.size()); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/searchers/FqnSeacherTest.java b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/searchers/FqnSeacherTest.java new file mode 100644 index 000000000..27f399a06 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/searchers/FqnSeacherTest.java @@ -0,0 +1,36 @@ +package kg.ash.javavi.searchers; + +import kg.ash.javavi.clazz.ClassImport; +import kg.ash.javavi.clazz.SourceClass; +import org.junit.Assert; +import org.junit.Test; + +public class FqnSeacherTest { + + @Test + public void testGetFqn() { + SourceClass clazz = new SourceClass(); + clazz.getImports().add(new ClassImport("kg.ash.javavi.clazz.SourceClass", false, false)); + clazz.getImports().add(new ClassImport("java.util.List", false, false)); + + String sourcesDirectory = System.getProperty("user.dir") + "/src/main/java/"; + FqnSearcher seacher = new FqnSearcher(sourcesDirectory); + + Assert.assertEquals("FakeClass", seacher.getFqn(new SourceClass(), "FakeClass")); + Assert.assertEquals("kg.ash.javavi.clazz.SourceClass", + seacher.getFqn(clazz, "SourceClass")); + Assert.assertEquals("java.util.List", seacher.getFqn(clazz, "List")); + Assert.assertEquals("java.util.List", + seacher.getFqn(clazz, "List")); + + clazz = new SourceClass(); + clazz.setPackage("kg.ash.javavi.clazz"); + clazz.getImports().add(new ClassImport("java.util.*", false, true)); + + Assert.assertEquals("kg.ash.javavi.clazz.SourceClass", + seacher.getFqn(clazz, "SourceClass")); + Assert.assertEquals("java.util.List", seacher.getFqn(clazz, "List")); + Assert.assertEquals("java.util.List", + seacher.getFqn(clazz, "List")); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/searchers/PackagesLoaderTest.java b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/searchers/PackagesLoaderTest.java new file mode 100644 index 000000000..d76e24761 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/searchers/PackagesLoaderTest.java @@ -0,0 +1,104 @@ +package kg.ash.javavi.searchers; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class PackagesLoaderTest { + + private PackagesLoader ps; + private HashMap result; + private List searchers; + + @Before + public void init() { + result = new HashMap<>(); + ps = new PackagesLoader(""); + searchers = new ArrayList<>(); + ps.setSearchers(searchers); + } + + @Test + public void testCorrect() { + PackageSeacherIFace searcher = () -> { + List entries = new ArrayList<>(); + entries.add( + new PackageEntry("java/util/List.class", JavaClassMap.SOURCETYPE_CLASSPATH)); + entries.add( + new PackageEntry("java/util/ArrayList.class", JavaClassMap.SOURCETYPE_CLASSPATH)); + entries.add(new PackageEntry("foo.bar.class", JavaClassMap.SOURCETYPE_CLASSPATH)); + entries.add( + new PackageEntry("kg/ash/javavi/Javavi.class", JavaClassMap.SOURCETYPE_CLASSPATH)); + return entries; + }; + + searchers.add(searcher); + ps.collectPackages(result); + + Assert.assertTrue(result.get("List").contains("java.util")); + Assert.assertTrue(result.get("ArrayList").contains("java.util")); + Assert.assertTrue(result.get("Javavi").contains("kg.ash.javavi")); + Assert.assertNotEquals(null, result.get("java")); + Assert.assertEquals(2, result.get("java.util").getPaths().size()); + Assert.assertEquals(1, result.get("kg.ash.javavi").getPaths().size()); + Assert.assertEquals(1, result.get("kg.ash").getPaths().size()); + Assert.assertEquals(null, result.get("foo.bar")); + } + + @Test + public void testHandleEmpty() { + PackageSeacherIFace searcher = () -> { + List entries = new ArrayList<>(); + entries.add(new PackageEntry("", JavaClassMap.SOURCETYPE_CLASSPATH)); + return entries; + }; + + searchers.add(searcher); + ps.collectPackages(result); + + Assert.assertTrue(result.isEmpty()); + } + + @Test + public void testHandleTooManySlashes() { + PackageSeacherIFace searcher = () -> { + List entries = new ArrayList<>(); + entries.add(new PackageEntry("/////", JavaClassMap.SOURCETYPE_CLASSPATH)); + entries.add(new PackageEntry("/////.class", JavaClassMap.SOURCETYPE_CLASSPATH)); + entries.add(new PackageEntry("/////a.class", JavaClassMap.SOURCETYPE_CLASSPATH)); + return entries; + }; + + searchers.add(searcher); + ps.collectPackages(result); + + Assert.assertTrue(result.isEmpty()); + } + + @Test + public void testNestedClasses() { + PackageSeacherIFace searcher = () -> { + List entries = new ArrayList<>(); + entries.add(new PackageEntry("java/util/HashMap$KeySet.class", + JavaClassMap.SOURCETYPE_CLASSPATH)); + entries.add(new PackageEntry("java/util/HashMap$TreeNode.class", + JavaClassMap.SOURCETYPE_CLASSPATH)); + entries.add( + new PackageEntry("foo/bar/TreeNode.class", JavaClassMap.SOURCETYPE_CLASSPATH)); + entries.add( + new PackageEntry("java/util/ArrayList.class", JavaClassMap.SOURCETYPE_CLASSPATH)); + return entries; + }; + + searchers.add(searcher); + ps.collectPackages(result); + + Assert.assertTrue(result.get("KeySet").contains("java.util.HashMap$")); + Assert.assertTrue(result.get("TreeNode").contains("java.util.HashMap$")); + Assert.assertTrue(result.get("TreeNode").contains("foo.bar")); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/searchers/SourceFileVisitorTest.java b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/searchers/SourceFileVisitorTest.java new file mode 100644 index 000000000..2f11c2081 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/test/java/kg/ash/javavi/searchers/SourceFileVisitorTest.java @@ -0,0 +1,49 @@ +package kg.ash.javavi.searchers; + +import org.junit.Assert; +import org.junit.Test; + +import java.io.File; + +public class SourceFileVisitorTest { + + @Test + public void testCorrectPackageClass() { + SourceFileVisitor visitor = new SourceFileVisitor("foo.bar.Baz"); + visitor.visitFile(new File("src/foo/bar/Baz.java").toPath(), null); + + Assert.assertEquals("src/foo/bar/Baz.java", visitor.getTargetFile()); + } + + @Test + public void testCorrectClass() { + SourceFileVisitor visitor = new SourceFileVisitor("Baz"); + visitor.visitFile(new File("/root/src/foo/bar/Baz.java").toPath(), null); + + Assert.assertEquals("/root/src/foo/bar/Baz.java", visitor.getTargetFile()); + } + + @Test + public void testEmptyClassname() { + SourceFileVisitor visitor = new SourceFileVisitor(""); + visitor.visitFile(new File("src/foo/bar/Baz.java").toPath(), null); + + Assert.assertEquals(null, visitor.getTargetFile()); + } + + @Test + public void testSimilarPackageShouldNotBeFound() { + SourceFileVisitor visitor = new SourceFileVisitor("foo.baz.Baz"); + visitor.visitFile(new File("src/foo/bar/Baz.java").toPath(), null); + + Assert.assertEquals(null, visitor.getTargetFile()); + } + + @Test + public void testNullClassname() { + SourceFileVisitor visitor = new SourceFileVisitor(null); + visitor.visitFile(new File("src/foo/bar/Baz.java").toPath(), null); + + Assert.assertEquals(null, visitor.getTargetFile()); + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/test/resources/kg/ash/javavi/ClassWithClasses.java b/bundle/vim-javacomplete2/libs/javavi/src/test/resources/kg/ash/javavi/ClassWithClasses.java new file mode 100644 index 000000000..9be76f268 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/test/resources/kg/ash/javavi/ClassWithClasses.java @@ -0,0 +1,16 @@ +package kg.ash.javavi; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +public class ClassWithClasses extends LinkedList { + + List list = new ArrayList(); + + public String method() { + BigDecimal bd = BigDecimal.ONE; + return "return"; + } +} diff --git a/bundle/vim-javacomplete2/libs/javavi/src/test/resources/kg/ash/javavi/ResourceClassForClassFetcherTest.java b/bundle/vim-javacomplete2/libs/javavi/src/test/resources/kg/ash/javavi/ResourceClassForClassFetcherTest.java new file mode 100644 index 000000000..78ad63be4 --- /dev/null +++ b/bundle/vim-javacomplete2/libs/javavi/src/test/resources/kg/ash/javavi/ResourceClassForClassFetcherTest.java @@ -0,0 +1,79 @@ +package kg.ash.javavi; + +import static java.text.MessageFormat.format; + +import org.apache.log4j.Logger; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.annotation.Resource; +import javax.ejb.EJB; +import javax.jws.WebMethod; +import javax.jws.WebParam; +import javax.jws.WebService; + +/** + * @author Artur Shaikhullin + */ + +@WebService(serviceName = "ResourceClassForClassFetcherTest") +@InterceptorRefs({ + @InterceptorRef("authStack"), @InterceptorRef("loggingStack") +}) +public class ResourceClassForClassFetcherTest { + + @Resource(attr = Attr.SOME_ATTR) private UserTransaction tx; + + @ParentAnnotation({ + @ChildAnnotation + }) private static final Logger logger = Logger.getLogger( + ResourceClassForClassFetcherTest.class.getName()); + private static final HashMap hashMap1 = new HashMap(); + private static final HashMap hashMap2 = new HashMap(); + + @EJB private BeanClass1 bean1; + @EJB private BeanClass2 bean2; + + @AnnotationForConstractor + public ResourceClassForClassFetcherTest() { + + } + + private TestClass getTestClass(SomeClass source, String hash) + throws TestClassForbiddenException { + try { + TestClass testClass = bean1.getByUniq(format()); + String result = StaticClassName.staticMethod(); + String result2 = AnotherStatic.staticReference; + return testClass; + } catch (TestClassForbiddenException ex) { + logger.warn(String.format("Wrong testClass: %s %s", format(hash), source)); + throw ex; + } + } + + /** + * @return TestResponse + */ + @WebMethod(operationName = "test") + public TestResponse test( + @WebParam(name = "testClassID", fake = FakeAttr.ATTR) final String testClassID, + @WebParam(name = "username") final String username, + @WebParam(name = "hash") final HashLine hash) throws TestException { + + List myList = new ArrayList<>(); + Set mySet = new HashSet<>(); + Map.Entry.Fake e; + logger.info( + String.format("Test(id: %s, username: %s, hash: %s)", testClassID, username, hash)); + Map.Entry e; + logger.info(ar); + return ar; + } + + private class InnerClass { + private unusualClassName UnusualObjectName; + } +} diff --git a/bundle/vim-javacomplete2/plugin/javacomplete.vim b/bundle/vim-javacomplete2/plugin/javacomplete.vim new file mode 100644 index 000000000..e6598db55 --- /dev/null +++ b/bundle/vim-javacomplete2/plugin/javacomplete.vim @@ -0,0 +1,714 @@ +" Java complete plugin file +" Maintainer: artur shaik +" this file comtains command,custom g:var init and maps + +"" +" @section Overview, overview +" @library +" @order overview usage faq history todo thanks +" This is javacomplete, an omni-completion script of JAVA language +" for vim 7 and above. It includes javacomplete.vim, java_parser.vim, +" javavi library, javaparser library and javacomplete.txt. + + +"" +" @section Features, features +" @parentsection overview +" 1. List members of a class, including (static) fields, (static) methods and ctors; +" 2. List classes or subpackages of a package; +" 3. Provide parameters information of a method, list all overload methods; +" 4. Complete an incomplete word; +" 5. Provide a complete JAVA parser written in Vim script language; +" 6. Use the JVM to obtain most information; +" 7. Use the embedded parser to obtain the class information from source files; +" 8. JSP is supported, Builtin objects such as request, session can be recognized; +" 9. The classes and jar files in the WEB-INF will be appended automatically to the classpath; +" 10. Server side java reflection class loader and parsing library; +" 11. Search class files automatically; +" 12. Complete class name; +" 13. Add import statement for a given class name; +" 14. Complete methods declaration after '@Override'; +" 15. Support for maven, gradle and Eclipse's '.classpath'; +" 16. Cross-session cache; +" 17. Auto insert methods that need to be implemented; +" 18. `toString`, `equals`, `hashCode`, Accessors generation. + + +"" +" @section Requirements, requirements +" @parentsection overview +" +" 1. Vim version 7.4 and above with python support; +" 2. JDK8. +" + +"" +" @section Download, download +" @parentsection overview +" You can download the lastest version from this url: + " https://github.com/artur-shaik/vim-javacomplete2 + +"" +" @section Install, install +" @parentsection overview +" 1. This assumes you are using `Vundle`. +" Adapt for your plugin manager of choice. Put this into your `.vimrc`. +" > +" " Java completion plugin. +" Plugin 'artur-shaik/vim-javacomplete2' +" < +" 2. Set 'omnifunc' option. e.g. +" > +" autocmd Filetype java setlocal omnifunc=javacomplete#Complete +" < +" 3. Map keys you prefer: +" For smart (trying to guess import option) insert class import with : +" > +" nmap (JavaComplete-Imports-AddSmart) +" imap (JavaComplete-Imports-AddSmart) +" < +" For usual (will ask for import option) insert class import with : +" > +" nmap (JavaComplete-Imports-Add) +" imap (JavaComplete-Imports-Add) +" < +" For add all missing imports with : +" > +" nmap (JavaComplete-Imports-AddMissing) +" imap (JavaComplete-Imports-AddMissing) +" < +" For remove all missing imports with : +" > +" nmap (JavaComplete-Imports-RemoveUnused) +" imap (JavaComplete-Imports-RemoveUnused) +" < +" For sorting all imports with : +" > +" nmap (JavaComplete-Imports-SortImports) +" imap (JavaComplete-Imports-SortImports) +" < +" +" Default mappings: +" > +" nmap jI (JavaComplete-Imports-AddMissing) +" nmap jR (JavaComplete-Imports-RemoveUnused) +" nmap ji (JavaComplete-Imports-AddSmart) +" nmap jii (JavaComplete-Imports-Add) +" nmap jis (JavaComplete-Imports-SortImports) +" +" imap I (JavaComplete-Imports-AddMissing) +" imap R (JavaComplete-Imports-RemoveUnused) +" imap i (JavaComplete-Imports-AddSmart) +" imap ii (JavaComplete-Imports-Add) +" +" nmap jM (JavaComplete-Generate-AbstractMethods) +" +" imap jM (JavaComplete-Generate-AbstractMethods) +" +" nmap jA (JavaComplete-Generate-Accessors) +" nmap js (JavaComplete-Generate-AccessorSetter) +" nmap jg (JavaComplete-Generate-AccessorGetter) +" nmap ja (JavaComplete-Generate-AccessorSetterGetter) +" nmap jts (JavaComplete-Generate-ToString) +" nmap jeq (JavaComplete-Generate-EqualsAndHashCode) +" nmap jc (JavaComplete-Generate-Constructor) +" nmap jcc (JavaComplete-Generate-DefaultConstructor) +" +" imap s (JavaComplete-Generate-AccessorSetter) +" imap g (JavaComplete-Generate-AccessorGetter) +" imap a (JavaComplete-Generate-AccessorSetterGetter) +" +" vmap js (JavaComplete-Generate-AccessorSetter) +" vmap jg (JavaComplete-Generate-AccessorGetter) +" vmap ja (JavaComplete-Generate-AccessorSetterGetter) +" < +" +" 4. Javavi library will be automatcally compiled when you +" use first time. +" If no libs/javavi/target is generated, check that you have the write permission +" and jdk installed. + +"" +" @section History, history +" +" This section document the history of `vim-javacomplete2`. +" +" - v2.3.4 2015-12-14 +" +" Use maven, gradle, or Eclipse's 'classpath` file to generate classpath +" +" Complete methods declaration on '@Override'. +" +" - v2.3.3 2015-10-08 +" +" Jsp files support, no taglibs yet. +" +" Vimscript refactored. +" +" Read eclipse ".classpath" file. +" +" Option to set jvm launcher and compiler for javavi server. +" +" Using mappings. +" +" Bug fixes. +" +" - v2.3.2 2015-09-18 +" +" Nested classes. +" +" Vimscript refactored. +" +" - v2.3.1 2015-09-07 +" +" Better experience with imports. +" +" Commands added. +" +" - v2.3 2015-07-29 +" +" Annotations completion support. +" +" Option to swtich use of FQN in completion suggestions. +" +" Check python support before start. +" +" - v2.2 2015-07-08 +" +" Lambda expressions parsing. +" +" - v2.1 2015-06-12 +" +" Generics completion. Bug fixes. +" +" Added g:JavaComplete_MavenRepositoryDisable option. +" +" - v2.0 2015-05-26 +" +" Writed new library for reflection and parsing. Parsing make by +" +" third party library. Library run in server like way. +" +" Added class name completion and insertion of class import. +" +" Added auto classpath searcher. +" +" - 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 +" +" Added GetCurrentFileKey() avoid empty key of s:files for current buffer. +" +" Use a new strategy for searching inherited members. +" +" Supported new contexts "jav|", "var|", just after an incomplete word. +" +" Supported new context "abs(|)", a imported static method. +" +" Improved FoundClassDeclaration() +" +" Fixed bug calling cursor(0, 0) +" +" Rewrote DoGetClassInfo(), GetFQN() and IsFQN() +" +" Fixed a bug when merging superclass's members +" +" Improved s:MergeLines() and s:ExtractCleanExpr(). +" +" Rewrote CompleteAfterDot(). Added ParseExpr(). Removed s:GetNextSubexprType() +" +" Supported accessible static imported members. +" +" Supported accessible inherited members. +" +" Used b:changedtick and getftime() to check buffer (or other file) for changing. +" +" 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. +" +" 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 +" +" 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. +" +" Support accessible nested classes. +" +" Support import static members and import accessible nested classes. +" +" 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. +" +" 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. + +"" +" @section Thanks, thanks +" * Cheng Fang author of original javacomplete plugin; +" * Zhang Li author of vim-javacompleteex plugin; +" * http://github.com/javaparser/javaparser library. +" +" FeedBack: +" Any problem, bug or suggest are welcome to send to ashaihullin@gmail.com + +let s:save_cpo = &cpoptions +set cpoptions&vim + +if exists('g:JavaComplete_PluginLoaded') + finish +endif +let g:JavaComplete_PluginLoaded = 1 + +let g:JavaComplete_IsWindows = javacomplete#util#IsWindows() + +if g:JavaComplete_IsWindows + let g:PATH_SEP = ';' + let g:FILE_SEP = '\' +else + let g:PATH_SEP = ':' + let g:FILE_SEP = '/' +endif + +"" +" @section Options, config +" @parentsection usage +" All these options are supported when encoding with java project. + + +"" +" Base cache directory of javacomplete2 (default is ~/.cache): +" > +" let g:JavaComplete_BaseDir = '~/.your_cache_dir' +" < +let g:JavaComplete_BaseDir = + \ get(g:, 'JavaComplete_BaseDir', expand('~'. g:FILE_SEP. '.cache')) + +"" +" In the import selection the default behavior is to use the first option available: +" > +" let g:JavaComplete_ImportDefault = 0 +" < +" To avoid this behavior use: +" > +" let g:JavaComplete_ImportDefault = -1 +" < +let g:JavaComplete_ImportDefault = + \ get(g:, 'JavaComplete_ImportDefault', 0) +"" +" Import selection is activated automatically when completing new class +" name. This can be avoided by setting: +" > +" let g:JavaComplete_InsertImports = 0 +" < +let g:JavaComplete_InsertImports = + \ get(g:, 'JavaComplete_InsertImports', 1) +"" +" Set the path of gradle executable file. by default it is empty string. +let g:JavaComplete_GradleExecutable = '' +"" +" The Java daemon should kill itself when Vim stops. +" Also its possible to configure the timeout, +" so if there is no request during this time the daemon will stop. +" To configure the timemout use the following (in seconds). +" By default this option is 0. +let g:JavaComplete_ServerAutoShutdownTime = 0 +let g:JavaComplete_ShowExternalCommandsOutput = + \ get(g:, 'JavaComplete_ShowExternalCommandsOutput', 0) + +let g:JavaComplete_ClasspathGenerationOrder = + \ get(g:, 'JavaComplete_ClasspathGenerationOrder', ['Maven', 'Eclipse', 'Gradle', 'Ant']) +"" +" Sorting can by jar archives `jarName` or by package names `packageName`. +" This option is to set the imports sorting type. By default this +" option is `jarName`: +" > +" let g:JavaComplete_ImportSortType = 'jarName' +" < +" +let g:JavaComplete_ImportSortType = + \ get(g:, 'JavaComplete_ImportSortType', 'jarName') +"" +" Specifies the order of import groups, when use `packageName` +" sorting type, for example: +" > +" let g:JavaComplete_ImportOrder = ['java.', 'javax.', 'com.', 'org.', 'net.'] +" < +" An import group is a list of individual import statements that all +" start with the same beginning of package name surrounded by blank +" lines above and below the group. +let g:JavaComplete_ImportOrder = + \ get(g:, 'JavaComplete_ImportOrder', ['java.', 'javax.', 'com.', 'org.', 'net.']) + +let g:JavaComplete_StaticImportsAtTop = + \ get(g:, 'JavaComplete_StaticImportsAtTop', 0) +"" +" Regular class names that will be used automatically +" when you insert import: +" > +" let g:JavaComplete_RegularClasses = ['java.lang.String', 'java.lang.Object'] +" < +" You can populate it with your custom classes, +" or it will be populated automatically when you choose any import option. +" List will be persisted, so it will be used next time you run the same project. +let g:JavaComplete_RegularClasses = + \ get(g:, 'JavaComplete_RegularClasses', ['java.lang.String', 'java.lang.Object', 'java.lang.Exception', 'java.lang.StringBuilder', 'java.lang.Override', 'java.lang.UnsupportedOperationException', 'java.math.BigDecimal', 'java.lang.Byte', 'java.lang.Short', 'java.lang.Integer', 'java.lang.Long', 'java.lang.Float', 'java.lang.Double', 'java.lang.Character', 'java.lang.Boolean']) +"" +" Disable automatic startup of server: +" > +" let g:JavaComplete_AutoStartServer = 0 +" < +" By default this option is disabled (1). +let g:JavaComplete_AutoStartServer = + \ get(g:, 'JavaComplete_AutoStartServer', 1) +"" +" Use fully qualified name in description: +" > +" let g:JavaComplete_UseFQN = 1 +" < +" By default this option is disabled (0). +let g:JavaComplete_UseFQN = get(g:, 'JavaComplete_UseFQN', 0) +"" +" Enable or disable default key mappings, +" by default this option is 1, and default mappings are defined. +" To disable default mappings, set this option to 1. +" > +" let g:JavaComplete_EnableDefaultMappings = 1 +" < +let g:JavaComplete_EnableDefaultMappings = 0 +"" +" Set pom.xml path explicitly: +" > +" let g:JavaComplete_PomPath = /path/to/pom.xml +" < +" It will be set automatically, if pom.xml is in underlying path. +let g:JavaComplete_PomPath = '' +"" +" Close brace on method declaration completion: +" > +" let g:JavaComplete_ClosingBrace = 1 +" < +" Add close brace automatically, when complete method declaration. +" By default this option is enabled (1). +" Disable if it conflicts with another plugins. +let g:JavaComplete_ClosingBrace = 1 + +"" +" Set the directory where to write server logs. By default this option is +" empty. +let g:JavaComplete_JavaviLogDirectory = '' +let g:JavaComplete_CompletionResultSort = + \ get(g:, 'JavaComplete_CompletionResultSort', 0) +"" +" Set directory that contains custom templates for class creation, +" for example: +" > +" let g:JavaComplete_CustomTemplateDirectory = '~/jc_templates' +" < +" By default this options is empty string. +let g:JavaComplete_CustomTemplateDirectory = '' + +"" +" @section Commands, commands +" @parentsection usage +" All these commands are supported when encoding with java project. + +"" +" add all missing 'imports' +command! JCimportsAddMissing call javacomplete#imports#AddMissing() +command! JCDisable call javacomplete#Disable() +command! JCEnable call javacomplete#Enable() +"" +" remove all unsused 'imports' +command! JCimportsRemoveUnused call javacomplete#imports#RemoveUnused() +"" +" add 'import' for classname that is under cursor, or before it +command! JCimportAdd call javacomplete#imports#Add() +"" +" add 'import' for classname trying to guess variant without ask user to choose an option (it will ask on false guessing) +command! JCimportAddSmart call javacomplete#imports#Add(1) +"" +" sort all 'imports' +command! JCimportsSort call javacomplete#imports#SortImports() + +command! JCGetSymbolType call javacomplete#imports#getType() + +"" +" show port, through which vim plugin communicates with server; +command! JCserverShowPort call javacomplete#server#ShowPort() +"" +" show server process identificator; +command! JCserverShowPID call javacomplete#server#ShowPID() +"" +" start server manually; +command! JCserverStart call javacomplete#server#Start() +"" +" stop server manually; +command! JCserverTerminate call javacomplete#server#Terminate() +"" +" compile server manually; +command! JCserverCompile call javacomplete#server#Compile() +command! JCserverLog call javacomplete#server#GetLogContent() +command! JCserverEnableDebug call javacomplete#server#EnableDebug() +command! JCserverEnableTraceDebug call javacomplete#server#EnableTraceDebug() + +"" +" enable logs; +command! JCdebugEnableLogs call javacomplete#logger#Enable() + +"" +" disable logs; +command! JCdebugDisableLogs call javacomplete#logger#Disable() +"" +" get debug logs; +command! JCdebugGetLogContent call javacomplete#logger#GetContent() + +"" +" clear cache manually. +command! JCcacheClear call javacomplete#ClearCache() + +command! JCstart call javacomplete#Start() + +"" +" generate methods that need to be implemented +command! JCgenerateAbstractMethods call javacomplete#generators#AbstractDeclaration() +"" +" generate getters and setters for all fields; +command! JCgenerateAccessors call javacomplete#generators#Accessors() +"" +" generate setter for field under cursor; +command! JCgenerateAccessorSetter call javacomplete#generators#Accessor('s') +"" +" generate getter for field under cursor; +command! JCgenerateAccessorGetter call javacomplete#generators#Accessor('g') +"" +" generate getter and setter for field under cursor; +command! JCgenerateAccessorSetterGetter call javacomplete#generators#Accessor('sg') +"" +" generate 'toString' method; +command! JCgenerateToString call javacomplete#generators#GenerateToString() +"" +" generate 'equals' and 'hashCode' methods; +command! JCgenerateEqualsAndHashCode call javacomplete#generators#GenerateEqualsAndHashCode() +"" +" generate constructor with chosen fields; +command! JCgenerateConstructor call javacomplete#generators#GenerateConstructor(0) +"" +" generate default constructor; +command! JCgenerateConstructorDefault call javacomplete#generators#GenerateConstructor(1) + +command! JCclasspathGenerate call javacomplete#classpath#classpath#RebuildClassPath() + +"" +" open prompt to enter class creation command; +command! JCclassNew call javacomplete#newclass#CreateClass() +"" +" open prompt to choose template that will be used for creation class boilerplate in current empty file; +command! JCclassInFile call javacomplete#newclass#CreateInFile() + +if g:JavaComplete_AutoStartServer + augroup vim_javacomplete2 + autocmd! + autocmd Filetype java,jsp JCstart + augroup END +endif + +function! s:nop(s) + return '' +endfunction + +nnoremap (JavaComplete-Imports-AddMissing) :call javacomplete#imports#AddMissing() +inoremap (JavaComplete-Imports-AddMissing) =nop(javacomplete#imports#AddMissing()) +nnoremap (JavaComplete-Imports-RemoveUnused) :call javacomplete#imports#RemoveUnused() +inoremap (JavaComplete-Imports-RemoveUnused) =nop(javacomplete#imports#RemoveUnused()) +nnoremap (JavaComplete-Imports-Add) :call javacomplete#imports#Add() +inoremap (JavaComplete-Imports-Add) =nop(javacomplete#imports#Add()) +nnoremap (JavaComplete-Imports-AddSmart) :call javacomplete#imports#Add(1) +inoremap (JavaComplete-Imports-AddSmart) =nop(javacomplete#imports#Add(1)) +nnoremap (JavaComplete-Generate-AbstractMethods) :call javacomplete#generators#AbstractDeclaration() +inoremap (JavaComplete-Generate-AbstractMethods) =nop(javacomplete#generators#AbstractDeclaration()) +nnoremap (JavaComplete-Generate-Accessors) :call javacomplete#generators#Accessors() +nnoremap (JavaComplete-Generate-AccessorSetter) :call javacomplete#generators#Accessor('s') +nnoremap (JavaComplete-Generate-AccessorGetter) :call javacomplete#generators#Accessor('g') +nnoremap (JavaComplete-Generate-AccessorSetterGetter) :call javacomplete#generators#Accessor('sg') +inoremap (JavaComplete-Generate-AccessorSetter) =nop(javacomplete#generators#Accessor('s')) +inoremap (JavaComplete-Generate-AccessorGetter) =nop(javacomplete#generators#Accessor('g')) +inoremap (JavaComplete-Generate-AccessorSetterGetter) =nop(javacomplete#generators#Accessor('sg')) +vnoremap (JavaComplete-Generate-AccessorSetter) :call javacomplete#generators#Accessor('s') +vnoremap (JavaComplete-Generate-AccessorGetter) :call javacomplete#generators#Accessor('g') +vnoremap (JavaComplete-Generate-AccessorSetterGetter) :call javacomplete#generators#Accessor('sg') +nnoremap (JavaComplete-Generate-ToString) :call javacomplete#generators#GenerateToString() +nnoremap (JavaComplete-Generate-EqualsAndHashCode) :call javacomplete#generators#GenerateEqualsAndHashCode() +nnoremap (JavaComplete-Generate-Constructor) :call javacomplete#generators#GenerateConstructor(0) +nnoremap (JavaComplete-Generate-DefaultConstructor) :call javacomplete#generators#GenerateConstructor(1) +nnoremap (JavaComplete-Generate-NewClass) :call javacomplete#newclass#CreateClass() +nnoremap (JavaComplete-Generate-ClassInFile) :call javacomplete#newclass#CreateInFile() +nnoremap (JavaComplete-Imports-SortImports) :call javacomplete#imports#SortImports() +inoremap (JavaComplete-Imports-SortImports) =nop(javacomplete#imports#SortImports()) + +let &cpoptions = s:save_cpo +unlet s:save_cpo +augroup vim_javacomplete2 + autocmd User CmSetup call cm#sources#java#register() +augroup END +" vim:set fdm=marker sw=2 nowrap: diff --git a/bundle/vim-javacomplete2/plugin/res/gen__class.tpl b/bundle/vim-javacomplete2/plugin/res/gen__class.tpl new file mode 100644 index 000000000..c607947ce --- /dev/null +++ b/bundle/vim-javacomplete2/plugin/res/gen__class.tpl @@ -0,0 +1,16 @@ +function! s:__class(class, options) + let result = "package ". a:options.package .";\n\n" + let result .= "public class ". a:options.name + if has_key(a:options, 'extends') + let result .= " extends ". a:options['extends'] + endif + if has_key(a:options, 'implements') + let result .= " implements ". a:options['implements'] + endif + let result .= " {\n" + for fieldKey in keys(get(a:options, 'fields', {})) + let field = a:options['fields'][fieldKey] + let result .= field['mod']. " ". field['type']. " ". field['name']. ";\n" + endfor + return result . "\n}" +endfunction diff --git a/bundle/vim-javacomplete2/plugin/res/gen__class_android_activity.tpl b/bundle/vim-javacomplete2/plugin/res/gen__class_android_activity.tpl new file mode 100644 index 000000000..ccf6c2680 --- /dev/null +++ b/bundle/vim-javacomplete2/plugin/res/gen__class_android_activity.tpl @@ -0,0 +1,21 @@ +function! s:__class_android_activity(class, options) + let result = "package ". a:options.package .";\n\n" + let result .= "public class ". a:options.name + if has_key(a:options, 'extends') + let result .= " extends ". a:options['extends'] + else + let result .= " extends Activity" + endif + if has_key(a:options, 'implements') + let result .= " implements ". a:options['implements'] + endif + let result .= " {\n" + for fieldKey in keys(get(a:options, 'fields', {})) + let field = a:options['fields'][fieldKey] + let result .= field['mod']. " ". field['type']. " ". field['name']. ";\n" + endfor + let result .= "\n@Override\n" + let result .= "public void onCreate(Bundle savedInstanceState) {\n" + let result .= "super.onCreate(savedInstanceBundle);\n}\n" + return result . "\n}" +endfunction diff --git a/bundle/vim-javacomplete2/plugin/res/gen__class_android_broadcast_receiver.tpl b/bundle/vim-javacomplete2/plugin/res/gen__class_android_broadcast_receiver.tpl new file mode 100644 index 000000000..aafdfbeee --- /dev/null +++ b/bundle/vim-javacomplete2/plugin/res/gen__class_android_broadcast_receiver.tpl @@ -0,0 +1,21 @@ +function! s:__class_android_broadcast_receiver(class, options) + let result = "package ". a:options.package .";\n\n" + let result .= "public class ". a:options.name + if has_key(a:options, 'extends') + let result .= " extends ". a:options['extends'] + else + let result .= " extends BroadcastReceiver" + endif + if has_key(a:options, 'implements') + let result .= " implements ". a:options['implements'] + endif + let result .= " {\n" + for fieldKey in keys(get(a:options, 'fields', {})) + let field = a:options['fields'][fieldKey] + let result .= field['mod']. " ". field['type']. " ". field['name']. ";\n" + endfor + let result .= "\n@Override\n" + let result .= "public void onReceive(Context context, Intent intent) {\n" + let result .= "return null;\n}\n" + return result . "\n}" +endfunction diff --git a/bundle/vim-javacomplete2/plugin/res/gen__class_android_fragment.tpl b/bundle/vim-javacomplete2/plugin/res/gen__class_android_fragment.tpl new file mode 100644 index 000000000..6adb6c015 --- /dev/null +++ b/bundle/vim-javacomplete2/plugin/res/gen__class_android_fragment.tpl @@ -0,0 +1,21 @@ +function! s:__class_android_fragment(class, options) + let result = "package ". a:options.package .";\n\n" + let result .= "public class ". a:options.name + if has_key(a:options, 'extends') + let result .= " extends ". a:options['extends'] + else + let result .= " extends Fragment" + endif + if has_key(a:options, 'implements') + let result .= " implements ". a:options['implements'] + endif + let result .= " {\n" + for fieldKey in keys(get(a:options, 'fields', {})) + let field = a:options['fields'][fieldKey] + let result .= field['mod']. " ". field['type']. " ". field['name']. ";\n" + endfor + let result .= "\n@Override\n" + let result .= "public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {\n" + let result .= "return null;\n}\n" + return result . "\n}" +endfunction diff --git a/bundle/vim-javacomplete2/plugin/res/gen__class_android_service.tpl b/bundle/vim-javacomplete2/plugin/res/gen__class_android_service.tpl new file mode 100644 index 000000000..dd7492379 --- /dev/null +++ b/bundle/vim-javacomplete2/plugin/res/gen__class_android_service.tpl @@ -0,0 +1,21 @@ +function! s:__class_android_service(class, options) + let result = "package ". a:options.package .";\n\n" + let result .= "public class ". a:options.name + if has_key(a:options, 'extends') + let result .= " extends ". a:options['extends'] + else + let result .= " extends Service" + endif + if has_key(a:options, 'implements') + let result .= " implements ". a:options['implements'] + endif + let result .= " {\n" + for fieldKey in keys(get(a:options, 'fields', {})) + let field = a:options['fields'][fieldKey] + let result .= field['mod']. " ". field['type']. " ". field['name']. ";\n" + endfor + let result .= "\n@Override\n" + let result .= "public IBinder onBind(Intent intent) {\n" + let result .= "return null;\n}\n" + return result . "\n}" +endfunction diff --git a/bundle/vim-javacomplete2/plugin/res/gen__class_annotation.tpl b/bundle/vim-javacomplete2/plugin/res/gen__class_annotation.tpl new file mode 100644 index 000000000..0f5a760cd --- /dev/null +++ b/bundle/vim-javacomplete2/plugin/res/gen__class_annotation.tpl @@ -0,0 +1,16 @@ +function! s:__class_annotation(class, options) + let result = "package ". a:options.package .";\n\n" + let result .= "public @interface ". a:options.name + if has_key(a:options, 'extends') + let result .= " extends ". a:options['extends'] + endif + if has_key(a:options, 'implements') + let result .= " implements ". a:options['implements'] + endif + let result .= " {\n" + for fieldKey in keys(get(a:options, 'fields', {})) + let field = a:options['fields'][fieldKey] + let result .= field['mod']. " ". field['type']. " ". field['name']. ";\n" + endfor + return result . "\n}" +endfunction diff --git a/bundle/vim-javacomplete2/plugin/res/gen__class_enum.tpl b/bundle/vim-javacomplete2/plugin/res/gen__class_enum.tpl new file mode 100644 index 000000000..cfeafb7c7 --- /dev/null +++ b/bundle/vim-javacomplete2/plugin/res/gen__class_enum.tpl @@ -0,0 +1,10 @@ +function! s:__class_enum(class, options) + let result = "package ". a:options.package .";\n\n" + let result .= "public enum ". a:options.name + let result .= " {\n" + for fieldKey in keys(get(a:options, 'fields', {})) + let field = a:options['fields'][fieldKey] + let result .= field['mod']. " ". field['type']. " ". field['name']. ";\n" + endfor + return result . "\n}" +endfunction diff --git a/bundle/vim-javacomplete2/plugin/res/gen__class_exception.tpl b/bundle/vim-javacomplete2/plugin/res/gen__class_exception.tpl new file mode 100644 index 000000000..556f0401e --- /dev/null +++ b/bundle/vim-javacomplete2/plugin/res/gen__class_exception.tpl @@ -0,0 +1,20 @@ +function! s:__class_exception(class, options) + let result = "package ". a:options.package .";\n\n" + let result .= "public class ". a:options.name + if has_key(a:options, 'extends') + let result .= " extends ". a:options['extends'] + else + let result .= " extends Exception" + endif + if has_key(a:options, 'implements') + let result .= " implements ". a:options['implements'] + endif + let result .= " {\n" + for fieldKey in keys(get(a:options, 'fields', {})) + let field = a:options['fields'][fieldKey] + let result .= field['mod']. " ". field['type']. " ". field['name']. ";\n" + endfor + let result .= "\npublic ". a:options.name. "() {\n\n}\n" + let result .= "\npublic ". a:options.name. "(String msg) {\nsuper(msg);\n}\n" + return result . "\n}" +endfunction diff --git a/bundle/vim-javacomplete2/plugin/res/gen__class_interface.tpl b/bundle/vim-javacomplete2/plugin/res/gen__class_interface.tpl new file mode 100644 index 000000000..67299ddc1 --- /dev/null +++ b/bundle/vim-javacomplete2/plugin/res/gen__class_interface.tpl @@ -0,0 +1,13 @@ +function! s:__class_interface(class, options) + let result = "package ". a:options.package .";\n\n" + let result .= "public interface ". a:options.name + if has_key(a:options, 'extends') + let result .= " extends ". a:options['extends'] + endif + let result .= " {\n" + for fieldKey in keys(get(a:options, 'fields', {})) + let field = a:options['fields'][fieldKey] + let result .= field['mod']. " ". field['type']. " ". field['name']. "();\n" + endfor + return result . "\n}" +endfunction diff --git a/bundle/vim-javacomplete2/plugin/res/gen__class_junit.tpl b/bundle/vim-javacomplete2/plugin/res/gen__class_junit.tpl new file mode 100644 index 000000000..7f76a8dac --- /dev/null +++ b/bundle/vim-javacomplete2/plugin/res/gen__class_junit.tpl @@ -0,0 +1,18 @@ +function! s:__class_junit(class, options) + let result = "package ". a:options.package .";\n\n" + let result .= "import static org.junit.Assert.*;\n\n" + let result .= "public class ". a:options.name + if has_key(a:options, 'extends') + let result .= " extends ". a:options['extends'] + endif + if has_key(a:options, 'implements') + let result .= " implements ". a:options['implements'] + endif + let result .= " {\n" + for fieldKey in keys(get(a:options, 'fields', {})) + let field = a:options['fields'][fieldKey] + let result .= field['mod']. " ". field['type']. " ". field['name']. ";\n" + endfor + let result .= "\n@Before\npublic void setUp() {\n\n}\n" + return result . "\n}" +endfunction diff --git a/bundle/vim-javacomplete2/plugin/res/gen__class_main.tpl b/bundle/vim-javacomplete2/plugin/res/gen__class_main.tpl new file mode 100644 index 000000000..86912f0a1 --- /dev/null +++ b/bundle/vim-javacomplete2/plugin/res/gen__class_main.tpl @@ -0,0 +1,17 @@ +function! s:__class_main(class, options) + let result = "package ". a:options.package .";\n\n" + let result .= "public class ". a:options.name + if has_key(a:options, 'extends') + let result .= " extends ". a:options['extends'] + endif + if has_key(a:options, 'implements') + let result .= " implements ". a:options['implements'] + endif + let result .= " {\n" + for fieldKey in keys(get(a:options, 'fields', {})) + let field = a:options['fields'][fieldKey] + let result .= field['mod']. " ". field['type']. " ". field['name']. ";\n" + endfor + let result .= "\npublic static void main(String[] args) {\n\n}\n" + return result . "\n}" +endfunction diff --git a/bundle/vim-javacomplete2/plugin/res/gen__class_servlet.tpl b/bundle/vim-javacomplete2/plugin/res/gen__class_servlet.tpl new file mode 100644 index 000000000..06004c515 --- /dev/null +++ b/bundle/vim-javacomplete2/plugin/res/gen__class_servlet.tpl @@ -0,0 +1,45 @@ +function! s:__class_servlet(class, options) + let name = a:options.name + let url = tolower(substitute(name, '\C\([A-Z]\)', '/\1', 'g')) + let result = "package ". a:options.package .";\n\n" + let result .= "@WebServlet(name = \"". name. "\", urlPatterns = {\"". url. "\"})\n" + let result .= "public class ". name + if has_key(a:options, 'extends') + let result .= " extends ". a:options['extends'] + else + let result .= " extends HttpServlet" + endif + if has_key(a:options, 'implements') + let result .= " implements ". a:options['implements'] + endif + let result .= " {\n" + for fieldKey in keys(get(a:options, 'fields', {})) + let field = a:options['fields'][fieldKey] + let result .= field['mod']. " ". field['type']. " ". field['name']. ";\n" + endfor + let result .= "\nprotected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {\n" + let result .= "response.setContentType(\"text/html;charset=UTF-8\");\n" + let result .= "try (PrintWriter out = response.getWriter()) {\n" + let result .= "try (PrintWriter out = response.getWriter()) {\n" + let result .= "out.println(\"\");\n" + let result .= "out.println(\"\");\n" + let result .= "out.println(\"Servlet ". name. "\"); \n" + let result .= "out.println(\"\");\n" + let result .= "out.println(\"\");\n" + let result .= "out.println(\"

Servlet ". name. " at ". url. "

\");\n" + let result .= "out.println(\"\");\n" + let result .= "out.println(\"\");\n" + let result .= "} finally {\n" + let result .= "out.close();\n" + let result .= "}\n" + let result .= "}\n" + let result .= "}\n" + let result .= "\nprotected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {\n" + let result .= "processRequest(request, response);\n" + let result .= "}\n" + let result .= "\nprotected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {\n" + let result .= "processRequest(request, response);\n" + let result .= "}\n" + return result . "\n}" +endfunction diff --git a/bundle/vim-javacomplete2/plugin/res/gen__class_singleton.tpl b/bundle/vim-javacomplete2/plugin/res/gen__class_singleton.tpl new file mode 100644 index 000000000..720651a52 --- /dev/null +++ b/bundle/vim-javacomplete2/plugin/res/gen__class_singleton.tpl @@ -0,0 +1,24 @@ +function! s:__class_singleton(class, options) + let result = "package ". a:options.package .";\n\n" + let result .= "public class ". a:options.name + if has_key(a:options, 'extends') + let result .= " extends ". a:options['extends'] + endif + if has_key(a:options, 'implements') + let result .= " implements ". a:options['implements'] + endif + let result .= " {\n" + for fieldKey in keys(get(a:options, 'fields', {})) + let field = a:options['fields'][fieldKey] + let result .= field['mod']. " ". field['type']. " ". field['name']. ";\n" + endfor + let name = a:options['name'] + let result .= "\nprivate ". name. "() {\n\n}\n" + let result .= "\npublic static ". name. " getInstance() {\n" + let result .= "return ". name. "Holder.INSTANCE;\n" + let result .= "}\n" + let result .= "\nprivate static class ". name. "Holder {\n" + let result .= "private static final ". name. " INSTANCE = new ". name. "();\n" + let result .= "}\n" + return result . "\n}" +endfunction diff --git a/bundle/vim-javacomplete2/plugin/res/gen__constructor.tpl b/bundle/vim-javacomplete2/plugin/res/gen__constructor.tpl new file mode 100644 index 000000000..4a2d8e8cf --- /dev/null +++ b/bundle/vim-javacomplete2/plugin/res/gen__constructor.tpl @@ -0,0 +1,18 @@ +function! s:__constructor(class, ...) + let parameters = "" + let body = "" + let idx = 0 + if a:0 == 0 || a:1.default != 1 + for field in a:class.fields + if idx != 0 + let parameters .= ", " + endif + let parameters .= field.type . " ". field.name + let body .= "this.". field.name ." = ". field.name .";\n" + let idx += 1 + endfor + endif + let result = "public ". a:class.name ."(". parameters. ") {\n" + let result .= body + return result . "}" +endfunction diff --git a/bundle/vim-javacomplete2/plugin/res/gen__equals.tpl b/bundle/vim-javacomplete2/plugin/res/gen__equals.tpl new file mode 100644 index 000000000..632931759 --- /dev/null +++ b/bundle/vim-javacomplete2/plugin/res/gen__equals.tpl @@ -0,0 +1,35 @@ +function! s:__equals(class) + let result = "@Override\n" + let result .= "public boolean equals(Object o) {\n" + let result .= "if (this == o) return true;\n" + let result .= "if (o == null || getClass() != o.getClass()) return false;\n\n" + let result .= a:class.name ." object = (". a:class.name .") o;\n\n" + let idx = 0 + for field in a:class.fields + if idx != len(a:class.fields) - 1 + let result .= "if " + else + let result .= "return !" + endif + if index(g:J_PRIMITIVE_TYPES, field.type) > -1 + if field.type == "double" + let result .= "(Double.compare(". field.name .", object.". field.name .") != 0)" + elseif field.type == "float" + let result .= "(Float.compare(". field.name .", object.". field.name .") != 0)" + else + let result .= "(". field.name ." != object.". field.name .")" + endif + elseif field.isArray + let result .= "(!java.util.Arrays.equals(". field.name .", object.". field.name ."))" + else + let result .= "(". field.name ." != null ? !". field.name .".equals(object.". field.name .") : object.". field.name ." != null)" + endif + if idx != len(a:class.fields) - 1 + let result .= " return false;\n" + else + let result .= ";\n" + endif + let idx += 1 + endfor + return result. "}" +endfunction' diff --git a/bundle/vim-javacomplete2/plugin/res/gen__hashCode.tpl b/bundle/vim-javacomplete2/plugin/res/gen__hashCode.tpl new file mode 100644 index 000000000..70fc1ec7a --- /dev/null +++ b/bundle/vim-javacomplete2/plugin/res/gen__hashCode.tpl @@ -0,0 +1,26 @@ +function! s:__hashCode(class) + let result = "@Override\n" + let result .= "public int hashCode() {\n" + let result .= "int result = 17;\n" + for field in a:class.fields + if index(g:J_PRIMITIVE_TYPES, field.type) > -1 + if field.type == "boolean" + let result .= "result = 31 * result + (". field.name . " ? 0 : 1);\n" + elseif field.type == "long" + let result .= "result = 31 * result + (int)(". field.name . " ^ (". field.name . " >>> 32));\n" + elseif field.type == "float" + let result .= "result = 31 * result + Float.floatToIntBits(". field.name . ");\n" + elseif field.type == "double" + let result .= "long ". field.name . "Long = Double.doubleToLongBits(". field.name .");\n" + let result .= "result = 31 * result + (int)(". field.name . "Long ^ (". field.name . "Long >>> 32));\n" + else + let result .= "result = 31 * result + (int)". field.name . ";\n" + endif + elseif field.isArray + let result .= "result = 31 * result + java.util.Arrays.hashCode(". field.name . ");\n" + else + let result .= "result = 31 * result + (". field.name . " != null ? ". field.name .".hashCode() : 0);\n" + endif + endfor + return result. "return result;\n}" +endfunction' diff --git a/bundle/vim-javacomplete2/plugin/res/gen__toString_StringBuilder.tpl b/bundle/vim-javacomplete2/plugin/res/gen__toString_StringBuilder.tpl new file mode 100644 index 000000000..9def8a272 --- /dev/null +++ b/bundle/vim-javacomplete2/plugin/res/gen__toString_StringBuilder.tpl @@ -0,0 +1,22 @@ +function! s:__toString_StringBuilder(class) + let result = "@Override\n" + let result .= "public String toString() {\n" + let result .= "final StringBuilder sb = new StringBuilder(\"". a:class.name . "{\");\n" + let i = 0 + for field in a:class.fields + if i > 0 + let result .= "\nsb.append(\", " + else + let result .= "sb.append(\"" + let i += 1 + endif + if has_key(field, "getter") + let f = field.getter + else + let f = field.name + endif + let f = field.isArray ? "java.util.Arrays.toString(". f .")" : f + let result .= field.name ." = \").append(". f. ");" + endfor + return result . "\nreturn sb.append(\"}\").toString();\n}" +endfunction' diff --git a/bundle/vim-javacomplete2/plugin/res/gen__toString_concat.tpl b/bundle/vim-javacomplete2/plugin/res/gen__toString_concat.tpl new file mode 100644 index 000000000..b566ea01c --- /dev/null +++ b/bundle/vim-javacomplete2/plugin/res/gen__toString_concat.tpl @@ -0,0 +1,22 @@ +function! s:__toString_concat(class) + let result = "@Override\n" + let result .= "public String toString() {\n" + let result .= "return \"". a:class.name ."{\" +\n" + let i = 0 + for field in a:class.fields + if i > 0 + let result .= "\n\", " + else + let result .= "\"" + let i += 1 + endif + if has_key(field, "getter") + let f = field.getter + else + let f = field.name + endif + let f = field.isArray ? "java.util.Arrays.toString(". f .")" : f + let result .= field.name ." = \" + ". f. " +" + endfor + return result . "\n\"}\";\n}" +endfunction' diff --git a/bundle/vim-javacomplete2/rplugin/python3/deoplete/sources/javacomplete2.py b/bundle/vim-javacomplete2/rplugin/python3/deoplete/sources/javacomplete2.py new file mode 100644 index 000000000..396a23b57 --- /dev/null +++ b/bundle/vim-javacomplete2/rplugin/python3/deoplete/sources/javacomplete2.py @@ -0,0 +1,27 @@ +import pynvim +from deoplete.base.source import Base + +class Source(Base): + def __init__(self, vim): + Base.__init__(self, vim) + + self.name = 'javacomplete2' + self.mark = '[jc]' + self.filetypes = ['java', 'jsp'] + self.is_bytepos = True + self.input_pattern = '[^. \t0-9]\.\w*' + self.rank = 500 + self.max_pattern_length = -1 + self.matchers = ['matcher_full_fuzzy'] + + def get_complete_position(self, context): + return self.vim.call('javacomplete#complete#complete#Complete', + 1, '', 0) + + def gather_candidates(self, context): + try: + return self.vim.call('javacomplete#complete#complete#Complete', + 0, context['complete_str'], 0) + except pynvim.api.common.NvimError as er: + print(er) + raise er diff --git a/bundle/vim-javacomplete2/t/collector.vim b/bundle/vim-javacomplete2/t/collector.vim new file mode 100644 index 000000000..64f4395a3 --- /dev/null +++ b/bundle/vim-javacomplete2/t/collector.vim @@ -0,0 +1,75 @@ +source plugin/javacomplete.vim +source autoload/javacomplete.vim +source autoload/javacomplete/collector.vim +source t/javacomplete.vim + +call vspec#hint({'sid': 'g:SID("collector")', 'scope': 'SScope()'}) + +describe 'javacomplete-test' + it 'CollectFQNs test' + Expect Call('s:CollectFQNs', 'List', 'kg.ash.foo', '', '') == ['kg.ash.foo.List','java.lang.List', 'java.lang.Object'] + Expect Call('s:CollectFQNs', 'java.util.List', 'kg.ash.foo', '', '') == ['java.util.List'] + Expect Call('s:CollectFQNs', 'Object', 'kg.ash.foo', '', '') == ['kg.ash.foo.Object','java.lang.Object'] + + new + source autoload/javacomplete.vim + put ='import java.util.List;' + Expect Call('s:CollectFQNs', 'List', '', '', '') == ['java.util.List'] + + new + source autoload/javacomplete.vim + put ='import java.util.*;' + put ='import java.foo.*;' + Expect Call('s:CollectFQNs', 'List', '', '', '') == ['List', 'java.lang.List', 'java.util.List', 'java.foo.List', 'java.lang.Object'] + Expect Call('s:CollectFQNs', 'List', 'kg.ash.foo', '', '') == ['kg.ash.foo.List', 'java.lang.List', 'java.util.List', 'java.foo.List', 'java.lang.Object'] + end + + it 'GetPackageName test' + + Expect Call('javacomplete#collector#GetPackageName') == '' + + new + put ='package foo.bar.baz' + Expect Call('javacomplete#collector#GetPackageName') == '' + + new + put ='package foo.bar.baz;' + Expect Call('javacomplete#collector#GetPackageName') == 'foo.bar.baz' + end + + it 'CollectTypeArguments test' + source autoload/javacomplete.vim + Expect Call('s:CollectTypeArguments', '', '', '') == '' + + Expect Call('s:CollectTypeArguments', 'Integer', '', '') == '<(Integer|java.lang.Integer|java.lang.Object)>' + Expect Call('s:CollectTypeArguments', 'Integer[]', '', '') == '<(Integer[]|java.lang.Integer[]|java.lang.Object)>' + Expect Call('s:CollectTypeArguments', '? super Integer[]', '', '') == '<(Integer[]|java.lang.Integer[]|java.lang.Object)>' + + new + source autoload/javacomplete.vim + put ='import java.util.List;' + put ='import java.util.HashMap;' + Expect Call('s:CollectTypeArguments', 'List>', '', '') == '>>' + Expect Call('s:CollectTypeArguments', 'HashMap', '', '') == '>' + Expect Call('s:CollectTypeArguments', 'String,BigDecimal', '', '') == '<(String|java.lang.String|java.lang.Object),(BigDecimal|java.lang.BigDecimal|java.lang.Object)>' + put ='import java.math.BigDecimal;' + Expect Call('s:CollectTypeArguments', 'String,BigDecimal', '', '') == '<(String|java.lang.String|java.lang.Object),java.math.BigDecimal>' + + Expect Call('s:CollectTypeArguments', 'MyClass', '', '') == '<(MyClass|java.lang.MyClass|java.lang.Object)>' + Expect Call('s:CollectTypeArguments', 'MyClass', 'foo.bar.baz', '') == '<(foo.bar.baz.MyClass|java.lang.MyClass|java.lang.Object)>' + + Expect Call('s:CollectTypeArguments', 'Object,Object,String', 'kg.test', '') == '<(kg.test.Object|java.lang.Object),(kg.test.Object|java.lang.Object),(kg.test.String|java.lang.String|java.lang.Object)>' + end + + it 'SplitTypeArguments test' + Expect Call('s:SplitTypeArguments', 'java.util.List') == ['java.util.List', 'Integer'] + Expect Call('s:SplitTypeArguments', 'java.util.List') == ['java.util.List', 'java.lang.Integer'] + Expect Call('s:SplitTypeArguments', 'java.util.HashMap') == ['java.util.HashMap', 'Integer,String'] + Expect Call('s:SplitTypeArguments', 'java.util.List') == ['java.util.List', '? extends java.lang.Integer[]'] + Expect Call('s:SplitTypeArguments', 'List') == ['List', '? extends java.lang.Integer[]'] + Expect Call('s:SplitTypeArguments', 'java.util.HashMap') == ['java.util.HashMap', '? super Integer,? extends String'] + Expect Call('s:SplitTypeArguments', 'java.util.function.ToIntFunction') == ['java.util.function.ToIntFunction', '? super java.lang.Integer[]'] + Expect Call('s:SplitTypeArguments', 'java.lang.Class') == ['java.lang.Class', 0] + end + +end diff --git a/bundle/vim-javacomplete2/t/complete.vim b/bundle/vim-javacomplete2/t/complete.vim new file mode 100644 index 000000000..605046f04 --- /dev/null +++ b/bundle/vim-javacomplete2/t/complete.vim @@ -0,0 +1,21 @@ +source plugin/javacomplete.vim +source autoload/javacomplete.vim +source autoload/javacomplete/complete/complete.vim +source t/javacomplete.vim + +call vspec#hint({'sid': 'g:SID("complete/complete")', 'scope': 'SScope()'}) + +describe 'javacomplete-test' + it 'Regexps test' + let reTypeArgument = g:RE_TYPE_ARGUMENT + let reTypeArgumentExtends = g:RE_TYPE_ARGUMENT_EXTENDS + Expect 'Integer[]' =~ reTypeArgument + Expect 'Integer[]' !~ reTypeArgumentExtends + Expect '? super Integer[]' =~ reTypeArgument + Expect '? super Integer[]' =~ reTypeArgumentExtends + + let qualid = g:RE_QUALID + Expect 'java.util.function.ToIntFunction' =~ '^\s*' . qualid . '\s*$' + end + +end diff --git a/bundle/vim-javacomplete2/t/data/LambdaAnonClass.java b/bundle/vim-javacomplete2/t/data/LambdaAnonClass.java new file mode 100644 index 000000000..28477df72 --- /dev/null +++ b/bundle/vim-javacomplete2/t/data/LambdaAnonClass.java @@ -0,0 +1,24 @@ +package kg.ash.demo; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.math.BigDecimal; + +public class LambdaAnonClass { + + public LambdaAnonClass() { + List lint = new ArrayList<>(); + + lint.stream().filter(t -> { + Integer i = new Integer(); + t.; + }); + + lint.stream().filter((t, d) -> { + Integer i = new Integer(); + d. + }); + } + +} diff --git a/bundle/vim-javacomplete2/t/data/LambdaNamedClass.java b/bundle/vim-javacomplete2/t/data/LambdaNamedClass.java new file mode 100644 index 000000000..8a8f0f798 --- /dev/null +++ b/bundle/vim-javacomplete2/t/data/LambdaNamedClass.java @@ -0,0 +1,23 @@ +package kg.ash.demo; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.math.BigDecimal; + +public class LambdaNamedClass { + + public LambdaNamedClass() { + List lint = new ArrayList<>(); + List>> list + = new ArrayList<>(); + + lint.stream().filter((String t, BigDecimal d) -> { + Integer i = new Integer(); + t. + d. + }); + + } + +} diff --git a/bundle/vim-javacomplete2/t/data/LambdaReturnClass.java b/bundle/vim-javacomplete2/t/data/LambdaReturnClass.java new file mode 100644 index 000000000..0cf231086 --- /dev/null +++ b/bundle/vim-javacomplete2/t/data/LambdaReturnClass.java @@ -0,0 +1,11 @@ +package kg.ash.javavi; + +import java.util.function.Predicate; + +public class LambdaReturnClass { + + public Predicate getCondition() { + return p -> p. + } + +} diff --git a/bundle/vim-javacomplete2/t/imports.vim b/bundle/vim-javacomplete2/t/imports.vim new file mode 100644 index 000000000..7a23b085f --- /dev/null +++ b/bundle/vim-javacomplete2/t/imports.vim @@ -0,0 +1,44 @@ +source autoload/javacomplete/imports.vim +source plugin/javacomplete.vim +source t/javacomplete.vim + +call vspec#hint({'sid': 'g:SID("imports")', 'scope': 'SScope()'}) + +describe 'javacomplete imports test' + it 'AddImport test' + new + source autoload/javacomplete.vim + put! ='package kg.ash.foo;' + + call Call('s:AddImport', 'java.util.List') + Expect getline(3) == 'import java.util.List;' + + call Call('s:AddImport', 'java.util.ArrayList') + Expect getline(3) == 'import java.util.List;' + + call Call('s:AddImport', 'foo.bar.Baz') + Expect getline(5) == 'import foo.bar.Baz;' + + call Call('s:AddImport', 'zoo.bar.Baz') + Expect getline(5) == 'import zoo.bar.Baz;' + + call Call('s:AddImport', 'zoo.bar.Baz') + Expect getline(5) == 'import zoo.bar.Baz;' + + new + + source autoload/javacomplete.vim + call Call('s:AddImport', 'java.util.List') + Expect getline(1) == 'import java.util.List;' + + call Call('s:AddImport', 'java.util.ArrayList') + Expect getline(2) == 'import java.util.ArrayList;' + + call Call('s:AddImport', 'foo.bar.Baz') + Expect getline(3) == 'import foo.bar.Baz;' + + call Call('s:AddImport', 'zoo.bar.Baz') + Expect getline(3) == 'import zoo.bar.Baz;' + + end +end diff --git a/bundle/vim-javacomplete2/t/java_parser.vim b/bundle/vim-javacomplete2/t/java_parser.vim new file mode 100644 index 000000000..caef2013b --- /dev/null +++ b/bundle/vim-javacomplete2/t/java_parser.vim @@ -0,0 +1,48 @@ +source autoload/java_parser.vim +source t/javacomplete.vim + +function! SID() abort + redir => l:scriptnames + silent scriptnames + redir END + for line in split(l:scriptnames, '\n') + let [l:sid, l:path] = matchlist(line, '^\s*\(\d\+\):\s*\(.*\)$')[1:2] + if l:path =~# '\' . l:sid . '_' + endif + endfor +endfunction +call vspec#hint({'sid': 'SID()', 'scope': 'java_parser#SScope()'}) + + +describe 'javaparser test' + it 'test' + call Call('java_parser#InitParser', []) + call Call('java_parser#compilationUnit') + Expect Call('java_parser#expression') == {"tag": "ERRONEOUS", "errs": [], "pos": 0} + + let source = ['package foo.bar.baz;', + \ 'public class DemoClass {', + \ ' String str = new String();', + \ ' public DemoClass() {', + \ ' Integer in = new Integer();', + \ ' }', + \ '}'] + + call Call('java_parser#InitParser', source) + Expect Call('java_parser#compilationUnit') != 0 + call Call('java_parser#GotoPosition', '96') + Expect Call('java_parser#statement') != 0 + Expect Call('java_parser#nextToken') == 0 + end + + it 'GetInnerText test' + let tree = Call('javacomplete#parseradapter#Parse', 't/data/LambdaNamedClass.java') + + call Call('java_parser#GotoPosition', 380) + Expect Call('s:GetInnerText', '(') == '(String t, BigDecimal d)' + + call Call('java_parser#GotoPosition', 400) + Expect Call('s:GetInnerText', '{') == '{ Integer i = new Integer(); t. d. }' + end +end diff --git a/bundle/vim-javacomplete2/t/javacomplete.vim b/bundle/vim-javacomplete2/t/javacomplete.vim new file mode 100644 index 000000000..f6db623bc --- /dev/null +++ b/bundle/vim-javacomplete2/t/javacomplete.vim @@ -0,0 +1,15 @@ +source plugin/javacomplete.vim +source autoload/javacomplete.vim +source autoload/java_parser.vim + +function! g:SID(file) abort + redir => l:scriptnames + silent scriptnames + redir END + for line in split(l:scriptnames, '\n') + let [l:sid, l:path] = matchlist(line, '^\s*\(\d\+\):\s*\(.*\)$')[1:2] + if l:path =~# '\' . l:sid . '_' + endif + endfor +endfunction diff --git a/bundle/vim-javacomplete2/t/newclass.vim b/bundle/vim-javacomplete2/t/newclass.vim new file mode 100644 index 000000000..c385e2895 --- /dev/null +++ b/bundle/vim-javacomplete2/t/newclass.vim @@ -0,0 +1,317 @@ +source plugin/javacomplete.vim +source autoload/javacomplete.vim +source autoload/javacomplete/newclass.vim +source t/javacomplete.vim + +call vspec#hint({'sid': 'g:SID("newclass")', 'scope': 'SScope()'}) + +describe 'javacomplete-test' + before + let b:currentPath = reverse(split('/home/foo/project/src/kg/foo/bar/CurrentClass.java', '/')[:-2]) + end + + it 'ParseInput same package class test' + Expect Call('s:ParseInput', + \ 'NewClass', + \ b:currentPath, + \ split('kg.foo.bar', '\.')) + \ == + \ { + \ 'path' : '', + \ 'class' : 'NewClass', + \ 'package' : 'kg.foo.bar' + \ } + end + + it 'ParseInput root path search test' + Expect Call('s:ParseInput', + \ '/foo.baz.NewClass', + \ b:currentPath, + \ split('kg.foo.bar', '\.')) + \ == + \ { + \ 'path' : '../baz', + \ 'class' : 'NewClass', + \ 'package' : 'kg.foo.baz' + \ } + Expect Call('s:ParseInput', + \ '/foo.baz.NewClass', + \ b:currentPath, + \ split('kg.foo.bar', '\.')) + \ == + \ { + \ 'path' : '../baz', + \ 'class' : 'NewClass', + \ 'package' : 'kg.foo.baz' + \ } + Expect Call('s:ParseInput', + \ '/.org.foo.baz.NewClass', + \ b:currentPath, + \ split('kg.foo.bar', '\.')) + \ == + \ { + \ 'path' : '../../../org/foo/baz', + \ 'class' : 'NewClass', + \ 'package' : 'org.foo.baz' + \ } + Expect Call('s:ParseInput', + \ '/.foo.baz.bar.NewClass', + \ b:currentPath, + \ split('kg.foo.bar', '\.')) + \ == + \ { + \ 'path' : '../baz/bar', + \ 'class' : 'NewClass', + \ 'package' : 'kg.foo.baz.bar' + \ } + Expect Call('s:ParseInput', + \ '/bar.NewClass', + \ b:currentPath, + \ split('kg.foo.bar', '\.')) + \ == + \ { + \ 'path' : '', + \ 'class' : 'NewClass', + \ 'package' : 'kg.foo.bar' + \ } + end + + it 'ParseInput relative path test' + Expect Call('s:ParseInput', + \ 'foo.baz.NewClass', + \ b:currentPath, + \ split('kg.foo.bar', '\.')) + \ == + \ { + \ 'path' : 'foo/baz', + \ 'class' : 'NewClass', + \ 'package' : 'kg.foo.bar.foo.baz' + \ } + end + + it 'ParseInput relative path test' + let currentPath = reverse(split('/home/foo/project/src/kg/foo/bar/baz/bad/bas/CurrentClass.java', '/')[:-2]) + Expect Call('s:ParseInput', + \ '/bar.fee.NewClass', + \ currentPath, + \ split('kg.foo.bar.baz.bad.bas', '\.')) + \ == + \ { + \ 'path' : '../../../fee', + \ 'class' : 'NewClass', + \ 'package' : 'kg.foo.bar.fee' + \ } + Expect Call('s:ParseInput', + \ '/bad.NewClass', + \ currentPath, + \ split('kg.foo.bar.baz.bad.bas', '\.')) + \ == + \ { + \ 'path' : '../', + \ 'class' : 'NewClass', + \ 'package' : 'kg.foo.bar.baz.bad' + \ } + end + + it 'ParseInput wrong package' + Expect Call('s:ParseInput', + \ '/foo.baz.NewClass', + \ b:currentPath, + \ split('kf.foo.bar', '\.')) + \ == + \ { + \ 'path' : 'foo/baz', + \ 'class' : 'NewClass', + \ 'package' : 'kf.foo.bar.foo.baz' + \ } + end + + it 'ParseInput class with fields' + Expect Call('s:ParseInput', + \ '/foo.baz.NewClass(String foo, public static Integer bar, public final static int CONSTANT)', + \ b:currentPath, + \ split('kg.foo.bar', '\.')) + \ == + \ { + \ 'path' : '../baz', + \ 'class' : 'NewClass', + \ 'package' : 'kg.foo.baz', + \ 'fields' : { + \ '1' : { + \ 'mod' : 'private', + \ 'type' : 'String', + \ 'name' : 'foo' + \ }, + \ '2' : { + \ 'mod' : 'public static', + \ 'type' : 'Integer', + \ 'name' : 'bar' + \ }, + \ '3' : { + \ 'mod' : 'public final static', + \ 'type' : 'int', + \ 'name' : 'CONSTANT' + \ } + \ } + \ } + end + + it 'ParseInput class extends/implements' + Expect Call('s:ParseInput', + \ '/foo.baz.NewClass extends kg.FooClass', + \ b:currentPath, + \ split('kg.foo.bar', '\.')) + \ == + \ { + \ 'path' : '../baz', + \ 'class' : 'NewClass', + \ 'package' : 'kg.foo.baz', + \ 'extends' : 'kg.FooClass' + \ } + Expect Call('s:ParseInput', + \ '/foo.baz.NewClass implements kg.FooClassIFace', + \ b:currentPath, + \ split('kg.foo.bar', '\.')) + \ == + \ { + \ 'path' : '../baz', + \ 'class' : 'NewClass', + \ 'package' : 'kg.foo.baz', + \ 'implements' : 'kg.FooClassIFace' + \ } + Expect Call('s:ParseInput', + \ '/foo.baz.NewClass extends FooClass implements kg.FooClassIFace', + \ b:currentPath, + \ split('kg.foo.bar', '\.')) + \ == + \ { + \ 'path' : '../baz', + \ 'class' : 'NewClass', + \ 'package' : 'kg.foo.baz', + \ 'extends' : 'FooClass', + \ 'implements' : 'kg.FooClassIFace' + \ } + end + + it 'ParseInput class with methods' + Expect Call('s:ParseInput', + \ '/foo.baz.NewClass:constructor', + \ b:currentPath, + \ split('kg.foo.bar', '\.')) + \ == + \ { + \ 'path' : '../baz', + \ 'class' : 'NewClass', + \ 'package' : 'kg.foo.baz', + \ 'methods' : { + \ 'constructor' : [] + \ } + \ } + Expect Call('s:ParseInput', + \ '/foo.baz.NewClass:constructor(*)', + \ b:currentPath, + \ split('kg.foo.bar', '\.')) + \ == + \ { + \ 'path' : '../baz', + \ 'class' : 'NewClass', + \ 'package' : 'kg.foo.baz', + \ 'methods' : { + \ 'constructor' : ['*'] + \ } + \ } + Expect Call('s:ParseInput', + \ '/foo.baz.NewClass(String one, Integer two):constructor(1,2)', + \ b:currentPath, + \ split('kg.foo.bar', '\.')) + \ == + \ { + \ 'path' : '../baz', + \ 'class' : 'NewClass', + \ 'package' : 'kg.foo.baz', + \ 'fields' : { + \ '1' : { + \ 'mod' : 'private', + \ 'type' : 'String', + \ 'name' : 'one' + \ }, + \ '2' : { + \ 'mod' : 'private', + \ 'type' : 'Integer', + \ 'name' : 'two' + \ } + \ }, + \ 'methods' : { + \ 'constructor' : [1, 2] + \ } + \ } + Expect Call('s:ParseInput', + \ '/foo.baz.NewClass(String one, Integer two):constructor:toString(2):hashCode(1)', + \ b:currentPath, + \ split('kg.foo.bar', '\.')) + \ == + \ { + \ 'path' : '../baz', + \ 'class' : 'NewClass', + \ 'package' : 'kg.foo.baz', + \ 'fields' : { + \ '1' : { + \ 'mod' : 'private', + \ 'type' : 'String', + \ 'name' : 'one' + \ }, + \ '2' : { + \ 'mod' : 'private', + \ 'type' : 'Integer', + \ 'name' : 'two' + \ } + \ }, + \ 'methods' : { + \ 'constructor' : [], + \ 'toString' : [2], + \ 'hashCode' : [1] + \ } + \ } + end + + it 'ParseInput template test' + Expect Call('s:ParseInput', + \ 'interface:foo.baz.NewClass', + \ b:currentPath, + \ split('kg.foo.bar', '\.')) + \ == + \ { + \ 'path' : 'foo/baz', + \ 'class' : 'NewClass', + \ 'package' : 'kg.foo.bar.foo.baz', + \ 'template' : 'interface' + \ } + end + + it 'ParseInput subdir test' + Expect Call('s:ParseInput', + \ '[test]:NewClass', + \ b:currentPath, + \ split('kg.foo.bar', '\.')) + \ == + \ { + \ 'path' : '../../../test/java/kg/foo/bar/', + \ 'class' : 'NewClass', + \ 'package' : 'kg.foo.bar' + \ } + end + + it 'ParseInput template with subdir test' + Expect Call('s:ParseInput', + \ 'junit5:[test]:/foo.baz.NewClass', + \ b:currentPath, + \ split('kg.foo.bar', '\.')) + \ == + \ { + \ 'path' : '../../../test/java/kg/foo/bar/../baz', + \ 'class' : 'NewClass', + \ 'package' : 'kg.foo.baz', + \ 'template' : 'junit5' + \ } + end +end diff --git a/bundle/vim-javacomplete2/t/parseradapter.vim b/bundle/vim-javacomplete2/t/parseradapter.vim new file mode 100644 index 000000000..19a05dc17 --- /dev/null +++ b/bundle/vim-javacomplete2/t/parseradapter.vim @@ -0,0 +1,32 @@ +source plugin/javacomplete.vim +source autoload/javacomplete.vim +source autoload/javacomplete/parseradapter.vim +source t/javacomplete.vim + +call vspec#hint({'sid': 'g:SID("parseradapter")', 'scope': 'SScope()'}) + +describe 'javacomplete parseradapter test' + it 'Lambdas anonym argument search test' + let tree = Call('javacomplete#parseradapter#Parse', 't/data/LambdaAnonClass.java') + + let result = Call('javacomplete#parseradapter#SearchNameInAST', tree, 't', 388, 1) + Expect result[0].tag == 'LAMBDA' + Expect result[0].args.tag == 'IDENT' + Expect result[0].args.name == 't' + + let result = Call('javacomplete#parseradapter#SearchNameInAST', tree, 'd', 463, 1) + Expect result[1].tag == 'LAMBDA' + Expect result[1].args[0].tag == 'IDENT' + Expect result[1].args[0].name == 't' + end + + it 'Lambdas in method return' + let tree = Call('javacomplete#parseradapter#Parse', 't/data/LambdaReturnClass.java') + + let result = Call('javacomplete#parseradapter#SearchNameInAST', tree, 'p', 171, 1) + Expect result[0].tag == 'LAMBDA' + Expect result[0].args.tag == 'IDENT' + Expect result[0].args.name == 'p' + end + +end diff --git a/bundle/vim-javacomplete2/t/scanner.vim b/bundle/vim-javacomplete2/t/scanner.vim new file mode 100644 index 000000000..208d17059 --- /dev/null +++ b/bundle/vim-javacomplete2/t/scanner.vim @@ -0,0 +1,32 @@ +source autoload/javacomplete/scanner.vim +source t/javacomplete.vim + +call vspec#hint({'sid': 'g:SID("scanner")', 'scope': 'SScope()'}) + +describe 'javacomplete scanner test' + it 'ParseExpr test' + Expect Call('javacomplete#scanner#ParseExpr', 'var') == ['var'] + Expect Call('javacomplete#scanner#ParseExpr', 'var.') == ['var'] + Expect Call('javacomplete#scanner#ParseExpr', 'var.method().') == ['var', 'method()'] + Expect Call('javacomplete#scanner#ParseExpr', 'var.vari') == ['var', 'vari'] + Expect Call('javacomplete#scanner#ParseExpr', 'var.vari.') == ['var', 'vari'] + Expect Call('javacomplete#scanner#ParseExpr', 'var[].') == ['var[]'] + Expect Call('javacomplete#scanner#ParseExpr', '(Boolean) var.') == [' var'] + Expect Call('javacomplete#scanner#ParseExpr', '((Boolean) var).') == ['(Boolean)obj.'] + Expect Call('javacomplete#scanner#ParseExpr', '((Boolean) var).method()') == ['(Boolean)obj.', 'method()'] + Expect Call('javacomplete#scanner#ParseExpr', 'System.out::') == ['System', 'out'] + Expect Call('javacomplete#scanner#ParseExpr', 'System.out:') == ['System', 'out'] + end + + it 'ExtractCleanExpr test' + Expect Call('javacomplete#scanner#ExtractCleanExpr', 'var') == 'var' + Expect Call('javacomplete#scanner#ExtractCleanExpr', ' var.') == 'var.' + Expect Call('javacomplete#scanner#ExtractCleanExpr', 'var [ 0 ].') == 'var[0].' + Expect Call('javacomplete#scanner#ExtractCleanExpr', 'Boolean b = ((Boolean) var).method()') == '((Boolean)var).method()' + Expect Call('javacomplete#scanner#ExtractCleanExpr', 'List::') == 'List::' + Expect Call('javacomplete#scanner#ExtractCleanExpr', 'import ="java.util') == 'java.util' + Expect Call('javacomplete#scanner#ExtractCleanExpr', 'import = "java.util') == 'java.util' + Expect Call('javacomplete#scanner#ExtractCleanExpr', '? super java.lang.String') == 'java.lang.String' + end + +end diff --git a/bundle/vim-javacomplete2/t/utils.vim b/bundle/vim-javacomplete2/t/utils.vim new file mode 100644 index 000000000..b129e95ff --- /dev/null +++ b/bundle/vim-javacomplete2/t/utils.vim @@ -0,0 +1,100 @@ +source autoload/javacomplete/util.vim +source t/javacomplete.vim + +call vspec#hint({'sid': 'g:SID("util")'}) + + +describe 'javacomplete utils test' + it 'CountDims test' + Expect Call('javacomplete#util#CountDims', '') == 0 + Expect Call('javacomplete#util#CountDims', 'String[]') == 1 + Expect Call('javacomplete#util#CountDims', 'String[][]') == 2 + Expect Call('javacomplete#util#CountDims', 'String[][][][][]') == 5 + Expect Call('javacomplete#util#CountDims', 'String]') == 1 + Expect Call('javacomplete#util#CountDims', 'String[') == 0 + Expect Call('javacomplete#util#CountDims', 'String[[') == 0 + Expect Call('javacomplete#util#CountDims', 'String]]') == 1 + end + + it 'GetClassNameWithScope test' + new + put ='ArrayLi' + Expect Call('javacomplete#util#GetClassNameWithScope') == 'ArrayLi' + + new + put ='ArrayList ' + Expect Call('javacomplete#util#GetClassNameWithScope') == 'ArrayList' + + new + put ='ArrayList l' + call cursor(0, 10) + Expect Call('javacomplete#util#GetClassNameWithScope') == 'ArrayList' + + new + call cursor(1, 1) + put! ='ArrayList l' + call cursor(1, 11) + Expect Call('javacomplete#util#GetClassNameWithScope') == 'String' + + new + call cursor(1, 1) + put! ='List l = new ArrayList()' + call cursor(1, 1) + Expect Call('javacomplete#util#GetClassNameWithScope') == 'List' + call cursor(1, 14) + Expect Call('javacomplete#util#GetClassNameWithScope') == 'ArrayList' + call cursor(1, 31) + Expect Call('javacomplete#util#GetClassNameWithScope') == '' + + new + call cursor(1, 1) + put! ='@Annotation' + call cursor(1, 2) + Expect Call('javacomplete#util#GetClassNameWithScope') == '@Annotation' + + new + call cursor(1, 1) + put! ='ArrayList. ' + call cursor(1, 12) + Expect Call('javacomplete#util#GetClassNameWithScope') == 'ArrayList' + + end + + it 'CleanFQN test' + Expect Call('javacomplete#util#CleanFQN', '') == '' + Expect Call('javacomplete#util#CleanFQN', 'java.lang.Object') == 'Object' + Expect Call('javacomplete#util#CleanFQN', 'java.lang.Object java.util.HashMap.get()') == 'Object get()' + Expect Call('javacomplete#util#CleanFQN', 'public java.math.BigDecimal java.util.HashMap.computeIfAbsent(java.lang.String,java.util.function.Function)') == 'public BigDecimal computeIfAbsent(String,Function)' + + Expect Call('javacomplete#util#CleanFQN', 'public boolean scala.Tuple2.specInstance$()') == 'public boolean specInstance$()' + end + + it 'Prune test' + Expect Call('javacomplete#util#Prune', ' sb. /* block comment*/ append( "stringliteral" ) // comment ') == 'sb. append( "" ) ' + Expect Call('javacomplete#util#Prune', ' list.stream(\n\t\ts -> {System.out.println(s)}).') == 'list.stream(\n\t\ts -> {System.out.println(s)}). ' + end + + it 'HasKeyword test' + Expect Call('javacomplete#util#HasKeyword', 'java.util.function.ToIntFunction') == 0 + end + + it 'GenMethodParamsDeclaration test' + let m = {'p': ['int', 'int'], 'r': 'java.util.List', 'c': 'java.util.List', 'd': 'public abstract java.util.List java.util.List.subList(int,int)', 'm': '10000000001', 'n': 'subList'} + Expect Call('javacomplete#util#GenMethodParamsDeclaration', m) == 'public abstract java.util.List java.util.List.subList(int i1, int i2)' + + let m = {'p': ['java.lang.Object'], 'r': 'int', 'c': 'int', 'd': 'public abstract int java.util.List.indexOf(java.lang.Object)', 'm': '10000000001', 'n': 'indexOf'} + Expect Call('javacomplete#util#GenMethodParamsDeclaration', m) == 'public abstract int java.util.List.indexOf(Object object)' + + let m = {'p': ['java.util.Collection'], 'r': 'boolean', 'c': 'boolean', 'd': 'public abstract boolean java.util.List.addAll(java.util.Collection)', 'm': '10000000001', 'n': 'addAll'} + Expect Call('javacomplete#util#GenMethodParamsDeclaration', m) == 'public abstract boolean java.util.List.addAll(Collection collection)' + + let m = {'p': ['T[]'], 'r': 'T[]', 'c': 'T[]', 'd': 'public abstract T[] java.util.List.toArray(T[])', 'm': '10000000001', 'n': 'toArray'} + Expect Call('javacomplete#util#GenMethodParamsDeclaration', m) == 'public abstract T[] java.util.List.toArray(T[] t)' + + let m = {'p': ['java.math.BigDecimal', 'java.math.BigDecimal'], 'r': 'int', 'c': 'int', 'd': 'public abstract int indexOf(java.math.BigDecimal,java.math.BigDecimal)', 'm': '10000000001', 'n': 'indexOf'} + Expect Call('javacomplete#util#GenMethodParamsDeclaration', m) == 'public abstract int indexOf(BigDecimal bigDecimal1, BigDecimal bigDecimal2)' + + let m = {'p': ['java.math.BigDecimal', 'java.math.BigDecimal'], 'r': 'int', 'c': 'int', 'd': 'public abstract int indexOf(java.math.BigDecimal,java.math.BigDecimal) throws java.lang.Exception', 'm': '10000000001', 'n': 'indexOf'} + Expect Call('javacomplete#util#GenMethodParamsDeclaration', m) == 'public abstract int indexOf(BigDecimal bigDecimal1, BigDecimal bigDecimal2) throws java.lang.Exception' + end +end diff --git a/bundle/vim-javacomplete2/t/version.vim b/bundle/vim-javacomplete2/t/version.vim new file mode 100644 index 000000000..75b247226 --- /dev/null +++ b/bundle/vim-javacomplete2/t/version.vim @@ -0,0 +1,15 @@ +source autoload/javacomplete/version.vim +source t/javacomplete.vim + +call vspec#hint({'sid': 'g:SID("util")'}) + + +describe 'javacomplete utils test' + it 'ServerAccordance test' + Expect Call('javacomplete#version#CompareVersions', '2.5.6', '2.5.6') == 0 + Expect Call('javacomplete#version#CompareVersions', '2.5.6', '2.5.5') == 1 + Expect Call('javacomplete#version#CompareVersions', '2.5.6', '2.5.7') == -1 + Expect Call('javacomplete#version#CompareVersions', '2.5.6', '2.5.6.1') == -1 + Expect Call('javacomplete#version#CompareVersions', '2.5.6', '2.4.7') == 1 + end +end