Skip to content

Commit d1fc282

Browse files
committed
Use bulk VFS listener to track configuration files changes reliably, e.g. during version control system operations (#164)
1 parent a9c1fe3 commit d1fc282

File tree

3 files changed

+82
-76
lines changed

3 files changed

+82
-76
lines changed

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

Lines changed: 71 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@
1010
import com.google.common.collect.Lists;
1111
import com.google.common.collect.Maps;
1212
import com.google.common.collect.Sets;
13-
import com.google.common.collect.Streams;
1413
import com.google.gson.Gson;
1514
import com.google.gson.JsonSyntaxException;
1615
import com.intellij.ide.plugins.IdeaPluginDescriptor;
1716
import com.intellij.ide.plugins.PluginManager;
1817
import com.intellij.ide.scratch.ScratchFileType;
1918
import com.intellij.injected.editor.VirtualFileWindow;
19+
import com.intellij.json.JsonFileType;
2020
import com.intellij.lang.jsgraphql.GraphQLFileType;
2121
import com.intellij.lang.jsgraphql.GraphQLLanguage;
2222
import com.intellij.lang.jsgraphql.ide.editor.GraphQLIntrospectionHelper;
@@ -40,7 +40,12 @@
4040
import com.intellij.openapi.roots.ModuleRootManager;
4141
import com.intellij.openapi.util.Ref;
4242
import com.intellij.openapi.util.text.StringUtil;
43-
import com.intellij.openapi.vfs.*;
43+
import com.intellij.openapi.vfs.LocalFileSystem;
44+
import com.intellij.openapi.vfs.VirtualFile;
45+
import com.intellij.openapi.vfs.VirtualFileManager;
46+
import com.intellij.openapi.vfs.newvfs.BulkFileListener;
47+
import com.intellij.openapi.vfs.newvfs.events.VFileEvent;
48+
import com.intellij.openapi.vfs.newvfs.events.VFilePropertyChangeEvent;
4449
import com.intellij.psi.*;
4550
import com.intellij.psi.search.FilenameIndex;
4651
import com.intellij.psi.search.GlobalSearchScope;
@@ -87,6 +92,8 @@ public class GraphQLConfigManager {
8792
GRAPHQLCONFIG_YAML
8893
};
8994

95+
private static final Set<String> GRAPHQLCONFIG_FILE_NAMES_SET = Sets.newHashSet(GRAPHQLCONFIG_FILE_NAMES);
96+
9097
private static final GraphQLNamedScope NONE = new GraphQLNamedScope("", null);
9198

9299
private final Project myProject;
@@ -98,7 +105,6 @@ public class GraphQLConfigManager {
98105
private volatile Map<GraphQLResolvedConfigData, GraphQLFile> configDataToEntryFiles = Maps.newConcurrentMap();
99106
private volatile Map<GraphQLResolvedConfigData, GraphQLConfigPackageSet> configDataToPackageset = Maps.newConcurrentMap();
100107
private final Map<String, GraphQLNamedScope> virtualFilePathToScopes = Maps.newConcurrentMap();
101-
private VirtualFileListener virtualFileListener;
102108

103109
public GraphQLConfigManager(Project myProject) {
104110
this.myProject = myProject;
@@ -195,7 +201,7 @@ public void createAndOpenConfigFile(VirtualFile configBaseDir, Boolean openEdito
195201
/**
196202
* Gets the currently discovered configurations
197203
*
198-
* @see #buildConfigurationModel()
204+
* @see #buildConfigurationModel(List)
199205
*/
200206
public Map<VirtualFile, GraphQLConfigData> getConfigurationsByPath() {
201207
return configPathToConfigurations;
@@ -264,41 +270,38 @@ public List<GraphQLConfigEndpoint> getEndpoints(VirtualFile virtualFile) {
264270
}
265271

266272
void initialize() {
267-
virtualFileListener = new VirtualFileListener() {
268-
273+
myProject.getMessageBus().connect().subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener() {
269274
@Override
270-
public void propertyChanged(@NotNull VirtualFilePropertyEvent event) {
271-
onVirtualFileChange(event);
272-
}
273-
274-
@Override
275-
public void contentsChanged(@NotNull VirtualFileEvent event) {
276-
onVirtualFileChange(event);
277-
}
278-
279-
@Override
280-
public void fileDeleted(@NotNull VirtualFileEvent event) {
281-
onVirtualFileChange(event);
282-
}
283-
284-
@Override
285-
public void fileCreated(@NotNull VirtualFileEvent event) {
286-
onVirtualFileChange(event);
287-
}
288-
289-
@Override
290-
public void fileCopied(@NotNull VirtualFileCopyEvent event) {
291-
onVirtualFileChange(event);
292-
}
293-
294-
@Override
295-
public void fileMoved(@NotNull VirtualFileMoveEvent event) {
296-
onVirtualFileChange(event);
275+
public void after(@NotNull List<? extends VFileEvent> events) {
276+
final List<VirtualFile> changedConfigFiles = Lists.newArrayList();
277+
boolean configurationsRenamed = false;
278+
for (VFileEvent event : events) {
279+
final VirtualFile file = event.getFile();
280+
if (file != null) {
281+
if (event instanceof VFilePropertyChangeEvent) {
282+
// renames
283+
final VFilePropertyChangeEvent propertyChangeEvent = (VFilePropertyChangeEvent) event;
284+
if (VirtualFile.PROP_NAME.equals(propertyChangeEvent.getPropertyName())) {
285+
if (propertyChangeEvent.getNewValue() instanceof String && GRAPHQLCONFIG_FILE_NAMES_SET.contains(propertyChangeEvent.getNewValue())) {
286+
configurationsRenamed = true;
287+
} else if (propertyChangeEvent.getOldValue() instanceof String && GRAPHQLCONFIG_FILE_NAMES_SET.contains(propertyChangeEvent.getOldValue())) {
288+
configurationsRenamed = true;
289+
}
290+
}
291+
} else {
292+
// other changes
293+
final String name = file.getName();
294+
if (GRAPHQLCONFIG_FILE_NAMES_SET.contains(name)) {
295+
changedConfigFiles.add(file);
296+
}
297+
}
298+
}
299+
}
300+
if (!changedConfigFiles.isEmpty() || configurationsRenamed) {
301+
buildConfigurationModel(changedConfigFiles);
302+
}
297303
}
298-
299-
300-
};
301-
VirtualFileManager.getInstance().addVirtualFileListener(virtualFileListener);
304+
});
302305

303306
PsiManager.getInstance(myProject).addPsiTreeChangeListener(new PsiTreeChangeAdapter() {
304307

@@ -314,7 +317,7 @@ public void childReplaced(@NotNull PsiTreeChangeEvent event) {
314317
}
315318
});
316319

317-
buildConfigurationModel();
320+
buildConfigurationModel(null);
318321

319322
introspectEndpoints();
320323

@@ -327,39 +330,27 @@ private boolean hasGraphQLConfigComment(PsiElement psiElement) {
327330
return false;
328331
}
329332

330-
private void onVirtualFileChange(VirtualFileEvent event) {
331-
if (myProject.isDisposed()) {
332-
VirtualFileManager.getInstance().removeVirtualFileListener(virtualFileListener);
333-
return;
334-
}
335-
String oldName = null;
336-
if (event instanceof VirtualFilePropertyEvent) {
337-
final VirtualFilePropertyEvent propertyEvent = (VirtualFilePropertyEvent) event;
338-
if (VirtualFile.PROP_NAME.equals(propertyEvent.getPropertyName())) {
339-
oldName = String.valueOf(propertyEvent.getOldValue());
340-
}
341-
}
342-
final String[] names = new String[]{event.getFile().getName(), oldName};
343-
for (String name : names) {
344-
if (name == null) {
345-
continue;
346-
}
347-
if (name.startsWith(GRAPHQLCONFIG)) {
348-
if (name.length() == GRAPHQLCONFIG.length() || name.equals(GRAPHQLCONFIG_YML) || name.equals(GRAPHQLCONFIG_YAML)) {
349-
buildConfigurationModel();
350-
break;
351-
}
352-
}
353-
354-
}
355-
}
356-
357-
public void buildConfigurationModel() {
333+
/**
334+
* Builds a model of the .graphqlconfig files in the project
335+
*
336+
* @param changedConfigurationFiles config files that were changed in the Virtual File System and should be explicitly processed given that they haven't been indexed yet
337+
*/
338+
public void buildConfigurationModel(@Nullable List<VirtualFile> changedConfigurationFiles) {
358339

359340
final Map<VirtualFile, GraphQLConfigData> newConfigPathToConfigurations = Maps.newConcurrentMap();
360341

361342
// JSON format
362-
final Collection<VirtualFile> jsonFiles = FilenameIndex.getVirtualFilesByName(myProject, GRAPHQLCONFIG, projectScope);
343+
final Collection<VirtualFile> jsonFiles = Sets.newLinkedHashSet(FilenameIndex.getVirtualFilesByName(myProject, GRAPHQLCONFIG, projectScope));
344+
if (changedConfigurationFiles != null) {
345+
for (VirtualFile configurationFile : changedConfigurationFiles) {
346+
if (configurationFile.getFileType().equals(JsonFileType.INSTANCE)) {
347+
if (configurationFile.isValid()) { // don't process deletions
348+
jsonFiles.add(configurationFile);
349+
}
350+
}
351+
}
352+
}
353+
363354
final Gson gson = new Gson();
364355
for (VirtualFile jsonFile : jsonFiles) {
365356
try {
@@ -374,13 +365,22 @@ public void buildConfigurationModel() {
374365
}
375366

376367
// YAML format
377-
final Collection<VirtualFile> ymlFiles = FilenameIndex.getVirtualFilesByName(myProject, GRAPHQLCONFIG_YML, projectScope);
378-
final Collection<VirtualFile> yamlFiles = FilenameIndex.getVirtualFilesByName(myProject, GRAPHQLCONFIG_YAML, projectScope);
379-
if (!ymlFiles.isEmpty() || !yamlFiles.isEmpty()) {
368+
final Collection<VirtualFile> yamlFiles = Sets.newLinkedHashSet(FilenameIndex.getVirtualFilesByName(myProject, GRAPHQLCONFIG_YML, projectScope));
369+
yamlFiles.addAll(FilenameIndex.getVirtualFilesByName(myProject, GRAPHQLCONFIG_YAML, projectScope));
370+
if (changedConfigurationFiles != null) {
371+
for (VirtualFile configurationFile : changedConfigurationFiles) {
372+
if (configurationFile.getName().equals(GRAPHQLCONFIG_YML) || configurationFile.getName().equals(GRAPHQLCONFIG_YAML)) {
373+
if (configurationFile.isValid()) { // don't process deletions
374+
yamlFiles.add(configurationFile);
375+
}
376+
}
377+
}
378+
}
379+
if (!yamlFiles.isEmpty()) {
380380
final Representer representer = new Representer();
381381
representer.getPropertyUtils().setSkipMissingProperties(true);
382382
final Yaml yaml = new Yaml(new Constructor(GraphQLConfigData.class), representer);
383-
Streams.concat(ymlFiles.stream(), yamlFiles.stream()).forEach(yamlFile -> {
383+
yamlFiles.forEach(yamlFile -> {
384384
try {
385385
final String yamlText = new String(yamlFile.contentsToByteArray(), yamlFile.getCharset());
386386
final GraphQLConfigData graphQLConfigData = yaml.load(yamlText);

src/main/com/intellij/lang/jsgraphql/ide/project/schemastatus/GraphQLSchemasPanel.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ public void actionPerformed(AnActionEvent e) {
159159
@Override
160160
public void actionPerformed(AnActionEvent e) {
161161
myProject.getMessageBus().syncPublisher(GraphQLSchemaChangeListener.TOPIC).onGraphQLSchemaChanged();
162-
GraphQLConfigManager.getService(myProject).buildConfigurationModel();
162+
GraphQLConfigManager.getService(myProject).buildConfigurationModel(null);
163163
}
164164
});
165165
leftActionGroup.add(new ContextHelpAction(""));

src/main/com/intellij/lang/jsgraphql/schema/GraphQLSchemaChangeListener.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import com.intellij.json.psi.JsonFile;
1212
import com.intellij.lang.jsgraphql.endpoint.psi.JSGraphQLEndpointFile;
1313
import com.intellij.lang.jsgraphql.ide.project.GraphQLInjectionSearchHelper;
14+
import com.intellij.lang.jsgraphql.ide.project.graphqlconfig.GraphQLConfigManager;
1415
import com.intellij.lang.jsgraphql.psi.GraphQLFile;
1516
import com.intellij.lang.jsgraphql.psi.GraphQLFragmentDefinition;
1617
import com.intellij.lang.jsgraphql.psi.GraphQLOperationDefinition;
@@ -20,6 +21,7 @@
2021
import com.intellij.psi.*;
2122
import com.intellij.psi.impl.PsiTreeChangeEventImpl;
2223
import com.intellij.psi.util.PsiTreeUtil;
24+
import com.intellij.util.messages.MessageBusConnection;
2325
import com.intellij.util.messages.Topic;
2426
import org.jetbrains.annotations.NotNull;
2527

@@ -77,10 +79,6 @@ private void checkForSchemaChange(PsiTreeChangeEvent event) {
7779
}
7880
}
7981

80-
private void signalSchemaChanged() {
81-
myProject.getMessageBus().syncPublisher(GraphQLSchemaChangeListener.TOPIC).onGraphQLSchemaChanged();
82-
}
83-
8482
@Override
8583
public void propertyChanged(@NotNull PsiTreeChangeEvent event) {
8684
checkForSchemaChange(event);
@@ -118,6 +116,14 @@ public void childrenChanged(@NotNull PsiTreeChangeEvent event) {
118116
}
119117
};
120118
psiManager.addPsiTreeChangeListener(listener);
119+
120+
// also consider the schema changed when the underlying schema configuration files change
121+
final MessageBusConnection connection = myProject.getMessageBus().connect();
122+
connection.subscribe(GraphQLConfigManager.TOPIC, this::signalSchemaChanged);
123+
}
124+
125+
private void signalSchemaChanged() {
126+
myProject.getMessageBus().syncPublisher(GraphQLSchemaChangeListener.TOPIC).onGraphQLSchemaChanged();
121127
}
122128

123129
/**

0 commit comments

Comments
 (0)