diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/AbstractExternalSystemLocalSettings.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/AbstractExternalSystemLocalSettings.java index eecd5540f5a7..fe70a5cc620b 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/AbstractExternalSystemLocalSettings.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/AbstractExternalSystemLocalSettings.java @@ -46,20 +46,22 @@ public abstract class AbstractExternalSystemLocalSettings { = !SystemProperties.getBooleanProperty("external.system.forget.expand.nodes.state", false); private final AtomicReference> myExpandStates - = + = new AtomicReference>(new HashMap()); - private final AtomicReference> myRecentTasks = + private final AtomicReference> myRecentTasks = new AtomicReference>( ContainerUtilRt.newArrayList() ); - private final AtomicReference>> myAvailableProjects = + private final AtomicReference>> myAvailableProjects = new AtomicReference>>( ContainerUtilRt.>newHashMap() ); - private final AtomicReference>> myAvailableTasks = + private final AtomicReference>> myAvailableTasks = new AtomicReference>>( ContainerUtilRt.>newHashMap() ); + private final AtomicReference> myExternalConfigModificationStamps = + new AtomicReference>(ContainerUtilRt.newHashMap()); @NotNull private final ProjectSystemId myExternalSystemId; @NotNull private final Project myProject; @@ -71,7 +73,7 @@ public abstract class AbstractExternalSystemLocalSettings { /** * Asks current settings to drop all information related to external project which root config is located at the given path. - * + * * @param linkedProjectPathsToForget target root external project's path */ public void forgetExternalProject(@NotNull Set linkedProjectPathsToForget) { @@ -101,6 +103,11 @@ public abstract class AbstractExternalSystemLocalSettings { it.remove(); } } + + Map modificationStamps = myExternalConfigModificationStamps.get(); + for (String path : linkedProjectPathsToForget) { + modificationStamps.remove(path); + } } @SuppressWarnings("UnusedDeclaration") @@ -135,7 +142,18 @@ public abstract class AbstractExternalSystemLocalSettings { public void setRecentTasks(@NotNull List tasks) { myRecentTasks.set(tasks); } + + @NotNull + public Map getExternalConfigModificationStamps() { + return myExternalConfigModificationStamps.get(); + } + @SuppressWarnings("UnusedDeclaration") + public void setExternalConfigModificationStamps(@NotNull Map modificationStamps) { + // Required for IJ serialization. + myExternalConfigModificationStamps.set(modificationStamps); + } + public void fillState(@NotNull State state) { if (PRESERVE_EXPAND_STATE) { state.tasksExpandState = myExpandStates.get(); @@ -146,12 +164,14 @@ public abstract class AbstractExternalSystemLocalSettings { state.recentTasks = myRecentTasks.get(); state.availableProjects = myAvailableProjects.get(); state.availableTasks = myAvailableTasks.get(); + state.modificationStamps = myExternalConfigModificationStamps.get(); } public void loadState(@NotNull State state) { setIfNotNull(myExpandStates, state.tasksExpandState); setIfNotNull(myAvailableProjects, state.availableProjects); setIfNotNull(myAvailableTasks, state.availableTasks); + setIfNotNull(myExternalConfigModificationStamps, state.modificationStamps); if (state.recentTasks != null) { List recentTasks = myRecentTasks.get(); recentTasks.clear(); @@ -172,5 +192,8 @@ public abstract class AbstractExternalSystemLocalSettings { public List recentTasks = ContainerUtilRt.newArrayList(); public Map> availableProjects = ContainerUtilRt.newHashMap(); public Map> availableTasks = ContainerUtilRt.newHashMap(); + + public Map modificationStamps + = ContainerUtilRt.newHashMap(); } } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/RefreshAllExternalProjectsAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/RefreshAllExternalProjectsAction.java index 9d88d5f1bc1f..57c4cf7f9a5a 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/RefreshAllExternalProjectsAction.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/RefreshAllExternalProjectsAction.java @@ -60,6 +60,6 @@ public class RefreshAllExternalProjectsAction extends AnAction implements DumbAw // We save all documents because there is a possible case that there is an external system config file changed inside the ide. FileDocumentManager.getInstance().saveAllDocuments(); - ExternalSystemUtil.refreshProjects(project, externalSystemId); + ExternalSystemUtil.refreshProjects(project, externalSystemId, true); } } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ExternalSystemStartupActivity.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ExternalSystemStartupActivity.java index 6fd9e030337a..2641bf9a972d 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ExternalSystemStartupActivity.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/ExternalSystemStartupActivity.java @@ -39,7 +39,7 @@ public class ExternalSystemStartupActivity implements StartupActivity { public void run() { if (!SystemProperties.getBooleanProperty(ExternalSystemConstants.NEWLY_IMPORTED_PROJECT, false)) { for (ExternalSystemManager manager : ExternalSystemManager.EP_NAME.getExtensions()) { - ExternalSystemUtil.refreshProjects(project, manager.getSystemId()); + ExternalSystemUtil.refreshProjects(project, manager.getSystemId(), false); } } ExternalSystemAutoImporter.letTheMagicBegin(project); diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalSystemUtil.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalSystemUtil.java index cb48408726e2..0381d5cc3384 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalSystemUtil.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/util/ExternalSystemUtil.java @@ -50,6 +50,8 @@ import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowId; import com.intellij.openapi.wm.ToolWindowManager; @@ -160,8 +162,9 @@ public class ExternalSystemUtil { * * @param project target ide project * @param externalSystemId target external system which projects should be refreshed + * @param force flag which defines if external project refresh should be performed if it's config is up-to-date */ - public static void refreshProjects(@NotNull final Project project, @NotNull final ProjectSystemId externalSystemId) { + public static void refreshProjects(@NotNull final Project project, @NotNull final ProjectSystemId externalSystemId, boolean force) { ExternalSystemManager manager = ExternalSystemApiUtil.getManager(externalSystemId); if (manager == null) { return; @@ -173,12 +176,12 @@ public class ExternalSystemUtil { } final ProjectDataManager projectDataManager = ServiceManager.getService(ProjectDataManager.class); + final int[] counter = new int[1]; ExternalProjectRefreshCallback callback = new ExternalProjectRefreshCallback() { @NotNull private final Set myExternalModuleNames = ContainerUtilRt.newHashSet(); - private int myCounter = projectsSettings.size(); @Override public void onSuccess(@Nullable DataNode externalProject) { @@ -190,14 +193,14 @@ public class ExternalSystemUtil { myExternalModuleNames.add(node.getData().getName()); } projectDataManager.importData(externalProject.getKey(), Collections.singleton(externalProject), project, false); - if (--myCounter <= 0) { + if (--counter[0] <= 0) { processOrphanModules(); } } @Override public void onFailure(@NotNull String errorMessage, @Nullable String errorDetails) { - myCounter = Integer.MAX_VALUE; // Don't process orphan modules if there was an error on refresh. + counter[0] = Integer.MAX_VALUE; // Don't process orphan modules if there was an error on refresh. } private void processOrphanModules() { @@ -211,15 +214,34 @@ public class ExternalSystemUtil { orphanIdeModules.add(module); } } - + if (!orphanIdeModules.isEmpty()) { ruleOrphanModules(orphanIdeModules, project, externalSystemId); } } }; + + Map modificationStamps = manager.getLocalSettingsProvider().fun(project).getExternalConfigModificationStamps(); + Set toRefresh = ContainerUtilRt.newHashSet(); for (ExternalProjectSettings setting : projectsSettings) { - refreshProject(project, externalSystemId, setting.getExternalProjectPath(), callback, true, false); + Long oldModificationStamp = modificationStamps.get(setting.getExternalProjectPath()); + long currentModificationStamp = getTimeStamp(setting.getExternalProjectPath()); + if (force || currentModificationStamp < 0 || oldModificationStamp == null || oldModificationStamp < currentModificationStamp) { + toRefresh.add(setting.getExternalProjectPath()); + } } + + if (!toRefresh.isEmpty()) { + counter[0] = toRefresh.size(); + for (String path : toRefresh) { + refreshProject(project, externalSystemId, path, callback, true, false); + } + } + } + + private static long getTimeStamp(@NotNull String path) { + VirtualFile vFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(new File(path)); + return vFile == null ? -1 : vFile.getTimeStamp(); } /** @@ -232,7 +254,7 @@ public class ExternalSystemUtil { * * * This method handles that situation, i.e. it asks a user what should be done and acts accordingly. - * + * * @param orphanModules modules which correspond to the un-linked external project * @param project current ide project * @param externalSystemId id of the external system which project has been un-linked from ide project @@ -336,6 +358,12 @@ public class ExternalSystemUtil { task.execute(indicator); final Throwable error = task.getError(); if (error == null) { + long stamp = getTimeStamp(externalProjectPath); + if (stamp > 0) { + ExternalSystemManager manager = ExternalSystemApiUtil.getManager(externalSystemId); + assert manager != null; + manager.getLocalSettingsProvider().fun(project).getExternalConfigModificationStamps().put(externalProjectPath, stamp); + } DataNode externalProject = task.getExternalProject(); callback.onSuccess(externalProject); return; @@ -347,7 +375,7 @@ public class ExternalSystemUtil { externalSystemId.getReadableName(), externalProjectPath, message ); } - + callback.onFailure(message, extractDetails(error)); ExternalSystemIdeNotificationManager notificationManager = ServiceManager.getService(ExternalSystemIdeNotificationManager.class);