├── .gitignore ├── README.md └── pythonx └── cm_sources └── phpactor.py /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | /test.py 3 | /test.php 4 | /composer.lock 5 | __pycache__ 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [phpactor](https://github.com/phpactor/phpactor) integration for 2 | [nvim-completion-manager](https://github.com/roxma/nvim-completion-manager) 3 | 4 | ![phpactor](https://user-images.githubusercontent.com/4538941/30627852-67643a22-9e05-11e7-90d1-aa75c2d0654c.gif) 5 | 6 | 7 | ## Installation 8 | 9 | Assuming you're using [vim-plug](https://github.com/junegunn/vim-plug) 10 | 11 | ```vim 12 | " requires nvim-completion-manager 13 | Plug 'roxma/nvim-completion-manager' 14 | 15 | " requires phpactor 16 | Plug 'phpactor/phpactor' , {'do': 'composer install'} 17 | 18 | Plug 'roxma/ncm-phpactor' 19 | ``` 20 | 21 | -------------------------------------------------------------------------------- /pythonx/cm_sources/phpactor.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # For debugging, use this command to start neovim: 4 | # 5 | # NVIM_PYTHON_LOG_FILE=nvim.log NVIM_PYTHON_LOG_LEVEL=INFO nvim 6 | # 7 | # 8 | # Please register source before executing any other code, this allow cm_core to 9 | # read basic information about the source without loading the whole module, and 10 | # modules required by this module 11 | from cm import register_source, getLogger, Base 12 | 13 | register_source(name='phpactor', 14 | priority=9, 15 | abbreviation='php', 16 | word_pattern=r'[\w]+', 17 | scoping=True, 18 | scopes=['php'], 19 | early_cache=1, 20 | cm_refresh_patterns=[r'$', r'-\>', r'::'],) 21 | 22 | import json 23 | import os 24 | import subprocess 25 | import glob 26 | import re 27 | 28 | logger = getLogger(__name__) 29 | 30 | 31 | class Source(Base): 32 | 33 | def __init__(self, nvim): 34 | super(Source, self).__init__(nvim) 35 | 36 | self._phpactor = nvim.eval(r"""globpath(&rtp, 'bin/phpactor', 1)""") 37 | 38 | if not self._phpactor: 39 | self.message('error', 'phpactor not found, please install https://github.com/phpactor/phpactor') 40 | 41 | def cm_refresh(self, info, ctx, *args): 42 | 43 | src = self.get_src(ctx).encode('utf-8') 44 | lnum = ctx['lnum'] 45 | col = ctx['col'] 46 | filepath = ctx['filepath'] 47 | startcol = ctx['startcol'] 48 | 49 | args = ['php', self._phpactor, 'complete', '--format=json', 'stdin', '%s' % self.get_pos(lnum, col, src)] 50 | proc = subprocess.Popen(args=args, 51 | stdin=subprocess.PIPE, 52 | stdout=subprocess.PIPE, 53 | stderr=subprocess.DEVNULL, 54 | cwd=self.nvim.eval('getcwd()')) 55 | 56 | result, errs = proc.communicate(src, timeout=30) 57 | 58 | result = result.decode() 59 | 60 | logger.debug("args: %s, result: [%s]", args, result) 61 | 62 | result = json.loads(result) 63 | 64 | # { 65 | # "suggestions": [ 66 | # { 67 | # "type": "f", 68 | # "name": "setFormatter", 69 | # "info": "pub setFormatter(OutputFormatterInterface $formatter)" 70 | # } 71 | # ] 72 | # } 73 | 74 | matches = [] 75 | 76 | for e in result['suggestions']: 77 | menu = e['info'] 78 | word = e['name'] 79 | t = e['type'] 80 | 81 | item = dict(word=word, menu=menu) 82 | 83 | # snippet support 84 | m = re.search(r'(\w+\s+)?\w+\((.*)\)', menu) 85 | 86 | if m and t == 'f': 87 | 88 | params = m.group(2) 89 | 90 | placeholders = [] 91 | num = 1 92 | snip_args = '' 93 | 94 | if params != '': 95 | 96 | params = params.split(',') 97 | 98 | for param in params: 99 | 100 | if "=" in param: 101 | # skip params with default value 102 | break 103 | else: 104 | param = re.search(r'\$\w+', param).group() 105 | ph = self.snippet_placeholder(num, param) 106 | placeholders.append(ph) 107 | num += 1 108 | 109 | snip_args = ', '.join(placeholders) 110 | 111 | if len(placeholders) == 0: 112 | # don't jump out of parentheses if function has 113 | # parameters 114 | snip_args = self.snippet_placeholder(1) 115 | 116 | ph0 = self.snippet_placeholder(0) 117 | snippet = '%s(%s)%s' % (word, snip_args, ph0) 118 | 119 | item['snippet'] = snippet 120 | 121 | matches.append(item) 122 | 123 | logger.debug("startcol [%s] matches: [%s]", startcol, matches) 124 | 125 | self.complete(info, ctx, startcol, matches) 126 | --------------------------------------------------------------------------------