From 87eb3116296b61fe5a6ddf35bb35e846df89d3be Mon Sep 17 00:00:00 2001 From: Aleksey Pivovarov Date: Mon, 8 Jul 2019 18:41:24 +0300 Subject: [PATCH] IDEA-212693 vcs: show navigatable links in annotations popup GitOrigin-RevId: 37b2b1c079bc75b0ba908034e0311cb702496472 --- .../annotate/AnnotationTooltipBuilder.java | 90 +++++++++++++++++++ .../git4idea/annotate/GitFileAnnotation.java | 33 ++----- .../org/zmlx/hg4idea/HgVcsMessages.properties | 1 - .../provider/annotate/HgAnnotation.java | 37 +++++--- .../svn/annotate/BaseSvnFileAnnotation.java | 27 ++++-- 5 files changed, 144 insertions(+), 44 deletions(-) create mode 100644 platform/vcs-impl/src/git4idea/annotate/AnnotationTooltipBuilder.java diff --git a/platform/vcs-impl/src/git4idea/annotate/AnnotationTooltipBuilder.java b/platform/vcs-impl/src/git4idea/annotate/AnnotationTooltipBuilder.java new file mode 100644 index 000000000000..45a6853ddb44 --- /dev/null +++ b/platform/vcs-impl/src/git4idea/annotate/AnnotationTooltipBuilder.java @@ -0,0 +1,90 @@ +// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package git4idea.annotate; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.vcs.changes.issueLinks.IssueLinkHtmlRenderer; +import com.intellij.openapi.vcs.history.VcsRevisionNumber; +import com.intellij.util.containers.Convertor; +import com.intellij.vcsUtil.VcsUtil; +import com.intellij.xml.util.XmlStringUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class AnnotationTooltipBuilder { + @NotNull private final Project myProject; + private final boolean myAsHtml; + + private final StringBuilder sb = new StringBuilder(); + + public AnnotationTooltipBuilder(@NotNull Project project, boolean asHtml) { + myProject = project; + myAsHtml = asHtml; + } + + private void append(@NotNull String text) { + sb.append(myAsHtml ? XmlStringUtil.escapeString(text) : text); + } + + private void appendRaw(@NotNull String text) { + sb.append(text); + } + + public void appendRevisionLine(@NotNull VcsRevisionNumber revisionNumber, + @Nullable Convertor linkBuilder) { + appendNewline(); + append("commit "); + + String link = myAsHtml && linkBuilder != null ? linkBuilder.convert(revisionNumber) : null; + if (link != null) { + appendRaw(link); + } + else { + append(revisionNumber.asString()); + } + } + + public void appendLine(@NotNull String content) { + appendNewline(); + append(content); + } + + public void appendCommitMessageBlock(@NotNull String message) { + append("\n\n"); + appendTextWithLinks(message); + } + + public void appendTextWithLinks(@NotNull String message) { + if (myAsHtml) { + appendRaw(IssueLinkHtmlRenderer.formatTextWithLinks(myProject, message)); + } + else { + append(VcsUtil.trimCommitMessageToSaneSize(message)); + } + } + + private void appendNewline() { + if (sb.length() != 0) append("\n"); + } + + @Override + public String toString() { + return sb.toString(); + } + + @NotNull + public static String buildSimpleTooltip(@NotNull Project project, + boolean asHtml, + @NotNull String prefix, + @NotNull String revision, + @Nullable String commitMessage) { + AnnotationTooltipBuilder builder = new AnnotationTooltipBuilder(project, asHtml); + builder.append(prefix); + builder.append(" "); + builder.append(revision); + if (commitMessage != null) { + builder.append(": "); + builder.appendTextWithLinks(commitMessage); + } + return builder.toString(); + } +} diff --git a/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java b/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java index 2f8d9c286301..cf7b8cb80c7f 100644 --- a/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java +++ b/plugins/git4idea/src/git4idea/annotate/GitFileAnnotation.java @@ -26,7 +26,6 @@ import com.intellij.openapi.vcs.annotate.FileAnnotation; import com.intellij.openapi.vcs.annotate.LineAnnotationAspect; import com.intellij.openapi.vcs.annotate.LineAnnotationAspectAdapter; import com.intellij.openapi.vcs.changes.ContentRevision; -import com.intellij.openapi.vcs.changes.issueLinks.IssueLinkHtmlRenderer; import com.intellij.openapi.vcs.history.VcsFileRevision; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vcs.impl.AbstractVcsHelperImpl; @@ -35,7 +34,6 @@ import com.intellij.util.containers.ContainerUtil; import com.intellij.util.text.DateFormatUtil; import com.intellij.vcs.log.VcsUser; import com.intellij.vcsUtil.VcsUtil; -import com.intellij.xml.util.XmlStringUtil; import git4idea.GitContentRevision; import git4idea.GitFileRevision; import git4idea.GitRevisionNumber; @@ -168,40 +166,23 @@ public class GitFileAnnotation extends FileAnnotation { LineInfo lineInfo = getLineInfo(lineNumber); if (lineInfo == null) return null; - StringBuilder sb = new StringBuilder(); + AnnotationTooltipBuilder atb = new AnnotationTooltipBuilder(myProject, asHtml); GitRevisionNumber revisionNumber = lineInfo.getRevisionNumber(); - String revisionLink = asHtml ? GitCommitTooltipLinkHandler.createLink(revisionNumber.asString(), revisionNumber) : null; - if (revisionLink != null) { - sb.append("commit ").append(revisionLink); - } - else { - appendLine(sb, asHtml, "commit " + revisionNumber.asString()); - } - - appendLine(sb, asHtml, "Author: " + lineInfo.getAuthor()); - appendLine(sb, asHtml, "Date: " + DateFormatUtil.formatDateTime(lineInfo.getAuthorDate())); + atb.appendRevisionLine(revisionNumber, it -> GitCommitTooltipLinkHandler.createLink(it.asString(), it)); + atb.appendLine("Author: " + lineInfo.getAuthor()); + atb.appendLine("Date: " + DateFormatUtil.formatDateTime(lineInfo.getAuthorDate())); if (!myFilePath.equals(lineInfo.myFilePath)) { String path = FileUtil.getLocationRelativeToUserHome(lineInfo.myFilePath.getPresentableUrl()); - appendLine(sb, asHtml, "Path: " + path); + atb.appendLine("Path: " + path); } String commitMessage = getCommitMessage(revisionNumber); if (commitMessage == null) commitMessage = lineInfo.getSubject() + "\n..."; - if (asHtml) { - sb.append("\n\n").append(IssueLinkHtmlRenderer.formatTextWithLinks(myProject, commitMessage)); - } - else { - sb.append("\n\n").append(VcsUtil.trimCommitMessageToSaneSize(commitMessage)); - } + atb.appendCommitMessageBlock(commitMessage); - return sb.toString(); - } - - private static void appendLine(@NotNull StringBuilder sb, boolean asHtml, @NotNull String content) { - if (sb.length() != 0) sb.append('\n'); - sb.append(asHtml ? XmlStringUtil.escapeString(content) : content); + return atb.toString(); } @Nullable diff --git a/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties b/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties index b3b3b65e710c..4d467e9823be 100644 --- a/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties +++ b/plugins/hg4idea/resources/org/zmlx/hg4idea/HgVcsMessages.properties @@ -136,7 +136,6 @@ hg4idea.exception.file.not.under.hg=The file {0} is not under Mercurial. hg4idea.changelist.column.branch=Branch -hg4idea.annotation.tool.tip=commit {0}\nAuthor: {1}\nDate: {2}\n\n{3} hg4idea.push.asNewBranch=push as &new remote branch hg4idea.push.bookmark=Book&mark diff --git a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/annotate/HgAnnotation.java b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/annotate/HgAnnotation.java index e3ad3b5aad88..45f28edd3af7 100644 --- a/plugins/hg4idea/src/org/zmlx/hg4idea/provider/annotate/HgAnnotation.java +++ b/plugins/hg4idea/src/org/zmlx/hg4idea/provider/annotate/HgAnnotation.java @@ -18,17 +18,21 @@ package org.zmlx.hg4idea.provider.annotate; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.VcsKey; -import com.intellij.openapi.vcs.annotate.*; +import com.intellij.openapi.vcs.annotate.FileAnnotation; +import com.intellij.openapi.vcs.annotate.LineAnnotationAspect; +import com.intellij.openapi.vcs.annotate.LineAnnotationAspectAdapter; +import com.intellij.openapi.vcs.annotate.ShowAllAffectedGenericAction; import com.intellij.openapi.vcs.history.VcsFileRevision; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.openapi.vfs.LocalFileSystem; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.util.containers.ContainerUtil; +import git4idea.annotate.AnnotationTooltipBuilder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.zmlx.hg4idea.HgFile; import org.zmlx.hg4idea.HgFileRevision; import org.zmlx.hg4idea.HgVcs; -import org.zmlx.hg4idea.HgVcsMessages; import java.util.Date; import java.util.LinkedList; @@ -80,9 +84,20 @@ public class HgAnnotation extends FileAnnotation { }; } - @Override @Nullable + @Override public String getToolTip(int lineNumber) { + return getToolTip(lineNumber, false); + } + + @Nullable + @Override + public String getHtmlToolTip(int lineNumber) { + return getToolTip(lineNumber, true); + } + + @Nullable + private String getToolTip(int lineNumber, boolean asHtml) { if ( myLines.size() <= lineNumber || lineNumber < 0 ) { return null; } @@ -91,14 +106,16 @@ public class HgAnnotation extends FileAnnotation { return null; } - for (HgFileRevision revision : myFileRevisions) { - if (revision.getRevisionNumber().equals(info.getVcsRevisionNumber())) { - return HgVcsMessages.message("hg4idea.annotation.tool.tip", revision.getRevisionNumber().asString(), - revision.getAuthor(), revision.getRevisionDate(), revision.getCommitMessage()); - } - } + HgFileRevision revision = ContainerUtil.find(myFileRevisions, it -> it.getRevisionNumber().equals(info.getVcsRevisionNumber())); + if (revision == null) return null; - return null; + AnnotationTooltipBuilder atb = new AnnotationTooltipBuilder(myProject, asHtml); + atb.appendRevisionLine(revision.getRevisionNumber(), null); + atb.appendLine("Author: " + revision.getAuthor()); + atb.appendLine("Date: " + revision.getRevisionDate()); + String message = revision.getCommitMessage(); + if (message != null) atb.appendCommitMessageBlock(message); + return atb.toString(); } @Override diff --git a/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/BaseSvnFileAnnotation.java b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/BaseSvnFileAnnotation.java index c6929da8b95f..58bcbf092713 100644 --- a/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/BaseSvnFileAnnotation.java +++ b/plugins/svn4idea/src/org/jetbrains/idea/svn/annotate/BaseSvnFileAnnotation.java @@ -7,6 +7,7 @@ import com.intellij.openapi.vcs.history.VcsFileRevision; import com.intellij.openapi.vcs.history.VcsRevisionNumber; import com.intellij.util.text.DateFormatUtil; import com.intellij.xml.util.XmlStringUtil; +import git4idea.annotate.AnnotationTooltipBuilder; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.idea.svn.SvnBundle; @@ -113,18 +114,30 @@ public abstract class BaseSvnFileAnnotation extends FileAnnotation { return new LineAnnotationAspect[]{REVISION_ASPECT, DATE_ASPECT, AUTHOR_ASPECT}; } + @Nullable @Override - public String getToolTip(final int lineNumber) { + public String getToolTip(int lineNumber) { + return getToolTip(lineNumber, false); + } + + @Nullable + @Override + public String getHtmlToolTip(int lineNumber) { + return getToolTip(lineNumber, true); + } + + @Nullable + private String getToolTip(int lineNumber, boolean asHtml) { final CommitInfo info = myInfos.getOrNull(lineNumber); - if (info == null) return ""; + if (info == null) return null; SvnFileRevision revision = myRevisionMap.get(info.getRevisionNumber()); - if (revision != null) { - String prefix = myInfos.getAnnotationSource(lineNumber).showMerged() ? "Merge source revision" : "Revision"; + if (revision == null) return null; - return prefix + " " + info.getRevisionNumber() + ": " + revision.getCommitMessage(); - } - return ""; + String prefix = myInfos.getAnnotationSource(lineNumber).showMerged() ? "Merge source revision" : "Revision"; + return AnnotationTooltipBuilder.buildSimpleTooltip(getProject(), asHtml, prefix, + String.valueOf(info.getRevisionNumber()), + revision.getCommitMessage()); } @Override