Skip to content

Commit 4ba8dba

Browse files
committed
Migrated Endpoint Language schema discovery to use new .graphqlconfig format and scoping rules (#164)
1 parent 0b678ad commit 4ba8dba

23 files changed

+165
-532
lines changed

build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ intellij {
4444
plugins = ['JavaScriptLanguage', 'CSS']
4545
ideaDependencyCachePath = project.buildDir
4646
}
47+
48+
runIde {
49+
jvmArgs '-Xmx2G'
50+
}
51+
4752
patchPluginXml {
4853

4954
}

resources/META-INF/plugin.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,6 @@
259259

260260

261261
<!-- Editor notification bars and markers -->
262-
<!--<editorNotificationProvider implementation="com.intellij.lang.jsgraphql.v1.ide.notifications.JSGraphQLConfigEditorNotificationProvider"/>-->
263262
<!--
264263
<editorNotificationProvider implementation="JSGraphQLApolloLokkaEditorNotificationProvider"/>
265264
-->

src/main/com/intellij/lang/jsgraphql/endpoint/ide/completion/JSGraphQLEndpointCompletionContributor.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,8 @@ private boolean completeImportFile(@NotNull CompletionResultSet result, PsiFile
187187
if ((parent instanceof JSGraphQLEndpointQuotedString || parent instanceof JSGraphQLEndpointString) && PsiTreeUtil.getParentOfType(parent, JSGraphQLEndpointImportFileReference.class) != null) {
188188

189189
final Project project = file.getProject();
190-
final VirtualFile entryFile = JSGraphQLConfigurationProvider.getService(project).getEndpointEntryFile();
191-
final GlobalSearchScope scope = JSGraphQLEndpointPsiUtil.getImportScopeFromEntryFile(project, entryFile);
190+
final VirtualFile entryFile = JSGraphQLConfigurationProvider.getService(project).getEndpointEntryFile(file);
191+
final GlobalSearchScope scope = JSGraphQLEndpointPsiUtil.getImportScopeFromEntryFile(project, entryFile, file);
192192
final Collection<VirtualFile> files = FileTypeIndex.getFiles(JSGraphQLEndpointFileType.INSTANCE, scope);
193193
for (VirtualFile virtualFile : files) {
194194
if(virtualFile.equals(entryFile)) {

src/main/com/intellij/lang/jsgraphql/endpoint/ide/completion/JSGraphQLEndpointImportUtil.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ public class JSGraphQLEndpointImportUtil {
1717

1818

1919
public static String getImportName(Project project, PsiFile file) {
20-
final VirtualFile entryFile = JSGraphQLConfigurationProvider.getService(project).getEndpointEntryFile();
20+
final VirtualFile entryFile = JSGraphQLConfigurationProvider.getService(project).getEndpointEntryFile(file);
2121
final VirtualFile entryFileDir = entryFile != null ? entryFile.getParent() : null;
2222
final String name = StringUtils.substringBeforeLast(file.getName(), ".");
2323
return getImportName(entryFileDir, file.getVirtualFile(), name);

src/main/com/intellij/lang/jsgraphql/endpoint/ide/project/JSGraphQLEndpointNamedTypeRegistry.java

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
import com.google.common.collect.Maps;
1212
import com.google.common.collect.Sets;
1313
import com.intellij.lang.jsgraphql.endpoint.psi.*;
14+
import com.intellij.lang.jsgraphql.ide.project.graphqlconfig.GraphQLConfigManager;
15+
import com.intellij.lang.jsgraphql.ide.project.graphqlconfig.GraphQLNamedScope;
1416
import com.intellij.lang.jsgraphql.schema.GraphQLSchemaChangeListener;
1517
import com.intellij.lang.jsgraphql.schema.GraphQLSchemaEventListener;
1618
import com.intellij.lang.jsgraphql.schema.TypeDefinitionRegistryWithErrors;
@@ -40,11 +42,12 @@
4042
public class JSGraphQLEndpointNamedTypeRegistry implements JSGraphQLNamedTypeRegistry {
4143

4244
private final JSGraphQLConfigurationProvider configurationProvider;
45+
private final GraphQLConfigManager graphQLConfigManager;
4346
private final Project project;
4447

45-
private final Map<Project, Map<String, JSGraphQLNamedType>> endpointTypesByName = Maps.newConcurrentMap();
46-
private final Map<Project, PsiFile> endpointEntryPsiFile = Maps.newConcurrentMap();
47-
private final Map<Project, TypeDefinitionRegistryWithErrors> projectToRegistry = Maps.newConcurrentMap();
48+
private final Map<GraphQLNamedScope, Map<String, JSGraphQLNamedType>> endpointTypesByName = Maps.newConcurrentMap();
49+
private final Map<GraphQLNamedScope, PsiFile> endpointEntryPsiFile = Maps.newConcurrentMap();
50+
private final Map<GraphQLNamedScope, TypeDefinitionRegistryWithErrors> projectToRegistry = Maps.newConcurrentMap();
4851

4952
public static JSGraphQLEndpointNamedTypeRegistry getService(@NotNull Project project) {
5053
return ServiceManager.getService(project, JSGraphQLEndpointNamedTypeRegistry.class);
@@ -53,6 +56,7 @@ public static JSGraphQLEndpointNamedTypeRegistry getService(@NotNull Project pro
5356
public JSGraphQLEndpointNamedTypeRegistry(Project project) {
5457
this.project = project;
5558
this.configurationProvider = JSGraphQLConfigurationProvider.getService(project);
59+
graphQLConfigManager = GraphQLConfigManager.getService(project);
5660
project.getMessageBus().connect().subscribe(GraphQLSchemaChangeListener.TOPIC, new GraphQLSchemaEventListener() {
5761
@Override
5862
public void onGraphQLSchemaChanged() {
@@ -63,13 +67,17 @@ public void onGraphQLSchemaChanged() {
6367
});
6468
}
6569

66-
public boolean hasEndpointEntryFile() {
67-
return getEndpointEntryPsiFile() != null;
70+
public boolean hasEndpointEntryFile(PsiElement scopedPsiElement) {
71+
return getEndpointEntryPsiFile(scopedPsiElement) != null;
6872
}
6973

70-
private PsiFile getEndpointEntryPsiFile() {
71-
return endpointEntryPsiFile.computeIfAbsent(project, p -> {
72-
final VirtualFile endpointEntryFile = configurationProvider.getEndpointEntryFile();
74+
private PsiFile getEndpointEntryPsiFile(PsiElement scopedPsiElement) {
75+
final GraphQLNamedScope schemaScope = graphQLConfigManager.getSchemaScope(scopedPsiElement.getContainingFile().getVirtualFile());
76+
if(schemaScope == null) {
77+
return null;
78+
}
79+
return endpointEntryPsiFile.computeIfAbsent(schemaScope, p -> {
80+
final VirtualFile endpointEntryFile = configurationProvider.getEndpointEntryFile(scopedPsiElement.getContainingFile());
7381
if (endpointEntryFile != null) {
7482
return PsiManager.getInstance(project).findFile(endpointEntryFile);
7583
}
@@ -78,25 +86,29 @@ private PsiFile getEndpointEntryPsiFile() {
7886
}
7987

8088
@Override
81-
public JSGraphQLNamedType getNamedType(String typeNameToGet) {
82-
return computeNamedTypes().get(typeNameToGet);
89+
public JSGraphQLNamedType getNamedType(String typeNameToGet, PsiElement scopedElement) {
90+
return computeNamedTypes(scopedElement).get(typeNameToGet);
8391
}
8492

85-
public void enumerateTypes(Consumer<JSGraphQLNamedType> consumer) {
86-
computeNamedTypes().forEach((key, jsGraphQLNamedType) -> consumer.accept(jsGraphQLNamedType));
93+
public void enumerateTypes(PsiElement scopedElement, Consumer<JSGraphQLNamedType> consumer) {
94+
computeNamedTypes(scopedElement).forEach((key, jsGraphQLNamedType) -> consumer.accept(jsGraphQLNamedType));
8795
}
8896

89-
public TypeDefinitionRegistryWithErrors getTypesAsRegistry() {
90-
return projectToRegistry.computeIfAbsent(project, p -> doGetTypesAsRegistry());
97+
public TypeDefinitionRegistryWithErrors getTypesAsRegistry(PsiElement scopedElement) {
98+
final GraphQLNamedScope schemaScope = graphQLConfigManager.getSchemaScope(scopedElement.getContainingFile().getVirtualFile());
99+
if (schemaScope == null) {
100+
return new TypeDefinitionRegistryWithErrors(new TypeDefinitionRegistry(), Collections.emptyList());
101+
}
102+
return projectToRegistry.computeIfAbsent(schemaScope, p -> doGetTypesAsRegistry(scopedElement));
91103
}
92104

93-
private TypeDefinitionRegistryWithErrors doGetTypesAsRegistry() {
105+
private TypeDefinitionRegistryWithErrors doGetTypesAsRegistry(PsiElement scopedElement) {
94106

95107
final TypeDefinitionRegistry registry = new TypeDefinitionRegistry();
96108
final List<GraphQLException> errors = Lists.newArrayList();
97109
final TypeDefinitionRegistryWithErrors registryWithErrors = new TypeDefinitionRegistryWithErrors(registry, errors);
98110

99-
final Map<String, JSGraphQLNamedType> namedTypes = computeNamedTypes();
111+
final Map<String, JSGraphQLNamedType> namedTypes = computeNamedTypes(scopedElement);
100112

101113
final PsiRecursiveElementVisitor errorsVisitor = new PsiRecursiveElementVisitor() {
102114
@Override
@@ -353,10 +365,14 @@ private SourceLocation getSourceLocation(PsiElement psiSourceElement) {
353365
return new SourceLocation(-1, -1, psiSourceElement.getContainingFile().getName());
354366
}
355367

356-
private Map<String, JSGraphQLNamedType> computeNamedTypes() {
357-
return endpointTypesByName.computeIfAbsent(project, p -> {
368+
private Map<String, JSGraphQLNamedType> computeNamedTypes(PsiElement scopedPsiElement) {
369+
final GraphQLNamedScope schemaScope = graphQLConfigManager.getSchemaScope(scopedPsiElement.getContainingFile().getVirtualFile());
370+
if (schemaScope == null) {
371+
return Collections.emptyMap();
372+
}
373+
return endpointTypesByName.computeIfAbsent(schemaScope, p -> {
358374
final Map<String, JSGraphQLNamedType> result = Maps.newConcurrentMap();
359-
final PsiFile entryPsiFile = getEndpointEntryPsiFile();
375+
final PsiFile entryPsiFile = getEndpointEntryPsiFile(scopedPsiElement);
360376
if (entryPsiFile != null) {
361377
Collection<JSGraphQLEndpointNamedTypeDefinition> endpointNamedTypeDefinitions = JSGraphQLEndpointPsiUtil.getKnownDefinitions(
362378
entryPsiFile,

src/main/com/intellij/lang/jsgraphql/endpoint/ide/search/JSGraphQLEndpointDefinitionsSearchExecutor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ private static boolean doExecute(PsiElement sourceElement, final Processor<PsiEl
5656
final String interfaceName = sourceNamedTypeDef.get().getText();
5757

5858
final JSGraphQLEndpointNamedTypeRegistry typeRegistry = JSGraphQLEndpointNamedTypeRegistry.getService(sourceElement.getProject());
59-
typeRegistry.enumerateTypes(jsGraphQLNamedType -> {
59+
typeRegistry.enumerateTypes(sourceElement, jsGraphQLNamedType -> {
6060
if (jsGraphQLNamedType.definitionElement instanceof JSGraphQLEndpointObjectTypeDefinition) {
6161
final JSGraphQLEndpointObjectTypeDefinition typeDefinition = (JSGraphQLEndpointObjectTypeDefinition) jsGraphQLNamedType.definitionElement;
6262
final JSGraphQLEndpointImplementsInterfaces implementsInterfaces = typeDefinition.getImplementsInterfaces();

src/main/com/intellij/lang/jsgraphql/endpoint/psi/JSGraphQLEndpointImportFileReferencePsiElement.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ public JSGraphQLEndpointFilePsiReference(PsiNamedElement element, TextRange rang
107107
@Override
108108
public PsiElement resolve() {
109109
final Project project = this.getElement().getProject();
110-
final VirtualFile entryFile = JSGraphQLConfigurationProvider.getService(project).getEndpointEntryFile();
110+
final VirtualFile entryFile = JSGraphQLConfigurationProvider.getService(project).getEndpointEntryFile(this.getElement().getContainingFile());
111111
if(entryFile != null && entryFile.getParent() != null) {
112112
final String fileName = nameIdentifier.getText();
113113
if(fileName.startsWith(".") || fileName.startsWith("/")) {

src/main/com/intellij/lang/jsgraphql/endpoint/psi/JSGraphQLEndpointPsiUtil.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,9 @@ public class JSGraphQLEndpointPsiUtil {
3939
* @param entryFile the entry file, or null to look it up
4040
*/
4141
@NotNull
42-
public static GlobalSearchScope getImportScopeFromEntryFile(Project project, @Nullable VirtualFile entryFile) {
42+
public static GlobalSearchScope getImportScopeFromEntryFile(Project project, @Nullable VirtualFile entryFile, PsiElement scopedElement) {
4343
if(entryFile == null) {
44-
entryFile = JSGraphQLConfigurationProvider.getService(project).getEndpointEntryFile();
44+
entryFile = JSGraphQLConfigurationProvider.getService(project).getEndpointEntryFile(scopedElement.getContainingFile());
4545
}
4646
final GlobalSearchScope scope;
4747
if(entryFile != null) {
@@ -93,7 +93,7 @@ public static <T extends JSGraphQLEndpointNamedTypeDefinition> Collection<T> get
9393
}
9494

9595
if(includeAutoImportTypes) {
96-
final Collection<VirtualFile> knownFiles = FileTypeIndex.getFiles(JSGraphQLEndpointFileType.INSTANCE, getImportScopeFromEntryFile(file.getProject(), null));
96+
final Collection<VirtualFile> knownFiles = FileTypeIndex.getFiles(JSGraphQLEndpointFileType.INSTANCE, getImportScopeFromEntryFile(file.getProject(), null, file));
9797
final PsiManager psiManager = PsiManager.getInstance(file.getProject());
9898
knownFiles.forEach(virtualFile ->{
9999
final PsiFile psiFile = psiManager.findFile(virtualFile);

src/main/com/intellij/lang/jsgraphql/ide/project/graphqlconfig/GraphQLConfigManager.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,22 @@
1919
import com.intellij.json.JsonFileType;
2020
import com.intellij.lang.jsgraphql.GraphQLFileType;
2121
import com.intellij.lang.jsgraphql.GraphQLLanguage;
22+
import com.intellij.lang.jsgraphql.endpoint.JSGraphQLEndpointFileType;
2223
import com.intellij.lang.jsgraphql.ide.editor.GraphQLIntrospectionHelper;
2324
import com.intellij.lang.jsgraphql.ide.project.graphqlconfig.model.GraphQLConfigData;
2425
import com.intellij.lang.jsgraphql.ide.project.graphqlconfig.model.GraphQLConfigEndpoint;
2526
import com.intellij.lang.jsgraphql.ide.project.graphqlconfig.model.GraphQLResolvedConfigData;
2627
import com.intellij.lang.jsgraphql.psi.GraphQLFile;
2728
import com.intellij.lang.jsgraphql.v1.ide.configuration.JSGraphQLConfigurationListener;
29+
import com.intellij.lang.jsgraphql.v1.ide.configuration.JSGraphQLSchemaEndpointConfiguration;
2830
import com.intellij.notification.Notification;
2931
import com.intellij.notification.NotificationAction;
3032
import com.intellij.notification.NotificationType;
3133
import com.intellij.notification.Notifications;
3234
import com.intellij.openapi.actionSystem.AnActionEvent;
3335
import com.intellij.openapi.application.ApplicationManager;
3436
import com.intellij.openapi.components.ServiceManager;
37+
import com.intellij.openapi.diagnostic.Logger;
3538
import com.intellij.openapi.extensions.PluginId;
3639
import com.intellij.openapi.fileEditor.FileEditorManager;
3740
import com.intellij.openapi.module.Module;
@@ -72,6 +75,8 @@
7275
*/
7376
public class GraphQLConfigManager {
7477

78+
private final static Logger log = Logger.getInstance(GraphQLConfigManager.class);
79+
7580
public final static Topic<GraphQLConfigFileEventListener> TOPIC = new Topic<>(
7681
"GraphQL Configuration File Change Events",
7782
GraphQLConfigFileEventListener.class,
@@ -82,6 +87,7 @@ public class GraphQLConfigManager {
8287

8388
public static final String GRAPHQLCONFIG = ".graphqlconfig";
8489
public static final String GRAPHQLCONFIG_COMMENT = ".graphqlconfig=";
90+
public static final String ENDPOINT_LANGUAGE_EXTENSION = "endpoint-language";
8591

8692
private static final String GRAPHQLCONFIG_YML = ".graphqlconfig.yml";
8793
private static final String GRAPHQLCONFIG_YAML = ".graphqlconfig.yaml";
@@ -107,6 +113,7 @@ public class GraphQLConfigManager {
107113
private volatile Map<GraphQLResolvedConfigData, GraphQLFile> configDataToEntryFiles = Maps.newConcurrentMap();
108114
private volatile Map<GraphQLResolvedConfigData, GraphQLConfigPackageSet> configDataToPackageset = Maps.newConcurrentMap();
109115
private final Map<String, GraphQLNamedScope> virtualFilePathToScopes = Maps.newConcurrentMap();
116+
private final Map<GraphQLNamedScope, JSGraphQLSchemaEndpointConfiguration> scopeToSchemaEndpointLanguageConfiguration = Maps.newConcurrentMap();
110117

111118
public GraphQLConfigManager(Project myProject) {
112119
this.myProject = myProject;
@@ -224,6 +231,39 @@ public GraphQLFile getConfigurationEntryFile(GraphQLResolvedConfigData configDat
224231
});
225232
}
226233

234+
public JSGraphQLSchemaEndpointConfiguration getEndpointLanguageConfiguration(VirtualFile virtualFile, @Nullable Ref<VirtualFile> configBasedir) {
235+
if (virtualFile.getFileType() != GraphQLFileType.INSTANCE && virtualFile.getFileType() != JSGraphQLEndpointFileType.INSTANCE && !GraphQLFileType.isGraphQLScratchFile(myProject, virtualFile)) {
236+
return null;
237+
}
238+
GraphQLNamedScope schemaScope = getSchemaScope(virtualFile);
239+
if(schemaScope != null) {
240+
JSGraphQLSchemaEndpointConfiguration configuration = scopeToSchemaEndpointLanguageConfiguration.computeIfAbsent(schemaScope, scope -> {
241+
if (schemaScope.getConfigData() != null) {
242+
final Map<String, Object> extensions = schemaScope.getConfigData().extensions;
243+
if (extensions != null && extensions.containsKey(ENDPOINT_LANGUAGE_EXTENSION)) {
244+
try {
245+
final Gson gson = new Gson();
246+
final JSGraphQLSchemaEndpointConfiguration config = gson.fromJson(gson.toJsonTree(extensions.get(ENDPOINT_LANGUAGE_EXTENSION)), JSGraphQLSchemaEndpointConfiguration.class);
247+
return config;
248+
} catch (JsonSyntaxException je) {
249+
log.warn("Invalid JSON in config file", je);
250+
}
251+
252+
}
253+
}
254+
// using sentinel value to avoid re-computing values which happens on nulls
255+
return JSGraphQLSchemaEndpointConfiguration.NONE;
256+
});
257+
if(configuration != JSGraphQLSchemaEndpointConfiguration.NONE) {
258+
if(configBasedir != null) {
259+
configBasedir.set(schemaScope.getConfigBaseDir());
260+
}
261+
return configuration;
262+
}
263+
}
264+
return null;
265+
}
266+
227267
/**
228268
* Gets the endpoints that are within scope for the specified GraphQL virtual file.
229269
* <p>
@@ -430,6 +470,7 @@ public void buildConfigurationModel(@Nullable List<VirtualFile> changedConfigura
430470
this.virtualFilePathToScopes.clear();
431471
this.configDataToEntryFiles.clear();
432472
this.configDataToPackageset.clear();
473+
this.scopeToSchemaEndpointLanguageConfiguration.clear();
433474

434475
myProject.getMessageBus().syncPublisher(TOPIC).onGraphQLConfigurationFileChanged();
435476
myProject.getMessageBus().syncPublisher(JSGraphQLConfigurationListener.TOPIC).onEndpointsChanged();

src/main/com/intellij/lang/jsgraphql/ide/project/graphqlconfig/GraphQLNamedScope.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
*/
1616
package com.intellij.lang.jsgraphql.ide.project.graphqlconfig;
1717

18+
import com.intellij.lang.jsgraphql.ide.project.graphqlconfig.model.GraphQLResolvedConfigData;
19+
import com.intellij.openapi.vfs.VirtualFile;
1820
import com.intellij.psi.search.scope.packageSet.NamedScope;
1921
import com.intellij.psi.search.scope.packageSet.PackageSet;
2022
import org.jetbrains.annotations.NotNull;
@@ -28,8 +30,23 @@
2830
*/
2931
public class GraphQLNamedScope extends NamedScope {
3032

33+
private VirtualFile configBaseDir;
34+
private GraphQLResolvedConfigData configData;
35+
3136
public GraphQLNamedScope(@NotNull String name, @Nullable GraphQLConfigPackageSet value) {
3237
super(name, value);
38+
if(value != null) {
39+
this.configData = value.getConfigData();
40+
configBaseDir = value.getConfigBaseDir();
41+
}
42+
}
43+
44+
public GraphQLResolvedConfigData getConfigData() {
45+
return configData;
46+
}
47+
48+
public VirtualFile getConfigBaseDir() {
49+
return configBaseDir;
3350
}
3451

3552
public GraphQLConfigPackageSet getPackageSet() {

0 commit comments

Comments
 (0)