19
19
import com .intellij .lang .jsgraphql .GraphQLLanguage ;
20
20
import com .intellij .lang .jsgraphql .GraphQLSettings ;
21
21
import com .intellij .lang .jsgraphql .ide .project .graphqlconfig .GraphQLConfigManager ;
22
+ import com .intellij .lang .jsgraphql .ide .project .indexing .GraphQLFragmentNameIndex ;
22
23
import com .intellij .lang .jsgraphql .ide .project .indexing .GraphQLIdentifierIndex ;
23
24
import com .intellij .lang .jsgraphql .ide .project .scopes .ConditionalGlobalSearchScope ;
24
25
import com .intellij .lang .jsgraphql .ide .references .GraphQLFindUsagesUtil ;
42
43
import com .intellij .psi .impl .PsiManagerImpl ;
43
44
import com .intellij .psi .search .GlobalSearchScope ;
44
45
import com .intellij .psi .search .GlobalSearchScopesCore ;
45
- import com .intellij .psi .search .PsiSearchHelper ;
46
- import com .intellij .psi .search .UsageSearchContext ;
47
46
import com .intellij .psi .search .scope .packageSet .NamedScope ;
48
- import com .intellij .psi .util .PsiTreeUtil ;
49
47
import com .intellij .testFramework .LightVirtualFile ;
50
48
import com .intellij .util .Processor ;
51
49
import com .intellij .util .indexing .FileBasedIndex ;
@@ -73,7 +71,6 @@ public class GraphQLPsiSearchHelper {
73
71
private final Project myProject ;
74
72
private final GraphQLSettings mySettings ;
75
73
private final PluginDescriptor pluginDescriptor ;
76
- private final Map <String , GraphQLFragmentDefinition > fragmentDefinitionsByName = Maps .newConcurrentMap ();
77
74
private final Map <String , GlobalSearchScope > fileNameToSchemaScope = Maps .newConcurrentMap ();
78
75
private final GlobalSearchScope searchScope ;
79
76
private final GlobalSearchScope allBuiltInSchemaScopes ;
@@ -115,7 +112,6 @@ public GraphQLPsiSearchHelper(@NotNull final Project project) {
115
112
@ Override
116
113
public void beforePsiChanged (boolean isPhysical ) {
117
114
// clear the cache on each PSI change
118
- fragmentDefinitionsByName .clear ();
119
115
fileNameToSchemaScope .clear ();
120
116
}
121
117
});
@@ -190,23 +186,56 @@ public List<GraphQLFragmentDefinition> getKnownFragmentDefinitions(PsiElement sc
190
186
// include the fragments in the currently edited scratch file
191
187
schemaScope = schemaScope .union (GlobalSearchScope .fileScope (scopedElement .getContainingFile ()));
192
188
}
193
- // TODO JKM create fragment index
194
- PsiSearchHelper .getInstance (myProject ).processElementsWithWord ((psiElement , offsetInElement ) -> {
195
- if (psiElement .getNode ().getElementType () == GraphQLElementTypes .FRAGMENT_KEYWORD ) {
196
- final GraphQLFragmentDefinition fragmentDefinition = PsiTreeUtil .getParentOfType (psiElement , GraphQLFragmentDefinition .class );
197
- if (fragmentDefinition != null && fragmentDefinition .getNameIdentifier () != null ) {
198
- fragmentDefinitions .add (fragmentDefinition );
199
- }
189
+
190
+ final PsiManager psiManager = PsiManager .getInstance (myProject );
191
+
192
+ FileBasedIndex .getInstance ().processFilesContainingAllKeys (GraphQLFragmentNameIndex .NAME , Collections .singleton (GraphQLFragmentNameIndex .HAS_FRAGMENTS ), schemaScope , null , virtualFile -> {
193
+
194
+ final PsiFile psiFile = psiManager .findFile (virtualFile );
195
+ if (psiFile != null ) {
196
+ final Ref <PsiRecursiveElementVisitor > identifierVisitor = Ref .create ();
197
+ identifierVisitor .set (new PsiRecursiveElementVisitor () {
198
+ @ Override
199
+ public void visitElement (PsiElement element ) {
200
+ if (element instanceof GraphQLDefinition ) {
201
+ if (element instanceof GraphQLFragmentDefinition ) {
202
+ fragmentDefinitions .add ((GraphQLFragmentDefinition ) element );
203
+ }
204
+ return ; // no need to visit deeper than definitions since fragments are top level
205
+ } else if (element instanceof PsiLanguageInjectionHost ) {
206
+ if (visitLanguageInjectionHost ((PsiLanguageInjectionHost ) element , identifierVisitor )) {
207
+ return ;
208
+ }
209
+ }
210
+ super .visitElement (element );
211
+ }
212
+ });
213
+ psiFile .accept (identifierVisitor .get ());
200
214
}
201
- return true ;
202
- }, schemaScope , "fragment" , UsageSearchContext .IN_CODE , true , true );
215
+
216
+ return true ; // process all known fragments
217
+ });
203
218
return fragmentDefinitions ;
204
219
} catch (IndexNotReadyException e ) {
205
220
// can't search yet (e.g. during project startup)
206
221
}
207
222
return Collections .emptyList ();
208
223
}
209
224
225
+ /**
226
+ * Visits the potential GraphQL injection inside an injection host
227
+ * @return true if the host contained GraphQL and was visited, false otherwise
228
+ */
229
+ private boolean visitLanguageInjectionHost (PsiLanguageInjectionHost element , Ref <PsiRecursiveElementVisitor > identifierVisitor ) {
230
+ if (graphQLInjectionSearchHelper != null && graphQLInjectionSearchHelper .isJSGraphQLLanguageInjectionTarget (element )) {
231
+ injectedLanguageManager .enumerateEx (element , element .getContainingFile (), false , (injectedPsi , places ) -> {
232
+ injectedPsi .accept (identifierVisitor .get ());
233
+ });
234
+ return true ;
235
+ }
236
+ return false ;
237
+ }
238
+
210
239
/**
211
240
* Gets a resolved reference or null if no reference or resolved element is found
212
241
*
@@ -230,8 +259,8 @@ public static GraphQLIdentifier getResolvedReference(GraphQLNamedElement psiElem
230
259
/**
231
260
* Processes GraphQL identifiers whose name matches the specified word within the given schema scope.
232
261
* @param schemaScope the schema scope which limits the processing
233
- * @param word the word to match identifiers for
234
- * @param processor processor called for all GraphQL identifiers whose name match the specified word
262
+ * @param word the word to match identifiers for
263
+ * @param processor processor called for all GraphQL identifiers whose name match the specified word
235
264
* @see GraphQLIdentifierIndex
236
265
*/
237
266
private void processElementsWithWordUsingIdentifierIndex (GlobalSearchScope schemaScope , String word , Processor <PsiNamedElement > processor ) {
@@ -263,11 +292,8 @@ public void visitElement(PsiElement element) {
263
292
graphQLFile .accept (identifierVisitor .get ());
264
293
}
265
294
return ; // no need to visit deeper
266
- } else if (element instanceof PsiLanguageInjectionHost && graphQLInjectionSearchHelper != null ) {
267
- if (graphQLInjectionSearchHelper .isJSGraphQLLanguageInjectionTarget (element )) {
268
- injectedLanguageManager .enumerateEx (element , element .getContainingFile (), false , (injectedPsi , places ) -> {
269
- injectedPsi .accept (identifierVisitor .get ());
270
- });
295
+ } else if (element instanceof PsiLanguageInjectionHost ) {
296
+ if (visitLanguageInjectionHost ((PsiLanguageInjectionHost ) element , identifierVisitor )) {
271
297
return ;
272
298
}
273
299
}
0 commit comments