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
;
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
) {
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();
94 editor
.getGutter().closeAllAnnotations();
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
) {
133 fileAnnotationRef
.set(annotationProvider
.annotate(file
));
135 catch (VcsException e
) {
141 public void onCancel() {
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(),
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
);
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
);
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
,
225 LineAnnotationAspect aspect
,
226 TextAnnotationPresentation presentation
,
227 final HighlightAnnotationsActions highlighting
) {
228 super(annotation
, editor
, aspect
, presentation
);
229 myHighlighting
= highlighting
;
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
,
245 LineAnnotationAspect aspect
,
246 TextAnnotationPresentation highlighting
) {
247 super(annotation
, editor
, aspect
, highlighting
);
251 public ColorKey
getColor(int line
, Editor editor
) {
252 return AnnotationSource
.LOCAL
.getColor();
256 public String
getLineText(int line
, Editor editor
) {
257 final String value
= myAspect
.getValue(line
);
258 if (String
.valueOf(myAnnotation
.getLineRevisionNumber(line
)).equals(value
)) {
261 // shown in merge sources mode
262 return myTurnedOn ? value
: "";
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
,
286 LineAnnotationAspect aspect
,
287 TextAnnotationPresentation highlighting
) {
288 super(annotation
, editor
, aspect
, highlighting
);
292 public ColorKey
getColor(int line
, Editor editor
) {
293 return AnnotationSource
.LOCAL
.getColor();
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
;
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() {
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
;
357 myListeners
= new ArrayList
<Consumer
<AnnotationSource
>>();
358 myShowMerged
= mySwitcher
.getDefaultSource().showMerged();
361 public void addSourceSwitchListener(final Consumer
<AnnotationSource
> listener
) {
362 myListeners
.add(listener
);
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();