How can I implement "Go to Declaration" functionality in a PHPStorm plugin?
Heeeeyyy :)
I am developing a PHPStorm plugin for Laravel that provides autocompletion for form request fields. I've successfully implemented the autocompletion, but I am now trying to add "Go to Declaration" functionality, so when a user selects a form request field, they can navigate directly to its declaration in the rules
method of the FormRequest
class.
I need help implementing "Go to Declaration" for the completion elements. Specifically, when a user selects a field like name
or email
from the completion suggestions, I want to navigate them directly to the relevant line in the rules
method where this field is defined.
What steps should I take to achieve this?
Additionally, I would like to know if there is a better way to retrieve the PhpClass
of a VariableImpl
than using variable.getGlobalType().toString()
? Are there more efficient or reliable methods to achieve this?
CompletionContributor class:
package at.test.package.validation;
import at.test.package.BaseCompletion;
import at.test.package.support.LaravelVisionIcon;
import com.intellij.codeInsight.completion.*;
import com.intellij.codeInsight.lookup.LookupElementBuilder;
import com.intellij.patterns.PlatformPatterns;
import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.ProcessingContext;
import com.jetbrains.php.PhpIndex;
import com.jetbrains.php.lang.lexer.PhpTokenTypes;
import com.jetbrains.php.lang.psi.elements.Method;
import com.jetbrains.php.lang.psi.elements.PhpClass;
import com.jetbrains.php.lang.psi.elements.impl.*;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
public class FormRequestFieldCompletionContributor extends BaseCompletion {
public FormRequestFieldCompletionContributor() {
extend(CompletionType.BASIC, PlatformPatterns.psiElement().withElementType(PhpTokenTypes.IDENTIFIER), new CompletionProvider<>() {
@Override
protected void addCompletions(@NotNull CompletionParameters parameters, @NotNull ProcessingContext context, @NotNull CompletionResultSet result) {
PsiElement psiElement = parameters.getPosition();
VariableImpl variable = PsiTreeUtil.getPrevSiblingOfType(psiElement, VariableImpl.class);
if (variable != null) {
PhpIndex phpIndex = PhpIndex.getInstance(psiElement.getProject());
Collection<PhpClass> formRequestClass = phpIndex.getClassesByFQN(variable.getGlobalType().toString());
for (PhpClass phpClass : formRequestClass) {
Method rulesMethod = phpClass.findMethodByName("rules");
if (rulesMethod != null) {
processRulesMethod(rulesMethod, result);
}
}
}
}
});
}
private void processRulesMethod(@NotNull Method rulesMethod, @NotNull CompletionResultSet result) {
ArrayCreationExpressionImpl arrayCreationExpression = PsiTreeUtil.findChildOfType(rulesMethod, ArrayCreationExpressionImpl.class);
if (arrayCreationExpression != null) {
ArrayHashElementImpl[] arrayHashElements = PsiTreeUtil.getChildrenOfType(arrayCreationExpression, ArrayHashElementImpl.class);
if (arrayHashElements != null) {
for (ArrayHashElementImpl arrayHashElement : arrayHashElements) {
addCompletionFromArrayHashElement(arrayHashElement, result);
}
}
}
}
private void addCompletionFromArrayHashElement(@NotNull ArrayHashElementImpl arrayHashElement, @NotNull CompletionResultSet result) {
PsiElement keyElement = arrayHashElement.getKey();
if (keyElement != null) {
String keyText = keyElement.getText().replace("'", "");
LookupElementBuilder lookupElement = LookupElementBuilder.create(keyText)
.withLookupString(keyText)
.withPresentableText(keyText)
.withIcon(LaravelVisionIcon.LARAVEL_ICON)
.withPsiElement(keyElement);
result.addElement(PrioritizedLookupElement.withPriority(lookupElement, 100));
}
}
}
Here’s an example of how the rules
method might look like in a typical Laravel form request class:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class LoginRequest extends FormRequest
{
public function rules(): array
{
return [
'email' => 'required',
'password' => 'required'
];
}
}
Users will access the form request field in their code like this:
public function login(LoginRequest $request)
{
$request->email; //access the variable/key
return view('welcome');
}
Thx :)
Please sign in to leave a comment.
You need to provide PSI References https://plugins.jetbrains.com/docs/intellij/references-and-resolve.html (which can also act as completion item providers).