2 * Copyright 2000-2014 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 com.intellij.openapi.vcs.ex;
18 import com.intellij.diff.util.DiffUtil;
19 import com.intellij.openapi.application.Application;
20 import com.intellij.openapi.application.ApplicationManager;
21 import com.intellij.openapi.command.undo.UndoConstants;
22 import com.intellij.openapi.diagnostic.Logger;
23 import com.intellij.openapi.editor.Document;
24 import com.intellij.openapi.editor.event.DocumentAdapter;
25 import com.intellij.openapi.editor.event.DocumentEvent;
26 import com.intellij.openapi.editor.impl.DocumentImpl;
27 import com.intellij.openapi.editor.impl.DocumentMarkupModel;
28 import com.intellij.openapi.editor.markup.*;
29 import com.intellij.openapi.fileEditor.FileDocumentManager;
30 import com.intellij.openapi.fileEditor.FileEditor;
31 import com.intellij.openapi.fileEditor.FileEditorManager;
32 import com.intellij.openapi.progress.ProcessCanceledException;
33 import com.intellij.openapi.project.Project;
34 import com.intellij.openapi.util.Key;
35 import com.intellij.openapi.util.TextRange;
36 import com.intellij.openapi.util.registry.Registry;
37 import com.intellij.openapi.util.text.StringUtil;
38 import com.intellij.openapi.vcs.VcsBundle;
39 import com.intellij.openapi.vcs.changes.VcsDirtyScopeManager;
40 import com.intellij.openapi.vcs.history.VcsRevisionNumber;
41 import com.intellij.openapi.vfs.VirtualFile;
42 import com.intellij.ui.EditorNotificationPanel;
43 import com.intellij.util.containers.ContainerUtil;
44 import com.intellij.util.diff.FilesTooBigForDiffException;
45 import org.jetbrains.annotations.NotNull;
46 import org.jetbrains.annotations.Nullable;
48 import java.util.ArrayList;
49 import java.util.BitSet;
50 import java.util.Collections;
51 import java.util.List;
53 import static com.intellij.diff.util.DiffUtil.getLineCount;
59 public class LineStatusTracker {
60 public enum Mode {DEFAULT, SMART, SILENT}
62 public static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.ex.LineStatusTracker");
63 private static final Key<CanNotCalculateDiffPanel> PANEL_KEY =
64 new Key<CanNotCalculateDiffPanel>("LineStatusTracker.CanNotCalculateDiffPanel");
66 private final Object myLock = new Object();
67 private boolean myInitialized;
69 @NotNull private final Project myProject;
70 @NotNull private final Document myDocument;
71 @NotNull private final Document myVcsDocument;
72 @NotNull private final VirtualFile myVirtualFile;
74 @NotNull private final Application myApplication;
75 @NotNull private final FileEditorManager myFileEditorManager;
76 @NotNull private final VcsDirtyScopeManager myVcsDirtyScopeManager;
78 private MyDocumentListener myDocumentListener;
79 @Nullable private RevisionPack myBaseRevisionNumber;
81 private boolean mySuppressUpdate;
82 private boolean myBulkUpdate;
83 private boolean myAnathemaThrown;
84 private boolean myReleased;
86 @NotNull private Mode myMode;
88 @NotNull private List<Range> myRanges;
90 private LineStatusTracker(@NotNull final Document document,
91 @NotNull final Document vcsDocument,
92 @NotNull final Project project,
93 @NotNull final VirtualFile virtualFile,
94 @NotNull final Mode mode) {
95 myDocument = document;
96 myVcsDocument = vcsDocument;
98 myVirtualFile = virtualFile;
100 myApplication = ApplicationManager.getApplication();
101 myFileEditorManager = FileEditorManager.getInstance(myProject);
102 myVcsDirtyScopeManager = VcsDirtyScopeManager.getInstance(myProject);
106 myRanges = new ArrayList<Range>();
109 public void initialize(@NotNull final String vcsContent, @NotNull RevisionPack baseRevisionNumber) {
110 myApplication.assertIsDispatchThread();
112 synchronized (myLock) {
114 if (myReleased) return;
115 if (myBaseRevisionNumber != null && myBaseRevisionNumber.contains(baseRevisionNumber)) return;
117 myBaseRevisionNumber = baseRevisionNumber;
119 myVcsDocument.setReadOnly(false);
120 myVcsDocument.setText(vcsContent);
121 myVcsDocument.setReadOnly(true);
124 if (myDocumentListener == null) {
125 myDocumentListener = new MyDocumentListener();
126 myDocument.addDocumentListener(myDocumentListener);
130 myInitialized = true;
135 private void reinstallRanges() {
136 myApplication.assertIsDispatchThread();
138 synchronized (myLock) {
139 if (myReleased) return;
142 removeHighlightersFromMarkupModel();
144 myRanges = new RangesBuilder(myDocument, myVcsDocument, myMode).getRanges();
146 catch (FilesTooBigForDiffException e) {
150 for (final Range range : myRanges) {
151 range.setHighlighter(createHighlighter(range));
156 private void installAnathema() {
157 myAnathemaThrown = true;
158 final FileEditor[] editors = myFileEditorManager.getAllEditors(myVirtualFile);
159 for (FileEditor editor : editors) {
160 CanNotCalculateDiffPanel panel = editor.getUserData(PANEL_KEY);
162 final CanNotCalculateDiffPanel newPanel = new CanNotCalculateDiffPanel();
163 editor.putUserData(PANEL_KEY, newPanel);
164 myFileEditorManager.addTopComponent(editor, newPanel);
169 private void removeAnathema() {
170 if (!myAnathemaThrown) return;
171 myAnathemaThrown = false;
172 final FileEditor[] editors = myFileEditorManager.getEditors(myVirtualFile);
173 for (FileEditor editor : editors) {
174 final CanNotCalculateDiffPanel panel = editor.getUserData(PANEL_KEY);
176 myFileEditorManager.removeTopComponent(editor, panel);
177 editor.putUserData(PANEL_KEY, null);
182 public void setMode(@NotNull Mode mode) {
183 synchronized (myLock) {
184 if (myMode == mode) return;
191 private RangeHighlighter createHighlighter(@NotNull Range range) {
192 myApplication.assertIsDispatchThread();
194 LOG.assertTrue(!myReleased, "Already released");
196 if (myMode == Mode.SILENT) return null;
199 range.getLine1() >= getLineCount(myDocument) ? myDocument.getTextLength() : myDocument.getLineStartOffset(range.getLine1());
202 range.getLine2() >= getLineCount(myDocument) ? myDocument.getTextLength() : myDocument.getLineStartOffset(range.getLine2());
204 final TextAttributes attr = LineStatusTrackerDrawing.getAttributesFor(range);
205 final RangeHighlighter highlighter = DocumentMarkupModel.forDocument(myDocument, myProject, true)
206 .addRangeHighlighter(first, second, HighlighterLayer.FIRST - 1, attr, HighlighterTargetArea.LINES_IN_RANGE);
208 highlighter.setThinErrorStripeMark(true);
209 highlighter.setGreedyToLeft(true);
210 highlighter.setGreedyToRight(true);
211 highlighter.setLineMarkerRenderer(LineStatusTrackerDrawing.createRenderer(range, this));
212 highlighter.setEditorFilter(MarkupEditorFilterFactory.createIsNotDiffFilter());
214 final String tooltip;
215 if (range.getLine1() == range.getLine2()) {
216 if (range.getVcsLine1() + 1 == range.getVcsLine2()) {
217 tooltip = VcsBundle.message("tooltip.text.line.before.deleted", range.getLine1() + 1);
220 tooltip = VcsBundle.message("tooltip.text.lines.before.deleted", range.getLine1() + 1, range.getVcsLine2() - range.getVcsLine1());
223 else if (range.getLine1() + 1 == range.getLine2()) {
224 tooltip = VcsBundle.message("tooltip.text.line.changed", range.getLine1() + 1);
227 tooltip = VcsBundle.message("tooltip.text.lines.changed", range.getLine1() + 1, range.getLine2());
230 highlighter.setErrorStripeTooltip(tooltip);
234 public void release() {
235 synchronized (myLock) {
237 if (myDocumentListener != null) {
238 myDocument.removeDocumentListener(myDocumentListener);
241 if (myApplication.isDispatchThread()) {
243 removeHighlightersFromMarkupModel();
247 myApplication.invokeLater(new Runnable() {
251 removeHighlightersFromMarkupModel();
259 public Project getProject() {
264 public Document getDocument() {
269 public Document getVcsDocument() {
270 return myVcsDocument;
274 public VirtualFile getVirtualFile() {
275 return myVirtualFile;
279 public Mode getMode() {
283 public boolean isSilentMode() {
284 return myMode == Mode.SILENT;
288 public List<Range> getRanges() {
289 synchronized (myLock) {
290 return Collections.unmodifiableList(myRanges);
294 public void startBulkUpdate() {
295 synchronized (myLock) {
296 if (myReleased) return;
300 removeHighlightersFromMarkupModel();
304 private void removeHighlightersFromMarkupModel() {
305 myApplication.assertIsDispatchThread();
307 synchronized (myLock) {
308 for (Range range : myRanges) {
309 if (range.getHighlighter() != null) {
310 range.getHighlighter().dispose();
318 private void invalidateRanges() {
319 synchronized (myLock) {
320 for (Range range : myRanges) {
326 public void finishBulkUpdate() {
327 synchronized (myLock) {
328 if (myReleased) return;
330 myBulkUpdate = false;
335 private void markFileUnchanged() {
336 ApplicationManager.getApplication().invokeLater(new Runnable() {
339 FileDocumentManager.getInstance().saveDocument(myDocument);
341 synchronized (myLock) {
342 stillEmpty = myRanges.isEmpty();
345 // file was modified, and now it's not -> dirty local change
346 myVcsDirtyScopeManager.fileDirty(myVirtualFile);
352 private class MyDocumentListener extends DocumentAdapter {
353 // We have 3 document versions:
359 private int myBeforeChangedLines;
360 private int myBeforeTotalLines;
363 public void beforeDocumentChange(DocumentEvent e) {
364 myApplication.assertIsDispatchThread();
366 synchronized (myLock) {
367 if (myReleased) return;
368 if (myBulkUpdate || mySuppressUpdate || myAnathemaThrown || !myInitialized) return;
369 assert myDocument == e.getDocument();
372 myLine1 = myDocument.getLineNumber(e.getOffset());
373 if (e.getOldLength() == 0) {
374 myBeforeChangedLines = 1;
378 int line2 = myDocument.getLineNumber(e.getOffset() + e.getOldLength());
379 myBeforeChangedLines = line2 - line1 + 1;
382 myBeforeTotalLines = getLineCount(myDocument);
384 catch (ProcessCanceledException ignore) {
390 public void documentChanged(final DocumentEvent e) {
391 myApplication.assertIsDispatchThread();
393 synchronized (myLock) {
394 if (myReleased) return;
395 if (myBulkUpdate || mySuppressUpdate || myAnathemaThrown || !myInitialized) return;
396 assert myDocument == e.getDocument();
398 int afterChangedLines;
399 if (e.getNewLength() == 0) {
400 afterChangedLines = 1;
404 int line2 = myDocument.getLineNumber(e.getOffset() + e.getNewLength());
405 afterChangedLines = line2 - line1 + 1;
408 int linesShift = afterChangedLines - myBeforeChangedLines;
411 int line2 = line1 + myBeforeChangedLines;
413 int[] fixed = fixRanges(e, line1, line2);
417 doUpdateRanges(line1, line2, linesShift, myBeforeTotalLines);
423 private int[] fixRanges(@NotNull DocumentEvent e, int line1, int line2) {
424 CharSequence document = myDocument.getCharsSequence();
425 int offset = e.getOffset();
427 if (e.getOldLength() == 0 && e.getNewLength() != 0) {
428 if (StringUtil.endsWithChar(e.getNewFragment(), '\n') && isNewline(offset - 1, document)) {
429 return new int[]{line1, line2 - 1};
431 if (StringUtil.startsWithChar(e.getNewFragment(), '\n') && isNewline(offset + e.getNewLength(), document)) {
432 return new int[]{line1 + 1, line2};
435 if (e.getOldLength() != 0 && e.getNewLength() == 0) {
436 if (StringUtil.endsWithChar(e.getOldFragment(), '\n') && isNewline(offset - 1, document)) {
437 return new int[]{line1, line2 - 1};
439 if (StringUtil.startsWithChar(e.getOldFragment(), '\n') && isNewline(offset + e.getNewLength(), document)) {
440 return new int[]{line1 + 1, line2};
444 return new int[]{line1, line2};
447 private static boolean isNewline(int offset, @NotNull CharSequence sequence) {
448 if (offset < 0) return false;
449 if (offset >= sequence.length()) return false;
450 return sequence.charAt(offset) == '\n';
453 private void doUpdateRanges(int beforeChangedLine1,
454 int beforeChangedLine2,
456 int beforeTotalLines) {
457 List<Range> rangesBeforeChange = new ArrayList<Range>();
458 List<Range> rangesAfterChange = new ArrayList<Range>();
459 List<Range> changedRanges = new ArrayList<Range>();
461 sortRanges(beforeChangedLine1, beforeChangedLine2, linesShift, rangesBeforeChange, changedRanges, rangesAfterChange);
463 Range firstChangedRange = ContainerUtil.getFirstItem(changedRanges);
464 Range lastChangedRange = ContainerUtil.getLastItem(changedRanges);
466 if (firstChangedRange != null && firstChangedRange.getLine1() < beforeChangedLine1) {
467 beforeChangedLine1 = firstChangedRange.getLine1();
469 if (lastChangedRange != null && lastChangedRange.getLine2() > beforeChangedLine2) {
470 beforeChangedLine2 = lastChangedRange.getLine2();
473 doUpdateRanges(beforeChangedLine1, beforeChangedLine2, linesShift, beforeTotalLines,
474 rangesBeforeChange, changedRanges, rangesAfterChange);
477 private void doUpdateRanges(int beforeChangedLine1,
478 int beforeChangedLine2,
479 int linesShift, // before -> after
480 int beforeTotalLines,
481 @NotNull List<Range> rangesBefore,
482 @NotNull List<Range> changedRanges,
483 @NotNull List<Range> rangesAfter) {
485 int vcsTotalLines = getLineCount(myVcsDocument);
487 Range lastRangeBefore = ContainerUtil.getLastItem(rangesBefore);
488 Range firstRangeAfter = ContainerUtil.getFirstItem(rangesAfter);
490 //noinspection UnnecessaryLocalVariable
491 int afterChangedLine1 = beforeChangedLine1;
492 int afterChangedLine2 = beforeChangedLine2 + linesShift;
494 int vcsLine1 = getVcsLine1(lastRangeBefore, beforeChangedLine1);
495 int vcsLine2 = getVcsLine2(firstRangeAfter, beforeChangedLine2, beforeTotalLines, vcsTotalLines);
497 List<Range> newChangedRanges = getNewChangedRanges(afterChangedLine1, afterChangedLine2, vcsLine1, vcsLine2);
499 shiftRanges(rangesAfter, linesShift);
501 if (!changedRanges.equals(newChangedRanges)) {
502 replaceRanges(changedRanges, newChangedRanges);
504 myRanges = new ArrayList<Range>(rangesBefore.size() + newChangedRanges.size() + rangesAfter.size());
506 myRanges.addAll(rangesBefore);
507 myRanges.addAll(newChangedRanges);
508 myRanges.addAll(rangesAfter);
510 for (Range range : myRanges) {
511 if (!range.hasHighlighter()) range.setHighlighter(createHighlighter(range));
514 if (myRanges.isEmpty()) {
519 catch (ProcessCanceledException ignore) {
521 catch (FilesTooBigForDiffException e1) {
523 removeHighlightersFromMarkupModel();
527 private static int getVcsLine1(@Nullable Range range, int line) {
528 return range == null ? line : line + range.getVcsLine2() - range.getLine2();
531 private static int getVcsLine2(@Nullable Range range, int line, int totalLinesBefore, int totalLinesAfter) {
532 return range == null ? totalLinesAfter - totalLinesBefore + line : line + range.getVcsLine1() - range.getLine1();
535 private List<Range> getNewChangedRanges(int changedLine1, int changedLine2, int vcsLine1, int vcsLine2)
536 throws FilesTooBigForDiffException {
538 if (changedLine1 == changedLine2 && vcsLine1 == vcsLine2) {
539 return Collections.emptyList();
541 if (changedLine1 == changedLine2) {
542 return Collections.singletonList(new Range(changedLine1, changedLine2, vcsLine1, vcsLine2));
544 if (vcsLine1 == vcsLine2) {
545 return Collections.singletonList(new Range(changedLine1, changedLine2, vcsLine1, vcsLine2));
548 List<String> lines = new DocumentWrapper(myDocument).getLines(changedLine1, changedLine2 - 1);
549 List<String> vcsLines = new DocumentWrapper(myVcsDocument).getLines(vcsLine1, vcsLine2 - 1);
551 return new RangesBuilder(lines, vcsLines, changedLine1, vcsLine1, myMode).getRanges();
554 private void replaceRanges(@NotNull List<Range> rangesInChange, @NotNull List<Range> newRangesInChange) {
555 for (Range range : rangesInChange) {
556 if (range.getHighlighter() != null) {
557 range.getHighlighter().dispose();
559 range.setHighlighter(null);
562 for (Range range : newRangesInChange) {
563 range.setHighlighter(createHighlighter(range));
567 private static void shiftRanges(@NotNull List<Range> rangesAfterChange, int shift) {
568 for (final Range range : rangesAfterChange) {
573 private void sortRanges(int beforeChangedLine1,
574 int beforeChangedLine2,
576 @NotNull List<Range> rangesBeforeChange,
577 @NotNull List<Range> changedRanges,
578 @NotNull List<Range> rangesAfterChange) {
579 if (!Registry.is("diff.status.tracker.skip.spaces")) {
580 for (Range range : myRanges) {
581 if (range.getLine2() < beforeChangedLine1) {
582 rangesBeforeChange.add(range);
584 else if (range.getLine1() > beforeChangedLine2) {
585 rangesAfterChange.add(range);
588 changedRanges.add(range);
594 int firstAfter = myRanges.size();
595 for (int i = 0; i < myRanges.size(); i++) {
596 Range range = myRanges.get(i);
598 if (range.getLine2() < beforeChangedLine1) {
601 else if (range.getLine1() > beforeChangedLine2) {
608 // Expand on ranges, that are separated from changes only by empty/whitespaces lines
609 // This is needed to reduce amount of confusing cases, when changed blocks are matched wrong due to matched empty lines between them
610 // TODO: try to simplify logic, it's too high change that current one is broken somehow
611 CharSequence sequence = myDocument.getCharsSequence();
612 int lineCount = getLineCount(myDocument);
615 if (lastBefore == -1) break;
617 if (lastBefore < myRanges.size() - 1 && firstAfter - lastBefore > 1) {
618 Range firstChangedRange = myRanges.get(lastBefore + 1);
619 if (firstChangedRange.getLine1() < beforeChangedLine1) {
620 beforeChangedLine1 = firstChangedRange.getLine1();
624 if (beforeChangedLine1 < 0) break;
625 if (beforeChangedLine1 >= lineCount) break;
626 int offset1 = myDocument.getLineStartOffset(beforeChangedLine1) - 2;
629 while (offset1 > 0) {
630 char c = sequence.charAt(offset1);
631 if (!StringUtil.isWhiteSpace(c)) break;
632 if (c == '\n') deltaLines++;
636 if (deltaLines == 0) break;
637 beforeChangedLine1 -= deltaLines;
639 if (myRanges.get(lastBefore).getLine2() < beforeChangedLine1) break;
640 while (lastBefore != -1 && myRanges.get(lastBefore).getLine2() >= beforeChangedLine1) {
646 if (firstAfter == myRanges.size()) break;
648 if (firstAfter > 0 && firstAfter - lastBefore > 1) {
649 Range lastChangedRange = myRanges.get(firstAfter - 1);
650 if (lastChangedRange.getLine2() > beforeChangedLine2) {
651 beforeChangedLine2 = lastChangedRange.getLine2();
655 // TODO: "afterChangedLine2 >= getLineCount(myDocument)" shouldn't ever be true, but it is sometimes for some reason
656 int afterChangedLine2 = beforeChangedLine2 + linesShift - 1;
657 if (afterChangedLine2 < 0) break;
658 if (afterChangedLine2 >= lineCount) break;
659 int offset2 = myDocument.getLineEndOffset(afterChangedLine2) + 1;
662 while (offset2 < sequence.length()) {
663 char c = sequence.charAt(offset2);
664 if (!StringUtil.isWhiteSpace(c)) break;
665 if (c == '\n') deltaLines++;
669 if (deltaLines == 0) break;
670 beforeChangedLine2 += deltaLines;
672 if (myRanges.get(firstAfter).getLine1() > beforeChangedLine2) break;
673 while (firstAfter != myRanges.size() && myRanges.get(firstAfter).getLine1() <= beforeChangedLine2) {
679 for (int i = 0; i < myRanges.size(); i++) {
680 Range range = myRanges.get(i);
681 if (i <= lastBefore) {
682 rangesBeforeChange.add(range);
684 else if (i >= firstAfter) {
685 rangesAfterChange.add(range);
688 changedRanges.add(range);
695 public Range getNextRange(Range range) {
696 synchronized (myLock) {
697 final int index = myRanges.indexOf(range);
698 if (index == myRanges.size() - 1) return null;
699 return myRanges.get(index + 1);
704 public Range getPrevRange(Range range) {
705 synchronized (myLock) {
706 final int index = myRanges.indexOf(range);
707 if (index <= 0) return null;
708 return myRanges.get(index - 1);
713 public Range getNextRange(int line) {
714 synchronized (myLock) {
715 for (Range range : myRanges) {
716 if (line < range.getLine2() && !range.isSelectedByLine(line)) {
725 public Range getPrevRange(int line) {
726 synchronized (myLock) {
727 for (int i = myRanges.size() - 1; i >= 0; i--) {
728 Range range = myRanges.get(i);
729 if (line > range.getLine1() && !range.isSelectedByLine(line)) {
738 public Range getRangeForLine(int line) {
739 synchronized (myLock) {
740 for (final Range range : myRanges) {
741 if (range.isSelectedByLine(line)) return range;
747 private void doRollbackRange(@NotNull Range range) {
748 DiffUtil.applyModification(myDocument, range.getLine1(), range.getLine2(), myVcsDocument, range.getVcsLine1(), range.getVcsLine2());
750 markLinesUnchanged(range.getLine1(), range.getLine1() + range.getVcsLine2() - range.getVcsLine1());
753 private void markLinesUnchanged(int startLine, int endLine) {
754 if (myDocument.getTextLength() == 0) return; // empty document has no lines
755 ((DocumentImpl)myDocument).clearLineModificationFlags(startLine, endLine);
758 public void rollbackChanges(@NotNull Range range) {
759 myApplication.assertWriteAccessAllowed();
761 synchronized (myLock) {
762 if (myBulkUpdate) return;
764 if (!range.isValid()) {
765 LOG.warn("Rollback of invalid range");
769 doRollbackRange(range);
773 public void rollbackChanges(@NotNull final BitSet lines) {
774 runBulkRollback(new Runnable() {
781 for (Range range : myRanges) {
782 if (!range.isValid()) {
783 LOG.warn("Rollback of invalid range");
787 boolean check = DiffUtil.isSelectedByLine(lines, range.getLine1(), range.getLine2());
795 Range shiftedRange = new Range(range);
796 shiftedRange.shift(shift);
798 doRollbackRange(shiftedRange);
800 shift += (range.getVcsLine2() - range.getVcsLine1()) - (range.getLine2() - range.getLine1());
805 int beforeChangedLine1 = first.getLine1();
806 int beforeChangedLine2 = last.getLine2();
808 int beforeTotalLines = getLineCount(myDocument) - shift;
810 doUpdateRanges(beforeChangedLine1, beforeChangedLine2, shift, beforeTotalLines);
816 public void rollbackAllChanges() {
817 runBulkRollback(new Runnable() {
820 myDocument.setText(myVcsDocument.getText());
823 removeHighlightersFromMarkupModel();
830 private void runBulkRollback(@NotNull Runnable task) {
831 myApplication.assertWriteAccessAllowed();
833 synchronized (myLock) {
834 if (myBulkUpdate) return;
837 mySuppressUpdate = true;
845 catch (RuntimeException e) {
850 mySuppressUpdate = false;
856 public CharSequence getCurrentContent(@NotNull Range range) {
857 synchronized (myLock) {
858 TextRange textRange = getCurrentTextRange(range);
859 final int startOffset = textRange.getStartOffset();
860 final int endOffset = textRange.getEndOffset();
861 return myDocument.getImmutableCharSequence().subSequence(startOffset, endOffset);
866 public CharSequence getVcsContent(@NotNull Range range) {
867 synchronized (myLock) {
868 TextRange textRange = getVcsTextRange(range);
869 final int startOffset = textRange.getStartOffset();
870 final int endOffset = textRange.getEndOffset();
871 return myVcsDocument.getImmutableCharSequence().subSequence(startOffset, endOffset);
876 public TextRange getCurrentTextRange(@NotNull Range range) {
877 myApplication.assertReadAccessAllowed();
879 synchronized (myLock) {
880 if (!range.isValid()) {
881 LOG.warn("Current TextRange of invalid range");
884 return DiffUtil.getLinesRange(myDocument, range.getLine1(), range.getLine2());
889 public TextRange getVcsTextRange(@NotNull Range range) {
890 synchronized (myLock) {
891 if (!range.isValid()) {
892 LOG.warn("Vcs TextRange of invalid range");
895 return DiffUtil.getLinesRange(myVcsDocument, range.getVcsLine1(), range.getVcsLine2());
899 public static LineStatusTracker createOn(@NotNull VirtualFile virtualFile, @NotNull final Document document, final Project project,
900 @NotNull Mode mode) {
901 final Document vcsDocument = new DocumentImpl("", true);
902 vcsDocument.putUserData(UndoConstants.DONT_RECORD_UNDO, Boolean.TRUE);
903 return new LineStatusTracker(document, vcsDocument, project, virtualFile, mode);
906 public static class RevisionPack {
907 private final long myNumber;
908 private final VcsRevisionNumber myRevision;
910 public RevisionPack(long number, VcsRevisionNumber revision) {
912 myRevision = revision;
915 public long getNumber() {
919 public VcsRevisionNumber getRevision() {
923 public boolean contains(final RevisionPack previous) {
924 if (myRevision.equals(previous.getRevision()) && !myRevision.equals(VcsRevisionNumber.NULL)) return true;
925 return myNumber >= previous.getNumber();
929 public boolean equals(Object o) {
930 if (this == o) return true;
931 if (o == null || getClass() != o.getClass()) return false;
933 RevisionPack that = (RevisionPack)o;
935 return myRevision.equals(that.getRevision());
939 public int hashCode() {
940 return myRevision.hashCode();
944 private static class CanNotCalculateDiffPanel extends EditorNotificationPanel {
945 public CanNotCalculateDiffPanel() {
946 myLabel.setText("Can not highlight changed lines. File is too big and there are too many changes.");