VCS: annotations highlighting: additionally marked with *, highlight latest revision...
[fedora-idea.git] / vcs-impl / src / com / intellij / openapi / vcs / actions / AnnotateToggleAction.java
bloba61177b729caefd55f997dd0a1bad4028201ce5e
1 package com.intellij.openapi.vcs.actions;
3 import com.intellij.openapi.actionSystem.AnAction;
4 import com.intellij.openapi.actionSystem.AnActionEvent;
5 import com.intellij.openapi.actionSystem.ToggleAction;
6 import com.intellij.openapi.diagnostic.Logger;
7 import com.intellij.openapi.editor.Editor;
8 import com.intellij.openapi.editor.colors.ColorKey;
9 import com.intellij.openapi.editor.colors.EditorFontType;
10 import com.intellij.openapi.editor.ex.EditorGutterComponentEx;
11 import com.intellij.openapi.fileEditor.FileDocumentManager;
12 import com.intellij.openapi.fileEditor.FileEditor;
13 import com.intellij.openapi.fileEditor.FileEditorManager;
14 import com.intellij.openapi.fileEditor.TextEditor;
15 import com.intellij.openapi.fileTypes.FileType;
16 import com.intellij.openapi.fileTypes.FileTypeManager;
17 import com.intellij.openapi.fileTypes.StdFileTypes;
18 import com.intellij.openapi.localVcs.UpToDateLineNumberProvider;
19 import com.intellij.openapi.progress.ProgressIndicator;
20 import com.intellij.openapi.progress.ProgressManager;
21 import com.intellij.openapi.progress.Task;
22 import com.intellij.openapi.project.DumbAware;
23 import com.intellij.openapi.project.Project;
24 import com.intellij.openapi.util.Key;
25 import com.intellij.openapi.util.Ref;
26 import com.intellij.openapi.vcs.*;
27 import com.intellij.openapi.vcs.annotate.*;
28 import com.intellij.openapi.vcs.changes.BackgroundFromStartOption;
29 import com.intellij.openapi.vcs.impl.BackgroundableActionEnabledHandler;
30 import com.intellij.openapi.vcs.impl.ProjectLevelVcsManagerImpl;
31 import com.intellij.openapi.vcs.impl.UpToDateLineNumberProviderImpl;
32 import com.intellij.openapi.vfs.VirtualFile;
33 import com.intellij.util.Consumer;
34 import org.jetbrains.annotations.NotNull;
35 import org.jetbrains.annotations.Nullable;
37 import java.util.*;
39 /**
40 * author: lesya
42 public class AnnotateToggleAction extends ToggleAction implements DumbAware {
43 private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.actions.AnnotateToggleAction");
44 protected static final Key<Collection<ActiveAnnotationGutter>> KEY_IN_EDITOR = Key.create("Annotations");
46 public void update(AnActionEvent e) {
47 e.getPresentation().setEnabled(isEnabled(VcsContextFactory.SERVICE.getInstance().createContextOn(e)));
50 private static boolean isEnabled(final VcsContext context) {
51 VirtualFile[] selectedFiles = context.getSelectedFiles();
52 if (selectedFiles == null) return false;
53 if (selectedFiles.length != 1) return false;
54 VirtualFile file = selectedFiles[0];
55 if (file.isDirectory()) return false;
56 Project project = context.getProject();
57 if (project == null || project.isDisposed()) return false;
59 final ProjectLevelVcsManager plVcsManager = ProjectLevelVcsManager.getInstance(project);
60 final BackgroundableActionEnabledHandler handler = ((ProjectLevelVcsManagerImpl)plVcsManager)
61 .getBackgroundableActionHandler(ProjectLevelVcsManagerImpl.MyBackgroundableActions.ANNOTATE);
62 if (handler.isInProgress(file.getPath())) return false;
64 AbstractVcs vcs = plVcsManager.getVcsFor(file);
65 if (vcs == null) return false;
66 final AnnotationProvider annotationProvider = vcs.getAnnotationProvider();
67 if (annotationProvider == null) return false;
68 final FileStatus fileStatus = FileStatusManager.getInstance(project).getStatus(file);
69 if (fileStatus == FileStatus.UNKNOWN || fileStatus == FileStatus.ADDED) {
70 return false;
72 return hasTextEditor(file);
75 private static boolean hasTextEditor(VirtualFile selectedFile) {
76 FileTypeManager fileTypeManager = FileTypeManager.getInstance();
77 FileType fileType = fileTypeManager.getFileTypeByFile(selectedFile);
78 return !fileType.isBinary() && fileType != StdFileTypes.GUI_DESIGNER_FORM;
81 public boolean isSelected(AnActionEvent e) {
82 VcsContext context = VcsContextFactory.SERVICE.getInstance().createContextOn(e);
83 Editor editor = context.getEditor();
84 if (editor == null) return false;
85 Collection annotations = editor.getUserData(KEY_IN_EDITOR);
86 return annotations != null && !annotations.isEmpty();
89 public void setSelected(AnActionEvent e, boolean state) {
90 VcsContext context = VcsContextFactory.SERVICE.getInstance().createContextOn(e);
91 Editor editor = context.getEditor();
92 if (!state) {
93 if (editor != null) {
94 editor.getGutter().closeAllAnnotations();
97 else {
98 if (editor == null) {
99 VirtualFile selectedFile = context.getSelectedFile();
100 FileEditor[] fileEditors = FileEditorManager.getInstance(context.getProject()).openFile(selectedFile, false);
101 for (FileEditor fileEditor : fileEditors) {
102 if (fileEditor instanceof TextEditor) {
103 editor = ((TextEditor)fileEditor).getEditor();
108 LOG.assertTrue(editor != null);
110 doAnnotate(editor, context.getProject());
115 private static void doAnnotate(final Editor editor, final Project project) {
116 final VirtualFile file = FileDocumentManager.getInstance().getFile(editor.getDocument());
117 if (project == null) return;
118 final ProjectLevelVcsManager plVcsManager = ProjectLevelVcsManager.getInstance(project);
119 AbstractVcs vcs = plVcsManager.getVcsFor(file);
120 if (vcs == null) return;
121 final AnnotationProvider annotationProvider = vcs.getAnnotationProvider();
123 final Ref<FileAnnotation> fileAnnotationRef = new Ref<FileAnnotation>();
124 final Ref<VcsException> exceptionRef = new Ref<VcsException>();
126 final BackgroundableActionEnabledHandler handler = ((ProjectLevelVcsManagerImpl)plVcsManager).getBackgroundableActionHandler(ProjectLevelVcsManagerImpl.MyBackgroundableActions.ANNOTATE);
127 handler.register(file.getPath());
129 ProgressManager.getInstance().run(new Task.Backgroundable(project, VcsBundle.message("retrieving.annotations"), true,
130 BackgroundFromStartOption.getInstance()) {
131 public void run(@NotNull ProgressIndicator indicator) {
132 try {
133 fileAnnotationRef.set(annotationProvider.annotate(file));
135 catch (VcsException e) {
136 exceptionRef.set(e);
140 @Override
141 public void onCancel() {
142 onSuccess();
145 @Override
146 public void onSuccess() {
147 handler.completed(file.getPath());
149 if (! exceptionRef.isNull()) {
150 AbstractVcsHelper.getInstance(project).showErrors(Arrays.asList(exceptionRef.get()), VcsBundle.message("message.title.annotate"));
152 if (fileAnnotationRef.isNull()) return;
154 doAnnotate(editor, project, file, fileAnnotationRef.get());
159 public static void doAnnotate(final Editor editor, final Project project, final VirtualFile file, final FileAnnotation fileAnnotation) {
160 String upToDateContent = fileAnnotation.getAnnotatedContent();
162 final UpToDateLineNumberProvider getUpToDateLineNumber = new UpToDateLineNumberProviderImpl(
163 editor.getDocument(),
164 project,
165 upToDateContent);
167 editor.getGutter().closeAllAnnotations();
169 // be careful, not proxies but original items are put there (since only their presence not behaviour is important)
170 Collection<ActiveAnnotationGutter> annotations = editor.getUserData(KEY_IN_EDITOR);
171 if (annotations == null) {
172 annotations = new HashSet<ActiveAnnotationGutter>();
173 editor.putUserData(KEY_IN_EDITOR, annotations);
176 final EditorGutterComponentEx editorGutterComponentEx = (EditorGutterComponentEx)editor.getGutter();
177 final HighlightAnnotationsActions highlighting = new HighlightAnnotationsActions(project, file, fileAnnotation, editorGutterComponentEx);
178 final List<AnnotationFieldGutter> gutters = new ArrayList<AnnotationFieldGutter>();
179 final AnnotationSourceSwitcher switcher = fileAnnotation.getAnnotationSourceSwitcher();
180 final MyAnnotationPresentation presentation = new MyAnnotationPresentation(highlighting, switcher, editorGutterComponentEx);
182 if (switcher != null) {
184 switcher.switchTo(switcher.getDefaultSource());
185 final LineAnnotationAspect revisonAspect = switcher.getRevisionAspect();
186 final MyCurrentRevisionAnnotationFieldGutter currentRevisionGutter =
187 new MyCurrentRevisionAnnotationFieldGutter(fileAnnotation, editor, revisonAspect, presentation);
188 final MyMergeSourceAvailableMarkerGutter mergeSourceGutter =
189 new MyMergeSourceAvailableMarkerGutter(fileAnnotation, editor, null, presentation);
191 presentation.addSourceSwitchListener(currentRevisionGutter);
192 presentation.addSourceSwitchListener(mergeSourceGutter);
194 currentRevisionGutter.consume(switcher.getDefaultSource());
195 mergeSourceGutter.consume(switcher.getDefaultSource());
197 gutters.add(currentRevisionGutter);
198 gutters.add(mergeSourceGutter);
201 final LineAnnotationAspect[] aspects = fileAnnotation.getAspects();
202 for (LineAnnotationAspect aspect : aspects) {
203 final AnnotationFieldGutter gutter = new AnnotationFieldGutter(fileAnnotation, editor, aspect, presentation);
204 gutters.add(gutter);
206 gutters.add(new MyHighlightedAdditionalColumn(fileAnnotation, editor, null, presentation, highlighting));
208 for (AnnotationFieldGutter gutter : gutters) {
209 final AnnotationGutterLineConvertorProxy proxy = new AnnotationGutterLineConvertorProxy(getUpToDateLineNumber, gutter);
210 if (gutter.isGutterAction()) {
211 editor.getGutter().registerTextAnnotation(proxy, proxy);
213 else {
214 editor.getGutter().registerTextAnnotation(proxy);
216 annotations.add(gutter);
220 private static class MyHighlightedAdditionalColumn extends AnnotationFieldGutter {
221 private final HighlightAnnotationsActions myHighlighting;
223 private MyHighlightedAdditionalColumn(FileAnnotation annotation,
224 Editor editor,
225 LineAnnotationAspect aspect,
226 TextAnnotationPresentation presentation,
227 final HighlightAnnotationsActions highlighting) {
228 super(annotation, editor, aspect, presentation);
229 myHighlighting = highlighting;
232 @Override
233 public String getLineText(int line, Editor editor) {
234 return myHighlighting.isLineBold(line) ? "*" : "";
238 // !! shown additionally only when merge
239 private static class MyCurrentRevisionAnnotationFieldGutter extends AnnotationFieldGutter implements Consumer<AnnotationSource> {
240 // merge source showing is turned on
241 private boolean myTurnedOn;
243 private MyCurrentRevisionAnnotationFieldGutter(FileAnnotation annotation,
244 Editor editor,
245 LineAnnotationAspect aspect,
246 TextAnnotationPresentation highlighting) {
247 super(annotation, editor, aspect, highlighting);
250 @Override
251 public ColorKey getColor(int line, Editor editor) {
252 return AnnotationSource.LOCAL.getColor();
255 @Override
256 public String getLineText(int line, Editor editor) {
257 final String value = myAspect.getValue(line);
258 if (String.valueOf(myAnnotation.getLineRevisionNumber(line)).equals(value)) {
259 return "";
261 // shown in merge sources mode
262 return myTurnedOn ? value : "";
265 @Override
266 public String getToolTip(int line, Editor editor) {
267 final String aspectTooltip = myAspect.getTooltipText(line);
268 if (aspectTooltip != null) {
269 return aspectTooltip;
271 final String text = getLineText(line, editor);
272 return ((text == null) || (text.length() == 0)) ? "" : VcsBundle.message("annotation.original.revision.text", text);
275 public void consume(final AnnotationSource annotationSource) {
276 myTurnedOn = annotationSource.showMerged();
280 private static class MyMergeSourceAvailableMarkerGutter extends AnnotationFieldGutter implements Consumer<AnnotationSource> {
281 // merge source showing is turned on
282 private boolean myTurnedOn;
284 private MyMergeSourceAvailableMarkerGutter(FileAnnotation annotation,
285 Editor editor,
286 LineAnnotationAspect aspect,
287 TextAnnotationPresentation highlighting) {
288 super(annotation, editor, aspect, highlighting);
291 @Override
292 public ColorKey getColor(int line, Editor editor) {
293 return AnnotationSource.LOCAL.getColor();
296 @Override
297 public String getLineText(int line, Editor editor) {
298 if (myTurnedOn) return "";
299 final AnnotationSourceSwitcher switcher = myAnnotation.getAnnotationSourceSwitcher();
300 if (switcher == null) return "";
301 return switcher.mergeSourceAvailable(line) ? "M" : "";
304 public void consume(final AnnotationSource annotationSource) {
305 myTurnedOn = annotationSource.showMerged();
309 private static class MyAnnotationPresentation implements TextAnnotationPresentation {
310 private final HighlightAnnotationsActions myHighlighting;
311 @Nullable
312 private final AnnotationSourceSwitcher mySwitcher;
313 private final List<AnAction> myActions;
314 private MySwitchAnnotationSourceAction mySwitchAction;
316 public MyAnnotationPresentation(@NotNull final HighlightAnnotationsActions highlighting, @Nullable final AnnotationSourceSwitcher switcher,
317 final EditorGutterComponentEx gutter) {
318 myHighlighting = highlighting;
319 mySwitcher = switcher;
321 myActions = new ArrayList<AnAction>(myHighlighting.getList());
322 if (mySwitcher != null) {
323 mySwitchAction = new MySwitchAnnotationSourceAction(mySwitcher, gutter);
324 myActions.add(mySwitchAction);
328 public EditorFontType getFontType(final int line) {
329 return myHighlighting.isLineBold(line) ? EditorFontType.BOLD : EditorFontType.PLAIN;
332 public ColorKey getColor(final int line) {
333 if (mySwitcher == null) return AnnotationSource.LOCAL.getColor();
334 return mySwitcher.getAnnotationSource(line).getColor();
337 public List<AnAction> getActions() {
338 return myActions;
341 public void addSourceSwitchListener(final Consumer<AnnotationSource> listener) {
342 mySwitchAction.addSourceSwitchListener(listener);
346 private static class MySwitchAnnotationSourceAction extends AnAction {
347 private final static String ourShowMerged = VcsBundle.message("annotation.switch.to.merged.text");
348 private final static String ourHideMerged = VcsBundle.message("annotation.switch.to.original.text");
349 private final AnnotationSourceSwitcher mySwitcher;
350 private final EditorGutterComponentEx myGutter;
351 private final List<Consumer<AnnotationSource>> myListeners;
352 private boolean myShowMerged;
354 private MySwitchAnnotationSourceAction(final AnnotationSourceSwitcher switcher, final EditorGutterComponentEx gutter) {
355 mySwitcher = switcher;
356 myGutter = gutter;
357 myListeners = new ArrayList<Consumer<AnnotationSource>>();
358 myShowMerged = mySwitcher.getDefaultSource().showMerged();
361 public void addSourceSwitchListener(final Consumer<AnnotationSource> listener) {
362 myListeners.add(listener);
365 @Override
366 public void update(final AnActionEvent e) {
367 e.getPresentation().setText(myShowMerged ? ourHideMerged : ourShowMerged);
370 public void actionPerformed(AnActionEvent e) {
371 myShowMerged = ! myShowMerged;
372 final AnnotationSource newSource = AnnotationSource.getInstance(myShowMerged);
373 mySwitcher.switchTo(newSource);
374 for (Consumer<AnnotationSource> listener : myListeners) {
375 listener.consume(newSource);
377 myGutter.revalidateMarkup();