Full Code of m2mdas/phpcomplete-extended for AI

master 7cd1f9690a22 cached
31 files
23.5 MB
68.5k tokens
58 symbols
1 requests
Download .txt
Showing preview only (273K chars total). Download the full file or copy to clipboard to get everything.
Repository: m2mdas/phpcomplete-extended
Branch: master
Commit: 7cd1f9690a22
Files: 31
Total size: 23.5 MB

Directory structure:
gitextract_gva2ohpo/

├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── autoload/
│   ├── neocomplete/
│   │   └── sources/
│   │       └── php.vim
│   ├── neosnippet/
│   │   └── snippets/
│   │       ├── php.snip
│   │       └── vim.snip
│   ├── phpcomplete_extended/
│   │   ├── parser.vim
│   │   └── util.vim
│   ├── phpcomplete_extended.vim
│   ├── unite/
│   │   ├── kinds/
│   │   │   └── phpcomplete.vim
│   │   └── sources/
│   │       └── phpcomplete.vim
│   ├── vital/
│   │   ├── _62ad025/
│   │   │   ├── Data/
│   │   │   │   ├── List.vim
│   │   │   │   └── String.vim
│   │   │   ├── Prelude.vim
│   │   │   ├── System/
│   │   │   │   ├── Cache.vim
│   │   │   │   ├── File.vim
│   │   │   │   └── Filepath.vim
│   │   │   ├── Text/
│   │   │   │   ├── Lexer.vim
│   │   │   │   └── Parser.vim
│   │   │   └── Web/
│   │   │       └── JSON.vim
│   │   ├── _62ad025.vim
│   │   └── phpcomplete-extended.vital
│   └── vital.vim
├── bin/
│   ├── CorePHPDocParser.php
│   ├── IndexGenerator.php
│   └── core_index
├── doc/
│   └── phpcomplete-extended.txt
├── plugin/
│   └── phpcomplete_extended.vim
└── vest/
    ├── test-fowrard-parse.vim
    └── test-reverse-parser.vim

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitattributes
================================================
*.vim  text eol=lf
*.php  text eol=lf
*.txt  text eol=lf
*.md  text eol=lf
bin/core_index binary


================================================
FILE: .gitignore
================================================
tests/
phpcompletePSR.rar


================================================
FILE: LICENSE
================================================
 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files (the
 "Software"), to deal in the Software without restriction, including
 without limitation the rights to use, copy, modify, merge, publish,
 distribute, sublicense, and/or sell copies of the Software, and to
 permit persons to whom the Software is furnished to do so, subject to
 the following conditions:
 
 The above copyright notice and this permission notice shall be included
 in all copies or substantial portions of the Software.
 
 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: README.md
================================================
phpcomplete-extended
====================

phpcomplete-extended is a fast, extensible, context aware autocomplete plugin
for PHP composer projects. Initially it reads autoload classmap of a composer
project, parses doc-comments of each class and creates index from them. After
that it auto updates index as you type thanks to
[vimproc.vim](https://github.com/Shougo/vimproc.vim) plugin. Besides
autocomplete this plugin have several code inspection features,

* Includes full core PHP documentation
* See documentation of current word, be it class name, method or property. It is
  context aware.
* Go to definition of a symbol. Also context aware.
* Automatically add use statement of current completed word. Also added plugin
  command of this action.
* If [unite.vim](https://github.com/Shougo/unite.vim/) plugin installed following sources are available,
    * `phpcomplete/files`           : Lists PHP files of the project.
    * `phpcomplete/vendors`         : Lists vendor directories
    * `phpcomplete/extends`         : Lists classes that extends the class guessed from
      the current cursor word.
    * `phpcomplete/implements`      : Lists classes that implements the class guessed
      from the current cursor word.

Demo videos (click on the image to goto youtube)
-----------------------------------------------
## Autocomplete demo video:

[![ScreenShot](http://img.youtube.com/vi/yZYFKslqkC8/maxresdefault.jpg)](http://www.youtube.com/watch?v=yZYFKslqkC8)

## Unite sources demo video:

[![ScreenShot](http://i1.ytimg.com/vi/Wd5G7QA3OFw/maxresdefault.jpg)](http://www.youtube.com/watch?v=Wd5G7QA3OFw)

Installation
-------------

The plugin depends on following plugins,

*  [vimproc.vim](https://github.com/Shougo/vimproc.vim) : Asynchronous library for vim. Required for auto-update index. C
  compiler is needed to build the plugiin. For windows install cygwin or
  msys to get the compiler.
*  [unite.vim](https://github.com/Shougo/unite.vim/): Optional, but enables some good features.

Plugin managers are recommended for installing these plugins. Installation instructions for
various plugin managers are given bellow.

## Pathogen

Issue following commands.

```sh
git clone https://github.com/Shougo/vimproc.vim.git ~/.vim/bundle/vimproc.vim
cd ~/.vim/bundle/vimproc.vim
make
cd ..
git clone https://github.com/Shougo/unite.vim.git ~/.vim/bundle/unit.vim
git clone https://github.com/m2mdas/phpcomplete-extended.git ~/.vim/bundle/phpcomplete-extended
```

## NeoBundle (preferred)
Put these lines in `.vimrc` and issue `source %` command

```vim
NeoBundle 'Shougo/vimproc', {
      \ 'build' : {
      \     'windows' : 'make -f make_mingw32.mak',
      \     'cygwin' : 'make -f make_cygwin.mak',
      \     'mac' : 'make -f make_mac.mak',
      \     'unix' : 'make -f make_unix.mak',
      \    },
     \ }
NeoBundle 'Shougo/unite.vim'
NeoBundle 'm2mdas/phpcomplete-extended'
"your other plugins
NeoBundleCheck
```

If C compiler found for respective environment, `neobundle` will automatically
compile the library.

## Vundle

Put following lines in `.vimrc` and issue `:BundleInstall` command.
```vim
Bundle 'Shougo/vimproc'
Bundle 'Shougo/unite.vim'
Bundle 'm2mdas/phpcomplete-extended'
"your other plugins
"....
```

After installation goto `vimproc` folder and issue `make` command from command
line. C compiler must be installed.

Walk through of the plugin
-------------------------

To enable omni complete add following line to `.vimrc`,

    autocmd  FileType  php setlocal omnifunc=phpcomplete_extended#CompletePHP

Then issuing `<C-X><C-O>` in insert mode will open the completion pop-up menu.

This plugin is tested in Windows and Linux. It should work in OSX also. 

For now this plugin supports [Composer](http://getcomposer.org/) PHP projects.
Upon detecting a composer project the plugin scans classmap generated by `php
composer.phar dumpautoload --optimize` command. By default composer command is
set to `php composer.phar`. The command can be changed by setting
`g:phpcomplete_index_composer_command` global variable. 

The plugin is dependent on `@var`, `@param`, `@return` doc-comments to give proper context aware
autocomplete. So documenting your class will help tremendously. It does not
analyse full class. Just parses the variable declaration to get the relevant
tokens. Does not provide autocomplete suggestion if there is error in code.

Indexing may take some time depending on project size. To decrease index creation time I
would recommend disabling xdebug extension in cli. To do that go to the location
where `php.ini` resides and copy the `php.ini` file as `php-cli.ini` and comment
out the xdebug line. To verify issue `php -m | grep xdebug` in commandline.

For configuration reference see helpdoc.


Auto complete plugin supports
-----------------------------

## [neocomplete](https://github.com/Shougo/neocomplete.vim)

Neocomplete support is built-in and my preferred autocomplete plugin. It needs
vim with lua bindings. To know more about installation refer to the plugin
repository.

## [supertab](https://github.com/ervandew/supertab)

Minimal configuration for supertab support,

    autocmd  FileType  php setlocal omnifunc=phpcomplete_extended#CompletePHP
    let g:SuperTabDefaultCompletionType = "<c-x><c-o>"

## [YouCompleteMe](https://github.com/Valloric/YouCompleteMe)

If `omnifunc` set the omni completer of `YouCompleteMe` should get the completion
candidates. I haven't tested it though.

Extensions
---------

Phpcomplete Extended pluign exposes api hooks so that it can be possible to
provide framework specific autocomplete suggestion. For example facades in
laravel, DIC services in Symfony2 etc. The plugin api divided in two part, `PHP`
and `vim`. PHP part is responsible for creating index related to the framework, 
and vim part is responsible for providing autocomplete menu entries based on the
index. For reference see
[phpcomplete-extended-symfony](https://github.com/m2mdas/phpcomplete-extended-symfony)
and
[phpcomplete-extended-laravlel](https://github.com/m2mdas/phpcomplete-extended-laravel) plugins.

License
-------
MIT licensed.

Acknowledgement 
--------------- 

This plugin would not be possible without the works of 
[Shougo](https://github.com/Shougo),
[shawncplus](https://github.com/shawncplus/),
[tpope](https://github.com/tpope/), [vim-jp](https://github.com/vim-jp),
[thinca](https://github.com/thinca) and many others.



================================================
FILE: autoload/neocomplete/sources/php.vim
================================================
"=============================================================================
" AUTHOR:  Mun Mun Das <m2mdas at gmail.com>
" FILE: php.vim
" Last Modified: September 10, 2013
" License: MIT license  {{{
"     Permission is hereby granted, free of charge, to any person obtaining
"     a copy of this software and associated documentation files (the
"     "Software"), to deal in the Software without restriction, including
"     without limitation the rights to use, copy, modify, merge, publish,
"     distribute, sublicense, and/or sell copies of the Software, and to
"     permit persons to whom the Software is furnished to do so, subject to
"     the following conditions:
"
"     The above copyright notice and this permission notice shall be included
"     in all copies or substantial portions of the Software.
"
"     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
"     OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
"     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
"     IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
"     CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
"     TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
"     SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
" }}}
"=============================================================================


let s:save_cpo = &cpo
set cpo&vim

let s:source = {
      \ 'name' : 'php',
      \ 'kind' : 'manual',
      \ 'mark' : '[P]',
      \ 'rank' : 6,
      \ 'hooks' : {},
      \ 'filetypes' : { 'php' : 1},
      \}

let s:List = vital#of('neocomplete').import('Data.List')

function! s:source.get_complete_position(context) "{{{
    return s:get_complete_position(a:context)
endfunction "}}}

function! s:source.gather_candidates(context) "{{{
    if !phpcomplete_extended#is_phpcomplete_extended_project() || &ft != 'php'
        return []
    endif
    return s:gather_candidates(a:context)
endfunction"}}}

function! s:get_complete_position(context) "{{{
    return phpcomplete_extended#CompletePHP(1, "")
endfunction "}}}

function! s:gather_candidates(context) "{{{
    return phpcomplete_extended#CompletePHP(0, b:completeContext.base)
endfunction "}}}

function! neocomplete#sources#php#define() "{{{
  return s:source
endfunction"}}}

function! s:set_neocomplete_sources() "{{{
    if !phpcomplete_extended#is_phpcomplete_extended_project() || !g:loaded_neocomplete
        return
    endif

    let avail_sources = keys(neocomplete#available_sources())
    if index(avail_sources, "omni") != -1
        call remove(avail_sources, index(avail_sources, 'omni'))
    endif

    let b:neocomplete_sources = avail_sources
endfunction "}}}

augroup neocomplete_php
    autocmd!
    autocmd BufEnter *.php :call <SID>set_neocomplete_sources()
augroup END
let &cpo = s:save_cpo
unlet s:save_cpo

" vim: foldmethod=marker:expandtab:ts=4:sts=4:tw=78 


================================================
FILE: autoload/neosnippet/snippets/php.snip
================================================
snippet     phpcomplete_extended_plugin
    <?php

    class ${1:plugin_name}
    {

        /**
         *  
         * @var array 
         */
        private $index;

        public function __construct()
        {
            $this->index = array();
        }

        /**
         * called to check that this plugin script is valid for current project
         *
         * @return bool 
         */
        public function isValidForProject()
        {
            ${2}
            //return is_file('projects/unique/file');
        }

        /**
         * This hook is called before indexing started. Bootstraping code of the 
         * framework goes here
         * @parem ClassLoader $loader the composer ClassLoader object
         */
        public function init($loader)
        {

        }

        /**
         * Called after a class is processed
         *
         * @param string $fqcn FQCN of the class
         * @param string $file file name of the class
         * @param array $classData processed class info
         */
        public function postProcess($fqcn, $file, $classData)
        {

        }

        /**
         * Called after main index is created
         *
         * @param mixed $fullIndex the main index
         * @param object $generatorObject the parant generator object
         */
        public function postCreateIndex($fullIndex, $generatorObject)
        {

        }

        /**
         * Called after update script initialized
         *
         * @param mixed $prevIndex the previous index of this plugin
         */
        public function preUpdateIndex($prevIndex)
        {

        }

        /**
         * Called after update index created
         *
         * @param mixed $classData class data of processed class
         * @param mixed $fullIndex the main index
         * @param object $generatorObject the parant generator object
         */
        public function postUpdateIndex($classData, $fullIndex, $generatorObject)
        {

        }

        /**
         * Returns the plugin index
         *
         * @return mixed the index created by this plugin script
         */
        public function getIndex()
        {
            return $this->index;
        }
    }


================================================
FILE: autoload/neosnippet/snippets/vim.snip
================================================
snippet     phpcomplete_extended_plugin
    let s:${1:plugin_name}_plugin = {
        \    'name': '$1'
        \}

    if !exists("s:$1_index")
        let s:$1_index = {}
    endif

    function! phpcomplete_extended#$1#define() "{{{
        let is_$1_project = 0 "check this is a valid $1 project

        if is_$1_project
            return s:$1_plugin
        endif
        return {}
    endfunction "}}}

    function! s:$1_plugin.init() "{{{
        ${2}
    endfunction "}}}

    function! s:$1_plugin.is_valid_for_project() "{{{
        return true
    endfunction "}}}

    function! s:$1_plugin.get_fqcn(parentFQCN, token_data) "{{{
        let fqcn = ""
        "get fqcn

        return fqcn
    endfunction "}}}

    function! s:$1_plugin.get_menu_entries(fqcn, base, is_this, is_static) "{{{
        let menu_entries = []
        " get menu entries

        return menu_entries
    endfunction "}}}

    function! s:$1_plugin.set_index(index) "{{{
        let s:$1_index = a:index
    endfunction "}}}

    function! s:$1_plugin.resolve_fqcn(fqcn) "{{{
        return a:fqcn
    endfunction "}}}

    function! s:$1_plugin.get_inside_quote_menu_entries(parentFQCN, token_data) "{{{
        let menu_entries = []
        //process menu entries

        return menu_entries
    endfunction "}}}



================================================
FILE: autoload/phpcomplete_extended/parser.vim
================================================
let s:save_cpo = &cpo
set cpo&vim

let s:lexer_symbols = [
    \ ['new'               , '\<new\>'] ,
    \ ['identifier'        , '\h\w*'] , ['whitespace'         , '\s*'] ,
    \ ['open_brace'        , '(']     , ['close_brace'        , ')']   ,
    \ ['dollar'            , '\$']    ,
    \ ['single_quote'      , "'"]     , ['double_quote'       , '"']   ,
    \ ["object_resolutor"  , "->"]    , ['static_resolutor'   , "::"]  ,
    \ ['curly_brace_open'  , '{']     , ['curly_brace_close'  , '}']   ,
    \ ['square_brace_open' , '[']     , ['square_brace_close' , ']']   ,
    \ ['ns_seperator'      , "\\"]    , ['equal'              , '=']   ,
    \ ['front_slash'       , "/"]     , ['star'               , '*']   ,
    \ ['alpha'             , "@"]     , ['plus'               , '+']   ,
    \ ['OR'                , "|"]     , ['AND'                , '&']   ,
    \ ['negate'            , "!"]     , ['dash'               , '-']   ,
    \ ['semicolon'         , ';']     , ['colon'              , ':']   ,
    \ ['dot'               , '\.']    ,
    \ ['left_arrow'        , '>']     , ['right_arrow'        , '<']   ,
    \['comma'             , ','],
    \['others'            , '[^()\[\]""'',;:*/@|&+!-]\+'] ,
\]

function! phpcomplete_extended#parser#reverseParse(line, parsedTokens) "{{{
    let line = phpcomplete_extended#util#trim(a:line)
    if line =~ ';$'
        let line = phpcomplete_extended#util#getDataString().chop(line)
    endif
    if line == ""
        return [{'insideBraceText': '', 'methodPropertyText': '', 'nonClass': 1, 'start': 1}]
    endif
    let parsedTokens = a:parsedTokens
    let braceStack = []
    let quoteStack = []
    let L = phpcomplete_extended#util#getLexer()
    let P = phpcomplete_extended#util#getParser()
    let List = phpcomplete_extended#util#getDataList()
    let lexerTokens = reverse(L.lexer(s:lexer_symbols).exec(line))
    let parser = P.parser().exec(lexerTokens)
    try
    catch
        return []
    endtry
    let next = parser.next()
    let insideBraceText = ""
    let expectResolutor = 1
    let methodPropertyText  = ""
    let isMethod = 0
    let isArray = 0
    let isStatic = 0
    let maybeNew = 0
    let insideQuote = 0
    let startSkipping = 0
    let previousToken = ""
    while !parser.end()
        "PrettyPrint parser.next()
        "PrettyPrint braceStack
        "PrettyPrint parsedTokens
        "call parser.consume()
        "continue
        if parser.next_is('whitespace')
            call parser.consume()
            if empty(braceStack) && previousToken == "identifier" && !parser.next_is('new')
                let parsedObject = s:createParsedObject(insideBraceText, methodPropertyText, isMethod, 1)
                let parsedObject.nonClass = 1
                call insert(parsedTokens, parsedObject)
                break
            endif
            continue
        elseif startSkipping && !parser.next_is('open_brace')
            call parser.consume()
            continue
        elseif parser.next_is('identifier')
            let identifier = parser.consume()['matched_text']
            if empty(braceStack) && isArray 
                    \ && (parser.next_is('object_resolutor') || parser.next_is('static_resolutor') || parser.next_is('dollar'))
                    \ && previousToken == 'square_brace_open'
                "'$foo->bar["baz"]->bzz'
                let parsedObject = s:createParsedObject(insideBraceText, methodPropertyText, 0, 0)
                let parsedObject.isArrayElement = 1
                let insideBraceText = ""
                let methodPropertyText = identifier
                if insideQuote
                    let parsedObject.insideQuote = 1
                    let insideQuote = 0
                endif
                call insert(parsedTokens, parsedObject)
                let isArray = 0
            elseif (empty(insideBraceText) && empty(methodPropertyText) && parser.end()) 
                        \ || (parser.end() && previousToken == "open_brace")
                        \ || (parser.end() && previousToken == "static_resolutor")
                        \ || parser.next_is('open_brace')
                        \ || parser.next_is('square_brace_open')
                "foo_bar, foo(, Foo::, $this->get(foo_func('bar, $foo[Foo::Bar
                let methodPropertyText = identifier . methodPropertyText
                let start = 1
                let isMethod = 0
                let parsedObject = s:createParsedObject(insideBraceText, methodPropertyText, isMethod, start)
                let methodPropertyText = ""
                let insideBraceText = ""
                if insideQuote
                    let parsedObject.insideQuote = 1
                    let insideQuote = 0
                endif
                if previousToken == "static_resolutor"
                    let parsedObject.isStatic = 1
                    let parsedObject.nonClass = 0
                else
                    let parsedObject.nonClass = 1
                endif
                call insert(parsedTokens, parsedObject)
                break

            elseif empty(braceStack) && (
                \ parser.next_is('dollar')
                \ || parser.next_is('whitespace')
                \ || parser.next_is("object_resolutor")
                \ || parser.next_is("static_resolutor")
                \ || parser.next_is("ns_seperator")
                \)
                let methodPropertyText = identifier .methodPropertyText

            else
                if(isArray) 
                    let isArray  = 0
                endif
                let insideBraceText = identifier .insideBraceText
            endif
            let previousToken = "identifier"

        elseif parser.next_is('open_brace') || parser.next_is("square_brace_open")
            let open_brace = parser.consume()['matched_text']
            if !empty(braceStack) && open_brace == "(" && braceStack[-1] == ")"
                if startSkipping 
                    let startSkipping = 0
                endif
                call List.pop(braceStack)
                let isMethod = 1
            elseif !empty(braceStack) && open_brace == "[" && braceStack[-1] == "]"
                call List.pop(braceStack)
                let isArray = 1
            elseif (previousToken == "single_quote" || previousToken == "double_quote")
                    \ && parser.next_is("identifier") && open_brace == "["
                if !empty(quoteStack)
                    call List.pop(quoteStack)
                endif
                let isArray = 1
            elseif len(insideBraceText) &&  insideBraceText[0] !~ '''\|"'
                let insideBraceText = open_brace . insideBraceText
            endif
            let previousToken = "open_brace"
            if open_brace == "["
                let previousToken = "square_brace_open"
            endif
        elseif parser.next_is('close_brace') || parser.next_is("square_brace_close")
            let close_brace = parser.consume()['matched_text']
            "if empty(braceStack)
                    "\ && (previousToken == "static_resolutor" || previousToken == "object_resolutor")
                    "\ && (parser.next_is('close_brace'))

                ""(new Foo())->bar
                "let maybeNew = 1
                "continue
            if (empty(quoteStack)  && (
                        \ (close_brace == ')' && parser.next_is('open_brace'))
                        \ || (close_brace == ']' && parser.next_is('square_brace_open'))
                        \ || (previousToken == 'object_resolutor' || previousToken == 'static_resolutor')
                        \ || (parser.next_is('single_quote') || parser.next_is('double_quote'))
                        \ || (parser.next_is('identifier'))
                    \))
                call List.push(braceStack, close_brace)
                if (close_brace == ")" && isArray && previousToken == "square_brace_open")
                    "'$this->foo()[]->'
                    let parsedObject = s:createParsedObject(insideBraceText, '', 0, 0)
                    let parsedObject.isArrayElement = 1
                    let isArray = 0
                    let insideBraceText = ""
                    let methodPropertyText = ""
                    call insert(parsedTokens, parsedObject)
                endif
            else
                let insideBraceText = close_brace . insideBraceText
            endif
            let previousToken = "close_brace"
            if previousToken == "]"
                let previousToken = "square_brace_close"
            endif

        elseif parser.next_is('single_quote') || parser.next_is('double_quote')
            let quote = parser.consume()['matched_text']
            if empty(quoteStack) && !parser.next_is('ns_seperator')
                call List.push(quoteStack, quote)
                let insideBraceText = quote . insideBraceText
                let insideQuote = 1
                let insideBraceText  .= methodPropertyText
                let methodPropertyText = ""
            else
                if empty(quoteStack)
                    continue
                endif
                let qstack = copy(quoteStack)
                let q = List.pop(qstack)
                if q == quote && !parser.next_is('ns_seperator')
                    call List.pop(quoteStack)
                    let insideQuote = 0
                    let insideBraceText = quote . insideBraceText
                endif
            endif
            let previousToken = "single_quote"
            if quote == '"'
                let previousToken = "double_quote"
            endif

        elseif parser.next_is('static_resolutor') || parser.next_is('object_resolutor')
            let resolutor = parser.consume()['matched_text']
            if empty(braceStack) && (previousToken == "identifier" || empty(previousToken))
                    \ && (parser.next_is('identifier') 
                            \ ||  parser.next_is('close_brace') 
                            \ || parser.next_is('square_brace_close'))

                let parsedObject = s:createParsedObject(insideBraceText, methodPropertyText, isMethod, 0)
                let isMethod = 0
                if insideQuote
                    let parsedObject.insideQuote = 1
                    let insideQuote = 0
                endif
                if resolutor == "::"
                    let parsedObject.isStatic = 1
                    let isStatic = 0
                endif
                let methodPropertyText = ""
                let insideBraceText = ""
                call insert(parsedTokens, parsedObject)
            else
                let insideBraceText = resolutor .insideBraceText
            endif
            let previousToken = "object_resolutor"
            if resolutor == "::"
                let previousToken = "static_resolutor"
            endif

        elseif parser.next_is('new')
            let new = parser.consume()['matched_text']
            if empty(braceStack) 
                        \ &&  (parser.next_is('whitespace') || parser.end() || parser.next_is('open_brace'))
                let parsedObject = s:createParsedObject(insideBraceText, methodPropertyText, 0, 1)
                let parsedObject.isNew = 1
                call insert(parsedTokens, parsedObject)
                break
            else
                let insideBraceText = new .insideBraceText
            endif
            let previousToken = "new"
        elseif parser.next_is('dollar')
            let dollar = parser.consume()['matched_text']
            if empty(braceStack)
                let methodPropertyText = dollar . methodPropertyText
                let parsedObject = s:createParsedObject(insideBraceText, methodPropertyText, 0, 1)
                let methodPropertyText = ""
                let insideBraceText = ""
                if isArray
                    let parsedObject.isArrayElement = 1
                endif
                call insert(parsedTokens, parsedObject)
                break
            else
                let insideBraceText = dollar . insideBraceText
            endif

            let previousToken = "dollar"

        elseif parser.next_is('ns_seperator')
            let ns_seperator = parser.consume()['matched_text']
            if empty(braceStack)
                let methodPropertyText = ns_seperator . methodPropertyText
            else
                let insideBraceText = ns_seperator . insideBraceText
            endif
            let previousToken = "ns_seperator"
        elseif parser.end()
            let parsedObject = s:createparsedObject(insideBraceText, methodPropertyText, 0, 1)
            call insert(parsedTokens, parsedObject)
            break
        elseif parser.next_is("dot")
            let dot = parser.consume()['matched_text']
            let insideBraceText = '\.' . insideBraceText
        elseif parser.next_is('comma')
            let comma = parser.consume()['matched_text']
            "start skipping previous method arguments
            if len(insideBraceText) && (previousToken == 'single_quote' || parser.next_is('double_quote'))
                "TODO: have to refine skiping mechanism
                let startSkipping = 0
                let insideBraceText = comma . insideBraceText
            else
                let insideBraceText = comma . insideBraceText
            endif

        else
            let extraData = parser.consume()['matched_text']
            if !empty(braceStack)
                let insideBraceText = extraData . insideBraceText
            else
                let methodPropertyText = extraData . methodPropertyText
            endif
            let previousToken = "extradata"
        endif
    endwhile

    for token in parsedTokens
        if token['methodPropertyText'][0] == "\\"
            let token['methodPropertyText'] = matchstr(token['methodPropertyText'], '\\\zs.*')
        endif
    endfor

    return parsedTokens
endfunction "}}}


function! phpcomplete_extended#parser#forwardParse(line, parsedTokens) "{{{
    let line = a:line
    let parsedTokens = a:parsedTokens
    let braceStack = []
    let L = phpcomplete_extended#util#getLexer()
    let P = phpcomplete_extended#util#getParser()
    let List = phpcomplete_extended#util#getDataList()

    try
        let lexerTokens = L.lexer(s:lexer_symbols).exec(line)
        let parser = P.parser().exec(lexerTokens)
    catch 
        return []
    endtry
    let next = parser.next()
    let insideBraceText = ""
    let expectResolutor = 1
    let methodPropertyText  = ""
    let isMethod = 0
    let isDollar = 0
    let isNew = 0
    let previousToken = ""
    let isArray = 0

    while !parser.end()
        "PrettyPrint parser.next()
        "PrettyPrint braceStack
        "PrettyPrint parsedTokens
        if parser.next_is('whitespace')
            call parser.consume()
            let previousToken = "whitespace"
            continue
        elseif parser.next_is('dollar')
            let dollar = parser.consume()['matched_text']
            if empty(braceStack)
                let methodPropertyText .= dollar
            else
                let insideBraceText .= dollar
            endif
            let previousToken = "dollar"
        elseif parser.next_is('identifier')
            let identifier = parser.consume()['matched_text']

            if !empty(braceStack)
                let insideBraceText .= identifier

            elseif empty(previousToken) && parser.next_is('static_resolutor')
                let parsedObject = s:createParsedObject('', identifier, 0, 1)
                let parsedObject.isStatic = 1
                let parsedObject.nonClass = 1
                call add(parsedTokens, parsedObject)
                let methodPropertyText = ""
                let insideBraceText = ""
            elseif previousToken == "dollar" 
                    \ && (parser.next_is('object_resolutor') 
                        \ || parser.next_is('static_resolutor') 
                        \ || parser.next_is('square_brace_open') 
                        \ || parser.next_is('semicolon'))
                let methodPropertyText .= identifier
            elseif previousToken == "object_resolutor" && (parser.next_is('open_brace') 
                            \ || parser.next_is('square_brace_open') 
                            \ || parser.next_is('semicolon') 
                            \ || parser.next_is('object_resolutor'))
                let methodPropertyText .= identifier

            elseif previousToken == "whitespace"
                    \ && isNew && (parser.next_is('open_brace') || parser.next_is('close_brace'))
                let methodPropertyText = identifier

            endif
            let previousToken = "identifier"
        elseif parser.next_is("object_resolutor") || parser.next_is('static_resolutor')
            let resolutor = parser.consume()['matched_text']
            if empty(braceStack)
                \ && (previousToken == 'identifier' || previousToken == 'close_brace' || previousToken == 'square_brace_close')
                if resolutor == "::"
                    continue
                endif
                let parsedObject = s:createParsedObject(insideBraceText, methodPropertyText, isMethod, 0)
                if methodPropertyText[0] == "$"
                    let parsedObject.start = 1
                endif
                if isNew 
                    let parsedObject.isNew = 1
                    let isNew = 0
                endif
                if isArray
                    let parsedObject.isArrayElement = 1
                    let isArray = 0
                endif
                call add(parsedTokens, parsedObject)
                let insideBraceText = ""
                let methodPropertyText =""
            elseif !empty(braceStack)
                let insideBraceText .= resolutor
            endif
            let previousToken = "object_resolutor"
            if resolutor == "static_resolutor"
                let previousToken = "static_resolutor"
            endif
        elseif parser.next_is('new')
            let new_operator = parser.consume()['matched_text']
            if empty(braceStack) 
                    \ && (empty(previousToken) || previousToken == 'open_brace')
                let isNew = 1
            elseif !empty(braceStack)
                let insideBraceText .= new_operator
            endif
            let previousToken = "new"
        elseif parser.next_is('open_brace') || parser.next_is('square_brace_open')
            let open_brace = parser.consume()['matched_text']
            if (empty(previousToken)) && parser.next_is('new')
                continue

            elseif (previousToken == "close_brace" && open_brace == "[") 
                    \ || (previousToken == 'identifier')
                call List.push(braceStack, open_brace)
                if open_brace == "["
                    let isArray  = 1
                endif
            else
                let insideBraceText .= open_brace
            endif
            let previousToken = "open_brace"
            if open_brace == "["
                let previousToken = "square_brace_open"
            endif

        elseif parser.next_is('close_brace') || parser.next_is('square_brace_close')
            let close_brace = parser.consume()['matched_text']
            if empty(braceStack) && isNew && close_brace == ")" 
                    \ && (previousToken == "identifier" || previousToken == "close_brace")
                continue
            elseif !empty(braceStack)
                if (close_brace == ")" && braceStack[-1] == "(")
                        \ || (close_brace == "]" && braceStack[-1] == "[")
                    call List.pop(braceStack)
                    if close_brace == ')'
                        let isMethod = 1
                    endif
                    if (close_brace == ")" && parser.next_is('square_brace_open'))
                        let parsedObject = s:createParsedObject(insideBraceText, methodPropertyText, 0, 0)
                        let parsedObject.isArrayElement = 1
                        let parsedObject.isMethod = 1
                        call add(parsedTokens, parsedObject)
                        let methodPropertyText = ""
                        let insideBraceText = ""
                    endif
                endif
            else
                let insideBraceText .= close_brace
            endif
            let previousToken = "close_brace"
            if close_brace == "]"
                let previousToken = "square_brace_close"
            endif
        elseif parser.next_is('semicolon')
            let semicolon = parser.consume()['matched_text']
            if empty(braceStack) 
                    \&& (previousToken == "close_brace" 
                        \ || previousToken == "square_brace_close"
                        \ || previousToken == "identifier"
                        \ || empty(previousToken) && methodPropertyText[0] == "$")
                let parsedObject = s:createParsedObject(insideBraceText, methodPropertyText, 1, 0)
                let parsedObject.pEnd = 1
                if previousToken == "square_brace_close"
                    let parsedObject.isMethod = 0
                    let parsedObject.isArrayElement = 1
                elseif previousToken == "identifier"
                    let parsedObject.isMethod = 0
                endif
                call add(parsedTokens, parsedObject)
                break
            else
                let insideBraceText .= semicolon
            endif
            let previousToken = "semicolon"
        else
            let extraData = parser.consume()['matched_text']
            if !empty(braceStack)
                let insideBraceText .= extraData
            endif
            let previousToken = "extra_data"
        endif
    endwhile

    for token in parsedTokens
        if token['methodPropertyText'][0] == "\\"
            let token['methodPropertyText'] = matchstr(token['methodPropertyText'], '\\\zs.*')
        endif
    endfor

    return parsedTokens
endfunction "}}}

function! ParserTest(line) "{{{
    let line = a:line
    PrettyPrint line
    let tokens = phpcomplete_extended#parser#reverseParse(line, [])
    PrettyPrint tokens
    return tokens
endfunction "}}}

function! s:createParsedObject(insideBraceText, methodPropertyText, isMethod, start)
    let parsedObject = {}
    let parsedObject.insideBraceText = a:insideBraceText
    let parsedObject.methodPropertyText = a:methodPropertyText
    let parsedObject.isMethod = a:isMethod
    let parsedObject.start = a:start
    return parsedObject
endfunction


let &cpo = s:save_cpo
unlet s:save_cpo


================================================
FILE: autoload/phpcomplete_extended/util.vim
================================================
"=============================================================================
" AUTHOR:  Mun Mun Das <m2mdas at gmail.com>
" FILE: util.vim
" Last Modified: September 09, 2013
" License: MIT license  {{{
"     Permission is hereby granted, free of charge, to any person obtaining
"     a copy of this software and associated documentation files (the
"     "Software"), to deal in the Software without restriction, including
"     without limitation the rights to use, copy, modify, merge, publish,
"     distribute, sublicense, and/or sell copies of the Software, and to
"     permit persons to whom the Software is furnished to do so, subject to
"     the following conditions:
"
"     The above copyright notice and this permission notice shall be included
"     in all copies or substantial portions of the Software.
"
"     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
"     OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
"     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
"     IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
"     CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
"     TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
"     SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
" }}}
"=============================================================================

let s:save_cpo = &cpo
set cpo&vim

function! phpcomplete_extended#util#getVital()
    if !exists("s:V")
        let s:V = vital#of('phpcomplete-extended')
    endif
    return s:V
endfunction

function! phpcomplete_extended#util#getFile()
    if !exists("s:File")
        let s:File = phpcomplete_extended#util#getVital().import('System.File')
    endif
    return s:File
endfunction

function! phpcomplete_extended#util#getLexer()
    if !exists("s:L")
        let s:L = phpcomplete_extended#util#getVital().import('Text.Lexer')
    endif
    return s:L
endfunction

function! phpcomplete_extended#util#getParser()
    if !exists("s:P")
        let s:P = phpcomplete_extended#util#getVital().import('Text.Parser')
    endif
    return s:P
endfunction

function! phpcomplete_extended#util#getDataList()
    if !exists("s:List")
        let s:List = phpcomplete_extended#util#getVital().import('Data.List')
    endif
    return s:List
endfunction

function! phpcomplete_extended#util#print_message(message) "{{{
    echohl Comment | echo a:message | echohl none
endfunction "}}}

function! phpcomplete_extended#util#print_error_message(message) "{{{
    echohl ErrorMsg | echo a:message | echohl none
endfunction "}}}

function! phpcomplete_extended#util#getDataString()
    if !exists("s:String")
        let s:String = phpcomplete_extended#util#getVital().import('Data.String')
    endif
    return s:String
endfunction

function! phpcomplete_extended#util#reverse(str)
  return join(reverse(split(a:str, '.\zs')), '')
endfunction

function! phpcomplete_extended#util#split_leftright(expr, pattern)
    return phpcomplete_extended#util#getDataString().split_leftright(a:expr, a:pattern)
endfunction

function! phpcomplete_extended#util#trim(str)
  return matchstr(a:str,'^\s*\zs.\{-}\ze\s*$')
endfunction

function! phpcomplete_extended#util#add_padding(list) "{{{
    let list = a:list
    let max_len = 0

    for e in a:list
        let max_len = len(e) > max_len ? len(e) : max_len
    endfor

    let format = '%-'.max_len. 's'
    return map(list, "printf(format, v:val)")
endfunction "}}}

let s:is_windows = has('win16') || has('win32') || has('win64')

function! phpcomplete_extended#util#json_decode(...)
  return call(s:Json.decode, a:000)
endfunction

function! phpcomplete_extended#util#is_windows(...)
  return s:is_windows
endfunction

if phpcomplete_extended#util#is_windows()
  function! phpcomplete_extended#util#substitute_path_separator(...)
    let V = phpcomplete_extended#util#getVital()
    return call(V.substitute_path_separator, a:000)
  endfunction
else
  function! phpcomplete_extended#util#substitute_path_separator(path)
    return a:path
  endfunction
endif

function! phpcomplete_extended#util#reverse(str)
    return join(reverse(split(a:str, '.\zs')), '')
endfunction

function! phpcomplete_extended#util#copy(...)
  return call(phpcomplete_extended#util#getFile().copy, a:000)
endfunction

function! phpcomplete_extended#util#has_vimproc(...)
  return call(phpcomplete_extended#util#getVital().has_vimproc, a:000)
endfunction

function! phpcomplete_extended#util#system(...)
  return call(phpcomplete_extended#util#getVital().system, a:000)
endfunction

function! phpcomplete_extended#util#input_yesno(message) "{{{
  let yesno = input(a:message . ' [yes/no]: ')
  while yesno !~? '^\%(y\%[es]\|n\%[o]\)$'
    redraw
    if yesno == ''
      echo 'Canceled.'
      break
    endif

    " Retry.
    call phpcomplete_extended#print_error('Invalid input.')
    let yesno = input(a:message . ' [yes/no]: ')
  endwhile

  return yesno =~? 'y\%[es]'
endfunction"}}}

function! phpcomplete_extended#util#getLines(lineNum, lineCount, direction)
    if a:direction == "back"
        let lines = getline( a:lineNum - a:lineCount, a:lineNum)
    else
        let lines = getline(a:lineNum, a:lineNum + a:lineCount)
    endif
    let joinedLine = join(map(lines, 'phpcomplete_extended#util#trim(v:val)'), "")
    return joinedLine
endfunction

let &cpo = s:save_cpo
unlet s:save_cpo


================================================
FILE: autoload/phpcomplete_extended.vim
================================================
"=============================================================================
" AUTHOR:  Mun Mun Das <m2mdas at gmail.com>
" FILE: phpcomplete_extended.vim
" Last Modified: September 11, 2013
" License: MIT license  {{{
"     Permission is hereby granted, free of charge, to any person obtaining
"     a copy of this software and associated documentation files (the
"     "Software"), to deal in the Software without restriction, including
"     without limitation the rights to use, copy, modify, merge, publish,
"     distribute, sublicense, and/or sell copies of the Software, and to
"     permit persons to whom the Software is furnished to do so, subject to
"     the following conditions:
"
"     The above copyright notice and this permission notice shall be included
"     in all copies or substantial portions of the Software.
"
"     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
"     OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
"     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
"     IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
"     CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
"     TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
"     SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
" }}}
"=============================================================================
let s:save_cpo = &cpo
set cpo&vim

if !exists("s:current_project_dir")
    let s:current_project_dir = ""
endif
if !exists("s:update_info")
    let s:update_info = {}
endif
if !exists("s:plugins")
    let s:plugins = {}
endif
if !exists("s:plugin_ftime")
    let s:plugin_ftime = -1
endif
if !exists("s:plugin_index")
    let s:plugin_index = {}
endif
if !exists("s:plugin_php_files")
    let s:plugin_php_files = []
endif

if !exists("s:psr_class_complete")
    let s:psr_class_complete = 0
endif

if !exists("s:phpcomplete_enabled")
    let s:phpcomplete_enabled = 1
endif

let s:disabled_projects = []

let s:T = {
\     'number': type(0),
\     'string': type(''),
\     'function': type(function('function')),
\     'list': type([]),
\     'dictionary': type({}),
\     'float': type(0.0),
\   }


function! phpcomplete_extended#CompletePHP(findstart, base) " {{{
    if a:findstart
        let start = s:get_complete_start_pos()
        if !empty(b:completeContext)
            let b:completeContext.start = start
            let b:completeContext.base = getline('.')[start : col('.')-2]
        endif
        return start
    endif

    if !s:phpcomplete_enabled
        return []
    endif

    if !phpcomplete_extended#is_phpcomplete_extended_project()
        return []
    endif

    if empty(b:completeContext)
        return []
    endif

    let s:psr_class_complete = 0
    if b:completeContext.complete_type == "use"
        return s:getUseMenuEntries(a:base)

    elseif b:completeContext.complete_type == "nonclass"
        let s:psr_class_complete = 1
        return s:getNonClassMenuEntries(a:base)
    elseif b:completeContext.complete_type == "insideQuote"
        return s:get_plugin_inside_qoute_menu_entries(b:completeContext.fqcn, b:completeContext.lastToken)

    elseif b:completeContext.complete_type == "new"
        let s:psr_class_complete = 1
        return s:getClassMenuEntries(a:base)

    elseif b:completeContext.complete_type == "class"
        let is_static = 0
        let is_this = 0
        let lastResolutor = b:completeContext.last_resolutor
        let fqcn = b:completeContext.fqcn

        let sourceFile = phpcomplete_extended#util#substitute_path_separator(expand("%:."))
        let sourceFQCN = s:getFQCNFromFileLocation(sourceFile)

        if sourceFQCN == fqcn
            let is_this = 1
        endif

        if lastResolutor == '::'
            let is_static = 1
        endif
        if b:completeContext.complete_type == 'class'
            \ &&  lastResolutor == ""
            return []
        endif

        let menu_entries = []
        let baseMenuEntries = phpcomplete_extended#getMenuEntries(fqcn, a:base, is_this, is_static)
        "plugin callback
        let plugin_menu_entries = s:get_plugin_menu_entries(fqcn, a:base, is_this, is_static)
        let menu_entries += plugin_menu_entries
        let menu_entries += baseMenuEntries
        return menu_entries
    endif

endfunction "}}}

function! s:get_complete_start_pos() "{{{
    let line = getline('.')
    let start = col('.') - 1

    while start >= 0 && line[start - 1] =~ '[\\a-zA-Z_0-9\x7f-\xff$]'
        let start -= 1
    endwhile

    let b:completeContext = {}
    let completeContext = s:get_complete_context()
    if empty(completeContext) || !s:phpcomplete_enabled
        return start
    endif
    let b:completeContext = completeContext

    if b:completeContext.complete_type == "class" && getline('.')[start-2 : start-1] !~ '->\|::'
        let b:completeContext.last_resolutor = ""
    endif

    if !empty(completeContext)
                \ && has_key(completeContext, 'complete_type')
                \ && completeContext['complete_type'] == 'insideQuote'
                \ && match(completeContext.lastToken['insideBraceText'], '\.') != -1

        "having some problem with dot charater
        let splits = split(completeContext.lastToken['insideBraceText'], '\\\.')

        let displace = len(splits)
        if match(completeContext.lastToken['insideBraceText'], '\\\.$') < 0
            let displace += len(splits[-1]) -1
        endif
        let start = start - len(completeContext.lastToken['insideBraceText']) + displace
    endif
    return start
endfunction "}}}

function! s:get_complete_context() "{{{
    let cursorLine = getline('.')[0:col('.')-2]
    if !exists('phpcomplete_extended_context')
        let completeContext = {}
    endif

    let completeContext.complete_type = "class"

    if cursorLine =~? '^\s*use\s\+'
        " namespace completeion
        let completeContext.complete_type = "use"

    elseif cursorLine =~? '\(\s*new\|extends\)\s\+'
            \ && len(phpcomplete_extended#parsereverse(cursorLine, line('.'))) == 1
        "new class completion
        let completeContext.complete_type = "new"
    else
        if !phpcomplete_extended#is_phpcomplete_extended_project()
            return {}
        endif
        let parsedTokens = phpcomplete_extended#parsereverse(cursorLine, line('.'))

        if empty(parsedTokens)
            let  completeContext = {}
            return {}
        endif

        if has_key(parsedTokens[0], "nonClass") && parsedTokens[0]["nonClass"]
            let completeContext.complete_type = "nonclass"
        elseif has_key(parsedTokens[-1], "insideQuote")
            let lastToken = remove(parsedTokens, -1)
            let fqcn = s:guessTypeOfParsedTokens(deepcopy(parsedTokens))
            let completeContext.lastToken = lastToken
            let completeContext.lastToken['insideBraceText'] = matchstr(lastToken['insideBraceText'], '[''"]\?\zs.*\ze[''"]\?')
            let completeContext.fqcn = fqcn
            let completeContext.complete_type = "insideQuote"
        else
            let lastToken = remove(parsedTokens, -1)
            let fqcn = s:guessTypeOfParsedTokens(deepcopy(parsedTokens))
            let completeContext.complete_type = "class"
            let completeContext.last_resolutor = matchstr(cursorLine, '.*\zs\(->\|::\)\ze.*')

            let completeContext.lastToken = lastToken
            let completeContext.fqcn = fqcn
        endif
    endif
    return completeContext

endfunction "}}}

function! s:guessTypeOfParsedTokens(parsedTokens) "{{{
    if !exists('g:phpcomplete_index')
        return ""
    endif
    let parsedTokens = a:parsedTokens
    let objectGraph = []

    let sourceFile = phpcomplete_extended#util#substitute_path_separator(expand("%:."))
    let sourceFQCN = s:getFQCNFromFileLocation(sourceFile)

    "if empty(sourceFQCN)
        "return ""
    "endif

    if empty(parsedTokens)
        return ""
    endif

    let sourceNamespace = matchstr(sourceFQCN, '\zs.*\ze\\')

    let firstToken = remove(parsedTokens, 0)
    let currentFQCN = ""
    let parentFQCN = ""
    let nonClass = has_key(firstToken, "nonClass")? firstToken['nonClass']: 0

    if len(parsedTokens) == 1 && nonClass
        "normal class/function completion
    endif

    let isThis = 0
    let  pluginFQCN = s:get_plugin_fqcn(currentFQCN, firstToken)


    if firstToken['methodPropertyText'] == "$this"
        \ || firstToken['methodPropertyText'] == "self"
        \ || firstToken['methodPropertyText'] == "static"

        let currentFQCN = sourceFQCN
        let isThis = 1

    elseif firstToken['methodPropertyText'] == "parent"
        "parent
        let currentClassData = phpcomplete_extended#getClassData(sourceFQCN)
        if !has_key(currentClassData, 'parentclass')
            return ""
        endif
        let currentFQCN = currentClassData['parentclass']

    elseif pluginFQCN != ""
        let currentFQCN = pluginFQCN

    elseif has_key(firstToken, "isNew")
        let currentClassData = phpcomplete_extended#getClassData(sourceFQCN)
        let namespaces = {}
        if has_key(currentClassData, 'namespaces')
            let namespaces = currentClassData['namespaces']
        endif
        let currentFQCN = s:getFQCNForLocalVar(
            \firstToken['methodPropertyText'], namespaces)

    elseif firstToken['methodPropertyText'][0] == "$"
        "local variable
        let linesTilFunc = s:getLinesTilFunc(line('.'))
        let currentFQCN = s:geussLocalVarType(firstToken['methodPropertyText'],
                    \ sourceFQCN, linesTilFunc)

    elseif firstToken['methodPropertyText'][0] == "\\"
        let methodPropertyText = firstToken['methodPropertyText']
        let currentFQCN = s:getFQCNForNsKeyword(methodPropertyText, sourceFQCN)
        return currentFQCN
    else

        let methodPropertyText = firstToken['methodPropertyText']
        let sourceData = phpcomplete_extended#getClassData(sourceFQCN)

        let aliases = {}
        if !empty(sourceData) && has_key(sourceData['namespaces'], 'alias') && !empty(sourceData['namespaces']['alias'])
            let aliases = sourceData['namespaces']['alias']
        endif

        if empty(sourceData)
            let currentFQCN = ""

        elseif has_key(g:phpcomplete_index['classes'], methodPropertyText)
            \ || has_key(g:phpcomplete_extended_core_index['classes'], methodPropertyText)
            let currentFQCN = methodPropertyText
        elseif has_key(sourceData, 'namespaces')
                    \ && has_key(sourceData['namespaces'], 'uses')
                    \ && !empty(sourceData['namespaces']['uses'])
                    \ && has_key(sourceData['namespaces']['uses'], methodPropertyText)
            let currentFQCN = sourceData['namespaces']['uses'][methodPropertyText]. "\\" . methodPropertyText

        elseif !empty(aliases) && has_key(aliases, methodPropertyText)
            let uses_key = aliases[methodPropertyText]
            let uses_value = sourceData['namespaces']['uses'][uses_key]
            let currentFQCN = uses_value == uses_key? uses_key : uses_value. "\\" . uses_key
        endif
    endif

    if empty(currentFQCN)
        return ""
    endif

    return s:getFQCNFromTokens(parsedTokens, currentFQCN, isThis)

endfunction "}}}

function! s:getFQCNFromTokens(parsedTokens, currentFQCN, isThis) "{{{
    let parsedTokens = a:parsedTokens
    let currentFQCN = a:currentFQCN

    let isThis = a:isThis

    let isPrevTokenArray = 0

    if currentFQCN =~  '\[\]$' && len(parsedTokens) 
                \ && has_key(parsedTokens[0], 'isArrayElement')
        let currentFQCN = matchstr(currentFQCN, '\zs.*\ze\[\]$')
        let isPrevTokenArray = 1
    endif
    for token in parsedTokens
        let insideBraceText = token['insideBraceText']
        let methodPropertyText = token['methodPropertyText']
        let insideBraceText = token['insideBraceText']
        let isArrayElement = has_key(token, 'isArrayElement')? 1 :0
        let currentClassData = phpcomplete_extended#getClassData(currentFQCN)
        
        let  pluginFQCN = s:get_plugin_fqcn(currentFQCN,token)

        if insideBraceText[0] == "("
            let currentFQCN = ""
        elseif pluginFQCN != ""
            let currentFQCN = pluginFQCN
        elseif isArrayElement 
            let isPrevTokenArray = 0
            if phpcomplete_extended#isClassOfType(currentFQCN, 'ArrayAccess') 
                    \ && has_key(currentClassData['methods']['all'], 'offsetGet')
                let offsetType = currentClassData['methods']['all']['offsetGet']['return']
                if empty(offsetType)
                    return ""
                endif
                let currentFQCN = offsetType
            endif
            continue
        else
            let classPropertyType = token.isMethod == 1 ? 'method' : 'property'
            let [currentFQCN, isPrevTokenArray] = phpcomplete_extended#getFQCNForClassProperty(
                \ classPropertyType, methodPropertyText, currentFQCN, isThis)

        endif

        if isThis == 1
            let isThis = 0
        endif
        if empty(currentFQCN)
            return ""
        endif
    endfor
    if isPrevTokenArray
        return currentFQCN . '[]'
    endif
    return currentFQCN

endfunction "}}}

function! s:isScalar(type) "{{{
    let scalars =['boolean', 'integer',"float", "string", 'array', 'object',
        \ 'resource', 'mixed', 'number', 'callback', 'null', 'void',
        \ 'bool', 'self', 'int', 'callable']
    return index(scalars, a:type)
endfunction "}}}

function! s:getLinesTilFunc(currentLineNum) "{{{
    let lineNum = a:currentLineNum
    let lines = []
    "get the lines till function declaration
    while 1
        let line = phpcomplete_extended#util#trim(getline(lineNum))

        if !empty(line)
            call add(lines, line)
        endif

        if lineNum == 1 || match(line, 'function\s*\w\+\s*(.*)') > 1
            break
        endif

        let lineNum = lineNum - 1
    endwhile
    return lines
endfunction "}}}

function! s:geussLocalVarType(var, sourceFQCN, lines) "{{{
    let lines = a:lines
    let sourceFQCN = a:sourceFQCN
    let var = a:var
    let fqcn = ""
    for line in lines
        "echoerr match(line, a:var.'\s*=.*') >= 0
        if match(line, a:var.'\s*=.*') >= 0
            "var = .*
            "echoerr line
            let compiedLines = deepcopy(lines)
            let parsedTokens = s:parseForward(
                \ reverse(compiedLines),
                \ line)
            if empty(parsedTokens)
                return ""
            endif
            let fqcn = phpcomplete_extended#util#trim(s:guessTypeOfParsedTokens(parsedTokens))
            "echoerr fqcn
        elseif match(line, '@var\s*'.var.'\s*.*') >=0
            "@var $var fqcn"
            let fqcn = phpcomplete_extended#util#trim(matchstr(line, '@var\s*'.var.'\s*\zs.*\ze\s*\*'))
            let fqcn = phpcomplete_extended#getFQCNFromWord(fqcn)
        endif

        if fqcn != ""
            break
        endif
    endfor

    "try to guess from method params
    if empty(fqcn)
        let localMethodName = matchstr(lines[-1], 'function\s*\zs.*\ze(.*)')
        let sourceClassData = phpcomplete_extended#getClassData(sourceFQCN)

        if empty(sourceClassData)
            return ""
        endif

        if !has_key(sourceClassData['methods']['all'], localMethodName)
            return ""
        endif
        let methodParams = sourceClassData['methods']['all'][localMethodName]['params']
        if empty(methodParams)
            let fqcn = ""
        elseif has_key(methodParams, var) && s:isScalar(methodParams[var]) == -1
            let fqcn = methodParams[var]
        endif
    endif

    return fqcn
endfunction "}}}

function! s:parseLocalVar(lines, varLine) "{{{
    let parsedTokens = s:parseForward(a:lines, a:varLine)
endfunction "}}}

function! phpcomplete_extended#trackMenuChanges() "{{{
    let g:complete_word = ""
    let current_char = getline('.')[col('.') -2]

    if !pumvisible() && s:psr_class_complete
        \ && (current_char == '(' || current_char == ':' || current_char == ' ')
        let s:psr_class_complete = 0
        let cur_pos = getpos(".")
        let prev_pos = copy(cur_pos)
        let new_pos = copy(cur_pos)
        let new_pos[2] = new_pos[2] -2
        call setpos(".", new_pos)
        let cur_word = expand("<cword>")
        if cur_word[0] =~ '\d'
            let keyword = matchstr(getline('.')[0:new_pos[2]-1], '\s*\zs.\{-}-\(\d\+\)\?\ze')
            if match(keyword, '-') > 0
                let cur_word = keyword
            endif
            call feedkeys("\<C-o>dF-")
            call feedkeys("\<C-o>x")
            call feedkeys("\<C-o>l")
        else
            call setpos(".", prev_pos)
        endif
        let fqcn = ""
        if match(cur_word, '-') > 0
            let word = split(cur_word, '-')[0]
            let idx = split(cur_word, '-')[1] - 1
            "echoerr word

            if has_key(g:phpcomplete_index['class_fqcn'], word)
                \ && string(g:phpcomplete_index['class_fqcn'][word])[0] == '['

                let fqcn = g:phpcomplete_index['class_fqcn'][word][idx]
            endif
        elseif has_key(g:phpcomplete_index['class_fqcn'], cur_word)
                    \ && string(g:phpcomplete_index['class_fqcn'][cur_word])[0] == "'"
            let fqcn = g:phpcomplete_index['class_fqcn'][cur_word]
        endif

        if fqcn != "" && g:phpcomplete_extended_auto_add_use
            call phpcomplete_extended#addUse(cur_word, fqcn)
        endif
    endif
endfunction "}}}

function! phpcomplete_extended#addUse(word, fqcn) "{{{
    let word = a:word
    let fqcn = a:fqcn
    let cur_pos = getpos('.')

    if empty(fqcn)
        if !has_key(g:phpcomplete_index['class_fqcn'], word)
            return
        endif

        let fqcn_data = g:phpcomplete_index['class_fqcn'][word]

        if empty(fqcn) && string(fqcn_data)[0] == '['
            let fqcn_data = deepcopy(g:phpcomplete_index['class_fqcn'][word])
            let prompt_data =  map(copy(fqcn_data), 'v:key+1.". " . v:val')
            call insert(prompt_data, "Select FQCN:")
            let selected = inputlist(prompt_data)
            let fqcn = fqcn_data[selected-1]
        elseif empty(fqcn) && string(fqcn_data)[0] == "'"
            let fqcn = fqcn_data
        endif

        if empty(fqcn)
            return
        endif

    endif


    let lines_to_class = getline(0, search('^\s*\(class\|interface\)'))
    call setpos('.', cur_pos)

    if empty(lines_to_class) || phpcomplete_extended#util#trim(lines_to_class[0]) != "<?php"
        return
    endif

    let last_use_pos = -1
    let namespace_pos = -1

    for line in lines_to_class
        if match(line, '^\s*use\s*'.escape(fqcn, '\').'\s*;') >= 0
            return
        endif
        if match(line, '^\s*use\s*.*;') >=0
            let last_use_pos = index(lines_to_class, line)+1
        endif
        if match(line, '^\s*namespace\s*.*;') >=0
            let namespace_pos = index(lines_to_class, line)+1
        endif

    endfor
    if last_use_pos == -1
        let last_use_pos = namespace_pos == -1? 1 : namespace_pos + 1
    endif
    call append(last_use_pos, ['use '. fqcn. ';'])
    let cur_pos[1] = cur_pos[1] +1
    call setpos('.', cur_pos)

endfunction "}}}

function! phpcomplete_extended#gotoSymbolORDoc(type) "{{{
    let data = {}
    let type = a:type
    if match(expand('%'), 'phpcomplete_extended-Doc') == 0
        let type = 'doc'
    endif

    let cur_word  = expand("<cword>")
    let word_fqcn = s:get_plugin_resolve_fqcn(cur_word)
    if word_fqcn == cur_word
        let word_fqcn = phpcomplete_extended#getFQCNFromWord(cur_word)
    endif

    "messed up
    if word_fqcn != ""
        let classData = phpcomplete_extended#getClassData(word_fqcn)
        if !has_key(g:phpcomplete_index['fqcn_file'], word_fqcn)
            if type == "doc"
                let data = {
                    \'doc': classData['docComment'],
                    \ 'title': word_fqcn
                \}
            elseif type == "goto"
                let data = {}
            endif
        else
            let filename = g:phpcomplete_index['fqcn_file'][word_fqcn]
            let line = classData['startLine']
            let data = {}
            let data.file = filename
            let data.command = '+' . line
            let data.title = word_fqcn
            let data.doc = classData['docComment']
        endif
    else
        let data = s:getJumpDataOfCurrentWord(type)
    endif


    if empty(data)
        if type == 'doc'
            call feedkeys('K', 'n')
        elseif type == 'goto'
            call feedkeys("\<C-]>", 'n')
        endif
        return
    endif

    if type == 'doc'
        call s:openDoc(data)

    elseif type == 'goto'
        call s:gotoLine(data)
    endif
endfunction "}}}

function! phpcomplete_extended#getFQCNFromWord(word) "{{{
    let word = a:word
    let current_file_location = phpcomplete_extended#util#substitute_path_separator(fnamemodify(bufname('%'), ':p:.')) "current file
    let current_fqcn = s:getFQCNFromFileLocation(current_file_location)
    if current_fqcn == ""
        return ""
    endif
    let current_class_data = phpcomplete_extended#getClassData(current_fqcn)
    if empty(current_class_data)
        return ""
    endif

    let fqcn = s:getFQCNForLocalVar(word, current_class_data['namespaces'])
    if fqcn == ""
        return ""
    endif
    return fqcn
endfunction "}}}

function! s:gotoLine(data) "{{{
    silent! execute "e ". a:data.command . ' ' . a:data.file
    silent! execute "normal! zt"
    normal! zv
    normal! zz
endfunction "}}}

function! s:openDoc(data) "{{{
        let doc_buf_exists = 0
        for i in range(1, winnr('$'))
            let bufname = bufname(winbufnr(i))
            if match(bufname, 'phpcomplete_extended-Doc') != -1
                let doc_buf_exists = 1
                let winnr = i
            endif
        endfor

        "TODO: go to previous doc

        if doc_buf_exists
            silent! execute winnr."wincmd w"
            setlocal modifiable noreadonly
            normal ggdG
        else
            silent! execute "new"
            setlocal modifiable noreadonly
            setlocal nobuflisted
            setlocal buftype=nofile noswapfile
            setlocal bufhidden=delete
            setlocal nonumber
            let bufname = printf("%s: %s", 'phpcomplete_extended-Doc', a:data['title'])
            silent! file `=bufname`
        endif


        let doc = map(split(a:data['doc'], "\n"), "substitute(v:val, '    ', '', 'g')")
        call append(0, doc)
        "TODO: set ft=php "slow, have to do some thing about it
        normal! gg
        setlocal nomodifiable readonly
endfunction "}}}

function! s:getJumpDataOfCurrentWord(type) "{{{
    let cur_word  = expand("<cword>")
    let match_till_cur_word = matchstr(getline('.'), '\s*\zs\C.\{-}\<'.cur_word . '\>' .'(\?')
    if match_till_cur_word[len(match_till_cur_word)-1] == "("
        let match_till_cur_word .= "'')"
    endif
    let parsedTokens = phpcomplete_extended#parsereverse(match_till_cur_word, line('.'))

    if empty(parsedTokens)
        return {}
    endif

    "very messed up, have to break it
    let static_or_ref = 0
    if len(parsedTokens) == 1 &&
            \ (
            \ has_key(parsedTokens[0], 'isNew')
            \ || has_key(parsedTokens[0], 'nonClass')
            \ || parsedTokens[0]['methodPropertyText'][0] == "$"
            \)
        let lastToken = parsedTokens[0]
    "elseif len(parsedTokens) == 2
            "\ && match(match_till_cur_word, "::") > 0
        "let lastToken = remove(parsedTokens, 0)
        "let static_or_ref = 1
    else
        let lastToken = remove(parsedTokens, -1)
    endif

    if has_key(lastToken, 'nonClass')
        let fqcn = lastToken['methodPropertyText']
        let fqcn = s:get_plugin_resolve_fqcn(fqcn)
    else
        let fqcn = s:guessTypeOfParsedTokens(parsedTokens)
        let fqcn = s:get_plugin_resolve_fqcn(fqcn)

        if fqcn == ""
            return {}
        endif
    endif

    if static_or_ref
        let methodPropertyText = parsedTokens[0]['methodPropertyText']
    else
        let methodPropertyText = lastToken['methodPropertyText']
    endif


    let fqcn_data = {}

    let isInternal = 0
    let isMethod = 0
    let isClass = 0
    let isProperty = 0
    if has_key(lastToken, 'nonClass') && lastToken['nonClass']
        let isInternal = 1

        if static_or_ref
            let isMethod = 1
            let cur_word = fqcn
        endif

        if has_key(g:phpcomplete_extended_core_index['functions'], cur_word)
            let fqcn_data = g:phpcomplete_extended_core_index['functions'][cur_word]
        elseif has_key(g:phpcomplete_extended_core_index['classes'], cur_word)
            let fqcn_data = g:phpcomplete_extended_core_index['classes'][cur_word]
        endif

        if static_or_ref
            \ && has_key(fqcn_data['methods']['all'], methodPropertyText)
            let fqcn_data = fqcn_data['methods']['all'][methodPropertyText]
        endif

    elseif has_key(lastToken, 'isMethod')
        let isProperty = !lastToken['isMethod']
        let isMethod = lastToken['isMethod']
        let fqcn_data = {}

        let method_property_key = lastToken['isMethod']? 'methods' : 'properties'
        if has_key(g:phpcomplete_index['classes'], fqcn)
            \ && has_key(g:phpcomplete_index['classes'][fqcn][method_property_key]['all'], methodPropertyText)

            let fqcn_data = g:phpcomplete_index['classes'][fqcn][method_property_key]['all'][methodPropertyText]

        elseif has_key(g:phpcomplete_extended_core_index['classes'], fqcn)
            \ && has_key(g:phpcomplete_extended_core_index['classes'][fqcn][method_property_key]['all'], methodPropertyText)

            let fqcn_data = g:phpcomplete_extended_core_index['classes'][fqcn][method_property_key]['all'][methodPropertyText]
        endif

    elseif has_key(lastToken, 'isNew')
        let isClass = 1
        let fqcn_data = phpcomplete_extended#getClassData(fqcn)
    else
        let isClass = 1
        let fqcn_data = phpcomplete_extended#getClassData(fqcn)
    endif
    if empty(fqcn_data)
        return {}
    endif
    if a:type == 'doc'

        let title = fqcn
        if isMethod
            let title = fqcn. "--". methodPropertyText . "()"
        endif

        if isProperty
            let title = fqcn. "--". methodPropertyText
        endif
        if isInternal
            let title = fqcn
        endif
        return {
                \'doc': fqcn_data['docComment'],
                \ 'title': title
            \}
    elseif a:type == 'goto'
        if isInternal
            return {}
        endif
        let classData = phpcomplete_extended#getClassData(fqcn)

        let gotoData = {}

        if isClass
            let gotoData.file = fqcn_data['file']
            let gotoData.command = '+'.fqcn_data['startLine']
        elseif isMethod
            let gotoData.file = fqcn_data['origin']
            let gotoData.command = '+'.fqcn_data['startLine']
        elseif isProperty
            let gotoData.file = fqcn_data['origin']
            let gotoData.command ='+/^\\s*\\(private\\|public\\|protected\\|static\\)\\s*.*'.'$'.methodPropertyText
        endif
        return gotoData
    endif
    return {}
endfunction "}}}

function! s:parseForward(lines, varLine) "{{{
    call remove(a:lines, 0, index(a:lines, a:varLine))
    let lines = a:lines
    let varDecLine = phpcomplete_extended#util#trim(matchstr(a:varLine, '=\zs.*'))
    let joinedLine = varDecLine.join(lines, "")
    let parsedTokens = []
    let parsedTokens = phpcomplete_extended#parser#forwardParse(joinedLine, parsedTokens)

    if len(parsedTokens) && !has_key(parsedTokens[-1], 'pEnd')
        return []
    endif
    return parsedTokens
endfunction "}}}

function! phpcomplete_extended#parsereverse(cursorLine, cursorLineNumber) "{{{
    if !exists('g:phpcomplete_index')
        return []
    endif
    let cursorLine = phpcomplete_extended#util#trim(a:cursorLine)
    let parsedTokens = []
    let parsedTokens = phpcomplete_extended#parser#reverseParse(cursorLine, [])


    if empty(parsedTokens) 
            \ || (len(parsedTokens) && has_key(parsedTokens[0], 'start') && parsedTokens[0].start == 0)
        let linesTillFunc = s:getLinesTilFunc(a:cursorLineNumber)
        let joinedLines = join(reverse(linesTillFunc),"")
        let parsedTokens =  phpcomplete_extended#parser#reverseParse(joinedLines, [])
        return parsedTokens
    endif
    return parsedTokens
endfunction "}}}

function! s:getFQCNForNsKeyword(keyword, sourceFQCN) "{{{
    let sourceData = phpcomplete_extended#getClassData(a:sourceFQCN)
    let keyword = matchstr(a:keyword, '\\\zs.*')
    let fqcn = ""

    if empty(sourceData)
        return ""
    endif
    let sourceUses = sourceData['namespaces']['uses']

    let splitedKeyword = split(keyword, "\\")
    if has_key(sourceUses, splitedKeyword[0])
        let key = remove(splitedKeyword, 0)
        let fqcn = sourceUses[key] . "\\" .keyword
    else
        let keywordClassData = phpcomplete_extended#getClassData(keyword)
        if !empty(keywordClassData)
            let fqcn = keyword
        endif
    endif
    return fqcn

endfunction "}}}

function! s:getFQCNForLocalVar(classname, namespaces) " {{{
    let classname = a:classname
    let namespaces = a:namespaces
    let aliases = {}
    if has_key(a:namespaces, 'alias') && !empty(a:namespaces['alias'])
        let aliases = a:namespaces['alias']
    endif

    let fqcn = ""

    if has_key(g:phpcomplete_index['fqcn_file'], classname)
        return classname
    endif
    if has_key(namespaces, 'uses') && len(namespaces['uses']) != 0 && has_key(namespaces['uses'], classname) "if no data found json_encode make it to dictionary
        let fqcn = namespaces['uses'][classname]. "\\". classname
        return fqcn
    endif
    if !empty(aliases) && has_key(aliases, classname)
        let uses_key = aliases[classname]
        let uses_value = namespaces['uses'][uses_key]
        let fqcn = uses_value == uses_key? uses_key : uses_value. "\\" . uses_key
        return fqcn
    endif
    if has_key(g:phpcomplete_extended_core_index['classes'], classname)
        return classname
    endif
    let namespace_section = ""
    if has_key(namespaces, 'file')
        let namespace_section = namespaces['file'] . "\\"
    endif
    let full_fqcn = namespace_section . classname
    if has_key(g:phpcomplete_index['fqcn_file'], full_fqcn)
        return full_fqcn
    endif
    return ""
endfunction
" }}}

function! phpcomplete_extended#getFQCNForClassProperty(type, property, parent_fqcn, is_this) " {{{
    let type = a:type
    let is_this = a:is_this
    let property = a:property
    let classname = ''
    let isArray = 0

    let this_fqcn = a:parent_fqcn
    let this_fqcn = s:get_plugin_resolve_fqcn(this_fqcn)
    let this_class_data = phpcomplete_extended#getClassData(this_fqcn)

    if empty(this_class_data)
        return ["", 0]
    endif


    "in same namespace folder. so not declared in use section
    if type == 'property'
        let this_properties = this_class_data['properties']['all']
        if !has_key(this_properties, property)
            return ['', 0]
        endif
        let classname = this_properties[property]['type']
        if has_key(this_properties[property], 'array_type')
            let isArray = this_properties[property]['array_type']
        endif
    elseif type == 'method'
        let this_methods = this_class_data['methods']['all']
        if !has_key(this_methods, property)
            return ['', 0]
        endif
        if has_key(this_methods[property], 'return')
            let classname = this_methods[property]['return']
            if has_key(this_methods[property], 'array_return')
                let isArray = this_methods[property]['array_return']
            endif
        endif
    endif

    return [classname, isArray]
endfunction " }}}

function! s:getClassMenuEntries(base) "{{{
    let class_menu_entries = deepcopy(g:phpcomplete_index['class_func_menu_entries'])
    let class_menu_entries = filter(class_menu_entries, 'v:val.word =~ "^' . a:base .'" && v:val.kind == "c"')
    return class_menu_entries
endfunction "}}}

function! s:getUseMenuEntries(base) "{{{
    let menu_list = []
    let fqcns = deepcopy(keys(g:phpcomplete_index['fqcn_file']))
    if a:base != ''
        "echoerr escape(a:base, ' \')
        "let fqcns  = filter(fqcns, 'v:val =~ "^' . escape(a:base, ' \') .'"')
        let fqcns  = filter(fqcns, 'v:val =~ "^' . escape(a:base, ' \\') .'"')
    endif
    for fqcn in fqcns
        let menu_list += [{'word': fqcn,'kind': 'v', 'menu': fqcn, 'info': fqcn}]
    endfor
    return menu_list
endfunction "}}}

function! s:getNonClassMenuEntries(base) "{{{
    let menu_entries = []
    let class_func_menu_entries = deepcopy(g:phpcomplete_index['class_func_menu_entries'])
    let class_func_menu_entries = filter(class_func_menu_entries, 'v:val.word =~# "^' . a:base .'"')
    let plugin_menu_entries = s:get_plugin_menu_entries("", a:base, 1, 0)

    let menu_entries += plugin_menu_entries
    let menu_entries += class_func_menu_entries

    return menu_entries
endfunction "}}}

function! phpcomplete_extended#getMenuEntries(fqcn, base, is_this, is_static) " {{{
    let empty_dict = []
    let fqcn = a:fqcn
    let is_this = a:is_this
    let is_static = a:is_static
    if fqcn == ""
        return []
    endif

    let menu_list = []

    let class_data = phpcomplete_extended#getClassData(fqcn)
    if len(class_data) == 0
        return empty_dict
    endif

    "constants
    if is_static
        if len(class_data['constants']) != 0
            let constants = deepcopy(keys(class_data['constants']))
            if a:base != ''
                let constants  = filter(constants, 'v:val =~ "^' . a:base .'"')
            endif
            for constant in constants
                let menu_list += [{'word': constant,'kind': 'v', 'menu': constant, 'info': constant}]
            endfor
        endif
    endif

    "properties
    if len(class_data['properties']['all']) != 0
        if is_this
            let properties = deepcopy(keys(class_data['properties']['all']))
        elseif is_static
            let properties = deepcopy(class_data['properties']['modifier']['static'])
        else
            let properties = deepcopy(class_data['properties']['modifier']['public'])
        endif
        if a:base != ''
            let properties  = filter(properties, 'v:val =~ "^' . a:base .'"')
        endif
        for property in properties
            let property_data = class_data['properties']['all'][property]
            let menu_list += [{'word': property,'kind': 'v', 'menu': property, 'info': property_data['docComment']}]
        endfor
    endif

    "methods
    if len(class_data['methods']['all']) != 0
        if is_this
            let methods = deepcopy(keys(class_data['methods']['all']))
        elseif is_static
            let m = deepcopy(class_data['methods']['modifier']['static'])
            "sometime json_encode makes methods array as dictionary
            if type(m) == 4
                let methods = values(m)
            else
                let methods = m
            endif
        else
            let m = deepcopy(class_data['methods']['modifier']['public'])
            "sometime json_encode makes methods array as dictionary
            if type(m) == 4
                let methods = values(m)
            else
                let methods = m
            endif
        endif

        if a:base != ''
            let methods  = filter(methods, 'v:val =~ "^' . a:base .'"')
        endif
        for method in methods
            if match(method, '__') == 0
                continue
            endif

            let method_info = class_data['methods']['all'][method]
            let menu_list += [{'word': method,'kind': 'f', 'menu': method_info['signature'], 'info': method_info['docComment']}]
        endfor
    endif
    return menu_list
endfunction
" }}}

function! s:getFQCNFromFileLocation(filelocation) " {{{
    "TODO: add cache
    if !exists('g:phpcomplete_index')
        return ""
    endif
    let filelocation = a:filelocation
    if has('win32') || has('win64')
        let filelocation = substitute(filelocation,'\\', '/', 'g')
    endif
    let fqcn = ""
    if has_key(g:phpcomplete_index['file_fqcn'], filelocation)
        let fqcn = g:phpcomplete_index['file_fqcn'][filelocation]
    endif
    return fqcn
endfunction
" }}}

function! phpcomplete_extended#getFileFromFQCN(fqcn) "{{{
    let fqcn = a:fqcn
    let filelocation = ""
    if has_key(g:phpcomplete_index['fqcn_file'], fqcn)
        let filelocation = g:phpcomplete_index['fqcn_file'][fqcn]
        if has('win32') || has('win64')
            let filelocation = substitute(filelocation,'\\', '/', 'g')
        endif
    endif
    return filelocation
endfunction "}}}

function! phpcomplete_extended#getClassKeyFromFQCN(fqcn) "{{{
    let fqcn = a:fqcn
    let classKey = ""
    if !g:phpcomplete_extended_cache_disable && has_key(g:phpcomplete_index_cache['fqcn_classkey_cache'], fqcn)
        return g:phpcomplete_index_cache['fqcn_classkey_cache'][fqcn]
    else
        if has_key(g:phpcomplete_index['fqcn_classkey'], fqcn)
            let classKey = g:phpcomplete_index['fqcn_classkey'][fqcn]
            let g:phpcomplete_index_cache['fqcn_classkey_cache'][fqcn] = classKey
        endif
    endif
    return classKey
endfunction "}}}

function! phpcomplete_extended#getClassData(fqcn) " {{{
    let fqcn = a:fqcn
    let empty_dict = {}
    if g:phpcomplete_extended_cache_disable == 0 && has_key(g:phpcomplete_index_cache['classname_cache'], fqcn)
        let data = g:phpcomplete_index_cache['classname_cache'][fqcn]
    else

        if has_key(g:phpcomplete_index['classes'], fqcn)
            let data = g:phpcomplete_index['classes'][fqcn]
        elseif has_key(g:phpcomplete_extended_core_index['classes'], fqcn)
            let data = g:phpcomplete_extended_core_index['classes'][fqcn]
        else
            return empty_dict
        endif
        if has_key(data['methods']['all'], 'nnnnnnnn')
            call remove(data['methods']['all'], "nnnnnnnn")
        endif

        if has_key(data['properties']['all'], 'nnnnnnnn')
            call remove(data['properties']['all'], "nnnnnnnn")
        endif

        let g:phpcomplete_index_cache['classname_cache'][fqcn] = data
        return data
    endif
    return g:phpcomplete_index_cache['classname_cache'][fqcn]
endfunction
" }}}

function! s:setClassData(fqcn, file,  classData) "{{{
    let fqcn = a:fqcn
    let file = a:file
    let className = a:classData['classname']
    let classData = a:classData
    let g:phpcomplete_index['classes'][fqcn] = classData
    let g:phpcomplete_index['file_fqcn'][file] = fqcn
    let g:phpcomplete_index['fqcn_file'][fqcn] = file

    if index(g:phpcomplete_index['class_list'], className) == -1
        call add(g:phpcomplete_index['class_list'], className)
    endif

    if has_key(g:phpcomplete_index['class_fqcn'], className)
        if string(g:phpcomplete_index['class_fqcn'][className])[0] == "'"
            \ && g:phpcomplete_index['class_fqcn'][className] != fqcn

            let tmp_class_fqcn = []
            call add(tmp_class_fqcn, g:phpcomplete_index['class_fqcn'][className])
            call add(tmp_class_fqcn, fqcn)
            call remove(g:phpcomplete_index['class_fqcn'], className)
            let g:phpcomplete_index['class_fqcn'][className] = tmp_class_fqcn

        elseif string(g:phpcomplete_index['class_fqcn'][className])[0] == "["
                \ && index(g:phpcomplete_index['class_fqcn'][className], fqcn) == -1

            call add(g:phpcomplete_index['class_fqcn'][className], fqcn)
        endif
    else
        let g:phpcomplete_index['class_fqcn'][className] = fqcn
    endif

    if index(g:phpcomplete_index['class_func_const_list'], className) == -1
        call add(g:phpcomplete_index['class_func_const_list'], className)
    endif

    if has_key(g:phpcomplete_index_cache['classname_cache'], fqcn)
        call remove(g:phpcomplete_index_cache['classname_cache'], fqcn)
    endif
endfunction "}}}

function! s:makeCacheDir() "{{{
    let cache_dir = phpcomplete_extended#util#substitute_path_separator(fnamemodify(getcwd(), ':p:h').'/.phpcomplete_extended')
    if !isdirectory(cache_dir)
        call mkdir(cache_dir)
    endif
endfunction "}}}

function! phpcomplete_extended#saveIndexCache() " {{{
    if !phpcomplete_extended#is_phpcomplete_extended_project()
        return
    endif

    let cache_dir = phpcomplete_extended#util#substitute_path_separator(fnamemodify(getcwd(), ':p:h').'/.phpcomplete_extended')
    let index_cache_file = phpcomplete_extended#util#substitute_path_separator(fnamemodify(cache_dir, ':p:h')."/index_cache")
    let content = []
    if exists('g:phpcomplete_index_cache')
        call add(content, string(g:phpcomplete_index_cache))
        call writefile(content, index_cache_file)
    endif
endfunction
" }}}

function! s:getCacheFile() " {{{
    if !phpcomplete_extended#is_phpcomplete_extended_project()
        return
    endif
    let index_cache_file = phpcomplete_extended#util#substitute_path_separator(fnamemodify(getcwd(), ':p:h')."/.phpcomplete_extended/index_cache")
    return index_cache_file
endfunction
" }}}

function! s:loadIndexCache() " {{{
    if !phpcomplete_extended#is_phpcomplete_extended_project()
        return
    endif
    if exists('g:phpcomplete_index_cache_loaded') && g:phpcomplete_index_cache_loaded
        return
    endif

    let index_cache_file = s:getCacheFile()

    if !filereadable(index_cache_file)
        let g:phpcomplete_index_cache = {}
        let g:phpcomplete_index_cache['classname_cache'] = {}
        let g:phpcomplete_index_cache['namespace_cache'] = {}
        let g:phpcomplete_index_cache['methods_cache'] = {}
        let g:phpcomplete_index_cache['fqcn_classkey_cache'] = {}
    else
        let content = readfile(index_cache_file)
        if len(content) == 0
            let g:phpcomplete_index_cache = {}
            let g:phpcomplete_index_cache['classname_cache'] = {}
            let g:phpcomplete_index_cache['namespace_cache'] = {}
            let g:phpcomplete_index_cache['methods_cache'] = {}
            let g:phpcomplete_index_cache['fqcn_classkey_cache'] = {}
        else
            let true = 1
            let false = 0
            let null = 0
            sandbox let g:phpcomplete_index_cache = eval(join(content, '\n'))
        endif
    endif
    let g:phpcomplete_index_cache_loaded = 1
endfunction
" }}}

function! s:loadIndex() " {{{
    if !phpcomplete_extended#is_phpcomplete_extended_project()
        return
    endif

    if exists('g:phpcomplete_index_loaded') && g:phpcomplete_index_loaded
        return
    endif

    if index(s:disabled_projects, getcwd()) != -1
        return
    endif

    let index_file = phpcomplete_extended#util#substitute_path_separator(getcwd().'/.phpcomplete_extended/phpcomplete_index')
    let plugin_index_file = s:getPluginIndexFileName()

    if !filereadable(index_file)
        let initial_message = "Composer project detected, Do you want to create index?"
        let ret = phpcomplete_extended#util#input_yesno(initial_message)
        if !ret
            call add(s:disabled_projects, getcwd())
            return
        endif
        echo "\n\n"
        call phpcomplete_extended#generateIndex()
    endif

    if !g:phpcomplete_index_loaded
        call phpcomplete_extended#util#print_message("Loading Index")
        if filereadable(plugin_index_file)
            let s:plugin_index = s:readIndex(plugin_index_file)
            call s:set_plugin_indexes(plugin_index_file)
            let s:plugin_ftime = getftime(plugin_index_file)
        endif

        let g:phpcomplete_index = s:readIndex(index_file)
        call phpcomplete_extended#util#print_message("Index Loaded.")
        let g:phpcomplete_index_loaded = 1
    endif

endfunction
" }}}
"
function! phpcomplete_extended#clear_disabled_project() "{{{
    let s:disabled_projects = []
endfunction "}}}

function! s:clearIndex() " {{{
    if !phpcomplete_extended#is_phpcomplete_extended_project()
        return
    endif
    let g:phpcomplete_index = {}
    let g:phpcomplete_index_loaded = 0
endfunction
" }}}

function! phpcomplete_extended#readDataForProject() "{{{
    if !s:isCurrentProject()
        let s:current_project_dir = getcwd()
        let g:phpcomplete_index_loaded = 0
        call phpcomplete_extended#loadProject()
    endif
endfunction "}}}

function! phpcomplete_extended#clearIndexCache() " {{{
    if !phpcomplete_extended#is_phpcomplete_extended_project()
        return
    endif
    let index_cache_file = s:getCacheFile()
    if filereadable(index_cache_file)
        call delete(index_cache_file)
    endif
    let g:phpcomplete_index_cache = {}
    let g:phpcomplete_index_cache['classname_cache'] = {}
    let g:phpcomplete_index_cache['namespace_cache'] = {}
    let g:phpcomplete_index_cache['methods_cache'] = {}
    let g:phpcomplete_index_cache_loaded = 0
endfunction
" }}}

function! s:isCurrentProject() "{{{
    return s:current_project_dir == getcwd()
endfunction "}}}

function! phpcomplete_extended#is_phpcomplete_extended_project() " {{{
    if filereadable(getcwd(). '/composer.json') && filereadable(getcwd(). "/vendor/autoload.php")
        return 1
    endif
    return 0
endfunction
" }}}

function! phpcomplete_extended#loadProject() "{{{
    if !phpcomplete_extended#is_phpcomplete_extended_project()
        return
    endif

    let s:update_info = {}
    call s:register_plugins()

    call s:makeCacheDir()
    call phpcomplete_extended#loadCoreIndex()

    call s:loadIndex()
    call s:loadIndexCache()

endfunction "}}}

function! phpcomplete_extended#reload() " {{{
    if !phpcomplete_extended#is_phpcomplete_extended_project()
        return
    endif

    let s:update_info = {}
    call s:register_plugins()

    call s:makeCacheDir()
    call phpcomplete_extended#loadCoreIndex()

    call s:clearIndex()
    call phpcomplete_extended#clearIndexCache()

    call s:loadIndex()
    call s:loadIndexCache()
endfunction
" }}}

function! s:copyCoreIndex() "{{{
    let src  = phpcomplete_extended#util#substitute_path_separator(g:phpcomplete_extended_root_dir . "/bin/core_index")
    let cache_dir = phpcomplete_extended#util#substitute_path_separator(fnamemodify(getcwd(), ':p:h').'/.phpcomplete_extended')
    let dest = phpcomplete_extended#util#substitute_path_separator(cache_dir. "/core_index")
    if empty(findfile(dest, cache_dir))
        call phpcomplete_extended#util#copy(src, dest)
    endif
endfunction "}}}

function! phpcomplete_extended#loadCoreIndex() "{{{
    if exists('g:core_index_loaded') && g:core_index_loaded
        return
    endif
    let cache_dir = phpcomplete_extended#util#substitute_path_separator(fnamemodify(getcwd(), ':p:h').'/.phpcomplete_extended')
    let location = phpcomplete_extended#util#substitute_path_separator(cache_dir. "/core_index")

    if !filereadable(location)
        call s:copyCoreIndex()
    endif

    let content = readfile(location)
    let true = 1
    let false = 0
    let null = 0
    sandbox let g:phpcomplete_extended_core_index = eval(join(content, '\n'))
    let g:core_index_loaded = 1
endfunction "}}}

function! phpcomplete_extended#generateIndex(...) "{{{
    if !s:valid_composer_command()
        echoerr printf('The composer command "%s" is not a valid Composer command. Please set g:phpcomplete_index_composer_command in your .vimrc file', g:phpcomplete_index_composer_command)
        return
    endif

    call s:makeCacheDir()
    call s:copyCoreIndex()
    call s:register_plugins()

    let input = g:phpcomplete_extended_root_dir . "/bin/IndexGenerator.php generate"
    if len(a:000) == 1 && a:1 == '-verbose'
        let input .= ' -verbose'
    endif
    let input = phpcomplete_extended#util#substitute_path_separator(input)

    let plugin_php_file_command = join(map(copy(s:plugin_php_files), '" -u ".v:val'))

    let cmd = 'php ' . input . plugin_php_file_command
    "echoerr cmd
    "return

    let composer_command = g:phpcomplete_index_composer_command . " dumpautoload --optimize  1>/dev/null 2>&1"
    echomsg "Generating autoload classmap"
    call vimproc#system(composer_command)

    echomsg "Generating index..."
    let out =  vimproc#system(cmd)
    if out == "success"
        echomsg "Index generated"
    endif
    echo out
endfunction "}}}

function! s:valid_composer_command() "{{{
    let cmd    = printf('%s --version', g:phpcomplete_index_composer_command)
    let output = system(cmd)
    return output =~ 'Composer version'
endfunction "}}}

function! phpcomplete_extended#updateIndex(background) "{{{
    let s:update_info = {}
    if !phpcomplete_extended#is_phpcomplete_extended_project() || &ft != 'php'
        return
    endif
    let file_location = phpcomplete_extended#util#substitute_path_separator(fnamemodify(bufname('%'), ':p:.')) "current file
    let update_time = getftime(bufname('%'))
    let fileName = 'update_cache_'. update_time
    let plugin_php_file_command = join(map(copy(s:plugin_php_files), '" -u ".v:val'))
    let input = printf('%s %s %s %s', g:phpcomplete_extended_root_dir . "/bin/IndexGenerator.php update" , file_location,  fileName, plugin_php_file_command)
    let input = phpcomplete_extended#util#substitute_path_separator(input)
    let cmd = 'php '. input

    if a:background
        let cmd .= ' 1>/dev/null 2>/dev/null'
        call vimproc#system_bg(cmd)
    else
        let out =  vimproc#system(cmd)
        echo out
    endif

    let s:update_info['update_available'] = 1
    let s:update_info['update_time'] = update_time
    let s:update_info['update_file_name'] = fileName
    let s:update_info['updated_file'] = file_location
endfunction "}}}

function! s:get_update_command() "{{{

    return cmd
endfunction "}}}

function! phpcomplete_extended#checkUpdates() "{{{
    if !phpcomplete_extended#is_phpcomplete_extended_project()
        return
    endif
    let timeout = 1000
    "echoerr string(s:update_info)
    if has_key(s:update_info, 'update_available') && s:update_info['update_available']
        if localtime() - s:update_info['update_time'] > timeout
            let s:update_info = {}
            return
        endif
        let update_file = phpcomplete_extended#util#substitute_path_separator(fnamemodify(getcwd(), ':p:h').'/.phpcomplete_extended/'.s:update_info['update_file_name'])

        if filereadable(update_file)
            try
                let updateData = s:readIndex(update_file)
            catch 
                echoerr "Error occured while reading update index"
                return
            endtry

            call s:updateLocalCache(updateData)
            call delete(update_file)
            let s:update_info = {}
        endif
    endif

    let plugin_index_file = s:getPluginIndexFileName()

    if !filereadable(plugin_index_file)
        return
    endif

    let plugin_file_time = getftime(plugin_index_file)
    if plugin_file_time > s:plugin_ftime
        call s:set_plugin_indexes(plugin_index_file)
        let s:plugin_ftime = plugin_file_time
    endif

endfunction "}}}

function! s:getPluginIndexFileName() "{{{
    return phpcomplete_extended#util#substitute_path_separator(fnamemodify(getcwd(), ':p:h').'/.phpcomplete_extended/plugin_index')
endfunction "}}}

function! s:readIndex(filename) "{{{
    if(!filereadable(a:filename))
        echoerr printf('Could not read index file %s', fnamemodify(a:filename, ':.'))
        return
    endif
    let file_content = readfile(a:filename)
    let true = 1
    let false = 0
    let null = 0
    sandbox let eval_data = eval(file_content[0])
    return eval_data
endfunction "}}}

function! s:updateLocalCache(updateData) "{{{
    let updateData = a:updateData
    let fqcn = updateData['classdata']['fqcn']
    let file = updateData['classdata']['file']
    let classData = updateData['classdata']['data']
    let extendsData = updateData['extends']
    let implementsData = updateData['interfaces']
    call s:setClassData(fqcn, file, classData)
    call s:updateIntrospectionData('extends', fqcn, extendsData)
    call s:updateIntrospectionData('implements', fqcn, implementsData)
    call s:updateMenuEntries(classData['classname'])
endfunction "}}}

function! s:updateIntrospectionData(type,fqcn, data) "{{{
    let type = a:type "type is extends/implements
    let fqcn = a:fqcn
    let data = a:data
    let collection = g:phpcomplete_index[type]
    if type(data) != type({})
        return
    endif
    for added in data['added']
        if !has_key(collection, added)
            let collection[added] = []
        endif
        call add(collection[added], fqcn)
    endfor
    for removed in data['removed']
        if !has_key(collection, removed)
            continue
        endif

        let index = index(collection[removed], fqcn)
        if index < 0
            continue
        endif

        call remove(collection[removed], index(collection[removed], fqcn))
    endfor
endfunction "}}}

function! s:updateMenuEntries(className) "{{{
    let className = a:className
    let fqcns = g:phpcomplete_index['class_fqcn'][className]
    let class_menu_entries = g:phpcomplete_index['class_func_menu_entries']
    let idx = 0

    for class_menu_entry in class_menu_entries
        if class_menu_entry['word'] =~ '^'. className
            call remove(class_menu_entries, idx)
        else
            let idx = idx + 1
        endif
    endfor

    let menu_entries = []
    if type(fqcns) == type('')
        call add(menu_entries,  {
            \ 'word': className,
            \ 'kind': 'c',
            \ 'menu': fqcns,
            \ 'info': fqcns
            \ }
        \)

    elseif type(fqcns) == type([])
        let i = 1
        for fqcn in fqcns
            call add(menu_entries , {
                \ 'word': className. '-'. i,
                \ 'kind': 'c',
                \ 'menu': fqcn,
                \ 'info': fqcn
                \}
            \)
            let i = i + 1
        endfor
    endif
    for menu_entry in menu_entries
        " add at last for now
        call add(class_menu_entries, menu_entry)
    endfor

endfunction "}}}

function! phpcomplete_extended#isClassOfType(classFQCN, typeFQCN) "{{{
    if a:classFQCN == a:typeFQCN
        return 1
    endif

    if has_key(g:phpcomplete_index['extends'], a:typeFQCN)
        \ && index(g:phpcomplete_index['extends'][a:typeFQCN], a:classFQCN) >= 0
        return 1
    endif

    if has_key(g:phpcomplete_index['implements'], a:typeFQCN)
        \ && index(g:phpcomplete_index['implements'][a:typeFQCN], a:classFQCN) >= 0
        return 1
    endif

    return 0
endfunction "}}}

" Blatantly copied form thinca/vim-ref :)
let s:plugin_prototype = {}  " {{{ plugin prototype
let s:plugin_prototype.plugin_name = ""
function! s:plugin_prototype.set_index(index)
endfunction
function! s:plugin_prototype.get_fqcn(parentFQCN, token_data)
endfunction
function! s:plugin_prototype.get_menu_entries(fqcn, base, is_this, is_static)
endfunction
"}}}

function! s:register_plugins() "{{{
    let list = split(globpath(&runtimepath, 'autoload/phpcomplete_extended/*.vim'), "\n")
    let s:plugins = {}
    let s:plugin_php_files = []
    for script_file in list
        try
            let script_name = fnamemodify(script_file, ":t:r")
            call s:register(script_file, phpcomplete_extended#{script_name}#define())
        catch /:E\%(117\|716\):/
        endtry
    endfor
endfunction "}}}

function! s:register(script_file, plugin) "{{{
    if empty(a:plugin)
        return
    endif
    let plugin = extend(copy(s:plugin_prototype), a:plugin)
    call plugin.init()
    let s:plugins[plugin.name] = plugin

    let script_name = fnamemodify(a:script_file, ":t:r")
    let plugin_dir = fnamemodify(a:script_file, ":p:h:h:h")
    let plugin_php_file = phpcomplete_extended#util#substitute_path_separator(
                \ plugin_dir . "/bin/".script_name.".php"
                \)
    call add(s:plugin_php_files, plugin_php_file)

endfunction "}}}

function! s:validate(plugin, key, type) "{{{
  if !has_key(a:plugin, a:key)
    throw 'phpcomplete_extended: Invalid plugin: Without key ' . string(a:key)
  elseif type(a:plugin[a:key]) != s:T[a:type]
    throw 'phpcomplete_extended: Invalid plugin: Key ' . key . ' must be ' . a:type . ', ' .
    \     'but given value is' string(a:plugin[a:key])
  endif
endfunction "}}}

function! s:set_plugin_indexes(plugin_index_file) "{{{
    let s:plugin_index = s:readIndex(a:plugin_index_file)
    for plugin_name in keys(s:plugins)
        if has_key(s:plugin_index, plugin_name)
            call s:plugins[plugin_name].set_index(deepcopy(s:plugin_index[plugin_name]))
        endif
    endfor
endfunction "}}}

function! s:get_plugin_php_files() "{{{
    let php_files = []
    for plugin in keys(s:plugins)
        let php_file = s:plugins[plugin].get_php_filename()
        call add(php_files, php_file)
    endfor
    return php_files
endfunction "}}}

function! s:get_plugin_fqcn(parentFQCN, token_data) "{{{
    let fqcn = ""
    for plugin in keys(s:plugins)
        let fqcn = s:plugins[plugin].get_fqcn(a:parentFQCN, a:token_data)
        if !empty(fqcn)
            return fqcn
        endif
    endfor
    return fqcn
endfunction "}}}

function! s:get_plugin_resolve_fqcn(fqcn) "{{{
    let fqcn = a:fqcn
    for plugin in keys(s:plugins)
        let fqcn = s:plugins[plugin].resolve_fqcn(a:fqcn)
        if !empty(fqcn)
            return fqcn
        endif
    endfor
    return fqcn
endfunction "}}}

function! s:get_plugin_menu_entries(fqcn, base, is_this, is_static) "{{{
     let menu_entries = []
    for plugin in keys(s:plugins)
        let plugin_menu_entries = s:plugins[plugin].get_menu_entries(a:fqcn, a:base, a:is_this, a:is_static)
        if !empty(plugin_menu_entries)
            let menu_entries += plugin_menu_entries
            return menu_entries
        endif
    endfor
    return menu_entries
endfunction "}}}

function! s:get_plugin_inside_qoute_menu_entries(fqcn, lastToken) "{{{
      let menu_entries = []
    for plugin in keys(s:plugins)
        let plugin_menu_entries = s:plugins[plugin].get_inside_quote_menu_entries(a:fqcn, a:lastToken)
        if !empty(plugin_menu_entries)
            let menu_entries += plugin_menu_entries
            return menu_entries
        endif
    endfor
    return menu_entries
endfunction "}}}

function! phpcomplete_extended#init_autocmd() "{{{
    augroup phpcomplete-extended
        autocmd!
        "Todo add configuration option to load later
        autocmd BufWinEnter,BufEnter  * call phpcomplete_extended#readDataForProject()
        autocmd VimLeave *     call phpcomplete_extended#saveIndexCache()
        autocmd BufWritePost *.php call phpcomplete_extended#updateIndex(1)

        autocmd CursorHold *     call phpcomplete_extended#saveIndexCache()
        autocmd CursorHold *     call phpcomplete_extended#checkUpdates()
        autocmd CursorMoved,CursorMovedI *.php call phpcomplete_extended#checkUpdates()
        autocmd CursorMovedI *.php call phpcomplete_extended#trackMenuChanges()
    augroup END
endfunction "}}}

function! phpcomplete_extended#enable() "{{{
    let s:phpcomplete_enabled = 1
    call phpcomplete_extended#init_autocmd()

    command! -nargs=0 -bar PHPCompleteExtendedDisable
          \ call phpcomplete_extended#disable()

endfunction "}}}

function! phpcomplete_extended#disable() "{{{
    let s:phpcomplete_enabled = 0
  augroup phpcomplete-extended
    autocmd!
  augroup END

  silent! delcommand PHPCompleteExtendedDisable
endfunction "}}}

let &cpo = s:save_cpo
unlet s:save_cpo

" vim: foldmethod=marker:expandtab:ts=4:sts=4:tw=78


================================================
FILE: autoload/unite/kinds/phpcomplete.vim
================================================
"let s:save_cpo = &cpo
"set cpo&vim

function! unite#kinds#phpcomplete_extended#define() "{{{
  "return s:kind
endfunction"}}}

let s:kind = {
      \ 'name' : 'phpcomplete_extended',
      \ 'default_action' : 'insert',
      \ 'action_table': {},
      \}

let s:kind.action_table.insert_namespace = {
      \ 'description' : 'insert namespace',
      \ }
" TBD

"let &cpo = s:save_cpo
"unlet s:save_cpo

" vim: foldmethod=marker


================================================
FILE: autoload/unite/sources/phpcomplete.vim
================================================
"=============================================================================
" AUTHOR:  Mun Mun Das <m2mdas at gmail.com>
" FILE: phpcomplete.vim
" Last Modified: September 10, 2013
" License: MIT license  {{{
"     Permission is hereby granted, free of charge, to any person obtaining
"     a copy of this software and associated documentation files (the
"     "Software"), to deal in the Software without restriction, including
"     without limitation the rights to use, copy, modify, merge, publish,
"     distribute, sublicense, and/or sell copies of the Software, and to
"     permit persons to whom the Software is furnished to do so, subject to
"     the following conditions:
"
"     The above copyright notice and this permission notice shall be included
"     in all copies or substantial portions of the Software.
"
"     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
"     OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
"     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
"     IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
"     CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
"     TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
"     SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
" }}}
"=============================================================================


let s:save_cpo = &cpo
set cpo&vim

if !g:loaded_unite
    finish
endif


function! unite#sources#phpcomplete#define() "{{{
    return [s:source, s:source_find_extends, s:source_find_implements, s:vendors_source]
endfunction"}}}

let s:source = {
            \ 'name' : 'phpcomplete/files',
            \ 'description' : 'File candidates read from the phpcomplete index',
            \ 'action_table' : {},
            \ 'default_kind' : 'file',
            \}

function! s:source.gather_candidates(args, context) "{{{
    if !phpcomplete_extended#is_phpcomplete_extended_project() || !exists("g:phpcomplete_index_loaded") || !g:phpcomplete_index_loaded
        call unite#print_error("Not a valid composer project")
        return []
    endif

    let is_relative_path = 1
    let files = keys(g:phpcomplete_index['file_fqcn'])
    let entries = []

    for file in files
       let dict = {
          \ 'word' : file,
          \ 'action__path' : file,
          \ 'action__line' : 0,
          \ }
        call add(entries, dict)
    endfor
    return entries

endfunction"}}}

let s:source_find_extends = {
            \ 'name' : 'phpcomplete/extends',
            \ 'description' : 'File candidate class that extends form source argument word',
            \ 'action_table' : {},
            \ 'default_kind' : 'file',
            \}
function! s:source_find_extends.gather_candidates(args, context) "{{{
    if !phpcomplete_extended#is_phpcomplete_extended_project() || !g:phpcomplete_index_loaded
        call unite#print_error("Not a valid composer project")
        return []
    endif
    let word = get(a:args, 0, expand("<cword>"))
    return s:getCandidates('extends', word)
endfunction "}}}

let s:source_find_implements = {
            \ 'name' : 'phpcomplete/implements',
            \ 'description' : 'File candidate class that implements source argument word',
            \ 'action_table' : {},
            \ 'default_kind' : 'file',
            \}

function! s:source_find_implements.gather_candidates(args, context) "{{{
    if !phpcomplete_extended#is_phpcomplete_extended_project() || !g:phpcomplete_index_loaded
        call unite#print_error("Not a valid composer project")
        return []
    endif
    let word = get(a:args, 0, expand("<cword>"))
    return s:getCandidates('implements', word)
endfunction "}}}

function! s:getCandidates(type, word) "{{{
    if !phpcomplete_extended#is_phpcomplete_extended_project() || !g:phpcomplete_index_loaded
        call unite#print_error("Not a valid composer project")
        return []
    endif
    let word_fqcn = phpcomplete_extended#getFQCNFromWord(a:word)
    if word_fqcn == ""
        return []
    endif

    let fqcns = []
    let candidates = []
    if a:type == 'implements'
        let fqcns = get(g:phpcomplete_index['implements'], word_fqcn, [])
    elseif a:type == 'extends'
        let fqcns = get(g:phpcomplete_index['extends'], word_fqcn, [])
    endif
    if !len(fqcns)
        return []
    endif
    for fqcn in fqcns
        let filelocation = phpcomplete_extended#getFileFromFQCN(fqcn)
        if filelocation == ""
            continue
        endif
       let dict = {
          \ 'word' : fqcn,
          \ 'action__path' : unite#util#substitute_path_separator(
          \     filelocation),
          \ 'action__line' : 0,
          \ }
       call add(candidates, dict)
    endfor
    return candidates
endfunction "}}}

let s:vendors_source = {
      \ 'name' : 'phpcomplete/vendors',
      \ 'description' : 'Vendor library candidates',
      \ 'action_table' : {},
      \ }

function! s:vendors_source.gather_candidates(args, context) "{{{
    if !phpcomplete_extended#is_phpcomplete_extended_project() || !g:phpcomplete_index_loaded
        call unite#print_error("Not a valid composer project")
        return []
    endif
    return s:get_vendors()
endfunction "}}}

function! s:get_vendors() "{{{
    let vendor_list = deepcopy(g:phpcomplete_index['vendor_libs'])
    let vendor_names = keys(vendor_list)
    let padded_vendor_list = phpcomplete_extended#util#add_padding(copy(vendor_names))
    let entries = []
    for vendor in vendor_names
        let dir = unite#util#substitute_path_separator(vendor_list[vendor])
        let vendor_index = index(vendor_names, vendor)
        let dict = {}
        let dict = {
          \ 'word' : dir,
          \ 'abbr' : printf('%s %s', padded_vendor_list[vendor_index], dir ),
        \   'kind' : 'directory',
          \ 'action__path' : dir,
          \ 'action__directory' : dir,
          \ }
        "let entries += dict
        call add(entries, dict)
    endfor
    return entries
endfunction "}}}

let &cpo = s:save_cpo
unlet s:save_cpo
" vim: foldmethod=marker sw=4 ts=4 sts=4 expandtab


================================================
FILE: autoload/vital/_62ad025/Data/List.vim
================================================
" Utilities for list.

let s:save_cpo = &cpo
set cpo&vim

function! s:pop(list)
  return remove(a:list, -1)
endfunction

function! s:push(list, val)
  call add(a:list, a:val)
  return a:list
endfunction

function! s:shift(list)
  return remove(a:list, 0)
endfunction

function! s:unshift(list, val)
  return insert(a:list, a:val)
endfunction

function! s:cons(x, xs)
  return [a:x] + a:xs
endfunction

" TODO spec
function! s:conj(xs, x)
  return a:xs + [a:x]
endfunction

" Removes duplicates from a list.
function! s:uniq(list, ...)
  let list = a:0 ? map(copy(a:list), printf('[v:val, %s]', a:1)) : copy(a:list)
  let i = 0
  let seen = {}
  while i < len(list)
    let key = string(a:0 ? list[i][1] : list[i])
    if has_key(seen, key)
      call remove(list, i)
    else
      let seen[key] = 1
      let i += 1
    endif
  endwhile
  return a:0 ? map(list, 'v:val[0]') : list
endfunction

function! s:clear(list)
  if !empty(a:list)
    unlet! a:list[0 : len(a:list) - 1]
  endif
  return a:list
endfunction

" Concatenates a list of lists.
" XXX: Should we verify the input?
function! s:concat(list)
  let list = []
  for Value in a:list
    let list += Value
  endfor
  return list
endfunction

" Take each elements from lists to a new list.
function! s:flatten(list, ...)
  let limit = a:0 > 0 ? a:1 : -1
  let list = []
  if limit == 0
    return a:list
  endif
  let limit -= 1
  for Value in a:list
    let list +=
          \ type(Value) == type([]) ?
          \   s:flatten(Value, limit) :
          \   [Value]
    unlet! Value
  endfor
  return list
endfunction

" Sorts a list with expression to compare each two values.
" a:a and a:b can be used in {expr}.
function! s:sort(list, expr)
  if type(a:expr) == type(function('function'))
    return sort(a:list, a:expr)
  endif
  let s:expr = a:expr
  return sort(a:list, 's:_compare')
endfunction

function! s:_compare(a, b)
  return eval(s:expr)
endfunction

" Sorts a list using a set of keys generated by mapping the values in the list
" through the given expr.
" v:val is used in {expr}
function! s:sort_by(list, expr)
  let pairs = map(a:list, printf('[v:val, %s]', a:expr))
  return map(s:sort(pairs,
  \      'a:a[1] ==# a:b[1] ? 0 : a:a[1] ># a:b[1] ? 1 : -1'), 'v:val[0]')
endfunction

function! s:max(list, expr)
  echoerr 'Data.List.max() is obsolete. Use its max_by() instead.'
  return s:max_by(a:list, a:expr)
endfunction

" Returns a maximum value in {list} through given {expr}.
" Returns 0 if {list} is empty.
" v:val is used in {expr}
function! s:max_by(list, expr)
  if empty(a:list)
    return 0
  endif
  let list = map(copy(a:list), a:expr)
  return a:list[index(list, max(list))]
endfunction

function! s:min(list, expr)
  echoerr 'Data.List.min() is obsolete. Use its min_by() instead.'
  return s:min_by(a:list, a:expr)
endfunction

" Returns a minimum value in {list} through given {expr}.
" Returns 0 if {list} is empty.
" v:val is used in {expr}
" FIXME: -0x80000000 == 0x80000000
function! s:min_by(list, expr)
  return s:max_by(a:list, '-(' . a:expr . ')')
endfunction

" Returns List of character sequence between [a:from, a:to]
" e.g.: s:char_range('a', 'c') returns ['a', 'b', 'c']
function! s:char_range(from, to)
  return map(
  \   range(char2nr(a:from), char2nr(a:to)),
  \   'nr2char(v:val)'
  \)
endfunction

" Returns true if a:list has a:value.
" Returns false otherwise.
function! s:has(list, value)
  return index(a:list, a:value) isnot -1
endfunction

" Returns true if a:list[a:index] exists.
" Returns false otherwise.
" NOTE: Returns false when a:index is negative number.
function! s:has_index(list, index)
  " Return true when negative index?
  " let index = a:index >= 0 ? a:index : len(a:list) + a:index
  return 0 <= a:index && a:index < len(a:list)
endfunction

" similar to Haskell's Data.List.span
function! s:span(f, xs)
  let border = len(a:xs)
  for i in range(len(a:xs))
    if !eval(substitute(a:f, 'v:val', string(a:xs[i]), 'g'))
      let border = i
      break
    endif
  endfor
  return border == 0 ? [[], copy(a:xs)] : [a:xs[: border - 1], a:xs[border :]]
endfunction

" similar to Haskell's Data.List.break
function! s:break(f, xs)
  return s:span(printf('!(%s)', a:f), a:xs)
endfunction

" similar to Haskell's Data.List.takeWhile
function! s:take_while(f, xs)
  return s:span(a:f, a:xs)[0]
endfunction

" similar to Haskell's Data.List.partition
function! s:partition(f, xs)
  return [filter(copy(a:xs), a:f), filter(copy(a:xs), '!(' . a:f . ')')]
endfunction

" similar to Haskell's Prelude.all
function! s:all(f, xs)
  return !s:any(printf('!(%s)', a:f), a:xs)
endfunction

" similar to Haskell's Prelude.any
function! s:any(f, xs)
  return !empty(filter(map(copy(a:xs), a:f), 'v:val'))
endfunction

" similar to Haskell's Prelude.and
function! s:and(xs)
  return s:all('v:val', a:xs)
endfunction

" similar to Haskell's Prelude.or
function! s:or(xs)
  return s:any('v:val', a:xs)
endfunction

" similar to Haskell's Prelude.foldl
function! s:foldl(f, init, xs)
  let memo = a:init
  for x in a:xs
    let expr = substitute(a:f, 'v:val', string(x), 'g')
    let expr = substitute(expr, 'v:memo', string(memo), 'g')
    unlet memo
    let memo = eval(expr)
  endfor
  return memo
endfunction

" similar to Haskell's Prelude.foldl1
function! s:foldl1(f, xs)
  if len(a:xs) == 0
    throw 'foldl1'
  endif
  return s:foldl(a:f, a:xs[0], a:xs[1:])
endfunction

" similar to Haskell's Prelude.foldr
function! s:foldr(f, init, xs)
  return s:foldl(a:f, a:init, reverse(copy(a:xs)))
endfunction

" similar to Haskell's Prelude.fold11
function! s:foldr1(f, xs)
  if len(a:xs) == 0
    throw 'foldr1'
  endif
  return s:foldr(a:f, a:xs[-1], a:xs[0:-2])
endfunction

" similar to python's zip()
function! s:zip(...)
  return map(range(min(map(copy(a:000), 'len(v:val)'))), "map(copy(a:000), 'v:val['.v:val.']')")
endfunction

" similar to zip(), but goes until the longer one.
function! s:zip_fill(xs, ys, filler)
  if empty(a:xs) && empty(a:ys)
    return []
  elseif empty(a:ys)
    return s:cons([a:xs[0], a:filler], s:zip_fill(a:xs[1 :], [], a:filler))
  elseif empty(a:xs)
    return s:cons([a:filler, a:ys[0]], s:zip_fill([], a:ys[1 :], a:filler))
  else
    return s:cons([a:xs[0], a:ys[0]], s:zip_fill(a:xs[1 :], a:ys[1: ], a:filler))
  endif
endfunction

" Inspired by Ruby's with_index method.
function! s:with_index(list, ...)
  let base = a:0 > 0 ? a:1 : 0
  return s:zip(a:list, range(base, len(a:list)+base-1))
endfunction

" similar to Ruby's detect or Haskell's find.
" TODO spec and doc
function! s:find(list, default, f)
  for x in a:list
    if eval(substitute(a:f, 'v:val', string(x), 'g'))
      return x
    endif
  endfor
  return a:default
endfunction

" Return non-zero if a:list1 and a:list2 have any common item(s).
" Return zero otherwise.
function! s:has_common_items(list1, list2)
  return !empty(filter(copy(a:list1), 'index(a:list2, v:val) isnot -1'))
endfunction

let &cpo = s:save_cpo
unlet s:save_cpo

" vim:set et ts=2 sts=2 sw=2 tw=0:


================================================
FILE: autoload/vital/_62ad025/Data/String.vim
================================================
" Utilities for string.

let s:save_cpo = &cpo
set cpo&vim

function! s:_vital_loaded(V)
  let s:V = a:V
  let s:L = s:V.import('Data.List')
endfunction

function! s:_vital_depends()
  return ['Data.List']
endfunction

" Substitute a:from => a:to by string.
" To substitute by pattern, use substitute() instead.
function! s:replace(str, from, to)
  return s:_replace(a:str, a:from, a:to, 'g')
endfunction

" Substitute a:from => a:to only once.
" cf. s:replace()
function! s:replace_first(str, from, to)
  return s:_replace(a:str, a:from, a:to, '')
endfunction

" implement of replace() and replace_first()
function! s:_replace(str, from, to, flags)
  return substitute(a:str, '\V'.escape(a:from, '\'), escape(a:to, '\'), a:flags)
endfunction

function! s:scan(str, pattern)
  let list = []
  call substitute(a:str, a:pattern, '\=add(list, submatch(0)) == [] ? "" : ""', 'g')
  return list
endfunction

function! s:reverse(str)
  return join(reverse(split(a:str, '.\zs')), '')
endfunction

function! s:common_head(strs)
  if empty(a:strs)
    return ''
  endif
  let len = len(a:strs)
  if len == 1
    return a:strs[0]
  endif
  let strs = len == 2 ? a:strs : sort(copy(a:strs))
  let pat = substitute(strs[0], '.', '[\0]', 'g')
  return pat == '' ? '' : matchstr(strs[-1], '^\%[' . pat . ']')
endfunction

" Split to two elements of List. ([left, right])
" e.g.: s:split3('neocomplcache', 'compl') returns ['neo', 'compl', 'cache']
function! s:split_leftright(expr, pattern)
  let [left, _, right] = s:split3(a:expr, a:pattern)
  return [left, right]
endfunction

function! s:split3(expr, pattern)
  let ERROR = ['', '', '']
  if a:expr ==# '' || a:pattern ==# ''
    return ERROR
  endif
  let begin = match(a:expr, a:pattern)
  if begin is -1
    return ERROR
  endif
  let end   = matchend(a:expr, a:pattern)
  let left  = begin <=# 0 ? '' : a:expr[: begin - 1]
  let right = a:expr[end :]
  return [left, a:expr[begin : end-1], right]
endfunction

" Slices into strings determines the number of substrings.
" e.g.: s:nsplit("neo compl cache", 2, '\s') returns ['neo', 'compl cache']
function! s:nsplit(expr, n, ...)
  let pattern = get(a:000, 0, '\s')
  let keepempty = get(a:000, 1, 1)
  let ret = []
  let expr = a:expr
  if a:n <= 1
    return [expr]
  endif
  while 1
    let pos = match(expr, pattern)
    if pos == -1
      if expr !~ pattern || keepempty
        call add(ret, expr)
      endif
      break
    elseif pos >= 0
      let left = pos > 0 ? expr[:pos-1] : ''
      if pos > 0 || keepempty
        call add(ret, left)
      endif
      let ml = len(matchstr(expr, pattern))
      if pos == 0 && ml == 0
        let pos = 1
      endif
      let expr = expr[pos+ml :]
    endif
    if len(expr) == 0
      break
    endif
    if len(ret) == a:n - 1
      call add(ret, expr)
      break
    endif
  endwhile
  return ret
endfunction

" Returns the number of character in a:str.
" NOTE: This returns proper value
" even if a:str contains multibyte character(s).
" s:strchars(str) {{{
if exists('*strchars')
  function! s:strchars(str)
    return strchars(a:str)
  endfunction
else
  function! s:strchars(str)
    return strlen(substitute(copy(a:str), '.', 'x', 'g'))
  endfunction
endif "}}}

" Returns the bool of contains any multibyte character in s:str
function! s:contains_multibyte(str) "{{{
  return strlen(a:str) != s:strchars(a:str)
endfunction "}}}

" Remove last character from a:str.
" NOTE: This returns proper value
" even if a:str contains multibyte character(s).
function! s:chop(str) "{{{
  return substitute(a:str, '.$', '', '')
endfunction "}}}

" Remove last \r,\n,\r\n from a:str.
function! s:chomp(str) "{{{
  return substitute(a:str, '\%(\r\n\|[\r\n]\)$', '', '')
endfunction "}}}

" wrap() and its internal functions
" * _split_by_wcswidth_once()
" * _split_by_wcswidth()
" * _concat()
" * wrap()
"
" NOTE _concat() is just a copy of Data.List.concat().
" FIXME don't repeat yourself
function! s:_split_by_wcswidth_once(body, x)
  let fst = s:V.strwidthpart(a:body, a:x)
  let snd = s:V.strwidthpart_reverse(a:body, s:V.wcswidth(a:body) - s:V.wcswidth(fst))
  return [fst, snd]
endfunction

function! s:_split_by_wcswidth(body, x)
  let memo = []
  let body = a:body
  while s:V.wcswidth(body) > a:x
    let [tmp, body] = s:_split_by_wcswidth_once(body, a:x)
    call add(memo, tmp)
  endwhile
  call add(memo, body)
  return memo
endfunction

function! s:trim(str)
  return matchstr(a:str,'^\s*\zs.\{-}\ze\s*$')
endfunction

function! s:wrap(str,...)
  let _columns = a:0 > 0 ? a:1 : &columns
  return s:L.concat(
        \ map(split(a:str, '\r\n\|[\r\n]'), 's:_split_by_wcswidth(v:val, _columns - 1)'))
endfunction

function! s:nr2byte(nr)
  if a:nr < 0x80
    return nr2char(a:nr)
  elseif a:nr < 0x800
    return nr2char(a:nr/64+192).nr2char(a:nr%64+128)
  else
    return nr2char(a:nr/4096%16+224).nr2char(a:nr/64%64+128).nr2char(a:nr%64+128)
  endif
endfunction

function! s:nr2enc_char(charcode)
  if &encoding == 'utf-8'
    return nr2char(a:charcode)
  endif
  let char = s:nr2byte(a:charcode)
  if strlen(char) > 1
    let char = strtrans(iconv(char, 'utf-8', &encoding))
  endif
  return char
endfunction

function! s:nr2hex(nr)
  let n = a:nr
  let r = ""
  while n
    let r = '0123456789ABCDEF'[n % 16] . r
    let n = n / 16
  endwhile
  return r
endfunction

" If a ==# b, returns -1.
" If a !=# b, returns first index of diffrent character.
function! s:diffidx(a, b)
  return a:a ==# a:b ? -1 : strlen(s:common_head([a:a, a:b]))
endfunction

function! s:substitute_last(expr, pat, sub)
  return substitute(a:expr, printf('.*\zs%s', a:pat), a:sub, '')
endfunction

function! s:dstring(expr)
  let x = substitute(string(a:expr), "^'\\|'$", '', 'g')
  let x = substitute(x, "''", "'", 'g')
  return printf('"%s"', escape(x, '"'))
endfunction

let &cpo = s:save_cpo
unlet s:save_cpo

" vim:set et ts=2 sts=2 sw=2 tw=0:


================================================
FILE: autoload/vital/_62ad025/Prelude.vim
================================================
let s:save_cpo = &cpo
set cpo&vim

" glob() wrapper which returns List
" and 'wildignore' does not affect
" this function's return value.
if v:version ># 703 ||
\  (v:version is 703 && has('patch465'))
  function! s:glob(expr)
    return glob(a:expr, 1, 1)
  endfunction
else
  function! s:glob(expr)
    let R = glob(a:expr, 1)
    return split(R, '\n')
  endfunction
endif

" globpath() wrapper which returns List
" and 'suffixes' and 'wildignore' does not affect
" this function's return value.
function! s:globpath(path, expr)
  let R = globpath(a:path, a:expr, 1)
  return split(R, '\n')
endfunction

" Wrapper functions for type().
let [
\   s:__TYPE_NUMBER,
\   s:__TYPE_STRING,
\   s:__TYPE_FUNCREF,
\   s:__TYPE_LIST,
\   s:__TYPE_DICT,
\   s:__TYPE_FLOAT] = [
      \   type(3),
      \   type(""),
      \   type(function('tr')),
      \   type([]),
      \   type({}),
      \   has('float') ? type(str2float('0')) : -1]
" __TYPE_FLOAT = -1 when -float
" This doesn't match to anything.

" Number or Float
function! s:is_numeric(Value)
  let _ = type(a:Value)
  return _ ==# s:__TYPE_NUMBER
  \   || _ ==# s:__TYPE_FLOAT
endfunction

" Number
function! s:is_number(Value)
  return type(a:Value) ==# s:__TYPE_NUMBER
endfunction

" Float
function! s:is_float(Value)
  return type(a:Value) ==# s:__TYPE_FLOAT
endfunction
" String
function! s:is_string(Value)
  return type(a:Value) ==# s:__TYPE_STRING
endfunction
" Funcref
function! s:is_funcref(Value)
  return type(a:Value) ==# s:__TYPE_FUNCREF
endfunction
" List
function! s:is_list(Value)
  return type(a:Value) ==# s:__TYPE_LIST
endfunction
" Dictionary
function! s:is_dict(Value)
  return type(a:Value) ==# s:__TYPE_DICT
endfunction

function! s:truncate_smart(str, max, footer_width, separator)
  echoerr 'Prelude.truncate_smart() is obsolete. Use its truncate_skipping() instead; they are equivalent.'
  return s:truncate_skipping(a:str, a:max, a:footer_width, a:separator)
endfunction

function! s:truncate_skipping(str, max, footer_width, separator)
  let width = s:wcswidth(a:str)
  if width <= a:max
    let ret = a:str
  else
    let header_width = a:max - s:wcswidth(a:separator) - a:footer_width
    let ret = s:strwidthpart(a:str, header_width) . a:separator
          \ . s:strwidthpart_reverse(a:str, a:footer_width)
  endif

  return s:truncate(ret, a:max)
endfunction

function! s:truncate(str, width)
  " Original function is from mattn.
  " http://github.com/mattn/googlereader-vim/tree/master

  if a:str =~# '^[\x00-\x7f]*$'
    return len(a:str) < a:width ?
          \ printf('%-'.a:width.'s', a:str) : strpart(a:str, 0, a:width)
  endif

  let ret = a:str
  let width = s:wcswidth(a:str)
  if width > a:width
    let ret = s:strwidthpart(ret, a:width)
    let width = s:wcswidth(ret)
  endif

  if width < a:width
    let ret .= repeat(' ', a:width - width)
  endif

  return ret
endfunction

function! s:strwidthpart(str, width)
  if a:width <= 0
    return ''
  endif
  let ret = a:str
  let width = s:wcswidth(a:str)
  while width > a:width
    let char = matchstr(ret, '.$')
    let ret = ret[: -1 - len(char)]
    let width -= s:wcswidth(char)
  endwhile

  return ret
endfunction
function! s:strwidthpart_reverse(str, width)
  if a:width <= 0
    return ''
  endif
  let ret = a:str
  let width = s:wcswidth(a:str)
  while width > a:width
    let char = matchstr(ret, '^.')
    let ret = ret[len(char) :]
    let width -= s:wcswidth(char)
  endwhile

  return ret
endfunction

if v:version >= 703
  " Use builtin function.
  function! s:wcswidth(str)
    return strwidth(a:str)
  endfunction
else
  function! s:wcswidth(str)
    if a:str =~# '^[\x00-\x7f]*$'
      return strlen(a:str)
    end

    let mx_first = '^\(.\)'
    let str = a:str
    let width = 0
    while 1
      let ucs = char2nr(substitute(str, mx_first, '\1', ''))
      if ucs == 0
        break
      endif
      let width += s:_wcwidth(ucs)
      let str = substitute(str, mx_first, '', '')
    endwhile
    return width
  endfunction

  " UTF-8 only.
  function! s:_wcwidth(ucs)
    let ucs = a:ucs
    if (ucs >= 0x1100
          \  && (ucs <= 0x115f
          \  || ucs == 0x2329
          \  || ucs == 0x232a
          \  || (ucs >= 0x2e80 && ucs <= 0xa4cf
          \      && ucs != 0x303f)
          \  || (ucs >= 0xac00 && ucs <= 0xd7a3)
          \  || (ucs >= 0xf900 && ucs <= 0xfaff)
          \  || (ucs >= 0xfe30 && ucs <= 0xfe6f)
          \  || (ucs >= 0xff00 && ucs <= 0xff60)
          \  || (ucs >= 0xffe0 && ucs <= 0xffe6)
          \  || (ucs >= 0x20000 && ucs <= 0x2fffd)
          \  || (ucs >= 0x30000 && ucs <= 0x3fffd)
          \  ))
      return 2
    endif
    return 1
  endfunction
endif

let s:is_windows = has('win16') || has('win32') || has('win64') || has('win95')
let s:is_cygwin = has('win32unix')
let s:is_mac = !s:is_windows && !s:is_cygwin
      \ && (has('mac') || has('macunix') || has('gui_macvim') ||
      \   (!isdirectory('/proc') && executable('sw_vers')))
let s:is_unix = has('unix')

function! s:is_windows()
  return s:is_windows
endfunction

function! s:is_cygwin()
  return s:is_cygwin
endfunction

function! s:is_mac()
  return s:is_mac
endfunction

function! s:is_unix()
  return s:is_unix
endfunction

function! s:_deprecated(fname, newname)
  echomsg printf("Vital.Prelude.%s is deprecated! Please use %s instead.",
        \ a:fname, a:newname)
endfunction

function! s:print_error(message)
  call s:_deprecated('print_error', 'Vital.Vim.Message.error')

  echohl ErrorMsg
  for m in split(a:message, "\n")
    echomsg m
  endfor
  echohl None
endfunction

function! s:smart_execute_command(action, word)
  execute a:action . ' ' . (a:word == '' ? '' : '`=a:word`')
endfunction

function! s:escape_file_searching(buffer_name)
  return escape(a:buffer_name, '*[]?{}, ')
endfunction

function! s:escape_pattern(str)
  return escape(a:str, '~"\.^$[]*')
endfunction

" iconv() wrapper for safety.
function! s:iconv(expr, from, to)
  if a:from == '' || a:to == '' || a:from ==? a:to
    return a:expr
  endif
  let result = iconv(a:expr, a:from, a:to)
  return result != '' ? result : a:expr
endfunction

" Like builtin getchar() but returns string always.
function! s:getchar(...)
  let c = call('getchar', a:000)
  return type(c) == type(0) ? nr2char(c) : c
endfunction

" Like builtin getchar() but returns string always.
" and do inputsave()/inputrestore() before/after getchar().
function! s:getchar_safe(...)
  let c = s:input_helper('getchar', a:000)
  return type(c) == type("") ? c : nr2char(c)
endfunction

" Like builtin getchar() but
" do inputsave()/inputrestore() before/after input().
function! s:input_safe(...)
    return s:input_helper('input', a:000)
endfunction

" Do inputsave()/inputrestore() before/after calling a:funcname.
function! s:input_helper(funcname, args)
    let success = 0
    if inputsave() !=# success
        throw 'inputsave() failed'
    endif
    try
        return call(a:funcname, a:args)
    finally
        if inputrestore() !=# success
            throw 'inputrestore() failed'
        endif
    endtry
endfunction

function! s:set_default(var, val)
  if !exists(a:var) || type({a:var}) != type(a:val)
    let {a:var} = a:val
  endif
endfunction

function! s:set_dictionary_helper(variable, keys, pattern)
  for key in split(a:keys, '\s*,\s*')
    if !has_key(a:variable, key)
      let a:variable[key] = a:pattern
    endif
  endfor
endfunction

function! s:substitute_path_separator(path)
  return s:is_windows ? substitute(a:path, '\\', '/', 'g') : a:path
endfunction

function! s:path2directory(path)
  return s:substitute_path_separator(isdirectory(a:path) ? a:path : fnamemodify(a:path, ':p:h'))
endfunction

function! s:_path2project_directory_git(path)
  let parent = a:path

  while 1
    let path = parent . '/.git'
    if isdirectory(path) || filereadable(path)
      return parent
    endif
    let next = fnamemodify(parent, ':h')
    if next == parent
      return ''
    endif
    let parent = next
  endwhile
endfunction

function! s:_path2project_directory_svn(path)
  let search_directory = a:path
  let directory = ''

  let find_directory = s:escape_file_searching(search_directory)
  let d = finddir('.svn', find_directory . ';')
  if d == ''
    return ''
  endif

  let directory = fnamemodify(d, ':p:h:h')

  " Search parent directories.
  let parent_directory = s:path2directory(
        \ fnamemodify(directory, ':h'))

  if parent_directory != ''
    let d = finddir('.svn', parent_directory . ';')
    if d != ''
      let directory = s:_path2project_directory_svn(parent_directory)
    endif
  endif
  return directory
endfunction

function! s:_path2project_directory_others(vcs, path)
  let vcs = a:vcs
  let search_directory = a:path
  let directory = ''

  let find_directory = s:escape_file_searching(search_directory)
  let d = finddir(vcs, find_directory . ';')
  if d == ''
    return ''
  endif
  return fnamemodify(d, ':p:h:h')
endfunction

function! s:path2project_directory(path, ...)
  let is_allow_empty = get(a:000, 0, 0)
  let search_directory = s:path2directory(a:path)
  let directory = ''

  " Search VCS directory.
  for vcs in ['.git', '.bzr', '.hg', '.svn']
    if vcs ==# '.git'
      let directory = s:_path2project_directory_git(search_directory)
    elseif vcs ==# '.svn'
      let directory = s:_path2project_directory_svn(search_directory)
    else
      let directory = s:_path2project_directory_others(vcs, search_directory)
    endif
    if directory != ''
      break
    endif
  endfor

  " Search project file.
  if directory == ''
    for d in ['build.xml', 'prj.el', '.project', 'pom.xml',
          \ 'Makefile', 'configure', 'Rakefile', 'NAnt.build', 'tags', 'gtags']
      let d = findfile(d, s:escape_file_searching(search_directory) . ';')
      if d != ''
        let directory = fnamemodify(d, ':p:h')
        break
      endif
    endfor
  endif

  if directory == ''
    " Search /src/ directory.
    let base = s:substitute_path_separator(search_directory)
    if base =~# '/src/'
      let directory = base[: strridx(base, '/src/') + 3]
    endif
  endif

  if directory == '' && !is_allow_empty
    " Use original path.
    let directory = search_directory
  endif

  return s:substitute_path_separator(directory)
endfunction

" Check vimproc.
function! s:has_vimproc()
  if !exists('s:exists_vimproc')
    try
      call vimproc#version()
      let s:exists_vimproc = 1
    catch
      let s:exists_vimproc = 0
    endtry
  endif
  return s:exists_vimproc
endfunction

function! s:system(str, ...)
  let command = a:str
  let input = a:0 >= 1 ? a:1 : ''
  let command = s:iconv(command, &encoding, 'char')
  let input = s:iconv(input, &encoding, 'char')

  if a:0 == 0
    let output = s:has_vimproc() ?
          \ vimproc#system(command) : system(command)
  elseif a:0 == 1
    let output = s:has_vimproc() ?
          \ vimproc#system(command, input) : system(command, input)
  else
    " ignores 3rd argument unless you have vimproc.
    let output = s:has_vimproc() ?
          \ vimproc#system(command, input, a:2) : system(command, input)
  endif

  let output = s:iconv(output, 'char', &encoding)

  return output
endfunction

function! s:get_last_status()
  return s:has_vimproc() ?
        \ vimproc#get_last_status() : v:shell_error
endfunction

let &cpo = s:save_cpo
unlet s:save_cpo

" vim:set et ts=2 sts=2 sw=2 tw=0:


================================================
FILE: autoload/vital/_62ad025/System/Cache.vim
================================================
" Utilities for output cache.

let s:save_cpo = &cpo
set cpo&vim

function! s:getfilename(cache_dir, filename)
  return s:_encode_name(a:cache_dir, a:filename)
endfunction

function! s:filereadable(cache_dir, filename)
  let cache_name = s:_encode_name(a:cache_dir, a:filename)
  return filereadable(cache_name)
endfunction

function! s:readfile(cache_dir, filename)
  let cache_name = s:_encode_name(a:cache_dir, a:filename)
  return filereadable(cache_name) ? readfile(cache_name) : []
endfunction

function! s:writefile(cache_dir, filename, list)
  let cache_name = s:_encode_name(a:cache_dir, a:filename)

  call writefile(a:list, cache_name)
endfunction

function! s:delete(cache_dir, filename)
  echoerr 'System.Cache.delete() is obsolete. Use its deletefile() instead.'
  return call('s:deletefile', a:cache_dir, a:filename)
endfunction

function! s:deletefile(cache_dir, filename)
  let cache_name = s:_encode_name(a:cache_dir, a:filename)
  return delete(cache_name)
endfunction

function! s:_encode_name(cache_dir, filename)
  " Check cache directory.
  if !isdirectory(a:cache_dir)
    call mkdir(a:cache_dir, 'p')
  endif
  let cache_dir = a:cache_dir
  if cache_dir !~ '/$'
    let cache_dir .= '/'
  endif

  return cache_dir . s:_create_hash(cache_dir, a:filename)
endfunction

function! s:check_old_cache(cache_dir, filename)
  " Check old cache file.
  let cache_name = s:_encode_name(a:cache_dir, a:filename)
  let ret = getftime(cache_name) == -1
        \ || getftime(cache_name) <= getftime(a:filename)
  if ret && filereadable(cache_name)
    " Delete old cache.
    call delete(cache_name)
  endif

  return ret
endfunction

" Check md5.
try
  call md5#md5()
  let s:exists_md5 = 1
catch
  let s:exists_md5 = 0
endtry

function! s:_create_hash(dir, str)
  if len(a:dir) + len(a:str) < 150
    let hash = substitute(substitute(
          \ a:str, ':', '=-', 'g'), '[/\\]', '=+', 'g')
  elseif s:exists_md5
    " Use md5.vim.
    let hash = md5#md5(a:str)
  else
    " Use simple hash.
    let sum = 0
    for i in range(len(a:str))
      let sum += char2nr(a:str[i]) * (i + 1)
    endfor

    let hash = printf('%x', sum)
  endif

  return hash
endfunction

let &cpo = s:save_cpo
unlet s:save_cpo

" vim:set et ts=2 sts=2 sw=2 tw=0:


================================================
FILE: autoload/vital/_62ad025/System/File.vim
================================================
" Utilities for file copy/move/mkdir/etc.

let s:save_cpo = &cpo
set cpo&vim

let s:is_unix = has('unix')
let s:is_windows = has('win16') || has('win32') || has('win64') || has('win95')
let s:is_cygwin = has('win32unix')
let s:is_mac = !s:is_windows && !s:is_cygwin
      \ && (has('mac') || has('macunix') || has('gui_macvim') ||
      \   (!isdirectory('/proc') && executable('sw_vers')))

" Open a file.
function! s:open(filename) "{{{
  let filename = fnamemodify(a:filename, ':p')

  " Detect desktop environment.
  if s:is_windows
    " For URI only.
    let filename = iconv(filename, &encoding, 'char')
    silent execute '!start rundll32 url.dll,FileProtocolHandler' filename
  elseif s:is_cygwin
    " Cygwin.
    call system(printf('%s %s', 'cygstart',
          \ shellescape(filename)))
  elseif executable('xdg-open')
    " Linux.
    call system(printf('%s %s &', 'xdg-open',
          \ shellescape(filename)))
  elseif exists('$KDE_FULL_SESSION') && $KDE_FULL_SESSION ==# 'true'
    " KDE.
    call system(printf('%s %s &', 'kioclient exec',
          \ shellescape(filename)))
  elseif exists('$GNOME_DESKTOP_SESSION_ID')
    " GNOME.
    call system(printf('%s %s &', 'gnome-open',
          \ shellescape(filename)))
  elseif executable('exo-open')
    " Xfce.
    call system(printf('%s %s &', 'exo-open',
          \ shellescape(filename)))
  elseif s:is_mac && executable('open')
    " Mac OS.
    call system(printf('%s %s &', 'open',
          \ shellescape(filename)))
  else
    " Give up.
    throw 'Not supported.'
  endif
endfunction "}}}


" Move a file.
" Dispatch s:move_exe() or s:move_vim().
function! s:move(src, dest) "{{{
  if s:_has_move_exe()
    return s:move_exe(a:src, a:dest)
  else
    return s:move_vim(a:src, a:dest)
  endif
endfunction "}}}

if s:is_unix
  function! s:_has_move_exe()
    return executable('mv')
  endfunction
elseif s:is_windows
  function! s:_has_move_exe()
    return 1
  endfunction
else
  function! s:_has_move_exe()
    throw 'vital: System.File._has_move_exe(): your platform is not supported'
  endfunction
endif

" Move a file.
" Implemented by external program.
if s:is_unix
  function! s:move_exe(src, dest)
    if !s:_has_move_exe() | return 0 | endif
    let [src, dest] = [a:src, a:dest]
    silent execute '!mv' shellescape(src) shellescape(dest)
    return !v:shell_error
  endfunction
elseif s:is_windows
  function! s:move_exe(src, dest)
    if !s:_has_move_exe() | return 0 | endif
    let [src, dest] = [a:src, a:dest]
    let src  = substitute(src, '/', '\', 'g')
    let dest = substitute(dest, '/', '\', 'g')
    silent execute '!cmd /c move' src dest
    return !v:shell_error
  endfunction
else
  function! s:move_exe()
    throw 'vital: System.File.move_exe(): your platform is not supported'
  endfunction
endif

" Move a file.
" Implemented by pure Vim script.
function! s:move_vim(src, dest) "{{{
  return !rename(a:src, a:dest)
endfunction "}}}

" Copy a file.
" Dispatch s:copy_exe() or s:copy_vim().
function! s:copy(src, dest) "{{{
  if s:_has_copy_exe()
    return s:copy_exe(a:src, a:dest)
  else
    return s:copy_vim(a:src, a:dest)
  endif
endfunction "}}}

if s:is_unix
  function! s:_has_copy_exe()
    return executable('cp')
  endfunction
elseif s:is_windows
  function! s:_has_copy_exe()
    return 1
  endfunction
else
  function! s:_has_copy_exe()
    throw 'vital: System.File._has_copy_exe(): your platform is not supported'
  endfunction
endif

" Copy a file.
" Implemented by external program.
if s:is_unix
  function! s:copy_exe(src, dest)
    if !s:_has_copy_exe() | return 0 | endif
    let [src, dest] = [a:src, a:dest]
    silent execute '!cp' shellescape(src) shellescape(dest)
    return !v:shell_error
  endfunction
elseif s:is_windows
  function! s:copy_exe(src, dest)
    if !s:_has_copy_exe() | return 0 | endif
    let [src, dest] = [a:src, a:dest]
    let src  = substitute(src, '/', '\', 'g')
    let dest = substitute(dest, '/', '\', 'g')
    silent execute '!cmd /c copy' src dest
    return !v:shell_error
  endfunction
else
  function! s:copy_exe()
    throw 'vital: System.File.copy_exe(): your platform is not supported'
  endfunction
endif

" Copy a file.
" Implemented by pure Vim script.
function! s:copy_vim(src, dest) "{{{
  let ret = writefile(readfile(a:src, "b"), a:dest, "b")
  if ret == -1
    return 0
  endif
  return 1
endfunction "}}}

" mkdir() but does not throw an exception.
" Returns true if success.
" Returns false if failure.
function! s:mkdir_nothrow(...) "{{{
  silent! return call('mkdir', a:000)
endfunction "}}}


" Delete a file/directory.
if s:is_unix
  function! s:rmdir(path, ...)
    let flags = a:0 ? a:1 : ''
    let cmd = flags =~# 'r' ? 'rm -r' : 'rmdir'
    let cmd .= flags =~# 'f' && cmd ==# 'rm -r' ? ' -f' : ''
    let ret = system(cmd . ' ' . shellescape(a:path))
    if v:shell_error
      throw substitute(iconv(ret, 'char', &encoding), '\n', '', 'g')
    endif
  endfunction

elseif s:is_windows
  function! s:rmdir(path, ...)
    let flags = a:0 ? a:1 : ''
    if &shell =~? "sh$"
      let cmd = flags =~# 'r' ? 'rm -r' : 'rmdir'
      let cmd .= flags =~# 'f' && cmd ==# 'rm -r' ? ' -f' : ''
      let ret = system(cmd . ' ' . shellescape(a:path))
    else
      " 'f' flag does not make sense.
      let cmd = 'rmdir /Q'
      let cmd .= flags =~# 'r' ? ' /S' : ''
      let ret = system(cmd . ' "' . a:path . '"')
    endif
    if v:shell_error
      throw substitute(iconv(ret, 'char', &encoding), '\n', '', 'g')
    endif
  endfunction

else
  function! s:rmdir(path, ...)
    throw 'vital: System.File.rmdir(): your platform is not supported'
  endfunction
endif


let &cpo = s:save_cpo
unlet s:save_cpo

" vim:set et ts=2 sts=2 sw=2 tw=0:


================================================
FILE: autoload/vital/_62ad025/System/Filepath.vim
================================================
" You should check the following related builtin functions.
" fnamemodify()
" resolve()
" simplify()

let s:save_cpo = &cpo
set cpo&vim

let s:path_sep_pattern = (exists('+shellslash') ? '[\\/]' : '/') . '\+'
let s:is_windows = has('win16') || has('win32') || has('win64') || has('win95')
let s:is_cygwin = has('win32unix')
let s:is_mac = !s:is_windows && !s:is_cygwin
      \ && (has('mac') || has('macunix') || has('gui_macvim') ||
      \   (!isdirectory('/proc') && executable('sw_vers')))

" Get the directory separator.
function! s:separator()
  return fnamemodify('.', ':p')[-1 :]
endfunction

" Get the path separator.
let s:path_separator = s:is_windows ? ';' : ':'
function! s:path_separator()
  return s:path_separator
endfunction

" Get the path extensions
function! s:path_extensions()
  if !exists('s:path_extensions')
    if s:is_windows
      if exists('$PATHEXT')
        let pathext = $PATHEXT
      else
        " get default PATHEXT
        let pathext = matchstr(system('set pathext'), '^pathext=\zs.*\ze\n', 'i')
      endif
      let s:path_extensions = map(split(pathext, s:path_separator), 'tolower(v:val)')
    elseif s:is_cygwin
      " cygwin is not use $PATHEXT
      let s:path_extensions = ['', '.exe']
    else
      let s:path_extensions = ['']
    endif
  endif
  return s:path_extensions
endfunction

" Convert all directory separators to "/".
function! s:unify_separator(path)
  return substitute(a:path, s:path_sep_pattern, '/', 'g')
endfunction

" Get the full path of command.
function! s:which(command, ...)
  let pathlist = a:command =~# s:path_sep_pattern ? [''] :
  \              !a:0                  ? split($PATH, s:path_separator) :
  \              type(a:1) == type([]) ? copy(a:1) :
  \                                      split(a:1, s:path_separator)

  let pathext = s:path_extensions()
  if index(pathext, '.' . tolower(fnamemodify(a:command, ':e'))) != -1
    let pathext = ['']
  endif

  let dirsep = s:separator()
  for dir in pathlist
    let head = dir ==# '' ? '' : dir . dirsep
    for ext in pathext
      let full = fnamemodify(head . a:command . ext, ':p')
      if filereadable(full)
        if s:is_case_tolerant()
          let full = glob(substitute(
          \               toupper(full), '\u:\@!', '[\0\L\0]', 'g'), 1)
        endif
        if full != ''
          return full
        endif
      endif
    endfor
  endfor

  return ''
endfunction

" Split the path with directory separator.
" Note that this includes the drive letter of MS Windows.
function! s:split(path)
  return split(a:path, s:path_sep_pattern)
endfunction

" Join the paths.
" join('foo', 'bar')            => 'foo/bar'
" join('foo/', 'bar')           => 'foo/bar'
" join('/foo/', ['bar', 'buz/']) => '/foo/bar/buz/'
function! s:join(...)
  let sep = s:separator()
  let path = ''
  for part in a:000
    let path .= sep .
    \ (type(part) is type([]) ? call('s:join', part) :
    \                           part)
    unlet part
  endfor
  return substitute(path[1 :], s:path_sep_pattern, sep, 'g')
endfunction

" Check if the path is absolute path.
if s:is_windows
  function! s:is_absolute(path)
    return a:path =~? '^[a-z]:[/\\]'
  endfunction
else
  function! s:is_absolute(path)
    return a:path[0] ==# '/'
  endfunction
endif

function! s:is_relative(path)
  return !s:is_absolute(a:path)
endfunction

" Return the parent directory of the path.
" NOTE: fnamemodify(path, ':h') does not return the parent directory
" when path[-1] is the separator.
function! s:dirname(path)
  let path = a:path
  let orig = a:path

  let path = s:remove_last_separator(path)
  if path == ''
    return orig    " root directory
  endif

  let path = fnamemodify(path, ':h')
  return path
endfunction

" Return the basename of the path.
" NOTE: fnamemodify(path, ':h') does not return basename
" when path[-1] is the separator.
function! s:basename(path)
  let path = a:path
  let orig = a:path

  let path = s:remove_last_separator(path)
  if path == ''
    return orig    " root directory
  endif

  let path = fnamemodify(path, ':t')
  return path
endfunction

" Remove the separator at the end of a:path.
function! s:remove_last_separator(path)
  let sep = s:separator()
  let pat = (sep == '\' ? '\\' : '/') . '\+$'
  return substitute(a:path, pat, '', '')
endfunction


" Return true if filesystem ignores alphabetic case of a filename.
" Return false otherwise.
let s:is_case_tolerant = filereadable(expand('<sfile>:r') . '.VIM')
function! s:is_case_tolerant()
  return s:is_case_tolerant
endfunction


let &cpo = s:save_cpo
unlet s:save_cpo

" vim:set et ts=2 sts=2 sw=2 tw=0:


================================================
FILE: autoload/vital/_62ad025/Text/Lexer.vim
================================================
let s:save_cpo = &cpo
set cpo&vim

function! s:_vital_loaded(V)
  let s:V = a:V
  let s:Prelude = s:V.import('Prelude')
endfunction

function! s:_list2dict(list)
  if s:Prelude.is_list(a:list)
    if len(a:list) < 2 | call s:_exception('too few arguments.') | endif
    if 2 < len(a:list) | call s:_exception('too many arguments.') | endif
    if ! s:Prelude.is_string(a:list[0]) | call s:_exception('element of list is not string.') | endif
    if ! s:Prelude.is_string(a:list[1]) | call s:_exception('element of list is not string.') | endif
    let tkn = { 'label' : a:list[0], 'regex' : a:list[1] }
    return tkn
  else
    call s:_exception('first argument is not list.')
  endif
endfunction

function! s:_exception(msg)
  throw printf('[Text.Lexer] %s', a:msg)
endfunction

let s:obj = { 'tokens' : [] }

function! s:obj.exec(string) dict
  let match_tokens = []
  let idx = 0
  while idx < len(a:string)
    let best_tkn = {}
    for tkn in self.tokens
      let matched_text = matchstr(a:string[(idx):],'^' . tkn.regex)
      if ! empty(matched_text)
        let best_tkn = s:token(tkn.label,matched_text,idx)
        break
      endif
    endfor
    if best_tkn == {}
      call s:_exception(printf('cannot match. col:%d',idx))
    else
      let idx += len(best_tkn.matched_text)
      let match_tokens += [best_tkn]
    endif
  endwhile
  return match_tokens
endfunction

function! s:lexer(patterns)
  let obj = deepcopy(s:obj)
  for e in a:patterns
    let obj.tokens += [(s:_list2dict(e))]
  endfor
  return obj
endfunction

function! s:token(label,matched_text,col)
  let obj = {}
  let obj['label'] = a:label
  let obj['matched_text'] = a:matched_text
  let obj['col'] = a:col
  return obj
endfunction

function! s:simple_parser(expr)
  echoerr 'Text.Lexer.simple_parser(expr) is obsolete. Use Text.Parser.parser() instead.'
  let obj = { 'expr' : a:expr, 'idx' : 0, 'tokens' : [] }
  function! obj.end() dict
    return len(self.expr) <= self.idx
  endfunction
  function! obj.next() dict
    if self.end()
      call s:_exception('Already end of tokens.')
    else
      return self.expr[self.idx]
    endif
  endfunction
  function! obj.next_is(label) dict
    return self.next().label ==# a:label
  endfunction
  function! obj.consume() dict
    if ! self.end()
      let next = self.next()
      let self.idx += 1
    else
      call s:_exception('Already end of tokens.')
    endif
    return next
  endfunction
  return deepcopy(obj)
endfunction

let &cpo = s:save_cpo
unlet s:save_cpo

" vim:set et ts=2 sts=2 sw=2 tw=0:


================================================
FILE: autoload/vital/_62ad025/Text/Parser.vim
================================================
let s:save_cpo = &cpo
set cpo&vim


function! s:_exception(msg)
  throw printf('[Text.Parser] %s', a:msg)
endfunction


let s:obj = { '_idx' : 0, '_tokens' : [], '_ignore_labels' : [] }

function! s:obj.config(dict) dict
  if has_key(a:dict,'ignore_labels')
    let self._ignore_labels = a:dict.ignore_labels
  endif
  return self
endfunction

function! s:obj.end() dict
  return len(self._tokens) <= self._idx
endfunction

function! s:obj.next() dict
  if self.end()
    call s:_exception('Already end of tokens.')
  else
    return self._tokens[self._idx]
  endif
endfunction

function! s:obj.next_is(labels) dict
  let labels = type([]) == type(a:labels) ? a:labels : [ a:labels ]
  if ! self.end()
    for lbl in labels
      if self.next().label ==# lbl
        return 1
      endif
    endfor
  endif
  return 0
endfunction

function! s:obj.ignore() dict
  while self.next_is(self._ignore_labels)
    call self.consume()
  endwhile
  return self
endfunction

function! s:obj.consume() dict
  if ! self.end()
    let next = self.next()
    let self._idx += 1
  else
    call s:_exception('Already end of tokens.')
  endif
  return next
endfunction

function! s:obj.tostring() dict
  if ! self.end()
    return ''
  else
    return join( map(deepcopy(self._tokens[(self._idx):]),'v:val.matched_text'), '')
  endif
endfunction

function! s:parser()
  let o = {}
  function! o.exec(lexered_tokens)
    let obj = deepcopy(s:obj)
    let obj._tokens = deepcopy(a:lexered_tokens)
    return obj
  endfunction
  return o
endfunction


let &cpo = s:save_cpo
unlet s:save_cpo

" vim:set et ts=2 sts=2 sw=2 tw=0:


================================================
FILE: autoload/vital/_62ad025/Web/JSON.vim
================================================
let s:save_cpo = &cpo
set cpo&vim

let s:V = vital#{expand('<sfile>:h:h:t:r')}#new()

function! s:_vital_depends()
  return ['Data.String']
endfunction

let s:string = s:V.import('Data.String')

function! s:decode(json)
  let json = iconv(a:json, "utf-8", &encoding)
  let json = substitute(json, '\n', '', 'g')
  let json = substitute(json, '\\u34;', '\\"', 'g')
  let json = substitute(json, '\\u\(\x\x\x\x\)', '\=s:string.nr2enc_char("0x".submatch(1))', 'g')
  let [null,true,false] = [0,1,0]
  sandbox let ret = eval(json)
  return ret
endfunction

function! s:encode(val)
  if type(a:val) == 0
    return a:val
  elseif type(a:val) == 1
    let json = '"' . escape(a:val, '"') . '"'
    let json = substitute(json, "\r", '\\r', 'g')
    let json = substitute(json, "\n", '\\n', 'g')
    let json = substitute(json, "\t", '\\t', 'g')
    return iconv(json, &encoding, "utf-8")
  elseif type(a:val) == 3
    return '[' . join(map(copy(a:val), 's:encode(v:val)'), ',') . ']'
  elseif type(a:val) == 4
    return '{' . join(map(keys(a:val), 's:encode(v:val).":".s:encode(a:val[v:val])'), ',') . '}'
  else
    return string(a:val)
  endif
endfunction

let &cpo = s:save_cpo
unlet s:save_cpo

" vim:set et ts=2 sts=2 sw=2 tw=0:


================================================
FILE: autoload/vital/_62ad025.vim
================================================
let s:self_version = expand('<sfile>:t:r')

" Note: The extra argument to globpath() was added in Patch 7.2.051.
let s:globpath_third_arg = v:version > 702 || v:version == 702 && has('patch51')

let s:loaded = {}

function! s:import(name, ...)
  let target = {}
  let functions = []
  for a in a:000
    if type(a) == type({})
      let target = a
    elseif type(a) == type([])
      let functions = a
    endif
    unlet a
  endfor
  let module = s:_import(a:name)
  if empty(functions)
    call extend(target, module, 'keep')
  else
    for f in functions
      if has_key(module, f) && !has_key(target, f)
        let target[f] = module[f]
      endif
    endfor
  endif
  return target
endfunction

function! s:load(...) dict
  for arg in a:000
    let [name; as] = type(arg) == type([]) ? arg[: 1] : [arg, arg]
    let target = split(join(as, ''), '\W\+')
    let dict = self
    while 1 <= len(target)
      let ns = remove(target, 0)
      if !has_key(dict, ns)
        let dict[ns] = {}
      endif
      if type(dict[ns]) == type({})
        let dict = dict[ns]
      else
        unlet dict
        break
      endif
    endwhile

    if exists('dict')
      call extend(dict, s:_import(name))
    endif
    unlet arg
  endfor
  return self
endfunction

function! s:unload()
  let s:loaded = {}
endfunction

function! s:_import(name)
  if type(a:name) == type(0)
    return s:_build_module(a:name)
  endif
  let path = s:_get_module_path(a:name)
  if path ==# ''
    throw 'vital: module not found: ' . a:name
  endif
  let sid = get(s:_scripts(), path, 0)
  if !sid
    try
      execute 'source' fnameescape(path)
    catch /^Vim\%((\a\+)\)\?:E484/
      throw 'vital: module not found: ' . a:name
    catch /^Vim\%((\a\+)\)\?:E127/
      " Ignore.
    endtry

    let sid = s:_scripts()[path]
  endif
  return s:_build_module(sid)
endfunction

function! s:_get_module_path(name)
  if s:_is_absolute_path(a:name) && filereadable(a:name)
    return s:_unify_path(a:name)
  endif
  if a:name ==# ''
    let tailpath = printf('autoload/vital/%s.vim', s:self_version)
  elseif a:name =~# '\v^\u\w*%(\.\u\w*)*$'
    let target = '/' . substitute(a:name, '\W\+', '/', 'g')
    let tailpath = printf('autoload/vital/%s%s.vim', s:self_version, target)
  else
    throw 'vital: Invalid module name: ' . a:name
  endif

  if s:globpath_third_arg
    let paths = split(globpath(&runtimepath, tailpath, 1), "\n")
  else
    let paths = split(globpath(&runtimepath, tailpath), "\n")
  endif
  call filter(paths, 'filereadable(v:val)')
  return s:_unify_path(get(paths, 0, ''))
endfunction

function! s:_scripts()
  let scripts = {}
  for line in filter(split(s:_redir('scriptnames'), "\n"),
  \                  'stridx(v:val, s:self_version) > 0')
    let list = matchlist(line, '^\s*\(\d\+\):\s\+\(.\+\)\s*$')
    if !empty(list)
      let scripts[s:_unify_path(list[2])] = list[1] - 0
    endif
  endfor
  return scripts
endfunction

if filereadable(expand('<sfile>:r') . '.VIM')
  function! s:_unify_path(path)
    " Note: On windows, vim can't expand path names from 8.3 formats.
    " So if getting full path via <sfile> and $HOME was set as 8.3 format,
    " vital load duplicated scripts. Below's :~ avoid this issue.
    return tolower(fnamemodify(resolve(fnamemodify(
    \              a:path, ':p:gs?[\\/]\+?/?')), ':~'))
  endfunction
else
  function! s:_unify_path(path)
    return resolve(fnamemodify(a:path, ':p:gs?[\\/]\+?/?'))
  endfunction
endif

" Copy from System.Filepath
if has('win16') || has('win32') || has('win64')
  function! s:_is_absolute_path(path)
    return a:path =~? '^[a-z]:[/\\]'
  endfunction
else
  function! s:_is_absolute_path(path)
    return a:path[0] ==# '/'
  endfunction
endif

function! s:_build_module(sid)
  if has_key(s:loaded, a:sid)
    return copy(s:loaded[a:sid])
  endif
  let functions = s:_get_functions(a:sid)

  let prefix = '<SNR>' . a:sid . '_'
  let module = {}
  for func in functions
    let module[func] = function(prefix . func)
  endfor
  if has_key(module, '_vital_loaded')
    let V = vital#{s:self_version}#new()
    if has_key(module, '_vital_depends')
      call call(V.load, module._vital_depends(), V)
    endif
    try
      call module._vital_loaded(V)
    catch
      " FIXME: Show an error message for debug.
    endtry
  endif
  if !get(g:, 'vital_debug', 0)
    call filter(module, 'v:key =~# "^\\a"')
  endif
  let s:loaded[a:sid] = module
  return copy(module)
endfunction

if exists('+regexpengine')
  function! s:_get_functions(sid)
    let funcs = s:_redir(printf("function /\\%%#=2^\<SNR>%d_", a:sid))
    let map_pat = '<SNR>' . a:sid . '_\zs\w\+'
    return map(split(funcs, "\n"), 'matchstr(v:val, map_pat)')
  endfunction
else
  function! s:_get_functions(sid)
    let prefix = '<SNR>' . a:sid . '_'
    let funcs = s:_redir('function')
    let filter_pat = '^\s*function ' . prefix
    let map_pat = prefix . '\zs\w\+'
    return map(filter(split(funcs, "\n"),
    \          'stridx(v:val, prefix) > 0 && v:val =~# filter_pat'),
    \          'matchstr(v:val, map_pat)')
  endfunction
endif

function! s:_redir(cmd)
  let [save_verbose, save_verbosefile] = [&verbose, &verbosefile]
  set verbose=0 verbosefile=
  redir => res
    silent! execute a:cmd
  redir END
  let [&verbose, &verbosefile] = [save_verbose, save_verbosefile]
  return res
endfunction

function! vital#{s:self_version}#new()
  return s:_import('').load(['Prelude', ''])
endfunction


================================================
FILE: autoload/vital/phpcomplete-extended.vital
================================================
62ad025

Data.List
Data.String
System.Cache
System.File
Web.JSON
Text.Lexer
Text.Parser
System.Filepath


================================================
FILE: autoload/vital.vim
================================================
function! vital#of(name)
  let files = globpath(&runtimepath, 'autoload/vital/' . a:name . '.vital')
  let file = split(files, "\n")
  if empty(file)
    throw 'vital: version file not found: ' . a:name
  endif
  let ver = readfile(file[0], 'b')
  if empty(ver)
    throw 'vital: invalid version file: ' . a:name
  endif
  return vital#_{substitute(ver[0], '\W', '', 'g')}#new()
endfunction


================================================
FILE: bin/CorePHPDocParser.php
================================================
<?php
/**
 *=============================================================================
 * AUTHOR:  Mun Mun Das <m2mdas at gmail.com>
 * FILE: CorePHPDocParser.php
 * Last Modified: September 10, 2013
 * License: MIT license  {{{
 *     Permission is hereby granted, free of charge, to any person obtaining
 *     a copy of this software and associated documentation files (the
 *     "Software"), to deal in the Software without restriction, including
 *     without limitation the rights to use, copy, modify, merge, publish,
 *     distribute, sublicense, and/or sell copies of the Software, and to
 *     permit persons to whom the Software is furnished to do so, subject to
 *     the following conditions:
 *
 *     The above copyright notice and this permission notice shall be included
 *     in all copies or substantial portions of the Software.
 *
 *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 *     OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *     IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 *     CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 *     TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 *     SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * }}}
 *=============================================================================
 */

/**
 * generated index from offline php core documentation
 * It can be downloaded from http://www.php.net/download-docs.php
 * Download and unter the Many html files tar file
 *
 * Usage:
 *  new CorePHPDocParser("/your/php/doc/location", "/filename/to/save/index")->parsePhpdoc();
 */
class CorePhpDocParser
{
    /**
     * undocumented class variable
     *
     * @var array
     */
    private $coreIndex;

    /**
     * location of php doc folder
     * @var string
     */
    private $docLocation;

    /**
     * processed function docs
     *
     * @var array
     */
    public $functionDocs;

    /**
     * processed class docs
     *
     * @var array
     */
    public $classDocs;

    /**
     * constants
     *
     * @var array 
     */
    public $constants;

    private $indexFileName;

    private $xmlParser;

    public function __construct($docLocation, $indexFileName)
    {
        $this->coreIndex = array();
        $this->docLocation = $docLocation;
        $this->indexFileName = $indexFileName;
        $this->functionDocs = array();
        $this->classDocs = array();
        $this->constants = array();
        $this->xmlParser = new XMLParser();
    }

    /**
     * @return string
     */
    public function setDocLocation($docLocation)
    {
        $this->docLocation = $docLocation;
    }

    /**
     * returns book links
     *
     * @return returns array of book links
     */
    public function getBookLinks()
    {
        $books = array();
        $dom = DomDocument::loadHTMLFile($this->docLocation . "/extensions.membership.html");
        $query = '//*[@id="extensions.membership"]//a[@class="xref"]';

        $xpath = new DOMXPath($dom);
        $entries = $xpath->query($query);

        foreach ($entries as $entry) {
            $books[] = $entry->attributes->getNamedItem('href')->nodeValue;
        }
        return $books;
    }

    public function parsePhpDoc() 
    {
        $bookLinks = $this->getBookLinks();
        foreach($bookLinks as $bookLink) {
            $this->parseSectionDocFile($bookLink);
        }

        //$extraPredefinedConstants = array('reserved.constants.php');
        //foreach ($extraPredefinedConstants as $ep) {
            //$constants = $this->parsePredefinedConstants($ep);
            //$this->constants = array_merge($this->constants, $constants);
        //}

        $this->parsePredefinedClasses ("reserved.interfaces.html");
        $this->parsePredefinedClasses ("reserved.exceptions.html");

        $funcList = array_keys($this->functionDocs);
        sort($funcList);
        $classList = array_keys($this->classDocs);
        sort($classList);

        //ldd($indexedFuncList);

        sort($this->constants);
        $outData = array(
            'functions' => $this->functionDocs,
            'classes' => $this->classDocs,
            'function_list' => $funcList,
            'class_list' => $classList,
            'predefined_constants' => $this->constants
        );

        file_put_contents($this->indexFileName, json_encode($outData));
    }

    public function getBookProperties($bookFile) 
    {
        $excludedTypes = array(
            'Requirements',
            'Installation',
            'Runtime Configuration',
            'Resource Types',
            'Introduction',
            'Installing/Configuring',
        );
        $ss = explode(".", $bookFile);
        $section = $ss[1];
        $propertyLinks = array();

        $dom = @DomDocument::loadHTMLFile($this->docLocation . "/" . $bookFile);
        $xpath = new DOMXPath($dom);

        $query = '//*[@class="chunklist chunklist_book"]/li/a';
        $entries = $xpath->query($query);

        $subSectionQuery = '//*[@class="chunklist chunklist_book chunklist_children"]/li/a';
        $subSectionEntries = $xpath->query($subSectionQuery);

        $validLocations = array('class', 'ref', 'constants');
        foreach ($entries as $entry) {
            $location = $entry->attributes->getNamedItem('href')->nodeValue;
            if(in_array(substr($location, 0, strpos($location, ".")), $validLocations) || strpos($location, 'constants') !== false) {
                $propertyValue = $entry->nodeValue;
                $propertyLinks[$propertyValue] = $location;
            }
        }

        foreach ($subSectionEntries as $subSectionEntry) {
            $location = $subSectionEntry->attributes->getNamedItem('href')->nodeValue;
            if(!in_array(substr($location, 0, strpos($location, ".")), $validLocations)) {
                continue;
            }
            $propertyValue = (string) $subSectionEntry->nodeValue;
            $propertyLinks[$propertyValue] = $location;
        }

        return $propertyLinks;
    }

    public function parseMethodDocFile($methodFile) 
    {
        $methodInfo = array(
            'params' => array(),
            'docComment' => "",
            'signature' => "",
            'inheritdoc' => 0,
            'modifier' => array(),
            'return'=> ""
        );
        $excludedFiles = array("swfbutton.setdown.html", 
            "function.mysqli-report.html",
            "swfbutton.sethit.html",
            "swfbutton.setover.html",
            "swfbutton.setup.html",
            "function.xdiff-file-diff-binary.html",
            "function.xdiff-file-patch-binary.html",
            "function.xdiff-string-patch-binary.html",
            "function.xdiff-string-diff-binary.html",
        );

        if(in_array($methodFile, $excludedFiles)) {
            return $methodInfo;
        }

        $dom = @DomDocument::loadHTMLFile($this->docLocation . "/" . $methodFile); 
        $descQuery = '//*[@class="refpurpose"]/span[@class="dc-title"]';

        $fullDoc = utf8_encode(trim($this->parseFullDoc($methodFile)));
        $methodInfo['docComment'] = $fullDoc;

        $xpath = new DOMXPath($dom);
        $descEntries = $xpath->query($descQuery);

        foreach ($descEntries as $descEntry) {
            $desc = simplexml_import_dom($descEntry);
            if(strpos((string)$desc, "Alias") !== false && strpos((string)$desc, "Alias") == 0) {
                //ld($methodFile);
                $aliasFilaName = (string)$desc->span->a['href'];
                return $this->parseMethodDocFile($aliasFilaName);
            }
        }

        $signatureQuery = '//*[@class="refsect1 description"]/div[@class="methodsynopsis dc-description"]';
        $signatureEntries = $xpath->query($signatureQuery);
        foreach ($signatureEntries as $signatureEntry) {
            $sxml =  simplexml_import_dom($signatureEntry);
            $paramStrings = array();
            //ld($sxml->asXML());
            $modifier = "";
            foreach ($sxml->children() as $children) {
                switch ($children['class']) {
                    case 'type':
                        $returns = isset($children->a)? (string) $children->a : (string) $children;
                        $methodInfo['return'] = $this->isScalar($returns)? "" : $returns;
                        break;
                    case 'modifier':
                        $modifier = (string) $children;
                        $methodInfo['modifier'][] = $modifier;
                        break;
                    case 'methodname':
                        $methodName = (string) $children->strong;
                        break;
                    case 'methodparam':
                        if((string) $children == "void") {
                            continue;
                        }
                        $paramType = isset($children->span->a)? (string) $children->span->a: (string) $children->span;
                        $paramVar = (string) $children->code;
                        $paramString = $paramType . " " . $paramVar;
                        if(isset($children->span[1])) {
                            $paramString = "[" . $paramString . "]";
                        }
                        $paramStrings[] = $paramString;

                        $methodInfo['params'][$paramVar] = $this->isScalar($paramType)? "" : $paramType;
                    default:
                        break;
                }
            }
            $methodInfo['signature'] = "(" . join(" ,", $paramStrings) . ") : ". (string)$returns;
        }

        return $methodInfo;
    }

    public function parseFullDoc($file)
    {
        $dom = @DomDocument::loadHTMLFile($this->docLocation . "/" . $file);
        $descQuery = '//*[@class="refentry"]';
        $descQuery2 = '//*[@class="reference"]'; //don't know the syntax of OR property text
        $xpath = new DOMXPath($dom);
        $descEntries = $xpath->query($descQuery);
        if($descEntries->length == 0) {
            $xpath = new DOMXPath($dom);
            $descEntries = $xpath->query($descQuery2);
        }
        foreach ($descEntries as $descEntry) {
            $simpleXML = simplexml_import_dom($descEntry);
            $xml = $simpleXML->asXML();
            $this->xmlParser->reset();

            $xml_parser = xml_parser_create();
            xml_set_element_handler($xml_parser, array($this->xmlParser, 'startElementHandler'), array($this->xmlParser, 'endElementHandler'));
            xml_set_character_data_handler($xml_parser, array($this->xmlParser, 'dataHandler'));

            if (!xml_parse($xml_parser, $xml, true)) {
                die(sprintf("XML error: %s at line %d",
                    xml_error_string(xml_get_error_code($xml_parser)),
                    xml_get_current_line_number($xml_parser)));
            }
        }
        $content = (string)$this->xmlParser->getContent();
        return $content;
    }

    public function parseRefDocoFile($refFile) 
    {
        //ld($refFile);
        $functionDocs = array();
        $dom = @DomDocument::loadHTMLFile($this->docLocation . "/" . $refFile);
        $query = '//*[@class="chunklist chunklist_reference"]//a';
        $xpath = new DOMXPath($dom);
        $entries = $xpath->query($query);
        foreach($entries as $methodEntry) {
            $methodLocation = $methodEntry->attributes->getNamedItem('href')->nodeValue;
            $methodName = $methodEntry->nodeValue;
            $functionDocs[$methodName] = $this->parseMethodDocFile($methodLocation);
        }
        return $functionDocs;
    }

    public function parsePredefinedConstants($constFile)
    {
        //ld($constFile);
        $constants = array();
        $dom = @DomDocument::loadHTMLFile($this->docLocation . "/" . $constFile);
        $dtQuery = '//*[@class="appendix"]//span[@class="term"]/strong/code';
        $trQuery = '//*[@class="chapter"]//td/strong/code';
        $xpath = new DOMXPath($dom);
        $entries = $xpath->query($dtQuery);
        if($entries->length == 0) {
            $entries = $xpath->query($trQuery);
        }

        foreach($entries as $constantEntry) {
            $constants[] = $constantEntry->nodeValue;
        }
        return $constants;
    }

    public function parseClassDocFile($classFile) 
    {
        //ld($classFile);
        $className= "";

        $dom = @DomDocument::loadHTMLFile($this->docLocation . "/" . $classFile);
        $propertyQuery = '//*[@class="fieldsynopsis"]';
        $methodQuery = '//*[@class="methodsynopsis dc-description"]//a[@class="methodname"]';
        $classNameQuery = '//*[@class="classsynopsisinfo"]//strong[@class="classname"]';
        $xpath = new DOMXPath($dom);
        $propertyEntries = $xpath->query($propertyQuery);
        $methodEntries = $xpath->query($methodQuery);
        $classNameEntries = $xpath->query($classNameQuery);
        foreach ($classNameEntries as $cq) {
            $className = $cq->nodeValue;
        }

        $classData = $this->getEmptyClassData($className);
        $fullDoc = utf8_encode(trim($this->parseFullDoc($classFile)));
        $classData['docComment'] = $fullDoc;



        foreach ($propertyEntries as $propertyEntry) {
            $propertyData = array(
                'type' => "",
                'Inheritdoc' => 0,
                'docComment' => $fullDoc,
            );
            $propertyModifiers = array();
            $propertyName = "";
            $propertyType = "";
            $propertyEntryXML = simplexml_import_dom($propertyEntry);
            //ld($propertyEntryXML->asXML());
            foreach ($propertyEntryXML->children() as $propertyChild) {
                switch ($propertyChild['class']) {
                case 'modifier':
                    if((string)$propertyChild == "readonly") {
                        continue;
                    }
                    if((string) $propertyChild == "") {
                        continue;
                    }
                    $propertyModifiers[] = (string) $propertyChild;
                    break;
                case 'varname':
                case 'fieldsynopsis_varname':
                    //ld($propertyEntryXML->asXML());
                    $propertyName = isset($propertyChild->a->var)?(string) $propertyChild->a->var : (string)$propertyChild->var;
                    break;
                case 'type':
                    $propertyType = isset($propertyChild->a)? (string) $propertyChild->a : (string) $propertyChild;
                default:

                    break;
                }

            }

            $propertyData['type'] = $this->isScalar($propertyType)? "" : $propertyType;
            $propertyData['modifier'] = $propertyModifiers;

            foreach ($propertyModifiers as $propertyModifier) {
                $classData['properties']['modifier'][$propertyModifier][] = $propertyName;
            }
            if($propertyName != "") {
                $classData['properties']['all'][$propertyName] = $propertyData;
            }
        }

        foreach ($methodEntries as $methodEntry) {
            $methodData = array(
                'params' => array(),
                'docComment' => "",
                'signature' => "",
                'Inheritdoc' => 0
            );
            $methodXml = simplexml_import_dom($methodEntry);
            $methodDocFile = (string) $methodXml['href'];
            $methodName = (string) $methodXml;
            if(strpos($methodName, "::")) {
                $methodName = substr($methodName, strpos($methodName, "::")+2);
            }
            $methodData = $this->parseMethodDocFile($methodDocFile);
            $methodModifiers = $methodData['modifier'];

            if(empty($methodModifiers)) {
                $classData['methods']['modifier']['public'][] = $methodName;
            }

            foreach ($methodModifiers as $methodModifier) {
                $classData['methods']['modifier'][$methodModifier][] = $methodName;
            }
            $classData['methods']['all'][$methodName] = $methodData;
        }
        return $classData;
    }

    public function parsePredefinedClasses($fileLocation)
    {
        $dom = DomDocument::loadHTMLFile($this->docLocation . "/" . $fileLocation);
        $query = '//*[@class="chunklist chunklist_part"]/li/a';
        $xpath = new DOMXPath($dom);
        $entries = $xpath->query($query);
        foreach ($entries as $entry) {
            $sxml = simplexml_import_dom($entry);
            $className = (string) $sxml;
            $classLocation = (string) $sxml['href'];
            $this->classDocs[$className] = $this->parseClassDocFile($classLocation);
        }
    }


    public function parseSectionDocFile($sectionFile)
    {
        $classes = array();
        $functionDocs = array();
        $bookPropertyLinks = $this->getBookProperties($sectionFile);
        foreach ($bookPropertyLinks as $key => $location) {
            $locationType = substr($location, 0, strpos($location, "."));
            if($locationType == "class") {
                $this->classDocs[$key] = $this->parseClassDocFile($location);
            } else if($locationType == "ref") {
                $this->functionDocs = array_merge($this->functionDocs, $this->parseRefDocoFile($location));
            } elseif(strpos($location, 'constants') !== false) {
                $this->constants = array_merge($this->constants,  $this->parsePredefinedConstants($location));
            }
        }
    }

    private function isScalar($type) 
    {
        $scalarsTypes = array('boolean', 'integer','float', 'string', 'array', 'object', 
            'resource', 'mixed', 'number', 'callback', 'null', 'void', 'bool', 'self', 'int', 'callable');
        return in_array(strtolower($type), $scalarsTypes);
    }

    public function createDictFile()
    {
        $signatures = array();
        foreach ($this->functionDocs as $function) {
            $signatures[] = $function['signature'];
        }
        file_put_contents('php.dict', join("\n", $signatures));
    }

    private function getEmptyClassData($className) 
    {
        $classData = array();
        $classData['classname'] = $className;
        $classData['docComment'] = "";
        $classData['methods'] = array(
            'modifier' => array(
                'public' => array(),
                'private' => array(),
                'protected' => array(),
                'final' => array(),
                'static' => array(),
                'interface' => array(),
                'abstract' => array(),
            ),
            'all' => array("nnnnnnnn" => "nnnnnnnnnnnn")
        );
        $classData['properties'] = array(
            'modifier' => array(
                'public' => array(),
                'private' => array(),
                'protected' => array(),
                'final' => array(),
                'static' => array(),
                'interface' => array(),
                'abstract' => array(),
            ),
            'all' => array("nnnnnnnn" => "nnnnnnnnnnnn")
        );
        $classData['parentclass'] = "";
        $classData['parentclasses'] = array();
        $classData['interfaces'] = array();
        $classData['file'] = "";
        $classData['namespaces'] = array(
            'uses' => array()
        );

        $classData['constants'] = array();

        return $classData;
    }

}


================================================
FILE: bin/IndexGenerator.php
================================================
<?php
/**
 *=============================================================================
 * AUTHOR:  Mun Mun Das <m2mdas at gmail.com>
 * FILE: IndexGenerator.php
 * Last Modified: October 04, 2013
 * License: MIT license  {{{
 *     Permission is hereby granted, free of charge, to any person obtaining
 *     a copy of this software and associated documentation files (the
 *     "Software"), to deal in the Software without restriction, including
 *     without limitation the rights to use, copy, modify, merge, publish,
 *     distribute, sublicense, and/or sell copies of the Software, and to
 *     permit persons to whom the Software is furnished to do so, subject to
 *     the following conditions:
 *
 *     The above copyright notice and this permission notice shall be included
 *     in all copies or substantial portions of the Software.
 *
 *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 *     OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 *     MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 *     IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 *     CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 *     TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 *     SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 * }}}
 *=============================================================================
 */

set_time_limit(0);
ini_set('memory_limit','1000M');
ini_set('display_errors', 'stderr');
if(php_sapi_name() == 'cli') {
    if(count($argv) < 2) {
        echo "error: not enough arguments";
    } elseif ($argv[1] == 'generate') {
        array_shift($argv);
        array_shift($argv);

        $verbose = false;
        if(count($argv) > 0 && $argv[0] == '-verbose') {
            array_shift($argv);
            $verbose = true;
        }

        //$time = microtime(true); 
        $phpCompletePsr  = new IndexGenerator($verbose);

        $plugins = explode("-u", implode("", $argv));
        foreach ($plugins as $pluginFile) {
            if(empty($pluginFile)) {
                continue;
            }
            $phpCompletePsr->addPlugin(trim($pluginFile));
        }

        $index  = $phpCompletePsr->generateIndex();

        $jsonIndex = json_encode($index);
        $lastJsonError = json_last_error();
        if($lastJsonError != JSON_ERROR_NONE) {
            printJsonError($lastJsonError);
            exit;
        }

        $phpCompletePsr->writeToFile($phpCompletePsr->getIndexFileName(), $jsonIndex);
        $phpCompletePsr->writeToFile($phpCompletePsr->getReportFileName(), implode("\n", $phpCompletePsr->getInvalidClasses()));
    } else if($argv[1] == 'update') {
        array_shift($argv);
        array_shift($argv);
        $file = array_shift($argv);
        $cacheFileName = array_shift($argv);
        $verbose = false;

        $p = new IndexGenerator($verbose);
        $plugins = explode("-u", implode("", $argv));
        foreach ($plugins as $pluginFile) {
            if(empty($pluginFile)) {
                continue;
            }
            $p->addPlugin($pluginFile);
        }

        $p->writeUpdatedClassInfo($file, $cacheFileName);
        //echo "Time Elapsed: ".(microtime(true) - $time)."s\n";
        //echo "highest memory ".  memory_get_peak_usage();
    } else {
        echo "not a valid argument";
        exit;
    }
} else {
    exit;
}

function printJsonError($errorCode)
{
    switch (json_last_error()) {
    case JSON_ERROR_NONE:
        echo ' - No errors';
        break;
    case JSON_ERROR_DEPTH:
        echo ' - Maximum stack depth exceeded';
        break;
    case JSON_ERROR_STATE_MISMATCH:
        echo ' - Underflow or the modes mismatch';
        break;
    case JSON_ERROR_CTRL_CHAR:
        echo ' - Unexpected control character found';
        break;
    case JSON_ERROR_SYNTAX:
        echo ' - Syntax error, malformed JSON';
        break;
    case JSON_ERROR_UTF8:
        echo ' - Malformed UTF-8 characters, possibly incorrectly encoded';
        break;
    default:
        echo ' - Unknown error';
        break;
    }
    echo "\n";
}

class IndexGenerator
{
    /**
     *
     * @var array
     */
    private $file_fqcn;

    /**
     * 
     *
     * @var array
     */
    private $fqcn_file;

    /**
     * @var array
     */
    private $classes;

    /**
     * @var array
     */
    private $class_fqcn;

    /**
     * List of valid files
     * @var array 
     */
    private $validFiles;

    /**
     * 
     *
     * @var array 
     */
    private $invalidClasses;

    /**
     * Array of processed classes
     *
     * @var string
     */
    private $processedClasses;

    /**
     * index file name
     *
     * @var string
     */
    private $indexFileName;

    /**
     * report filename
     *
     * @var string
     */
    private $reportFileName;

    /**
     * list of parsed classes
     *
     * @var array 
     */
    private $parsedClasses;

    /**
     * php core doc index
     * @var array
     */
    private $coreIndex;

    /**
     * php core doc index file
     * @var string
     */
    private $coreIndexFile;
    /**
     * 
     *
     * @var string
     */
    private $pluginIndexFile;

    private $loader;

    /**
     * Array of plugin classes
     * @var array
     */
    public $plugins;

    /**
     * Verbosity
     *
     * @var bool
     */
    private $verbose;

    public function __construct($verbose) 
    {
        $this->file_fqcn        = array();
        $this->fqcn_file        = array();
        $this->class_fqcn       = array();
        $this->classes          = array();
        $this->validFiles       = array();
        $this->invalidClasses   = array();
        $this->processedClasses = array();
        $this->indexFileName    = './.phpcomplete_extended/phpcomplete_index';
        $this->reportFileName   = './.phpcomplete_extended/report.txt';
        $this->coreIndexFile    = './.phpcomplete_extended/core_index';
        $this->pluginIndexFile  = './.phpcomplete_extended/plugin_index';
        $this->parsedClasses    = array();
        $this->plugins = array();
        $this->loader = require 'vendor/autoload.php';
        $this->verbose = $verbose;
    }

    public function addPlugin($pluginFile)
    {
        if(!is_file($pluginFile)) {
            echo "not a valid file \n";
            return;
        }
        include $pluginFile;
        $className = basename($pluginFile, ".php");
        $plugin = new $className;
        if($plugin->isValidForProject()){
            $this->plugins[] = $plugin;
        }
    }

    /**
     * calls plugin hooks
     * @param string $hookName name of the plugin hook
     * @param bool $return expect return date 
     */
    public function execHook($hookName, $return)
    {
        $args = func_get_args();
        $out = array();
        array_shift($args);
        $return = array_shift($args);
        $extraArgs = $args;
        foreach ($this->plugins as $plugin) {
            if(method_exists($plugin, $hookName)) {
                $pluginArgs = $extraArgs;

                if($hookName == "preUpdateIndex") {
                    $pluginIndex    = $extraArgs[0];
                    $indexForPlugin = $pluginIndex[strtolower(get_class($plugin))];
                    $pluginArgs     = array($indexForPlugin);
                }

                $ret = call_user_func_array(array($plugin, $hookName), $pluginArgs);
                if($return) {
                    $out[strtolower(get_class($plugin))] = $ret;
                }
            }
        }
        if($return) {
            return $out;
        }
    }

    public function writeUpdatedClassInfo($fileName, $cacheFileName) 
    {
        $time = microtime(true);
        $this->processCoreIndexFile();
        $fileName        = $this->normalizePath($fileName);
        $classCache      = json_decode(file_get_contents($this->indexFileName), true);
        $extends         = $classCache['extends'];
        $implements      = $classCache['implements'];
        $this->fqcn_file = $classCache['fqcn_file'];
        $this->file_fqcn = $classCache['file_fqcn'];
        $this->class_fqcn = $classCache['class_fqcn'];
        $fileData        = array();
        if(!is_file($this->pluginIndexFile)) {
            $pluginIndex = array();
        } else{
            $pluginIndex     = json_decode(file_get_contents($this->pluginIndexFile), true);
        }
        
        $this->execHook("init", false, $this->loader);
        $this->execHook("preUpdateIndex", false, $pluginIndex);

        $fqcn = $this->validateClass($fileName);
        if(empty($fqcn)) {
            return;
        }

        if(array_key_exists($fileName, $classCache['file_fqcn'])) {
            $prevData = $classCache['classes'][$fqcn];
        } else {
            $prevData = array(
                'parentclasses' => array(),
                'interfaces' =>  array()
            );
        }

        $classData                    = $this->processClass($fqcn);
        $classCache['classes'][$fqcn] = $classData;
        $classCache['class_fqcn'] = $this->class_fqcn;
        $classCache['class_func_menu_entries'] = $this->createMenuEntries($this->class_fqcn, $this->coreIndex['function_list']);

        $fileData['classdata']['file'] = $fileName;
        $fileData['classdata']['fqcn'] = $fqcn;
        $fileData['classdata']['data'] = $classData;

        $fileData['extends']    = $this->getUpdatedExtraData($fqcn, $prevData, $classData, $classCache, $extends, 'parentclasses', 'extends');
        $fileData['interfaces'] = $this->getUpdatedExtraData($fqcn, $prevData, $classData, $classCache, $implements, 'interfaces', 'implements');

        $classCache['file_fqcn'][$fileName] = $fqcn;
        $classCache['fqcn_file'][$fqcn]     = $fileName;

        file_put_contents('.phpcomplete_extended/'. $cacheFileName, json_encode($fileData));
        file_put_contents('.phpcomplete_extended/phpcomplete_index', json_encode($classCache));
        $this->execHook("postUpdateIndex", false, $classData, $classCache, $this);
        $this->writePluginIndexes();

        return array($classCache, $fileData);
    }

    private function getUpdatedExtraData($fqcn, &$prevData, &$classData, &$classCache, $extraDataList, $extraClassDataKey, $extraClassCacheKey)
    {
        $extraDataDiff = array(
            'added' => array(),
            'removed' => array()
        );
        $parentCountValues = array_count_values(
            array_merge($prevData[$extraClassDataKey], $classData[$extraClassDataKey])
        );

        foreach($parentCountValues as $value => $count) {
            if($count == 1) {
                if(in_array($value, $prevData[$extraClassDataKey])) { //removed
                    $extraDataDiff['removed'][] = $value;
                } else {
                    $extraDataDiff['added'][] = $value;
                }
            }
        }

        foreach($extraDataDiff['removed'] as $removed) {
            $removedExtendData = $extraDataList[$removed];
            array_splice($removedExtendData, array_search($fqcn, $removedExtendData), 1);
            $classCache[$extraClassCacheKey][$removed] = $removedExtendData;
        }

        foreach($extraDataDiff['added'] as $added) {
            $classCache[$extraClassCacheKey][$added][] = $fqcn;
        }
        return $extraDataDiff;
    }

    private function listVendorLibraries()
    {
        $vendorLibs = array();

        $autoloadNamespaces = require 'vendor/composer/autoload_namespaces.php';
        foreach ($autoloadNamespaces as $namespace => $directory) {
            if($namespace == "") {
                continue;
            }
            $vendorLibs[$namespace] = $this->normalizePath($directory[0]);
        }
        return $vendorLibs;
    }

    private function normalizePath($path)
    {
        if($path == "") {
            return "";
        }

        $cwd = str_replace('\\','/',getcwd())."/";
        if(strpos($cwd, ':') == 1) {
            $drive = strtolower(substr($cwd, 0, 2));
            $cwd = substr_replace($cwd, $drive, 0, 2);
        }
        $path = str_replace("\\", '/', $path);
        if(strpos($path, ':') == 1) {
            $drive = strtolower(substr($path, 0, 2));
            $path = substr_replace($path, $drive, 0, 2);
        }
        $path = str_replace($cwd, '', $path);
        return $path;
    }

    public function generateIndex() 
    {
        $this->processCoreIndexFile();
        $time = microtime(true); // Gets microseconds
        //TODO: pasre constructor for doctype
        $classMap = require 'vendor/composer/autoload_classmap.php';
        $cwd = str_replace('\\','/',getcwd())."/";
        if(strpos($cwd, ':') == 1) {
            $drive = strtolower(substr($cwd, 0, 2));
            $cwd = substr_replace($cwd, $drive, 0, 2);
        }
        array_walk($classMap, function(&$item, $key) use ($cwd){
            $item = str_replace("\\", '/', $item);
            if(strpos($item, ':') == 1) {
                $drive = strtolower(substr($item, 0, 2));
                $item = substr_replace($item, $drive, 0, 2);
            }
            $item = str_replace($cwd, '', $item);
        });
        $out = array();
        $out['namespaces'] = array();
        $out['interface'] = array();
        $out['fqcn_file'] = $classMap;
        $out['file_fqcn'] = array_flip($classMap);
        $out['extends']  = array();
        $out['implements'] = array();
        $out['vendor_libs'] = $this->listVendorLibraries();
        //$this->file_fqcn = $classMap;
        $this->file_fqcn = $out['file_fqcn'];
        $this->fqcn_file = $out['fqcn_file'];

        $this->execHook("init", false, $this->loader);

        $regex = '/(.*)(?=\\\\(\w+)$)|(.*)/';
        $count = 0;
        foreach($classMap as $fqcn => $file) {
            if(
                preg_match('/DateSelect/', $file) //zend
                //|| preg_match('/DateTime/', $file) //zend
                || preg_match('/DateTimeSelect/', $file) //zend
                || preg_match('/MonthSelect/', $file) //zend
                //|| preg_match('/PropelDataCollector/', $file) //zend
            ){
                continue;
            }

            if(
                !array_key_exists('PHPUnit_Framework_TestCase', $this->fqcn_file) &&
                (preg_match('/Test/',$file) 
                || preg_match('/TestCase/'         , $file)
                || preg_match('/0/'                , $fqcn)
                || preg_match('/Fixtures/'         , $file)
                || preg_match('/Test/'             , $file)
                //|| preg_match('/Command/'             , $file)
                || preg_match('/DataFixtures/'     , $file)
                )
            ){
                continue;
            }

            if($this->verbose) {
                echo "processing $file\n";
            }

            if(!$this->validateClass($file)) {
                $this->invalidClasses[] = $file;
                continue;
            }
            $classData = array();
            $ret = preg_match($regex, $fqcn, $matches);
            if(!$ret) {
                continue;
            }
            $className = count($matches) == 3? $matches[2] :$matches[3];
            $namespace = count($matches) == 3? $matches[1] : "";
            if(!empty($namespace)) {
                $out['namespaces'][] = $namespace;
            }

            $classData = $this->processClass($fqcn);
            $out['classes'][$fqcn] = $classData;

            if(!empty($classData['parentclasses'])) {
                $parentClasses = $classData['parentclasses'];
                foreach ($parentClasses as $parentClass) {
                    if(empty($parentClass)) {
                        continue;
                    }
                    $out['extends'][$parentClass][] = $fqcn;
                }
            }

            if(!empty($classData['interfaces'])) {
                $interfaces = $classData['interfaces'];
                foreach ($interfaces as $interface) {
                    if(empty($interface)) {
                        continue;
                    }
                    $out['implements'][$interface][] = $fqcn;
                }
            }
            $this->execHook("postProcess", false, $fqcn, $file, $classData);

            $count++;
        }
        foreach ($this->coreIndex['class_list'] as $coreClass) {
            $this->classes[] = $coreClass;
            $this->class_fqcn[$coreClass] = $coreClass;
        }
        $classFuncConstList =  array_merge($this->classes, $this->coreIndex['function_list']);
        sort($classFuncConstList);
        sort($this->classes);
        ksort($this->class_fqcn);

        $out['class_list']            = $this->classes;
        $out['class_fqcn']            = $this->class_fqcn;
        $out['class_func_const_list'] = $classFuncConstList;
        $fqcns = array_merge($this->classes, array_keys($this->fqcn_file));

        sort($fqcns);
        $classFuncMenuEntries = $this->createMenuEntries($this->class_fqcn, $this->coreIndex['function_list']);
        $out['class_func_menu_entries'] = $classFuncMenuEntries;
        $this->execHook("postCreateIndex", false, $out, $this);
        $this->writePluginIndexes();
        return $out;
    }

    private function createMenuEntries($class_fqcn, $functions)
    {
        $dict = array();
        asort($functions);
        foreach ($functions as $func) {
            if($func == "Constants for PDO_4D" || $func == "Examples with PDO_4D") {
                continue;
            }
            $signature = $this->coreIndex['functions'][$func]['signature'];
            $dict[] = array(
                'word' => $func,
                'kind' => 'f',
                'menu' => $signature,
                'info' => $signature,
            );
        }
        foreach ($class_fqcn as $keyword => $fqcns) {
            if(is_array($fqcns)) {
                $i = 0;
                foreach ($fqcns as $fqcn) {
                    $dict[] = array(
                        'word' => $keyword . "-" . ($i + 1),
                        'kind' => 'c',
                        'menu' => $fqcn,
                        'info' => $fqcn,
                    );
                    $i++;
                }
            } elseif(is_string($fqcns)) {
                $dict[] = array(
                    'word' => $keyword,
                    'kind' => 'c',
                    'menu' => $fqcns,
                    'info' => $fqcns,
                );
            }
        }
        return $dict;
    }

    public function writePluginIndexes()
    {
        $indexes = $this->execHook('getIndex', true);
        if(empty($indexes)) {
            return;
        }
        $this->writeToFile($this->pluginIndexFile, json_encode($indexes));
    }

    public function processClass($fqcn)
    {
        if(array_key_exists($fqcn, $this->processedClasses)) {
            return $this->processedClasses[$fqcn];
        }
        $out = array();
        $classData = $this->getClassInfo($fqcn);

        $baseProperties = $classData;
        $parentClass = $classData['parentclass'];
      
Download .txt
gitextract_gva2ohpo/

├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── autoload/
│   ├── neocomplete/
│   │   └── sources/
│   │       └── php.vim
│   ├── neosnippet/
│   │   └── snippets/
│   │       ├── php.snip
│   │       └── vim.snip
│   ├── phpcomplete_extended/
│   │   ├── parser.vim
│   │   └── util.vim
│   ├── phpcomplete_extended.vim
│   ├── unite/
│   │   ├── kinds/
│   │   │   └── phpcomplete.vim
│   │   └── sources/
│   │       └── phpcomplete.vim
│   ├── vital/
│   │   ├── _62ad025/
│   │   │   ├── Data/
│   │   │   │   ├── List.vim
│   │   │   │   └── String.vim
│   │   │   ├── Prelude.vim
│   │   │   ├── System/
│   │   │   │   ├── Cache.vim
│   │   │   │   ├── File.vim
│   │   │   │   └── Filepath.vim
│   │   │   ├── Text/
│   │   │   │   ├── Lexer.vim
│   │   │   │   └── Parser.vim
│   │   │   └── Web/
│   │   │       └── JSON.vim
│   │   ├── _62ad025.vim
│   │   └── phpcomplete-extended.vital
│   └── vital.vim
├── bin/
│   ├── CorePHPDocParser.php
│   ├── IndexGenerator.php
│   └── core_index
├── doc/
│   └── phpcomplete-extended.txt
├── plugin/
│   └── phpcomplete_extended.vim
└── vest/
    ├── test-fowrard-parse.vim
    └── test-reverse-parser.vim
Download .txt
SYMBOL INDEX (58 symbols across 2 files)

FILE: bin/CorePHPDocParser.php
  class CorePhpDocParser (line 38) | class CorePhpDocParser
    method __construct (line 78) | public function __construct($docLocation, $indexFileName)
    method setDocLocation (line 92) | public function setDocLocation($docLocation)
    method getBookLinks (line 102) | public function getBookLinks()
    method parsePhpDoc (line 117) | public function parsePhpDoc()
    method getBookProperties (line 152) | public function getBookProperties($bookFile)
    method parseMethodDocFile (line 196) | public function parseMethodDocFile($methodFile)
    method parseFullDoc (line 282) | public function parseFullDoc($file)
    method parseRefDocoFile (line 312) | public function parseRefDocoFile($refFile)
    method parsePredefinedConstants (line 328) | public function parsePredefinedConstants($constFile)
    method parseClassDocFile (line 347) | public function parseClassDocFile($classFile)
    method parsePredefinedClasses (line 445) | public function parsePredefinedClasses($fileLocation)
    method parseSectionDocFile (line 460) | public function parseSectionDocFile($sectionFile)
    method isScalar (line 477) | private function isScalar($type)
    method createDictFile (line 484) | public function createDictFile()
    method getEmptyClassData (line 493) | private function getEmptyClassData($className)

FILE: bin/IndexGenerator.php
  function printJsonError (line 95) | function printJsonError($errorCode)
  class IndexGenerator (line 123) | class IndexGenerator
    method __construct (line 222) | public function __construct($verbose)
    method addPlugin (line 241) | public function addPlugin($pluginFile)
    method execHook (line 260) | public function execHook($hookName, $return)
    method writeUpdatedClassInfo (line 288) | public function writeUpdatedClassInfo($fileName, $cacheFileName)
    method getUpdatedExtraData (line 346) | private function getUpdatedExtraData($fqcn, &$prevData, &$classData, &...
    method listVendorLibraries (line 378) | private function listVendorLibraries()
    method normalizePath (line 392) | private function normalizePath($path)
    method generateIndex (line 412) | public function generateIndex()
    method createMenuEntries (line 539) | private function createMenuEntries($class_fqcn, $functions)
    method writePluginIndexes (line 579) | public function writePluginIndexes()
    method processClass (line 588) | public function processClass($fqcn)
    method mergeClassProperties (line 613) | private function mergeClassProperties($baseProperties, $parentProperties)
    method validateClass (line 692) | public function validateClass($fileName)
    method getClassInfo (line 771) | private function getClassInfo($fqcn)
    method getMethodData (line 859) | private function getMethodData(ReflectionMethod $reflectionMethod, $cl...
    method getConstantData (line 938) | private function getConstantData(ReflectionClass $reflectionClass, &$c...
    method parseDocComment (line 954) | private function parseDocComment($docComment, $type)
    method getPropertyData (line 1007) | private function getPropertyData(ReflectionProperty $reflectionPropert...
    method guessClass (line 1043) | private function guessClass($classToken, $namespaces)
    method getClassContent (line 1106) | private function getClassContent($fileLocation, $className)
    method trimDocComment (line 1124) | private function trimDocComment($docComment)
    method fixUTF8 (line 1142) | private function fixUTF8($content)
    method getEmptyMergeProperty (line 1148) | private function getEmptyMergeProperty()
    method getEmptyClassData (line 1192) | private function getEmptyClassData($className)
    method isScalar (line 1237) | private function isScalar($type)
    method getFQCNfromFile (line 1244) | private function getFQCNfromFile($file, $file_fqcn)
    method getIndexFileName (line 1266) | public function getIndexFileName()
    method setIndexFileName (line 1278) | public function setIndexFileName($indexFileName ="./.phpcomplete_exten...
    method getReportFileName (line 1289) | public function getReportFileName()
    method setReportFileName (line 1301) | public function setReportFileName($reportFileName="./.phpcomplete_exte...
    method writeToFile (line 1307) | public function writeToFile($fileName, $data)
    method getInvalidClasses (line 1317) | public function getInvalidClasses()
    method setInvalidClasses (line 1329) | public function setInvalidClasses($invalidClasses)
    method formatClassContent (line 1340) | public function formatClassContent($classContent)
    method parseClass (line 1450) | public function parseClass($fileName)
    method getCoreIndex (line 1628) | public function getCoreIndex()
    method setCoreIndex (line 1640) | public function setCoreIndex($coreIndex)
    method getCoreIndexFile (line 1651) | public function getCoreIndexFile()
    method setCoreIndexFile (line 1663) | public function setCoreIndexFile($coreIndexFile)
    method processCoreIndexFile (line 1669) | public function processCoreIndexFile()
Condensed preview — 31 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (279K chars).
[
  {
    "path": ".gitattributes",
    "chars": 97,
    "preview": "*.vim  text eol=lf\n*.php  text eol=lf\n*.txt  text eol=lf\n*.md  text eol=lf\nbin/core_index binary\n"
  },
  {
    "path": ".gitignore",
    "chars": 26,
    "preview": "tests/\nphpcompletePSR.rar\n"
  },
  {
    "path": "LICENSE",
    "chars": 1041,
    "preview": " Permission is hereby granted, free of charge, to any person obtaining\n a copy of this software and associated documenta"
  },
  {
    "path": "README.md",
    "chars": 6442,
    "preview": "phpcomplete-extended\n====================\n\nphpcomplete-extended is a fast, extensible, context aware autocomplete plugin"
  },
  {
    "path": "autoload/neocomplete/sources/php.vim",
    "chars": 2961,
    "preview": "\"=============================================================================\n\" AUTHOR:  Mun Mun Das <m2mdas at gmail.c"
  },
  {
    "path": "autoload/neosnippet/snippets/php.snip",
    "chars": 2250,
    "preview": "snippet     phpcomplete_extended_plugin\n    <?php\n\n    class ${1:plugin_name}\n    {\n\n        /**\n         *  \n         *"
  },
  {
    "path": "autoload/neosnippet/snippets/vim.snip",
    "chars": 1309,
    "preview": "snippet     phpcomplete_extended_plugin\n    let s:${1:plugin_name}_plugin = {\n        \\    'name': '$1'\n        \\}\n\n    "
  },
  {
    "path": "autoload/phpcomplete_extended/parser.vim",
    "chars": 22848,
    "preview": "let s:save_cpo = &cpo\nset cpo&vim\n\nlet s:lexer_symbols = [\n    \\ ['new'               , '\\<new\\>'] ,\n    \\ ['identifier'"
  },
  {
    "path": "autoload/phpcomplete_extended/util.vim",
    "chars": 5421,
    "preview": "\"=============================================================================\n\" AUTHOR:  Mun Mun Das <m2mdas at gmail.c"
  },
  {
    "path": "autoload/phpcomplete_extended.vim",
    "chars": 60307,
    "preview": "\"=============================================================================\n\" AUTHOR:  Mun Mun Das <m2mdas at gmail.c"
  },
  {
    "path": "autoload/unite/kinds/phpcomplete.vim",
    "chars": 432,
    "preview": "\"let s:save_cpo = &cpo\n\"set cpo&vim\n\nfunction! unite#kinds#phpcomplete_extended#define() \"{{{\n  \"return s:kind\nendfuncti"
  },
  {
    "path": "autoload/unite/sources/phpcomplete.vim",
    "chars": 6173,
    "preview": "\"=============================================================================\n\" AUTHOR:  Mun Mun Das <m2mdas at gmail.c"
  },
  {
    "path": "autoload/vital/_62ad025/Data/List.vim",
    "chars": 6999,
    "preview": "\" Utilities for list.\n\nlet s:save_cpo = &cpo\nset cpo&vim\n\nfunction! s:pop(list)\n  return remove(a:list, -1)\nendfunction\n"
  },
  {
    "path": "autoload/vital/_62ad025/Data/String.vim",
    "chars": 5879,
    "preview": "\" Utilities for string.\n\nlet s:save_cpo = &cpo\nset cpo&vim\n\nfunction! s:_vital_loaded(V)\n  let s:V = a:V\n  let s:L = s:V"
  },
  {
    "path": "autoload/vital/_62ad025/Prelude.vim",
    "chars": 11372,
    "preview": "let s:save_cpo = &cpo\nset cpo&vim\n\n\" glob() wrapper which returns List\n\" and 'wildignore' does not affect\n\" this functio"
  },
  {
    "path": "autoload/vital/_62ad025/System/Cache.vim",
    "chars": 2255,
    "preview": "\" Utilities for output cache.\n\nlet s:save_cpo = &cpo\nset cpo&vim\n\nfunction! s:getfilename(cache_dir, filename)\n  return "
  },
  {
    "path": "autoload/vital/_62ad025/System/File.vim",
    "chars": 5726,
    "preview": "\" Utilities for file copy/move/mkdir/etc.\n\nlet s:save_cpo = &cpo\nset cpo&vim\n\nlet s:is_unix = has('unix')\nlet s:is_windo"
  },
  {
    "path": "autoload/vital/_62ad025/System/Filepath.vim",
    "chars": 4624,
    "preview": "\" You should check the following related builtin functions.\n\" fnamemodify()\n\" resolve()\n\" simplify()\n\nlet s:save_cpo = &"
  },
  {
    "path": "autoload/vital/_62ad025/Text/Lexer.vim",
    "chars": 2545,
    "preview": "let s:save_cpo = &cpo\nset cpo&vim\n\nfunction! s:_vital_loaded(V)\n  let s:V = a:V\n  let s:Prelude = s:V.import('Prelude')\n"
  },
  {
    "path": "autoload/vital/_62ad025/Text/Parser.vim",
    "chars": 1608,
    "preview": "let s:save_cpo = &cpo\nset cpo&vim\n\n\nfunction! s:_exception(msg)\n  throw printf('[Text.Parser] %s', a:msg)\nendfunction\n\n\n"
  },
  {
    "path": "autoload/vital/_62ad025/Web/JSON.vim",
    "chars": 1228,
    "preview": "let s:save_cpo = &cpo\nset cpo&vim\n\nlet s:V = vital#{expand('<sfile>:h:h:t:r')}#new()\n\nfunction! s:_vital_depends()\n  ret"
  },
  {
    "path": "autoload/vital/_62ad025.vim",
    "chars": 5445,
    "preview": "let s:self_version = expand('<sfile>:t:r')\n\n\" Note: The extra argument to globpath() was added in Patch 7.2.051.\nlet s:g"
  },
  {
    "path": "autoload/vital/phpcomplete-extended.vital",
    "chars": 104,
    "preview": "62ad025\n\nData.List\nData.String\nSystem.Cache\nSystem.File\nWeb.JSON\nText.Lexer\nText.Parser\nSystem.Filepath\n"
  },
  {
    "path": "autoload/vital.vim",
    "chars": 391,
    "preview": "function! vital#of(name)\n  let files = globpath(&runtimepath, 'autoload/vital/' . a:name . '.vital')\n  let file = split("
  },
  {
    "path": "bin/CorePHPDocParser.php",
    "chars": 19633,
    "preview": "<?php\n/**\n *=============================================================================\n * AUTHOR:  Mun Mun Das <m2mda"
  },
  {
    "path": "bin/IndexGenerator.php",
    "chars": 59984,
    "preview": "<?php\n/**\n *=============================================================================\n * AUTHOR:  Mun Mun Das <m2mda"
  },
  {
    "path": "doc/phpcomplete-extended.txt",
    "chars": 7362,
    "preview": "*phpcomplete-extended.txt*\tFast autocomplete plugin for PHP composer projects\n\nLicense: MIT license  {{{\n    Permission "
  },
  {
    "path": "plugin/phpcomplete_extended.vim",
    "chars": 4080,
    "preview": "\"=============================================================================\n\" AUTHOR:  Mun Mun Das <m2mdas at gmail.c"
  },
  {
    "path": "vest/test-fowrard-parse.vim",
    "chars": 4471,
    "preview": "scriptencoding utf-8\n\nlet s:save_cpo = &cpo\nscriptencoding utf-8\nset cpo&vim\n\nContext forward_parser\n    It tests revers"
  },
  {
    "path": "vest/test-reverse-parser.vim",
    "chars": 13364,
    "preview": "scriptencoding utf-8\n\nlet s:save_cpo = &cpo\nscriptencoding utf-8\nset cpo&vim\n\nContext reverse_parser\n    It tests revers"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the m2mdas/phpcomplete-extended GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 31 files (23.5 MB), approximately 68.5k tokens, and a symbol index with 58 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!