/* * 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.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.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.VcsFileRevision; 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.Collections; import java.util.List; public class AnnotateVcsVirtualFileAction { private static final Logger LOG = Logger.getInstance(AnnotateVcsVirtualFileAction.class); private 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 (VcsAnnotateUtil.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; } private static boolean isSuspended(AnActionEvent e) { VirtualFile file = e.getRequiredData(CommonDataKeys.VIRTUAL_FILE_ARRAY)[0]; return VcsAnnotateUtil.getBackgroundableLock(e.getRequiredData(CommonDataKeys.PROJECT), file).isLocked(); } private static boolean isAnnotated(AnActionEvent e) { Project project = e.getRequiredData(CommonDataKeys.PROJECT); VirtualFile file = e.getRequiredData(CommonDataKeys.VIRTUAL_FILE_ARRAY)[0]; List<Editor> editors = VcsAnnotateUtil.getEditors(project, file); return ContainerUtil.exists(editors, editor -> editor.getGutter().isAnnotationsShown()); } private 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<Editor> editors = VcsAnnotateUtil.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<FileAnnotation> fileAnnotationRef = new Ref<>(); final Ref<VcsException> exceptionRef = new Ref<>(); VcsAnnotateUtil.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() { VcsAnnotateUtil.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); } @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()); VcsFileRevision revision = ((VcsVirtualFile)file).getFileRevision(); revisionNumber = revision != null ? revision.getRevisionNumber() : null; } 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; } } public static class Provider implements AnnotateToggleAction.Provider { @Override public boolean isEnabled(AnActionEvent e) { return AnnotateVcsVirtualFileAction.isEnabled(e); } @Override public boolean isSuspended(AnActionEvent e) { return AnnotateVcsVirtualFileAction.isSuspended(e); } @Override public boolean isAnnotated(AnActionEvent e) { return AnnotateVcsVirtualFileAction.isAnnotated(e); } @Override public void perform(AnActionEvent e, boolean selected) { AnnotateVcsVirtualFileAction.perform(e, selected); } } }