From 6faa76372e0f914f33af204e5f2136b6feca53f8 Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Wed, 30 Dec 2015 17:36:49 +0300 Subject: [PATCH] IDEA-91843 vcs: allow to annotate non-local VirtualFiles --- .../vcs/actions/AnnotateToggleAction.java | 9 + .../actions/AnnotateVcsVirtualFileAction.java | 200 ++++++++++++++++++ 2 files changed, 209 insertions(+) create mode 100644 platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateVcsVirtualFileAction.java diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java index 7c854a3533ba..2ef652c1e82c 100644 --- a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateToggleAction.java @@ -65,6 +65,9 @@ public class AnnotateToggleAction extends ToggleAction implements DumbAware, Ann else if (AnnotateDiffViewerAction.isEnabled(e)) { enabled = !AnnotateDiffViewerAction.isSuspended(e); } + else if (AnnotateVcsVirtualFileAction.isEnabled(e)) { + enabled = !AnnotateVcsVirtualFileAction.isSuspended(e); + } e.getPresentation().setEnabled(enabled); } @@ -76,6 +79,9 @@ public class AnnotateToggleAction extends ToggleAction implements DumbAware, Ann else if (AnnotateDiffViewerAction.isEnabled(e)) { return AnnotateDiffViewerAction.isAnnotated(e); } + else if (AnnotateVcsVirtualFileAction.isEnabled(e)) { + return AnnotateVcsVirtualFileAction.isAnnotated(e); + } return false; } @@ -87,6 +93,9 @@ public class AnnotateToggleAction extends ToggleAction implements DumbAware, Ann else if (AnnotateDiffViewerAction.isEnabled(e)) { AnnotateDiffViewerAction.perform(e, selected); } + else if (AnnotateVcsVirtualFileAction.isEnabled(e)) { + AnnotateVcsVirtualFileAction.perform(e, selected); + } } public static void doAnnotate(@NotNull final Editor editor, diff --git a/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateVcsVirtualFileAction.java b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateVcsVirtualFileAction.java new file mode 100644 index 000000000000..5f7561d92736 --- /dev/null +++ b/platform/vcs-impl/src/com/intellij/openapi/vcs/actions/AnnotateVcsVirtualFileAction.java @@ -0,0 +1,200 @@ +/* + * Copyright 2000-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.intellij.openapi.vcs.actions; + +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.CommonDataKeys; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.fileEditor.TextEditor; +import com.intellij.openapi.progress.ProcessCanceledException; +import com.intellij.openapi.progress.ProgressIndicator; +import com.intellij.openapi.progress.ProgressManager; +import com.intellij.openapi.progress.Task; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Condition; +import com.intellij.openapi.util.Ref; +import com.intellij.openapi.vcs.*; +import com.intellij.openapi.vcs.annotate.AnnotationProvider; +import com.intellij.openapi.vcs.annotate.FileAnnotation; +import com.intellij.openapi.vcs.changes.ContentRevision; +import com.intellij.openapi.vcs.history.VcsRevisionNumber; +import com.intellij.openapi.vcs.vfs.ContentRevisionVirtualFile; +import com.intellij.openapi.vcs.vfs.VcsVirtualFile; +import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.containers.ContainerUtil; +import com.intellij.vcs.AnnotationProviderEx; +import com.intellij.vcsUtil.VcsUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class AnnotateVcsVirtualFileAction { + private static final Logger LOG = Logger.getInstance(AnnotateVcsVirtualFileAction.class); + + public static boolean isEnabled(AnActionEvent e) { + Project project = e.getData(CommonDataKeys.PROJECT); + if (project == null || project.isDisposed()) return false; + + VirtualFile[] selectedFiles = e.getData(CommonDataKeys.VIRTUAL_FILE_ARRAY); + if (selectedFiles == null || selectedFiles.length != 1) return false; + + VirtualFile file = selectedFiles[0]; + if (file.isDirectory() || file.getFileType().isBinary()) return false; + + if (getEditors(project, file).isEmpty()) return false; + + AnnotationData data = extractData(project, file); + if (data == null) return false; + + AnnotationProvider provider = data.vcs.getAnnotationProvider(); + return provider instanceof AnnotationProviderEx; + } + + public static boolean isSuspended(AnActionEvent e) { + VirtualFile file = e.getRequiredData(CommonDataKeys.VIRTUAL_FILE_ARRAY)[0]; + return AnnotateToggleAction.getBackgroundableLock(e.getRequiredData(CommonDataKeys.PROJECT), file).isLocked(); + } + + public static boolean isAnnotated(AnActionEvent e) { + Project project = e.getRequiredData(CommonDataKeys.PROJECT); + VirtualFile file = e.getRequiredData(CommonDataKeys.VIRTUAL_FILE_ARRAY)[0]; + List editors = getEditors(project, file); + return ContainerUtil.exists(editors, new Condition() { + @Override + public boolean value(Editor editor) { + return editor.getGutter().isAnnotationsShown(); + } + }); + } + + public static void perform(AnActionEvent e, boolean selected) { + final Project project = e.getRequiredData(CommonDataKeys.PROJECT); + final VirtualFile file = e.getRequiredData(CommonDataKeys.VIRTUAL_FILE_ARRAY)[0]; + List editors = getEditors(project, file); + + if (!selected) { + for (Editor editor : editors) { + editor.getGutter().closeAllAnnotations(); + } + } + else { + final Editor editor = editors.get(0); + doAnnotate(project, editor, file); + } + } + + private static void doAnnotate(@NotNull final Project project, @NotNull final Editor editor, @NotNull final VirtualFile file) { + final AnnotationData data = extractData(project, file); + assert data != null; + + final AnnotationProviderEx provider = (AnnotationProviderEx)data.vcs.getAnnotationProvider(); + assert provider != null; + + + final Ref fileAnnotationRef = new Ref(); + final Ref exceptionRef = new Ref(); + + AnnotateToggleAction.getBackgroundableLock(project, file).lock(); + + final Task.Backgroundable annotateTask = new Task.Backgroundable(project, VcsBundle.message("retrieving.annotations"), true) { + @Override + public void run(final @NotNull ProgressIndicator indicator) { + try { + fileAnnotationRef.set(provider.annotate(data.filePath, data.revisionNumber)); + } + catch (VcsException e) { + exceptionRef.set(e); + } + catch (ProcessCanceledException pce) { + throw pce; + } + catch (Throwable t) { + exceptionRef.set(new VcsException(t)); + } + } + + @Override + public void onCancel() { + onSuccess(); + } + + @Override + public void onSuccess() { + AnnotateToggleAction.getBackgroundableLock(project, file).unlock(); + + if (!exceptionRef.isNull()) { + LOG.warn(exceptionRef.get()); + AbstractVcsHelper.getInstance(project).showErrors(Collections.singletonList(exceptionRef.get()), VcsBundle.message("message.title.annotate")); + } + + if (!fileAnnotationRef.isNull()) { + AnnotateToggleAction.doAnnotate(editor, project, null, fileAnnotationRef.get(), data.vcs); + } + } + }; + ProgressManager.getInstance().run(annotateTask); + } + + @NotNull + private static List getEditors(@NotNull Project project, @NotNull VirtualFile file) { + List list = new ArrayList(); + for (FileEditor fileEditor : FileEditorManager.getInstance(project).getEditors(file)) { + if (fileEditor instanceof TextEditor) { + list.add(((TextEditor)fileEditor).getEditor()); + } + } + return list; + } + + @Nullable + private static AnnotationData extractData(@NotNull Project project, @NotNull VirtualFile file) { + FilePath filePath = null; + VcsRevisionNumber revisionNumber = null; + if (file instanceof VcsVirtualFile) { + filePath = VcsUtil.getFilePath(file.getPath()); + revisionNumber = ((VcsVirtualFile)file).getFileRevision().getRevisionNumber(); + } + else if (file instanceof ContentRevisionVirtualFile) { + ContentRevision revision = ((ContentRevisionVirtualFile)file).getContentRevision(); + filePath = revision.getFile(); + revisionNumber = revision.getRevisionNumber(); + } + if (filePath == null || revisionNumber == null) return null; + + AbstractVcs vcs = VcsUtil.getVcsFor(project, filePath); + return vcs != null ? new AnnotationData(vcs, filePath, revisionNumber) : null; + } + + private static class AnnotationData { + @NotNull public final AbstractVcs vcs; + @NotNull public final FilePath filePath; + @NotNull public final VcsRevisionNumber revisionNumber; + + public AnnotationData(@NotNull AbstractVcs vcs, + @NotNull FilePath filePath, + @NotNull VcsRevisionNumber revisionNumber) { + this.vcs = vcs; + this.filePath = filePath; + this.revisionNumber = revisionNumber; + } + } +}