IDEA-212693 vcs: show navigatable links in annotations popup
[idea/community.git] / plugins / hg4idea / src / org / zmlx / hg4idea / provider / annotate / HgAnnotation.java
1 /*
2  * Copyright 2008-2010 Victor Iacoban
3  * Copyright 2000-2012 JetBrains s.r.o.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16 */
17 package org.zmlx.hg4idea.provider.annotate;
18
19 import com.intellij.openapi.project.Project;
20 import com.intellij.openapi.vcs.VcsKey;
21 import com.intellij.openapi.vcs.annotate.FileAnnotation;
22 import com.intellij.openapi.vcs.annotate.LineAnnotationAspect;
23 import com.intellij.openapi.vcs.annotate.LineAnnotationAspectAdapter;
24 import com.intellij.openapi.vcs.annotate.ShowAllAffectedGenericAction;
25 import com.intellij.openapi.vcs.history.VcsFileRevision;
26 import com.intellij.openapi.vcs.history.VcsRevisionNumber;
27 import com.intellij.openapi.vfs.LocalFileSystem;
28 import com.intellij.openapi.vfs.VirtualFile;
29 import com.intellij.util.containers.ContainerUtil;
30 import git4idea.annotate.AnnotationTooltipBuilder;
31 import org.jetbrains.annotations.NotNull;
32 import org.jetbrains.annotations.Nullable;
33 import org.zmlx.hg4idea.HgFile;
34 import org.zmlx.hg4idea.HgFileRevision;
35 import org.zmlx.hg4idea.HgVcs;
36
37 import java.util.Date;
38 import java.util.LinkedList;
39 import java.util.List;
40
41 public class HgAnnotation extends FileAnnotation {
42
43   private StringBuilder myContentBuffer;
44
45   public enum FIELD {
46     USER, REVISION, DATE, LINE, CONTENT
47   }
48
49   private final HgLineAnnotationAspect dateAnnotationAspect = new HgLineAnnotationAspect(FIELD.DATE);
50   private final HgLineAnnotationAspect userAnnotationAspect = new HgLineAnnotationAspect(FIELD.USER);
51   private final HgLineAnnotationAspect revisionAnnotationAspect = new HgLineAnnotationAspect(FIELD.REVISION);
52
53   @NotNull private final Project myProject;
54   @NotNull private final List<? extends HgAnnotationLine> myLines;
55   @NotNull private final List<? extends HgFileRevision> myFileRevisions;
56   @NotNull private final HgFile myFile;
57   private final VcsRevisionNumber myCurrentRevision;
58
59   public HgAnnotation(@NotNull Project project, @NotNull HgFile hgFile, @NotNull List<? extends HgAnnotationLine> lines,
60                       @NotNull List<? extends HgFileRevision> vcsFileRevisions, VcsRevisionNumber revision) {
61     super(project);
62     myProject = project;
63     myLines = lines;
64     myFileRevisions = vcsFileRevisions;
65     myFile = hgFile;
66     myCurrentRevision = revision;
67   }
68
69   @Override
70   public int getLineCount() {
71     return myLines.size();
72   }
73
74   @Override
75   public void dispose() {
76   }
77
78   @Override
79   public LineAnnotationAspect[] getAspects() {
80     return new LineAnnotationAspect[] {
81       revisionAnnotationAspect,
82       dateAnnotationAspect,
83       userAnnotationAspect
84     };
85   }
86
87   @Nullable
88   @Override
89   public String getToolTip(int lineNumber) {
90     return getToolTip(lineNumber, false);
91   }
92
93   @Nullable
94   @Override
95   public String getHtmlToolTip(int lineNumber) {
96     return getToolTip(lineNumber, true);
97   }
98
99   @Nullable
100   private String getToolTip(int lineNumber, boolean asHtml) {
101     if ( myLines.size() <= lineNumber || lineNumber < 0 ) {
102       return null;
103     }
104     HgAnnotationLine info = myLines.get(lineNumber);
105     if (info == null) {
106       return null;
107     }
108
109     HgFileRevision revision = ContainerUtil.find(myFileRevisions, it -> it.getRevisionNumber().equals(info.getVcsRevisionNumber()));
110     if (revision == null) return null;
111
112     AnnotationTooltipBuilder atb = new AnnotationTooltipBuilder(myProject, asHtml);
113     atb.appendRevisionLine(revision.getRevisionNumber(), null);
114     atb.appendLine("Author: " + revision.getAuthor());
115     atb.appendLine("Date: " + revision.getRevisionDate());
116     String message = revision.getCommitMessage();
117     if (message != null) atb.appendCommitMessageBlock(message);
118     return atb.toString();
119   }
120
121   @Override
122   public String getAnnotatedContent() {
123     if (myContentBuffer == null) {
124       myContentBuffer = new StringBuilder();
125       for (HgAnnotationLine line : myLines) {
126         myContentBuffer.append(line.get(FIELD.CONTENT));
127       }
128     }
129     return myContentBuffer.toString();
130   }
131
132   @Override
133   @Nullable
134   public VcsRevisionNumber getLineRevisionNumber(int lineNumber) {
135     if (lineNumber >= myLines.size() || lineNumber < 0) {
136       return null;
137     }
138     HgAnnotationLine annotationLine = myLines.get(lineNumber);
139     return annotationLine.getVcsRevisionNumber();
140   }
141
142   @Override
143   @Nullable
144   public Date getLineDate(int lineNumber) {
145     if (lineNumber >= myLines.size() || lineNumber < 0) {
146       return null;
147     }
148     //lines.get(lineNumber).get(HgAnnotation.FIELD.DATE)
149     // todo : parse date
150     return null;
151   }
152
153   @Override
154   @Nullable
155   public List<VcsFileRevision> getRevisions() {
156     List<VcsFileRevision> result = new LinkedList<>();
157     result.addAll(myFileRevisions);
158     return result;
159   }
160
161   @Nullable
162   private static String id(FIELD field) {
163     switch (field) {
164       case USER: return LineAnnotationAspect.AUTHOR;
165       case REVISION: return LineAnnotationAspect.REVISION;
166       case DATE: return LineAnnotationAspect.DATE;
167       default: return null;
168     }
169   }
170
171   private static boolean isShowByDefault(FIELD aspectType) {
172     return aspectType == FIELD.DATE || aspectType == FIELD.USER;
173   }
174
175   private class HgLineAnnotationAspect extends LineAnnotationAspectAdapter {
176     private final FIELD myAspectType;
177
178     HgLineAnnotationAspect(FIELD aspectType) {
179       super(id(aspectType), HgAnnotation.isShowByDefault(aspectType));
180       this.myAspectType = aspectType;
181     }
182
183     @Override
184     public String getValue(int lineNumber) {
185       if (lineNumber >= myLines.size() || lineNumber < 0) {
186         return "";
187       }
188       HgAnnotationLine annotationLine = myLines.get(lineNumber);
189       return myAspectType == FIELD.REVISION
190         ? annotationLine.getVcsRevisionNumber().asString()
191         : annotationLine.get(myAspectType).toString();
192     }
193
194     @Override
195     protected void showAffectedPaths(int lineNum) {
196       if (lineNum >= 0 && lineNum < myLines.size()) {
197         HgAnnotationLine line = myLines.get(lineNum);
198         VirtualFile file = myFile.toFilePath().getVirtualFile();
199         if (line != null && file != null) {
200           ShowAllAffectedGenericAction.showSubmittedFiles(myProject, line.getVcsRevisionNumber(), file, HgVcs.getKey());
201         }
202       }
203     }
204   }
205
206   @Nullable
207   @Override
208   public VcsRevisionNumber getCurrentRevision() {
209     return myCurrentRevision;
210   }
211
212   @Override
213   public VcsKey getVcsKey() {
214     return HgVcs.getKey();
215   }
216
217   @Override
218   public VirtualFile getFile() {
219     return LocalFileSystem.getInstance().refreshAndFindFileByIoFile(myFile.getFile());
220   }
221 }