diff --git a/changelog/@unreleased/pr-10.v2.yml b/changelog/@unreleased/pr-10.v2.yml new file mode 100644 index 0000000..ac607df --- /dev/null +++ b/changelog/@unreleased/pr-10.v2.yml @@ -0,0 +1,5 @@ +type: improvement +improvement: + description: Smarter versions lock writing + links: + - https://github.com/palantir/gradle-consistent-versions-idea-plugin/pull/10 diff --git a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionPropsFileListener.java b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionPropsFileListener.java index 7edbd5c..d9fc505 100644 --- a/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionPropsFileListener.java +++ b/gradle-consistent-versions-idea-plugin/src/main/java/com/palantir/gradle/versions/intellij/VersionPropsFileListener.java @@ -20,16 +20,23 @@ import com.intellij.openapi.components.ComponentManager; import com.intellij.openapi.externalSystem.importing.ImportSpecBuilder; import com.intellij.openapi.externalSystem.model.execution.ExternalSystemTaskExecutionSettings; +import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode; +import com.intellij.openapi.externalSystem.task.TaskCallback; import com.intellij.openapi.externalSystem.util.ExternalSystemUtil; import com.intellij.openapi.project.Project; import com.intellij.openapi.project.ProjectManager; import com.intellij.openapi.vfs.AsyncFileListener; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent; import com.intellij.openapi.vfs.newvfs.events.VFileEvent; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiManager; +import com.intellij.psi.util.PsiTreeUtil; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Objects; import java.util.function.Predicate; import org.jetbrains.annotations.Nullable; import org.jetbrains.plugins.gradle.util.GradleConstants; @@ -38,19 +45,15 @@ public final class VersionPropsFileListener implements AsyncFileListener { private static final Logger log = LoggerFactory.getLogger(VersionPropsFileListener.class); - - public VersionPropsFileListener() { - log.debug("Got created"); - } + private static final String TASK_NAME = "writeVersionsLock"; @Nullable @Override public ChangeApplier prepareChange(List events) { - List versionPropsEvents = events.stream() - .filter(VFileEvent::isFromSave) // This is quite expensive and noisy so only run on save - .map(VFileEvent::getFile) - .filter(Objects::nonNull) - .filter(file -> "versions.props".equals(file.getName())) + List versionPropsEvents = events.stream() + .filter(event -> event instanceof VFileContentChangeEvent) + .map(event -> (VFileContentChangeEvent) event) + .filter(event -> "versions.props".equals(event.getFile().getName())) .toList(); if (versionPropsEvents.isEmpty()) { @@ -62,24 +65,80 @@ public ChangeApplier prepareChange(List events) { .filter(Project::isInitialized) .filter(Predicate.not(ComponentManager::isDisposed)) .filter(project -> versionPropsEvents.stream() - .anyMatch(event -> event.getPath().startsWith(project.getBasePath()))) + .anyMatch(event -> event.getPath().startsWith(project.getBasePath()) + && !isFileMalformed(project, event.getFile()))) .toList(); return new ChangeApplier() { @Override public void afterVfsChange() { projectsAffected.forEach(project -> { - ExternalSystemTaskExecutionSettings settings = new ExternalSystemTaskExecutionSettings(); - settings.setExternalProjectPath(project.getBasePath()); - settings.setTaskNames(Collections.singletonList("writeVersionsLock")); - settings.setExternalSystemIdString(GradleConstants.SYSTEM_ID.toString()); - - ExternalSystemUtil.runTask( - settings, DefaultRunExecutor.EXECUTOR_ID, project, GradleConstants.SYSTEM_ID); - ExternalSystemUtil.refreshProject( - project.getBasePath(), new ImportSpecBuilder(project, GradleConstants.SYSTEM_ID).build()); + if (hasBuildSrc(project)) { + runTaskThenRefresh(project); + } else { + refreshProjectWithTask(project); + } }); } }; } + + private boolean hasBuildSrc(Project project) { + return Files.exists(Paths.get(project.getBasePath(), "buildSrc")); + } + + private void runTaskThenRefresh(Project project) { + log.debug("Running task {} on project {}", TASK_NAME, project.getName()); + TaskCallback callback = new TaskCallback() { + @Override + public void onSuccess() { + log.debug("Task {} successfully executed", TASK_NAME); + refreshProject(project); + } + + @Override + public void onFailure() { + log.error("Task {} failed", TASK_NAME); + } + }; + ExternalSystemTaskExecutionSettings settings = createExecutionSettings(project); + ExternalSystemUtil.runTask( + settings, + DefaultRunExecutor.EXECUTOR_ID, + project, + GradleConstants.SYSTEM_ID, + callback, + ProgressExecutionMode.IN_BACKGROUND_ASYNC); + } + + private ExternalSystemTaskExecutionSettings createExecutionSettings(Project project) { + ExternalSystemTaskExecutionSettings settings = new ExternalSystemTaskExecutionSettings(); + settings.setExternalProjectPath(project.getBasePath()); + settings.setTaskNames(Collections.singletonList(TASK_NAME)); + settings.setExternalSystemIdString(GradleConstants.SYSTEM_ID.toString()); + return settings; + } + + private void refreshProjectWithTask(Project project) { + log.debug("Refreshing project {} with task {}", project.getName(), TASK_NAME); + refreshProject(project, new ImportSpecBuilder(project, GradleConstants.SYSTEM_ID).withArguments(TASK_NAME)); + } + + private void refreshProject(Project project) { + refreshProject(project, new ImportSpecBuilder(project, GradleConstants.SYSTEM_ID)); + } + + private void refreshProject(Project project, ImportSpecBuilder importSpec) { + ExternalSystemUtil.refreshProject(project.getBasePath(), importSpec); + } + + private static boolean isFileMalformed(Project project, VirtualFile file) { + PsiFile psiFile = PsiManager.getInstance(project).findFile(file); + + if (psiFile == null || !(psiFile.getFileType() instanceof VersionPropsFileType)) { + return true; + } + + return PsiTreeUtil.hasErrorElements(psiFile); + } }