diff --git a/src/NodeVisitor/DynamicLoader.php b/src/NodeVisitor/DynamicLoader.php new file mode 100644 index 0000000..454dbc0 --- /dev/null +++ b/src/NodeVisitor/DynamicLoader.php @@ -0,0 +1,92 @@ +definitionCollector = $definitionCollector; + $this->definitionResolver = $definitionResolver; + } + + // using the lowercase $modelName to find the FQN + public function findModelDef(String $filepath) { + $filename = $filepath . ".php"; + foreach ($this->definitionCollector->definitions as $def) { + $fqn = $def->fqn; + $si = $def->symbolInformation; + + if (!isset($si->location->uri)) continue; + $uri = strtolower($si->location->uri); + + $endsWith = substr_compare($uri, $filename, strlen($uri) - strlen($filename)) === 0; + + // if the file matches, find the first class + if ($endsWith && $si->kind === SymbolKind::CLASS_) { + return $def; + } + } + return NULL; + } + + public function enterNode(Node $node) + { + // check its name is 'model' + if (!($node instanceof Node\Expr\MethodCall && $node->name === 'model')) { + return; + } + // check its caller is a 'load' + $caller = $node->getAttribute('previousSibling'); + if (!($caller instanceof Node\Expr\PropertyFetch && $caller->name !== 'load')) { + return; + } + + // make sure the first argument is a string. + if (!(isset($node->args[0]) && $node->args[0]->value instanceof Node\Scalar\String_)) { + return; + } + //if(!($node instanceof Node\Expr\MethodCall && isset($node->args[0]))) return; + + $argstr = $node->args[0]->value->value; + + // $node's FQN is "className->memberSuffix()". But we need to construct loaded name. + $def = $this->findModelDef($argstr); + + //var_dump($def); + + // if we cannot find definition, just return. + if ($def === NULL) { + return; + } + + $fqn = $def->fqn; + $resolver = $this->definitionResolver; + + // add fqn to nodes and definitions. + $this->definitionCollector->nodes[$fqn] = $node; + + // Definition is created based on types and context of $node. we should construct by ourselves. + $definition = $resolver->createDefinitionFromNode($node, $fqn); + // Have to override type: + $definition->type = new Types\Object_; + // TODO: check if setting document path is correct. + + $this->definitionCollector->definitions[$fqn] = $definition; + } +} diff --git a/src/PhpDocument.php b/src/PhpDocument.php index ff3cf8b..e400fc5 100644 --- a/src/PhpDocument.php +++ b/src/PhpDocument.php @@ -3,6 +3,16 @@ declare(strict_types = 1); namespace LanguageServer; +use LanguageServer\Protocol\{Diagnostic, DiagnosticSeverity, Range, Position, TextEdit}; +use LanguageServer\NodeVisitor\{ + NodeAtPositionFinder, + ReferencesAdder, + DocBlockParser, + DefinitionCollector, + ColumnCalculator, + ReferencesCollector, + DynamicLoader +}; use LanguageServer\Index\Index; use LanguageServer\Protocol\{ Diagnostic, Position, Range @@ -152,6 +162,23 @@ class PhpDocument $this->definitionNodes = $treeAnalyzer->getDefinitionNodes(); + // Report errors from parsing docblocks + foreach ($docBlockParser->errors as $error) { + $this->diagnostics[] = Diagnostic::fromError($error, $this->content, DiagnosticSeverity::WARNING, 'php'); + } + + $traverser = new NodeTraverser; + + // Collect all definitions + $definitionCollector = new DefinitionCollector($this->definitionResolver); + $traverser->addVisitor($definitionCollector); + + $traverser->addVisitor(new DynamicLoader($definitionCollector, $this->definitionResolver)); + + // Collect all references + $referencesCollector = new ReferencesCollector($this->definitionResolver); + $traverser->addVisitor($referencesCollector); + $this->referenceNodes = $treeAnalyzer->getReferenceNodes(); foreach ($this->definitions as $fqn => $definition) {