Skip to content

Commit b5f865e

Browse files
committed
added event for custom completers
1 parent 839eda4 commit b5f865e

9 files changed

Lines changed: 133 additions & 68 deletions

File tree

specs/completer/resolver/ContextResolver.spec.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,21 @@
8787
$context = $this->resolver->getContext('$var->param;');
8888
expect($context->isObject())->to->be->false;
8989
});
90+
it('hasn\'t type object after object operator with (', function(){
91+
$context = $this->resolver->getContext('$var->param(');
92+
expect($context->isObject())->to->be->false;
93+
});
9094
it('hasn\'t type object after object operator with TString and space', function(){
9195
/** @var Context $context */
9296
$context = $this->resolver->getContext('$var->param ');
9397
expect($context->isObject())->to->be->false;
9498
});
9599
});
100+
describe("Method call", function () {
101+
it('has type method call after (', function () {
102+
$context = $this->resolver->getContext('$var->method(');
103+
expect($context->isMethodCall())->to->be->true;
104+
});
105+
});
96106
});
97107
});

specs/completer/resolver/NodeTypeResolver.spec.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ function createClass($classFQN, $fqcn){
5050
it('returns variable type from scope', function(){
5151
$node = new NodeVar;
5252
$node->name = $this->var->getName();
53-
expect($this->resolver->getChainType($node, $this->index, $this->scope))
53+
expect($this->resolver->getLastChainNodeType($node, $this->index, $this->scope))
5454
->to->equal($this->var->getType());
5555
});
5656
describe('Properties', function(){
@@ -61,12 +61,12 @@ function createClass($classFQN, $fqcn){
6161
});
6262
it('returns null for unknown property', function(){
6363
$this->node->name = 'param';
64-
expect($this->resolver->getChainType($this->node, $this->index, $this->scope))
64+
expect($this->resolver->getLastChainNodeType($this->node, $this->index, $this->scope))
6565
->to->be->null;
6666
});
6767
it('returns type for known property', function(){
6868
$this->node->name = 'param2';
69-
expect($this->resolver->getChainType($this->node, $this->index, $this->scope))
69+
expect($this->resolver->getLastChainNodeType($this->node, $this->index, $this->scope))
7070
->to->equal($this->anotherFQCN);
7171
});
7272
});
@@ -78,12 +78,12 @@ function createClass($classFQN, $fqcn){
7878
});
7979
it('returns null for unknown method', function(){
8080
$this->node->name = 'method';
81-
expect($this->resolver->getChainType($this->node, $this->index, $this->scope))
81+
expect($this->resolver->getLastChainNodeType($this->node, $this->index, $this->scope))
8282
->to->be->null;
8383
});
8484
it('returns type for known method', function(){
8585
$this->node->name = 'method2';
86-
expect($this->resolver->getChainType($this->node, $this->index, $this->scope))
86+
expect($this->resolver->getLastChainNodeType($this->node, $this->index, $this->scope))
8787
->to->equal($this->anotherFQCN);
8888
});
8989
});
@@ -102,7 +102,7 @@ function createClass($classFQN, $fqcn){
102102
$node = new PropertyFetch;
103103
$node->var = $this->node;
104104
$node->name = 'param2';
105-
expect($this->resolver->getChainType($node, $this->index, $this->scope))
105+
expect($this->resolver->getLastChainNodeType($node, $this->index, $this->scope))
106106
->to->equal($this->var->getType());
107107
});
108108
});

src/Application/BaseApplication.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,8 @@ protected function createContainer()
8282
protected $router;
8383
/** @var Loader */
8484
protected $pluginsLoader;
85-
static protected $projectsPool = [];
86-
static protected $currentProject = null;
85+
protected $projectsPool = [];
86+
protected $currentProject = null;
8787
/** @var Container */
8888
protected $container;
8989
protected $noFsIO = false;

src/Complete/Completer/CompleterFactory.php

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Complete\Completer;
44

5+
use Symfony\Component\EventDispatcher\EventDispatcher;
56
use Entity\Completion\Context;
67
use Entity\Completion\Scope;
78
use Entity\Project;
@@ -13,35 +14,36 @@ public function __construct(
1314
NamespaceCompleter $namespaceCompleter,
1415
ObjectCompleter $objectCompleter,
1516
StaticCompleter $staticCompleter,
16-
UseCompleter $useCompleter
17-
){
17+
UseCompleter $useCompleter,
18+
EventDispatcher $dispatcher
19+
) {
1820
$this->classNameCompleter = $classNameCompleter;
1921
$this->interfaceNameCompleter = $interfaceNameCompleter;
2022
$this->namespaceCompleter = $namespaceCompleter;
2123
$this->objectCompleter = $objectCompleter;
2224
$this->staticCompleter = $staticCompleter;
2325
$this->useCompleter = $useCompleter;
26+
$this->dispatcher = $dispatcher;
2427
}
25-
public function getCompleter(Context $context){
26-
if($context->isNamespace()){
28+
public function getCompleter(Context $context)
29+
{
30+
$event = new CustomCompleterEvent($context);
31+
if ($context->isNamespace()) {
2732
return $this->namespaceCompleter;
28-
}
29-
elseif($context->isUse()){
33+
} elseif ($context->isUse()) {
3034
return $this->useCompleter;
31-
}
32-
elseif($context->isClassName()){
35+
} elseif ($context->isClassName()) {
3336
return $this->classNameCompleter;
34-
}
35-
elseif($context->isInterfaceName()){
37+
} elseif ($context->isInterfaceName()) {
3638
return $this->interfaceNameCompleter;
37-
}
38-
elseif($context->isThis() || $context->isObject()){
39+
} elseif ($context->isThis() || $context->isObject()) {
3940
return $this->objectCompleter;
40-
}
41-
elseif($context->isClassStatic()){
41+
} elseif ($context->isClassStatic()) {
4242
return $this->staticCompleter;
43+
} else {
44+
$this->dispatcher->dispatch(self::CUSTOM_COMPLETER, $event);
4345
}
44-
return null;
46+
return $event->completer;
4547
}
4648

4749
private $classNameCompleter;
@@ -50,4 +52,7 @@ public function getCompleter(Context $context){
5052
private $objectCompleter;
5153
private $staticCompleter;
5254
private $useCompleter;
55+
private $dispatcher;
56+
57+
const CUSTOM_COMPLETER = 'completer.custom';
5358
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
namespace Complete\Completer;
4+
5+
use Entity\Completion\Context;
6+
use Symfony\Component\EventDispatcher\Event;
7+
8+
class CustomCompleterEvent extends Event
9+
{
10+
/** @var CompleterInterface */
11+
public $completer = null;
12+
/** @var Context */
13+
public $context;
14+
public function __construct(Context $context)
15+
{
16+
$this->context = $context;
17+
}
18+
}

src/Complete/Resolver/ContextResolver.php

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public function getContext($badLine, Index $index, Scope $scope = null){
4343
*/
4444
protected function getLastToken($badLine){
4545
try {
46-
$symbols = @token_get_all($this->prepareLine($badLine));
46+
$symbols = @token_get_all($this->prepareLine($badLine, $wrapFunctionCall));
4747
}
4848
catch(\Exception $e){
4949
$symbols = [0,0];
@@ -60,7 +60,7 @@ protected function createContext(Scope $scope, Token $token, $badLine, Index $in
6060
$context = new Context($scope, $token);
6161
$nodes = $this->parser->parse($this->prepareLine($badLine));
6262

63-
if ($token->isObjectOperator() || $token->isStaticOperator()) {
63+
if ($token->isObjectOperator() || $token->isStaticOperator() || $token->isMethodCall()) {
6464
if (is_array($nodes)) {
6565
$workingNode = array_pop($nodes);
6666
} else {
@@ -78,9 +78,11 @@ protected function createContext(Scope $scope, Token $token, $badLine, Index $in
7878
$isThis = true;
7979
}
8080
}
81+
$types = $this->typeResolver->getChainType($workingNode, $index, $scope);
8182
$context->setData([
82-
$this->typeResolver->getChainType($workingNode, $index, $scope),
83-
$isThis
83+
array_pop($types),
84+
$isThis,
85+
$types
8486
]);
8587
}
8688
if ($token->isUseOperator()
@@ -110,13 +112,17 @@ protected function addSymbolForToken($symbol, Token $token = null){
110112
return $token;
111113
}
112114

113-
protected function prepareLine($badLine){
114-
if(strpos($badLine, '<?php') === false
115+
protected function prepareLine($badLine, $wrapFunctionCall = true)
116+
{
117+
if (strpos($badLine, '<?php') === false
115118
|| strpos($badLine, '<?') === false
116-
){
119+
) {
117120
$badLine = '<?php ' . $badLine;
118121
}
119122
$badLine = str_replace(['elseif', 'else', 'catch'], '', $badLine);
123+
if ($wrapFunctionCall && $badLine[strlen($badLine)-1] === '(') {
124+
$badLine .= ')';
125+
}
120126
return $badLine;
121127
}
122128

src/Complete/Resolver/NodeTypeResolver.php

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,25 +52,40 @@ public function getType($node, Index $index, Scope $scope)
5252
|| $node instanceof MethodCall
5353
|| $node instanceof StaticCall
5454
) {
55-
return $this->getChainType($node, $index, $scope);
55+
return $this->getLastChainNodeType($node, $index, $scope);
5656
} elseif ($node instanceof New_) {
5757
return $this->useParser->getFQCN($node->class);
5858
}
5959
return null;
6060
}
6161

62+
/**
63+
* Calculates type of the passed last node in chain
64+
*
65+
* @param PhpParser\Node $node
66+
* @param Index $index
67+
* @param Scope $scope
68+
* @return FQCN[]
69+
*/
70+
public function getLastChainNodeType($node, Index $index, Scope $scope)
71+
{
72+
$types = $this->getChainType($node, $index, $scope);
73+
return array_pop($types);
74+
}
75+
6276
/**
6377
* Calculates type of the passed chained node
6478
*
6579
* @param PhpParser\Node $node
6680
* @param Index $index
6781
* @param Scope $scope
68-
* @return FQCN|null
82+
* @return FQCN[]
6983
*/
7084
public function getChainType($node, Index $index, Scope $scope)
7185
{
7286
/** @var FQCN */
7387
$type = null;
88+
$types = [];
7489
$chain = $this->createChain($node);
7590
$block = $chain;
7691
while ($block instanceof Chain) {
@@ -81,13 +96,13 @@ public function getChainType($node, Index $index, Scope $scope)
8196
$type = $this->getVarType($block->getName(), $scope);
8297
} elseif ($block->getType() === 'method') {
8398
if (!($type instanceof FQN)) {
84-
$type = null;
99+
$types[] = null;
85100
break;
86101
}
87102
$type = $this->getMethodType($block->getName(), $type, $index);
88103
} elseif ($block->getType() === 'property') {
89104
if (!($type instanceof FQN)) {
90-
$type = null;
105+
$types[] = null;
91106
break;
92107
}
93108
$type = $this->getPropertyType($block->getName(), $type, $index);
@@ -105,10 +120,11 @@ public function getChainType($node, Index $index, Scope $scope)
105120
$this->dispatcher->dispatch(self::BLOCK_END, $event);
106121
$type = $event->getType();
107122
$block = $block->getChild();
123+
$types[] = $type;
108124
}
109125
$event = new TypeResolveEvent($chain, $type);
110126
$this->dispatcher->dispatch(self::TYPE_RESOLVED, $event);
111-
return $type;
127+
return $types;
112128
}
113129

114130
/**

0 commit comments

Comments
 (0)