| /* |
| * Copyright 2000-2013 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.unscramble; |
| |
| import com.intellij.execution.filters.FileHyperlinkInfo; |
| import com.intellij.execution.filters.HyperlinkInfo; |
| import com.intellij.execution.impl.EditorHyperlinkSupport; |
| import com.intellij.icons.AllIcons; |
| import com.intellij.openapi.actionSystem.AnAction; |
| import com.intellij.openapi.actionSystem.AnActionEvent; |
| import com.intellij.openapi.application.ApplicationManager; |
| import com.intellij.openapi.editor.Editor; |
| import com.intellij.openapi.editor.EditorGutterAction; |
| import com.intellij.openapi.editor.colors.ColorKey; |
| import com.intellij.openapi.editor.colors.EditorColors; |
| import com.intellij.openapi.editor.colors.EditorColorsManager; |
| import com.intellij.openapi.editor.colors.EditorFontType; |
| import com.intellij.openapi.editor.ex.EditorGutterComponentEx; |
| import com.intellij.openapi.editor.markup.RangeHighlighter; |
| import com.intellij.openapi.fileEditor.OpenFileDescriptor; |
| import com.intellij.openapi.progress.PerformInBackgroundOption; |
| import com.intellij.openapi.progress.ProgressIndicator; |
| import com.intellij.openapi.progress.ProgressManager; |
| import com.intellij.openapi.progress.Task; |
| import com.intellij.openapi.project.DumbAware; |
| import com.intellij.openapi.project.Project; |
| import com.intellij.openapi.vcs.AbstractVcs; |
| import com.intellij.openapi.vcs.FilePath; |
| import com.intellij.openapi.vcs.VcsException; |
| import com.intellij.openapi.vcs.VcsKey; |
| import com.intellij.openapi.vcs.actions.ActiveAnnotationGutter; |
| import com.intellij.openapi.vcs.actions.VcsContextFactory; |
| import com.intellij.openapi.vcs.annotate.AnnotationSource; |
| import com.intellij.openapi.vcs.annotate.ShowAllAffectedGenericAction; |
| import com.intellij.openapi.vcs.ex.ProjectLevelVcsManagerEx; |
| import com.intellij.openapi.vcs.history.VcsFileRevision; |
| import com.intellij.openapi.vcs.history.VcsHistoryProvider; |
| import com.intellij.openapi.vcs.history.VcsHistorySession; |
| import com.intellij.openapi.vcs.history.VcsRevisionNumber; |
| import com.intellij.openapi.vfs.VirtualFile; |
| import com.intellij.util.text.DateFormatUtil; |
| import com.intellij.vcsUtil.VcsUtil; |
| import com.intellij.xml.util.XmlStringUtil; |
| import org.jetbrains.annotations.NotNull; |
| |
| import java.awt.*; |
| import java.util.*; |
| import java.util.List; |
| |
| /** |
| * @author Konstantin Bulenkov |
| */ |
| public class AnnotateStackTraceAction extends AnAction implements DumbAware { |
| private final EditorHyperlinkSupport myHyperlinks; |
| private Map<Integer, VcsFileRevision> cache; |
| private int newestLine = -1; |
| private int maxDateLength = 0; |
| private final Editor myEditor; |
| private boolean myGutterShowed = false; |
| private final HashMap<VirtualFile, List<Integer>> files2lines = new HashMap<VirtualFile, List<Integer>>(); |
| |
| public AnnotateStackTraceAction(@NotNull Editor editor, @NotNull EditorHyperlinkSupport hyperlinks) { |
| super("Show files modification info", null, AllIcons.Actions.Annotate); |
| myHyperlinks = hyperlinks; |
| myEditor = editor; |
| myEditor.getColorsScheme().setColor( |
| EditorColors.CARET_ROW_COLOR, EditorColorsManager.getInstance().getGlobalScheme().getColor(EditorColors.CARET_ROW_COLOR)); |
| } |
| |
| @Override |
| public void actionPerformed(AnActionEvent e) { |
| cache = new HashMap<Integer, VcsFileRevision>(); |
| |
| ProgressManager.getInstance().run( |
| new Task.Backgroundable(myEditor.getProject(), "Getting file history", true, PerformInBackgroundOption.ALWAYS_BACKGROUND) { |
| @Override |
| public boolean shouldStartInBackground() { |
| return true; |
| } |
| |
| @Override |
| public void onSuccess() { |
| } |
| |
| private void showGutter() { |
| final EditorGutterAction action = new EditorGutterAction() { |
| @Override |
| public void doAction(int lineNum) { |
| final VcsFileRevision revision = cache.get(lineNum); |
| final List<RangeHighlighter> links = myHyperlinks.findAllHyperlinksOnLine(lineNum); |
| if (!links.isEmpty()) { |
| final RangeHighlighter key = links.get(links.size() - 1); |
| HyperlinkInfo info = EditorHyperlinkSupport.getHyperlinkInfo(key); |
| |
| if (info instanceof FileHyperlinkInfo) { |
| final VirtualFile file = ((FileHyperlinkInfo)info).getDescriptor().getFile(); |
| final Project project = getProject(); |
| final AbstractVcs vcs = ProjectLevelVcsManagerEx.getInstanceEx(project).getVcsFor(file); |
| if (vcs != null) { |
| final VcsRevisionNumber number = revision.getRevisionNumber(); |
| final VcsKey vcsKey = vcs.getKeyInstanceMethod(); |
| ShowAllAffectedGenericAction.showSubmittedFiles(project, number, file, vcsKey); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public Cursor getCursor(int lineNum) { |
| return Cursor.getPredefinedCursor(Cursor.HAND_CURSOR); |
| } |
| }; |
| |
| myEditor.getGutter().registerTextAnnotation(new ActiveAnnotationGutter() { |
| @Override |
| public void doAction(int lineNum) { |
| } |
| |
| @Override |
| public Cursor getCursor(int lineNum) { |
| return Cursor.getDefaultCursor(); |
| } |
| |
| @Override |
| public String getLineText(int line, Editor editor) { |
| final VcsFileRevision revision = cache.get(line); |
| if (revision != null) { |
| return String.format("%"+maxDateLength+"s", DateFormatUtil.formatPrettyDate(revision.getRevisionDate())) + " " + revision.getAuthor(); |
| } |
| return ""; |
| } |
| |
| @Override |
| public String getToolTip(int line, Editor editor) { |
| final VcsFileRevision revision = cache.get(line); |
| if (revision != null) { |
| return XmlStringUtil.wrapInHtml( |
| revision.getAuthor() + |
| " " + |
| DateFormatUtil.formatDateTime(revision.getRevisionDate()) + |
| "<br/>" + |
| revision.getCommitMessage() |
| ); |
| } |
| return null; |
| } |
| |
| @Override |
| public EditorFontType getStyle(int line, Editor editor) { |
| return line == newestLine ? EditorFontType.BOLD : EditorFontType.PLAIN; |
| } |
| |
| @Override |
| public ColorKey getColor(int line, Editor editor) { |
| return AnnotationSource.LOCAL.getColor(); |
| } |
| |
| @Override |
| public Color getBgColor(int line, Editor editor) { |
| return null; |
| } |
| |
| @Override |
| public List<AnAction> getPopupActions(int line, Editor editor) { |
| return Collections.emptyList(); |
| } |
| |
| @Override |
| public void gutterClosed() { |
| myGutterShowed = false; |
| } |
| }, action); |
| |
| myGutterShowed = true; |
| } |
| |
| @Override |
| public void run(@NotNull ProgressIndicator indicator) { |
| Date newestDate = null; |
| |
| List<VirtualFile> files = new ArrayList<VirtualFile>(); |
| for (int line = 0; line < myEditor.getDocument().getLineCount(); line++) { |
| indicator.checkCanceled(); |
| final List<RangeHighlighter> links = myHyperlinks.findAllHyperlinksOnLine(line); |
| if (links.size() > 0) { |
| final RangeHighlighter key = links.get(links.size() - 1); |
| final HyperlinkInfo info = EditorHyperlinkSupport.getHyperlinkInfo(key); |
| if (info instanceof FileHyperlinkInfo) { |
| final OpenFileDescriptor fileDescriptor = ((FileHyperlinkInfo)info).getDescriptor(); |
| if (fileDescriptor != null) { |
| final VirtualFile file = fileDescriptor.getFile(); |
| if (files2lines.containsKey(file)) { |
| files2lines.get(file).add(line); |
| } else { |
| final ArrayList<Integer> lines = new ArrayList<Integer>(); |
| lines.add(line); |
| files2lines.put(file, lines); |
| files.add(file); |
| } |
| } |
| } |
| } |
| } |
| |
| |
| for (VirtualFile file : files) { |
| indicator.checkCanceled(); |
| final AbstractVcs vcs = VcsUtil.getVcsFor(myEditor.getProject(), file); |
| FilePath filePath = VcsContextFactory.SERVICE.getInstance().createFilePathOn(file); |
| if (vcs != null) { |
| try { |
| final VcsHistoryProvider provider = vcs.getVcsHistoryProvider(); |
| final VcsHistorySession session; |
| if (provider != null) { |
| session = provider.createSessionFor(filePath); |
| final List<VcsFileRevision> list; |
| if (session != null) { |
| list = session.getRevisionList(); |
| final List<Integer> lines = files2lines.get(file); |
| if (list != null && !list.isEmpty()) { |
| final VcsFileRevision revision = list.get(0); |
| final Date date = revision.getRevisionDate(); |
| if (newestDate == null || date.after(newestDate)) { |
| newestDate = date; |
| newestLine = lines.get(0); |
| } |
| final int length = DateFormatUtil.formatPrettyDate(date).length(); |
| if (length > maxDateLength) { |
| maxDateLength = length; |
| } |
| for (Integer line : lines) { |
| cache.put(line, revision); |
| } |
| ApplicationManager.getApplication().invokeLater(new Runnable() { |
| @Override |
| public void run() { |
| if (!myGutterShowed) { |
| showGutter(); |
| } else { |
| ((EditorGutterComponentEx)myEditor.getGutter()).revalidateMarkup(); |
| } |
| } |
| }); |
| } |
| } |
| } |
| } |
| catch (VcsException ignored) { |
| ignored.printStackTrace(); |
| } |
| } |
| |
| } |
| } |
| }); |
| } |
| |
| @Override |
| public void update(AnActionEvent e) { |
| e.getPresentation().setEnabled(cache == null || !myGutterShowed); |
| } |
| } |