diff: add annotate action to diff viewers
[idea/community.git] / platform / diff-impl / src / com / intellij / diff / tools / simple / SimpleOnesideDiffViewer.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.diff.tools.simple;
17
18 import com.intellij.diff.DiffContext;
19 import com.intellij.diff.actions.NavigationContextChecker;
20 import com.intellij.diff.contents.DocumentContent;
21 import com.intellij.diff.requests.ContentDiffRequest;
22 import com.intellij.diff.requests.DiffRequest;
23 import com.intellij.diff.tools.util.DiffDataKeys;
24 import com.intellij.diff.tools.util.base.HighlightPolicy;
25 import com.intellij.diff.tools.util.base.TextDiffViewerUtil;
26 import com.intellij.diff.tools.util.side.OnesideTextDiffViewer;
27 import com.intellij.diff.util.DiffDrawUtil;
28 import com.intellij.diff.util.DiffUtil;
29 import com.intellij.diff.util.LineRange;
30 import com.intellij.diff.util.TextDiffType;
31 import com.intellij.openapi.actionSystem.AnAction;
32 import com.intellij.openapi.actionSystem.Separator;
33 import com.intellij.openapi.diagnostic.Logger;
34 import com.intellij.openapi.diff.DiffNavigationContext;
35 import com.intellij.openapi.editor.Document;
36 import com.intellij.openapi.editor.LogicalPosition;
37 import com.intellij.openapi.editor.markup.RangeHighlighter;
38 import com.intellij.openapi.editor.markup.SeparatorPlacement;
39 import com.intellij.openapi.progress.ProgressIndicator;
40 import com.intellij.openapi.util.Pair;
41 import org.jetbrains.annotations.CalledInAwt;
42 import org.jetbrains.annotations.NonNls;
43 import org.jetbrains.annotations.NotNull;
44 import org.jetbrains.annotations.Nullable;
45
46 import java.util.ArrayList;
47 import java.util.Iterator;
48 import java.util.List;
49
50 import static com.intellij.diff.util.DiffUtil.getLineCount;
51
52 public class SimpleOnesideDiffViewer extends OnesideTextDiffViewer {
53   public static final Logger LOG = Logger.getInstance(SimpleOnesideDiffViewer.class);
54
55   @NotNull private final MyInitialScrollHelper myInitialScrollHelper = new MyInitialScrollHelper();
56
57   @NotNull private final List<RangeHighlighter> myHighlighters = new ArrayList<RangeHighlighter>();
58
59   public SimpleOnesideDiffViewer(@NotNull DiffContext context, @NotNull DiffRequest request) {
60     super(context, (ContentDiffRequest)request);
61   }
62
63   @Override
64   @CalledInAwt
65   protected void onDispose() {
66     for (RangeHighlighter highlighter : myHighlighters) {
67       highlighter.dispose();
68     }
69     myHighlighters.clear();
70     super.onDispose();
71   }
72
73   @NotNull
74   @Override
75   protected List<AnAction> createToolbarActions() {
76     List<AnAction> group = new ArrayList<AnAction>();
77
78     group.add(new MyIgnorePolicySettingAction());
79     group.add(new MyHighlightPolicySettingAction());
80     group.add(new MyReadOnlyLockAction());
81     group.add(myEditorSettingsAction);
82
83     group.add(Separator.getInstance());
84     group.addAll(super.createToolbarActions());
85
86     return group;
87   }
88
89   @NotNull
90   @Override
91   protected List<AnAction> createPopupActions() {
92     List<AnAction> group = new ArrayList<AnAction>();
93
94     group.add(Separator.getInstance());
95     group.add(new MyIgnorePolicySettingAction().getPopupGroup());
96     group.add(Separator.getInstance());
97     group.add(new MyHighlightPolicySettingAction().getPopupGroup());
98
99     group.add(Separator.getInstance());
100     group.addAll(super.createPopupActions());
101
102     return group;
103   }
104
105   @Override
106   @CalledInAwt
107   protected void processContextHints() {
108     super.processContextHints();
109     myInitialScrollHelper.processContext(myRequest);
110   }
111
112   @Override
113   @CalledInAwt
114   protected void updateContextHints() {
115     super.updateContextHints();
116     myInitialScrollHelper.updateContext(myRequest);
117   }
118
119   //
120   // Diff
121   //
122
123   @Override
124   @NotNull
125   protected Runnable performRediff(@NotNull final ProgressIndicator indicator) {
126     indicator.checkCanceled();
127
128     return new Runnable() {
129       @Override
130       public void run() {
131         clearDiffPresentation();
132
133         boolean shouldHighlight = getTextSettings().getHighlightPolicy() != HighlightPolicy.DO_NOT_HIGHLIGHT;
134         if (shouldHighlight) {
135           final DocumentContent content = getContent();
136           final Document document = content.getDocument();
137
138           int start = 0;
139           int end = document.getTextLength();
140           TextDiffType type = getSide().select(TextDiffType.DELETED, TextDiffType.INSERTED);
141
142           myHighlighters.addAll(DiffDrawUtil.createHighlighter(getEditor(), start, end, type, false));
143
144           int startLine = 0;
145           int endLine = getLineCount(document);
146
147           if (startLine != endLine) {
148             myHighlighters.addAll(DiffDrawUtil.createLineMarker(getEditor(), startLine, type, SeparatorPlacement.TOP));
149             myHighlighters.addAll(DiffDrawUtil.createLineMarker(getEditor(), endLine - 1, type, SeparatorPlacement.BOTTOM));
150           }
151         }
152
153         myInitialScrollHelper.onRediff();
154       }
155     };
156   }
157
158
159   private void clearDiffPresentation() {
160     myPanel.resetNotifications();
161
162     for (RangeHighlighter highlighter : myHighlighters) {
163       highlighter.dispose();
164     }
165     myHighlighters.clear();
166   }
167
168   //
169   // Impl
170   //
171
172   private void doScrollToChange(final boolean animated) {
173     DiffUtil.moveCaret(getEditor(), 0);
174     DiffUtil.scrollEditor(getEditor(), 0, animated);
175   }
176
177   protected boolean doScrollToContext(@NotNull DiffNavigationContext context) {
178     if (getSide().isLeft()) return false;
179
180     AllLinesIterator allLinesIterator = new AllLinesIterator();
181     NavigationContextChecker checker2 = new NavigationContextChecker(allLinesIterator, context);
182     int line = checker2.contextMatchCheck();
183     if (line == -1) return false;
184
185     scrollToLine(line);
186     return true;
187   }
188
189   //
190   // Misc
191   //
192
193   @SuppressWarnings("MethodOverridesStaticMethodOfSuperclass")
194   public static boolean canShowRequest(@NotNull DiffContext context, @NotNull DiffRequest request) {
195     return OnesideTextDiffViewer.canShowRequest(context, request);
196   }
197
198   //
199   // Actions
200   //
201
202   private class MyReadOnlyLockAction extends TextDiffViewerUtil.EditorReadOnlyLockAction {
203     public MyReadOnlyLockAction() {
204       super(getContext(), getEditableEditors());
205     }
206   }
207
208   //
209   // Modification operations
210   //
211
212   private class MyHighlightPolicySettingAction extends TextDiffViewerUtil.HighlightPolicySettingAction {
213     public MyHighlightPolicySettingAction() {
214       super(getTextSettings());
215     }
216
217     @Override
218     protected void onSettingsChanged() {
219       rediff();
220     }
221   }
222
223   private class MyIgnorePolicySettingAction extends TextDiffViewerUtil.IgnorePolicySettingAction {
224     public MyIgnorePolicySettingAction() {
225       super(getTextSettings());
226     }
227
228     @Override
229     protected void onSettingsChanged() {
230       rediff();
231     }
232   }
233
234   //
235   // Scroll from annotate
236   //
237
238   private class AllLinesIterator implements Iterator<Pair<Integer, CharSequence>> {
239     @NotNull private final Document myDocument;
240     private int myLine = 0;
241
242     private AllLinesIterator() {
243       myDocument = getEditor().getDocument();
244     }
245
246     @Override
247     public boolean hasNext() {
248       return myLine < getLineCount(myDocument);
249     }
250
251     @Override
252     public Pair<Integer, CharSequence> next() {
253       int offset1 = myDocument.getLineStartOffset(myLine);
254       int offset2 = myDocument.getLineEndOffset(myLine);
255
256       CharSequence text = myDocument.getImmutableCharSequence().subSequence(offset1, offset2);
257
258       Pair<Integer, CharSequence> pair = new Pair<Integer, CharSequence>(myLine, text);
259       myLine++;
260
261       return pair;
262     }
263
264     @Override
265     public void remove() {
266       throw new UnsupportedOperationException();
267     }
268   }
269
270   //
271   // Helpers
272   //
273
274   @Nullable
275   @Override
276   public Object getData(@NonNls String dataId) {
277     if (DiffDataKeys.CURRENT_CHANGE_RANGE.is(dataId)) {
278       int lineCount = getLineCount(getEditor().getDocument());
279       return new LineRange(0, lineCount);
280     }
281     return super.getData(dataId);
282   }
283
284   private class MyInitialScrollHelper extends MyInitialScrollPositionHelper {
285     @Override
286     protected boolean doScrollToChange() {
287       if (myScrollToChange == null) return false;
288       SimpleOnesideDiffViewer.this.doScrollToChange(false);
289       return true;
290     }
291
292     @Override
293     protected boolean doScrollToFirstChange() {
294       SimpleOnesideDiffViewer.this.doScrollToChange(false);
295       return true;
296     }
297
298     @Override
299     protected boolean doScrollToContext() {
300       if (myNavigationContext == null) return false;
301       return SimpleOnesideDiffViewer.this.doScrollToContext(myNavigationContext);
302     }
303
304     @Override
305     protected boolean doScrollToPosition() {
306       if (myCaretPosition == null) return false;
307
308       LogicalPosition position = getSide().select(myCaretPosition);
309       getEditor().getCaretModel().moveToLogicalPosition(position);
310
311       if (myEditorsPosition != null && myEditorsPosition.isSame(position)) {
312         DiffUtil.scrollToPoint(getEditor(), myEditorsPosition.myPoints[0], false);
313       }
314       else {
315         DiffUtil.scrollToCaret(getEditor(), false);
316       }
317       return true;
318     }
319
320     @Nullable
321     @Override
322     protected LogicalPosition[] getCaretPositions() {
323       int index = getSide().getIndex();
324       int otherIndex = getSide().other().getIndex();
325
326       LogicalPosition[] carets = new LogicalPosition[2];
327       carets[index] = getEditor().getCaretModel().getLogicalPosition();
328       carets[otherIndex] = new LogicalPosition(0, 0);
329       return carets;
330     }
331   }
332 }