diff --git a/CHANGE-NOTES.md b/CHANGE-NOTES.md index a5a491570..5e8e452dd 100644 --- a/CHANGE-NOTES.md +++ b/CHANGE-NOTES.md @@ -5,6 +5,7 @@ - Fixed poor performance of action updates, as reported by @ekitrak. - Fixed Slide Out action not executing during Traverse. - Fixed race conditions happening when a branch is deleted in Slide Out action. +- Improved Machete file editor UX: notifications on parser errors no longer show up when Machete file is opened & focused. ## v3.3.1 - Fixed a race condition in the Slide-out action. diff --git a/config/checker/com.intellij.astub b/config/checker/com.intellij.astub index 881e8f546..527342619 100644 --- a/config/checker/com.intellij.astub +++ b/config/checker/com.intellij.astub @@ -230,6 +230,18 @@ class WriteAction { @UIPackage package com.intellij.openapi.fileChooser; @UIPackage package com.intellij.openapi.fileEditor; +class FileEditorManager { + + @SafeEffect + FileEditor[] getAllEditors(VirtualFile file); + + @SafeEffect + static FileEditorManager getInstance(Project project); + + @SafeEffect + VirtualFile[] getSelectedFiles(); +} + package com.intellij.openapi.fileTypes.ex; diff --git a/frontend/file/src/main/java/com/virtuslab/gitmachete/frontend/file/MacheteFileUtils.java b/frontend/file/src/main/java/com/virtuslab/gitmachete/frontend/file/MacheteFileUtils.java index de7512f23..52f3278d7 100644 --- a/frontend/file/src/main/java/com/virtuslab/gitmachete/frontend/file/MacheteFileUtils.java +++ b/frontend/file/src/main/java/com/virtuslab/gitmachete/frontend/file/MacheteFileUtils.java @@ -1,8 +1,11 @@ package com.virtuslab.gitmachete.frontend.file; +import java.nio.file.Path; import java.util.Objects; import com.intellij.openapi.fileEditor.FileDocumentManager; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.project.Project; import com.intellij.psi.PsiFile; import git4idea.repo.GitRepository; import git4idea.repo.GitRepositoryManager; @@ -61,4 +64,15 @@ public static void saveDocument(PsiFile file) { val fileDocManager = FileDocumentManager.getInstance(); fileDocManager.saveDocument(Objects.requireNonNull(fileDocManager.getDocument(file.getVirtualFile()))); } + + public static boolean macheteFileIsOpenedAndFocused(Project project, Path macheteFilePath) { + val fileEditorManager = FileEditorManager.getInstance(project); + val macheteVirtualFile = List.of(fileEditorManager.getSelectedFiles()) + .find(virtualFile -> virtualFile.getPath().equals(macheteFilePath.toString())); + if (macheteVirtualFile.isEmpty()) { + return false; + } else { + return fileEditorManager.getAllEditors(macheteVirtualFile.get()).length > 0; + } + } } diff --git a/frontend/ui/impl/src/main/java/com/virtuslab/gitmachete/frontend/ui/impl/table/EnhancedGraphTable.java b/frontend/ui/impl/src/main/java/com/virtuslab/gitmachete/frontend/ui/impl/table/EnhancedGraphTable.java index e5c6cc745..d3e9896aa 100644 --- a/frontend/ui/impl/src/main/java/com/virtuslab/gitmachete/frontend/ui/impl/table/EnhancedGraphTable.java +++ b/frontend/ui/impl/src/main/java/com/virtuslab/gitmachete/frontend/ui/impl/table/EnhancedGraphTable.java @@ -4,6 +4,7 @@ import static com.virtuslab.gitmachete.frontend.common.WriteActionUtils.runWriteActionOnUIThread; import static com.virtuslab.gitmachete.frontend.datakeys.DataKeys.typeSafeCase; import static com.virtuslab.gitmachete.frontend.defs.ActionIds.OPEN_MACHETE_FILE; +import static com.virtuslab.gitmachete.frontend.file.MacheteFileUtils.macheteFileIsOpenedAndFocused; import static com.virtuslab.gitmachete.frontend.resourcebundles.GitMacheteBundle.getString; import static io.vavr.API.$; import static io.vavr.API.Case; @@ -28,7 +29,6 @@ import com.intellij.openapi.actionSystem.DataProvider; import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.fileEditor.FileEditorManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.vcs.VcsNotifier; @@ -44,7 +44,6 @@ import com.intellij.util.ui.JBUI; import git4idea.repo.GitRepository; import git4idea.repo.GitRepositoryChangeListener; -import io.vavr.collection.List; import io.vavr.collection.Set; import io.vavr.control.Option; import io.vavr.control.Try; @@ -363,7 +362,7 @@ private void refreshModel( setModel(new GraphTableModel(repositoryGraph)); - if (!macheteFileIsOpenedAndFocused(macheteFilePath)) { + if (!macheteFileIsOpenedAndFocused(project, macheteFilePath)) { // notify if a branch listed in the machete file does not exist Set skippedBranchNames = repositorySnapshot.getSkippedBranchNames(); if (skippedBranchNames.nonEmpty()) { @@ -386,18 +385,6 @@ private void refreshModel( doOnUIThreadWhenReady.run(); } - @UIEffect - private boolean macheteFileIsOpenedAndFocused(Path macheteFilePath) { - val fileEditorManager = FileEditorManager.getInstance(project); - val macheteVirtualFile = List.of(fileEditorManager.getSelectedFiles()) - .find(virtualFile -> virtualFile.getPath().equals(macheteFilePath.toString())); - if (macheteVirtualFile.isEmpty()) { - return false; - } else { - return fileEditorManager.getAllEditors(macheteVirtualFile.get()).length > 0; - } - } - private Notification getSkippedBranchesNotification(IGitMacheteRepositorySnapshot repositorySnapshot, GitRepository gitRepository) { val notification = VcsNotifier.STANDARD_NOTIFICATION.createNotification( diff --git a/frontend/ui/impl/src/main/java/com/virtuslab/gitmachete/frontend/ui/impl/table/GitMacheteRepositoryUpdateBackgroundable.java b/frontend/ui/impl/src/main/java/com/virtuslab/gitmachete/frontend/ui/impl/table/GitMacheteRepositoryUpdateBackgroundable.java index d4c460f67..2f9896802 100644 --- a/frontend/ui/impl/src/main/java/com/virtuslab/gitmachete/frontend/ui/impl/table/GitMacheteRepositoryUpdateBackgroundable.java +++ b/frontend/ui/impl/src/main/java/com/virtuslab/gitmachete/frontend/ui/impl/table/GitMacheteRepositoryUpdateBackgroundable.java @@ -1,9 +1,11 @@ package com.virtuslab.gitmachete.frontend.ui.impl.table; import static com.intellij.openapi.application.ModalityState.NON_MODAL; +import static com.virtuslab.gitmachete.frontend.file.MacheteFileUtils.macheteFileIsOpenedAndFocused; import static com.virtuslab.gitmachete.frontend.resourcebundles.GitMacheteBundle.getString; import java.nio.file.Path; +import java.util.Objects; import java.util.function.Consumer; import com.intellij.openapi.application.ApplicationManager; @@ -14,7 +16,6 @@ import com.intellij.openapi.vfs.VirtualFileManager; import com.intellij.util.ModalityUiUtil; import git4idea.repo.GitRepository; -import io.vavr.control.Try; import lombok.CustomLog; import lombok.experimental.ExtensionMethod; import lombok.val; @@ -25,6 +26,7 @@ import com.virtuslab.branchlayout.api.BranchLayout; import com.virtuslab.branchlayout.api.BranchLayoutException; import com.virtuslab.branchlayout.api.readwrite.IBranchLayoutReader; +import com.virtuslab.gitmachete.backend.api.GitMacheteException; import com.virtuslab.gitmachete.backend.api.IGitMacheteRepository; import com.virtuslab.gitmachete.backend.api.IGitMacheteRepositoryCache; import com.virtuslab.gitmachete.backend.api.IGitMacheteRepositorySnapshot; @@ -33,7 +35,7 @@ import com.virtuslab.gitmachete.frontend.vfsutils.GitVfsUtils; import com.virtuslab.qual.guieffect.UIThreadUnsafe; -@ExtensionMethod(GitVfsUtils.class) +@ExtensionMethod({GitVfsUtils.class, Objects.class}) @CustomLog public final class GitMacheteRepositoryUpdateBackgroundable extends Task.Backgroundable { @@ -81,8 +83,6 @@ public void run(ProgressIndicator indicator) { /** * Updates the repository snapshot which is the base of graph table model. The change will be seen after * {@link EnhancedGraphTable#refreshModel()} completes. - * - * This method is heavyweight and must never be invoked on the UI thread. */ @UIThreadUnsafe private @Nullable IGitMacheteRepositorySnapshot updateRepositorySnapshot() { @@ -100,13 +100,23 @@ public void run(ProgressIndicator indicator) { if (isMacheteFilePresent) { LOG.debug("Machete file is present. Trying to create a repository snapshot"); - return Try.of(() -> { + try { BranchLayout branchLayout = readBranchLayout(macheteFilePath); IGitMacheteRepository gitMacheteRepository = gitMacheteRepositoryCache.getInstance(rootDirectoryPath, mainGitDirectoryPath, worktreeGitDirectoryPath); gitMacheteRepositoryConsumer.accept(gitMacheteRepository); return gitMacheteRepository.createSnapshotForLayout(branchLayout); - }).onFailure(this::handleUpdateRepositoryException).getOrNull(); + } catch (MacheteFileReaderException e) { + LOG.warn("Unable to create Git Machete repository", e); + if (!macheteFileIsOpenedAndFocused(getProject(), macheteFilePath)) { + notifyUpdateRepositoryException(e); + } + return null; + } catch (GitMacheteException e) { + LOG.warn("Unable to create Git Machete repository", e); + notifyUpdateRepositoryException(e); + return null; + } } else { LOG.debug("Machete file is absent"); return null; @@ -123,19 +133,20 @@ private BranchLayout readBranchLayout(Path path) throws MacheteFileReaderExcepti } } - private void handleUpdateRepositoryException(Throwable t) { - LOG.warn("Unable to create Git Machete repository", t); + private void notifyUpdateRepositoryException(Throwable t) { + String exceptionMessage = getInnermostCause(t).getMessage().requireNonNullElse(""); - // Getting the innermost exception since it's usually the primary cause that gives most valuable message + VcsNotifier.getInstance(getProject()).notifyError(/* displayId */ null, + getString("action.GitMachete.GitMacheteRepositoryUpdateBackgroundable.notification.title.failed"), + exceptionMessage); + } + + private static Throwable getInnermostCause(Throwable t) { Throwable cause = t; while (cause.getCause() != null) { cause = cause.getCause(); } - String exceptionMessage = cause.getMessage(); - - VcsNotifier.getInstance(getProject()).notifyError(/* displayId */ null, - getString("action.GitMachete.GitMacheteRepositoryUpdateBackgroundable.notification.title.failed"), - exceptionMessage != null ? exceptionMessage : ""); + return cause; } }