in src/main/com/intellij/lang/jsgraphql/ide/resolve/GraphQLReferenceService.java [203:268]
PsiReference resolveFieldReference(@NotNull GraphQLReferenceMixin element, @NotNull GraphQLField field) {
final String name = element.getName();
Ref<PsiReference> reference = new Ref<>();
if (name != null) {
if (name.startsWith("__")) {
// __typename or introspection fields __schema and __type which implicitly extends the query root type
GraphQLResolveUtil.processFilesInLibrary(GraphQLBundledLibraryTypes.SPECIFICATION, element, file -> {
file.accept(new GraphQLRecursiveVisitor() {
@Override
public void visitElement(@NotNull GraphQLElement schemaElement) {
// TODO: [vepanimas] rework to use com.intellij.psi.PsiElement.processDeclarations or something similar,
// now it traverses the whole tree including comments, punctuation, arguments and so on,
// but we actually expect only field declarations in two predefined object types
if (schemaElement instanceof GraphQLReferenceElement &&
Objects.equals(((GraphQLReferenceElement)schemaElement).getReferenceName(), name)) {
reference.set(createReference(element, schemaElement));
stopWalking();
return;
}
super.visitElement(schemaElement);
}
});
return reference.isNull();
});
}
final GraphQLTypeScopeProvider typeScopeProvider = PsiTreeUtil.getParentOfType(field, GraphQLTypeScopeProvider.class);
if (reference.isNull() && typeScopeProvider != null) {
GraphQLType typeScope = typeScopeProvider.getTypeScope();
if (typeScope != null) {
final GraphQLType fieldType = GraphQLSchemaUtil.getUnmodified(typeScope);
myPsiSearchHelper.processNamedElements(element, name, psiNamedElement -> {
if (psiNamedElement.getParent() instanceof GraphQLFieldDefinition fieldDefinition) {
if (!Objects.equals(fieldDefinition.getName(), name)) {
// field name doesn't match, keep looking
return true;
}
boolean isTypeMatch = false;
final GraphQLTypeDefinition typeDefinition =
PsiTreeUtil.getParentOfType(psiNamedElement, GraphQLTypeDefinition.class);
if (typeDefinition != null) {
final GraphQLTypeNameDefinition typeNameDefinition =
PsiTreeUtil.findChildOfType(typeDefinition, GraphQLTypeNameDefinition.class);
isTypeMatch = typeNameDefinition != null &&
GraphQLSchemaUtil.getTypeName(fieldType).equals(typeNameDefinition.getName());
}
if (!isTypeMatch) {
// check type extension
final GraphQLTypeExtension typeExtension =
PsiTreeUtil.getParentOfType(psiNamedElement, GraphQLTypeExtension.class);
if (typeExtension != null) {
final GraphQLTypeName typeName = PsiTreeUtil.findChildOfType(typeExtension, GraphQLTypeName.class);
isTypeMatch = typeName != null && GraphQLSchemaUtil.getTypeName(fieldType).equals(typeName.getName());
}
}
if (isTypeMatch) {
reference.set(createReference(element, psiNamedElement));
return false; // done searching
}
}
return true;
});
}
}
}
return reference.get();
}