Skip to content

Commit 2267c2a

Browse files
committed
Improved service directive tooling (#164)
- Ignore errors from graphql-java KnownDirectives on type definitions as it only handles executable directive locations - Show directive completions based on declared directives and not only the schema which can be broken while typing
1 parent 3bad7c0 commit 2267c2a

File tree

2 files changed

+46
-4
lines changed

2 files changed

+46
-4
lines changed

src/main/com/intellij/lang/jsgraphql/ide/GraphQLValidationAnnotator.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,13 @@ public void invoke(@NotNull Project project, @NotNull PsiFile file, @NotNull Psi
334334
}
335335
} else if (elementType == GraphQLElementTypes.AT) {
336336
// mark the directive and not only the '@'
337+
if(validationErrorType == ValidationErrorType.MisplacedDirective) {
338+
// graphql-java KnownDirectives rule only recognizes executable directive locations, so ignore
339+
// the error if we're inside a type definition
340+
if(PsiTreeUtil.getTopmostParentOfType(errorPsiElement, GraphQLTypeSystemDefinition.class) != null) {
341+
continue;
342+
}
343+
}
337344
errorPsiElement = errorPsiElement.getParent();
338345
}
339346
if (errorPsiElement != null) {

src/main/com/intellij/lang/jsgraphql/ide/completion/GraphQLCompletionContributor.java

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import org.jetbrains.annotations.Nullable;
4747

4848
import java.util.Collection;
49+
import java.util.EnumSet;
4950
import java.util.List;
5051
import java.util.Optional;
5152
import java.util.Set;
@@ -496,10 +497,45 @@ protected void addCompletions(@NotNull final CompletionParameters parameters, Pr
496497
protected void addCompletions(@NotNull final CompletionParameters parameters, ProcessingContext context, @NotNull CompletionResultSet result) {
497498

498499
final PsiElement completionElement = Optional.ofNullable(parameters.getOriginalPosition()).orElse(parameters.getPosition());
500+
final TypeDefinitionRegistry registry = GraphQLTypeDefinitionRegistryServiceImpl.getService(completionElement.getProject()).getRegistry(completionElement);
501+
502+
final Set<String> addedDirectiveNames = Sets.newHashSet();
503+
504+
// directives declared - available even when schema validation errors are present, as in when typing/completing a directive name
505+
for (DirectiveDefinition directiveDefinition : registry.getDirectiveDefinitions().values()) {
506+
final EnumSet<Introspection.DirectiveLocation> validLocations = EnumSet.noneOf(Introspection.DirectiveLocation.class);
507+
for (DirectiveLocation directiveLocation : directiveDefinition.getDirectiveLocations()) {
508+
try {
509+
validLocations.add(Introspection.DirectiveLocation.valueOf(directiveLocation.getName()));
510+
} catch (IllegalArgumentException ignored) {
511+
}
512+
}
513+
if (!isValidDirectiveLocation(validLocations, parameters.getPosition())) {
514+
continue;
515+
}
516+
LookupElementBuilder element = LookupElementBuilder.create(directiveDefinition.getName());
517+
for (InputValueDefinition directiveArgument : directiveDefinition.getInputValueDefinitions()) {
518+
if (directiveArgument.getType() instanceof GraphQLNonNull) {
519+
// found a required argument so insert the '()' for arguments
520+
element = element.withInsertHandler((ctx, item) -> {
521+
ParenthesesInsertHandler.WITH_PARAMETERS.handleInsert(ctx, item);
522+
AutoPopupController.getInstance(ctx.getProject()).autoPopupMemberLookup(ctx.getEditor(), null);
523+
});
524+
break;
525+
}
526+
}
527+
addedDirectiveNames.add(directiveDefinition.getName());
528+
result.addElement(element);
529+
}
530+
531+
// directive including the built-in ones from a valid and working schema
499532
final GraphQLSchema schema = GraphQLTypeDefinitionRegistryServiceImpl.getService(completionElement.getProject()).getSchema(completionElement);
500533
if (schema != null) {
501534
for (graphql.schema.GraphQLDirective graphQLDirective : schema.getDirectives()) {
502-
if (!isValidDirectiveLocation(graphQLDirective, parameters.getPosition())) {
535+
if(!addedDirectiveNames.add(graphQLDirective.getName())) {
536+
continue;
537+
}
538+
if (!isValidDirectiveLocation(graphQLDirective.validLocations(), parameters.getPosition())) {
503539
continue;
504540
}
505541
LookupElementBuilder element = LookupElementBuilder.create(graphQLDirective.getName());
@@ -515,7 +551,6 @@ protected void addCompletions(@NotNull final CompletionParameters parameters, Pr
515551
}
516552
result.addElement(element);
517553
}
518-
// TODO: Also support SDL directives, e.g. auth annotations etc. for the endpoint implementation
519554
}
520555

521556
}
@@ -863,12 +898,12 @@ public boolean invokeAutoPopup(@NotNull PsiElement position, char typeChar) {
863898
return super.invokeAutoPopup(position, typeChar);
864899
}
865900

866-
private boolean isValidDirectiveLocation(graphql.schema.GraphQLDirective graphQLDirective, PsiElement completionPosition) {
901+
private boolean isValidDirectiveLocation(EnumSet<Introspection.DirectiveLocation> validLocations, PsiElement completionPosition) {
867902
final GraphQLDirectivesAware directivesAware = PsiTreeUtil.getParentOfType(completionPosition, GraphQLDirectivesAware.class);
868903
if (directivesAware == null) {
869904
return false;
870905
}
871-
for (Introspection.DirectiveLocation directiveLocation : graphQLDirective.validLocations()) {
906+
for (Introspection.DirectiveLocation directiveLocation : validLocations) {
872907
switch (directiveLocation) {
873908
// Executable locations
874909
case QUERY:

0 commit comments

Comments
 (0)