Git: debug for annotation changed source
[fedora-idea.git] / plugins / git4idea / src / git4idea / annotate / GitFileAnnotation.java
blob06150045a72a95e7918550d4ad1264dbf2e28cd1
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package git4idea.annotate;
18 import com.intellij.openapi.diagnostic.Logger;
19 import com.intellij.openapi.editor.EditorGutterAction;
20 import com.intellij.openapi.project.Project;
21 import com.intellij.openapi.vcs.FileStatus;
22 import com.intellij.openapi.vcs.FileStatusListener;
23 import com.intellij.openapi.vcs.FileStatusManager;
24 import com.intellij.openapi.vcs.VcsException;
25 import com.intellij.openapi.vcs.annotate.*;
26 import com.intellij.openapi.vcs.history.VcsFileRevision;
27 import com.intellij.openapi.vcs.history.VcsRevisionNumber;
28 import com.intellij.openapi.vfs.VirtualFile;
29 import com.intellij.openapi.vfs.VirtualFileAdapter;
30 import com.intellij.openapi.vfs.VirtualFileEvent;
31 import com.intellij.openapi.vfs.VirtualFileManager;
32 import com.intellij.util.EventDispatcher;
33 import com.intellij.util.text.SyncDateFormat;
34 import git4idea.GitRevisionNumber;
35 import git4idea.actions.GitShowAllSubmittedFilesAction;
36 import git4idea.i18n.GitBundle;
37 import org.jetbrains.annotations.NotNull;
39 import java.awt.*;
40 import java.text.SimpleDateFormat;
41 import java.util.*;
42 import java.util.List;
44 /**
45 * Git file annotation implementation
46 * <p/>
47 * Based on the JetBrains SVNAnnotationProvider.
49 public class GitFileAnnotation implements FileAnnotation {
50 private final static Logger LOG = Logger.getInstance("#git4idea.annotate.GitFileAnnotation");
52 /**
53 * the format of the date shown in annotations
55 private static final SyncDateFormat DATE_FORMAT = new SyncDateFormat(SimpleDateFormat.getDateInstance(SimpleDateFormat.SHORT));
56 /**
57 * annotated content
59 private final StringBuffer myContentBuffer = new StringBuffer();
60 /**
61 * The currently annotated lines
63 private final ArrayList<LineInfo> myLines = new ArrayList<LineInfo>();
64 /**
65 * The project reference
67 private final Project myProject;
68 /**
69 * Annotation change listeners
71 private final EventDispatcher<AnnotationListener> myListeners = EventDispatcher.create(AnnotationListener.class);
72 /**
73 * Map from revision numbers to revisions
75 private final Map<VcsRevisionNumber, VcsFileRevision> myRevisionMap = new HashMap<VcsRevisionNumber, VcsFileRevision>();
76 /**
77 * listener for file system events
79 private final VirtualFileAdapter myFileListener;
81 private final MyFileStatusListener myFileStatusListener;
83 /**
84 * the virtual file for which annotations are generated
86 private final VirtualFile myFile;
87 /**
88 * If true, file system is monitored for changes
90 private final boolean myMonitorFlag;
92 /**
93 * Date annotation aspect
95 private final LineAnnotationAspect DATE_ASPECT = new LineAnnotationAspectAdapter() {
96 public String getValue(int lineNumber) {
97 if (myLines.size() <= lineNumber || lineNumber < 0 || myLines.get(lineNumber) == null) {
98 return "";
100 else {
101 final Date date = myLines.get(lineNumber).getDate();
102 return date == null ? "" : DATE_FORMAT.format(date);
107 * revision annotation aspect
109 private final LineAnnotationAspect REVISION_ASPECT = new RevisionAnnotationAspect();
111 * author annotation aspect
113 private final LineAnnotationAspect AUTHOR_ASPECT = new LineAnnotationAspectAdapter() {
114 public String getValue(int lineNumber) {
115 if (myLines.size() <= lineNumber || lineNumber < 0 || myLines.get(lineNumber) == null) {
116 return "";
118 else {
119 final String author = myLines.get(lineNumber).getAuthor();
120 return author == null ? "" : author;
126 * A constructor
128 * @param project the project of annotation provider
129 * @param file the git root
130 * @param monitorFlag if false the file system will not be listened for changes (used for annotated files from the repository).
132 public GitFileAnnotation(@NotNull final Project project, @NotNull VirtualFile file, final boolean monitorFlag) {
133 myProject = project;
134 myFile = file;
135 myMonitorFlag = monitorFlag;
136 if (myMonitorFlag) {
137 myFileListener = new VirtualFileAdapter() {
138 @Override
139 public void contentsChanged(final VirtualFileEvent event) {
140 if (myFile != event.getFile()) return;
141 if (!event.isFromRefresh()) return;
142 fireAnnotationChanged();
145 VirtualFileManager.getInstance().addVirtualFileListener(myFileListener);
146 myFileStatusListener = new MyFileStatusListener();
147 FileStatusManager.getInstance(myProject).addFileStatusListener(myFileStatusListener);
149 else {
150 myFileListener = null;
151 myFileStatusListener = null;
156 * Add revisions to the list (from log)
158 * @param revisions revisions to add
160 public void addLogEntries(List<VcsFileRevision> revisions) {
161 for (VcsFileRevision vcsFileRevision : revisions) {
162 myRevisionMap.put(vcsFileRevision.getRevisionNumber(), vcsFileRevision);
167 * Fire annotation changed event
169 private void fireAnnotationChanged() {
170 myListeners.getMulticaster().onAnnotationChanged();
171 LOG.debug("annotations changed fired from...", new Throwable());
175 * {@inheritDoc}
177 public void addListener(AnnotationListener listener) {
178 myListeners.addListener(listener);
182 * {@inheritDoc}
184 public void removeListener(AnnotationListener listener) {
185 myListeners.removeListener(listener);
189 * {@inheritDoc}
191 public void dispose() {
192 if (myMonitorFlag) {
193 VirtualFileManager.getInstance().removeVirtualFileListener(myFileListener);
194 FileStatusManager.getInstance(myProject).removeFileStatusListener(myFileStatusListener);
199 * {@inheritDoc}
201 public LineAnnotationAspect[] getAspects() {
202 return new LineAnnotationAspect[]{REVISION_ASPECT, DATE_ASPECT, AUTHOR_ASPECT};
206 * {@inheritDoc}
208 public String getToolTip(final int lineNumber) {
209 if (myLines.size() <= lineNumber || lineNumber < 0) {
210 return "";
212 final LineInfo info = myLines.get(lineNumber);
213 if (info == null) {
214 return "";
216 VcsFileRevision fileRevision = myRevisionMap.get(info.getRevision());
217 if (fileRevision != null) {
218 return GitBundle
219 .message("annotation.tool.tip", info.getRevision().asString(), fileRevision.getAuthor(), fileRevision.getRevisionDate(),
220 fileRevision.getCommitMessage());
222 else {
223 return "";
228 * {@inheritDoc}
230 public String getAnnotatedContent() {
231 return myContentBuffer.toString();
235 * {@inheritDoc}
237 public List<VcsFileRevision> getRevisions() {
238 final List<VcsFileRevision> result = new ArrayList<VcsFileRevision>(myRevisionMap.values());
239 Collections.sort(result, new Comparator<VcsFileRevision>() {
240 public int compare(final VcsFileRevision o1, final VcsFileRevision o2) {
241 return -1 * o1.getRevisionNumber().compareTo(o2.getRevisionNumber());
244 return result;
247 public AnnotationSourceSwitcher getAnnotationSourceSwitcher() {
248 return null;
252 * {@inheritDoc}
254 public VcsRevisionNumber getLineRevisionNumber(final int lineNumber) {
255 if (myLines.size() <= lineNumber || lineNumber < 0 || myLines.get(lineNumber) == null) {
256 return null;
258 final LineInfo lineInfo = myLines.get(lineNumber);
259 return lineInfo == null ? null : lineInfo.getRevision();
263 * Get revision number for the line.
265 public VcsRevisionNumber originalRevision(int lineNumber) {
266 return getLineRevisionNumber(lineNumber);
270 * Append line info
272 * @param date the revision date
273 * @param revision the revision number
274 * @param author the author
275 * @param line the line content
276 * @param lineNumber the line number for revision
277 * @throws VcsException in case when line could not be processed
279 public void appendLineInfo(final Date date,
280 final GitRevisionNumber revision,
281 final String author,
282 final String line,
283 final long lineNumber) throws VcsException {
284 int expectedLineNo = myLines.size() + 1;
285 if (lineNumber != expectedLineNo) {
286 throw new VcsException("Adding for info for line " + lineNumber + " but we are expecting it to be for " + expectedLineNo);
288 myLines.add(new LineInfo(date, revision, author));
289 myContentBuffer.append(line);
293 * Revision annotation aspect implementation
295 private class RevisionAnnotationAspect extends LineAnnotationAspectAdapter implements EditorGutterAction {
297 * {@inheritDoc}
299 public String getValue(int lineNumber) {
300 if (myLines.size() <= lineNumber || lineNumber < 0 || myLines.get(lineNumber) == null) {
301 return "";
303 else {
304 final GitRevisionNumber revision = myLines.get(lineNumber).getRevision();
305 return revision == null ? "" : String.valueOf(revision.getShortRev());
310 * {@inheritDoc}
312 public Cursor getCursor(final int lineNum) {
313 return Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
317 * {@inheritDoc}
319 public void doAction(int lineNum) {
320 if (lineNum >= 0 && lineNum < myLines.size()) {
321 final LineInfo info = myLines.get(lineNum);
322 VcsFileRevision revision = myRevisionMap.get(info.getRevision());
323 if (revision != null) {
324 GitShowAllSubmittedFilesAction.showSubmittedFiles(myProject, revision, myFile);
331 * Line information
333 static class LineInfo {
335 * date of the change
337 private final Date myDate;
339 * revision number
341 private final GitRevisionNumber myRevision;
343 * the author of the change
345 private final String myAuthor;
348 * A constructor
350 * @param date date of the change
351 * @param revision revision number
352 * @param author the author of the change
354 public LineInfo(final Date date, final GitRevisionNumber revision, final String author) {
355 myDate = date;
356 myRevision = revision;
357 myAuthor = author;
361 * @return the revision date
363 public Date getDate() {
364 return myDate;
368 * @return the revision number
370 public GitRevisionNumber getRevision() {
371 return myRevision;
375 * @return the author of the change
377 public String getAuthor() {
378 return myAuthor;
382 private class MyFileStatusListener implements FileStatusListener {
383 public void fileStatusesChanged() {
384 checkAndFire();
387 public void fileStatusChanged(@NotNull VirtualFile virtualFile) {
388 if (myFile.equals(virtualFile)) {
389 checkAndFire();
393 private void checkAndFire() {
394 // for the case of commit changes... remove annotation gutter
395 if (FileStatus.NOT_CHANGED.equals(FileStatusManager.getInstance(myProject).getStatus(myFile))) {
396 fireAnnotationChanged();