mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
VCS: line status tracker installing/removing fix. All changes to line status trackers model (that in LineStatusTrackerManager) will be done under write action. + dummy implementation if lst is forbidden. + just translate into current line in VCS annotation, no false tracker creation. + fix for "Diff for change" when last symbol in line is added
This commit is contained in:
@@ -27,6 +27,7 @@
|
||||
<implementation-class>com.intellij.openapi.vcs.impl.VcsDirectoryMappingStorage</implementation-class>
|
||||
</component>
|
||||
<component>
|
||||
<interface-class>com.intellij.openapi.vcs.impl.LineStatusTrackerManagerI</interface-class>
|
||||
<implementation-class>com.intellij.openapi.vcs.impl.LineStatusTrackerManager</implementation-class>
|
||||
<option name="workspace" value="true"/>
|
||||
<skipForDefaultProject/>
|
||||
|
||||
@@ -20,6 +20,7 @@ import com.intellij.openapi.actionSystem.Presentation;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.vcs.ex.LineStatusTracker;
|
||||
import com.intellij.openapi.vcs.ex.LineStatusTrackerDrawing;
|
||||
import com.intellij.openapi.vcs.ex.Range;
|
||||
import com.intellij.openapi.vcs.impl.LineStatusTrackerManager;
|
||||
|
||||
@@ -92,7 +93,8 @@ public abstract class ShowChangeMarkerAction extends AbstractVcsAction {
|
||||
LineStatusTracker lineStatusTracker = myChangeMarkerContext.getLineStatusTracker(context);
|
||||
Range range = myChangeMarkerContext.getRange(context);
|
||||
|
||||
lineStatusTracker.moveToRange(range, editor);
|
||||
|
||||
LineStatusTrackerDrawing.moveToRange(range, editor, lineStatusTracker);
|
||||
}
|
||||
|
||||
protected interface ChangeMarkerContext {
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2000-2010 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.openapi.vcs.ex;
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
/**
|
||||
* @author irengrig
|
||||
*/
|
||||
public abstract class BaseLineStatusRangeAction extends AnAction implements DumbAware {
|
||||
protected final LineStatusTracker myLineStatusTracker;
|
||||
protected final Range myRange;
|
||||
|
||||
BaseLineStatusRangeAction(final String text, final Icon icon, final LineStatusTracker lineStatusTracker, final Range range) {
|
||||
super(text, null, icon);
|
||||
myLineStatusTracker = lineStatusTracker;
|
||||
myRange = range;
|
||||
}
|
||||
|
||||
public void update(final AnActionEvent e) {
|
||||
e.getPresentation().setEnabled(isEnabled());
|
||||
}
|
||||
|
||||
public abstract boolean isEnabled();
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2000-2010 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.openapi.vcs.ex;
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.ide.CopyPasteManager;
|
||||
import com.intellij.openapi.util.IconLoader;
|
||||
import com.intellij.openapi.vcs.VcsBundle;
|
||||
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
|
||||
/**
|
||||
* @author irengrig
|
||||
*/
|
||||
public class CopyLineStatusRangeAction extends BaseLineStatusRangeAction {
|
||||
CopyLineStatusRangeAction(final LineStatusTracker lineStatusTracker, final Range range) {
|
||||
super(VcsBundle.message("action.name.copy.old.text"), IconLoader.getIcon("/actions/copy.png"), lineStatusTracker, range);
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return Range.DELETED == myRange.getType() || Range.MODIFIED == myRange.getType();
|
||||
}
|
||||
|
||||
public void actionPerformed(final AnActionEvent e) {
|
||||
final String content = myLineStatusTracker.getUpToDateContent(myRange);
|
||||
CopyPasteManager.getInstance().setContents(new StringSelection(content));
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.intellij.openapi.vcs.ex;
|
||||
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -53,10 +54,8 @@ public class DocumentWrapper {
|
||||
return result;
|
||||
}
|
||||
|
||||
private String getLine(int i) {
|
||||
int lineStartOffset = myDocument.getLineStartOffset(i);
|
||||
String line = myDocument.getCharsSequence().subSequence(lineStartOffset, myDocument.getLineEndOffset(i)).toString();
|
||||
return line;
|
||||
private String getLine(final int i) {
|
||||
return myDocument.getText(new TextRange(myDocument.getLineStartOffset(i), myDocument.getLineEndOffset(i)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,135 +15,111 @@
|
||||
*/
|
||||
package com.intellij.openapi.vcs.ex;
|
||||
|
||||
import com.intellij.codeInsight.hint.EditorFragmentComponent;
|
||||
import com.intellij.codeInsight.hint.HintManagerImpl;
|
||||
import com.intellij.openapi.actionSystem.*;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.command.undo.UndoManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.diff.*;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.editor.ScrollType;
|
||||
import com.intellij.openapi.editor.colors.EditorColors;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsScheme;
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey;
|
||||
import com.intellij.openapi.editor.event.DocumentAdapter;
|
||||
import com.intellij.openapi.editor.event.DocumentEvent;
|
||||
import com.intellij.openapi.editor.ex.DocumentEx;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.ex.EditorGutterComponentEx;
|
||||
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
|
||||
import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory;
|
||||
import com.intellij.openapi.editor.markup.*;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.ide.CopyPasteManager;
|
||||
import com.intellij.openapi.project.DumbAware;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.IconLoader;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vcs.VcsBundle;
|
||||
import com.intellij.openapi.vcs.actions.ShowNextChangeMarkerAction;
|
||||
import com.intellij.openapi.vcs.actions.ShowPrevChangeMarkerAction;
|
||||
import com.intellij.openapi.vfs.ReadonlyStatusHandler;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.ui.ColoredSideBorder;
|
||||
import com.intellij.ui.HintListener;
|
||||
import com.intellij.ui.LightweightHint;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
/**
|
||||
* @author irengrig
|
||||
* author: lesya
|
||||
*/
|
||||
public class LineStatusTracker {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.ex.LineStatusTracker");
|
||||
// true -> have contents
|
||||
private boolean myBaseLoaded;
|
||||
|
||||
private final Document myDocument;
|
||||
private final Document myUpToDateDocument;
|
||||
@SuppressWarnings({"FieldAccessedSynchronizedAndUnsynchronized"}) private List<Range> myRanges = new ArrayList<Range>();
|
||||
|
||||
private List<Range> myRanges;
|
||||
|
||||
private final Project myProject;
|
||||
@SuppressWarnings({"FieldAccessedSynchronizedAndUnsynchronized"}) private int myHighlighterCount = 0;
|
||||
|
||||
private MyDocumentListener myDocumentListener;
|
||||
|
||||
private boolean myIsReleased = false;
|
||||
private boolean myIsInitialized = false;
|
||||
private boolean myBulkUpdate;
|
||||
private final Application myApplication;
|
||||
|
||||
public LineStatusTracker(Document document, Document upToDateDocument, Project project) {
|
||||
private LineStatusTracker(final Document document, final Document upToDateDocument, final Project project) {
|
||||
myApplication = ApplicationManager.getApplication();
|
||||
myDocument = document;
|
||||
myUpToDateDocument = upToDateDocument;
|
||||
myUpToDateDocument.putUserData(UndoManager.DONT_RECORD_UNDO, Boolean.TRUE);
|
||||
myProject = project;
|
||||
myBaseLoaded = false;
|
||||
myRanges = new ArrayList<Range>();
|
||||
}
|
||||
|
||||
public synchronized void initialize(@NotNull final String upToDateContent) {
|
||||
if (myIsReleased) return;
|
||||
LOG.assertTrue(!myIsInitialized);
|
||||
try {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
public void run() {
|
||||
public void initialize(@NotNull final String upToDateContent) {
|
||||
ApplicationManager.getApplication().isReadAccessAllowed();
|
||||
LOG.assertTrue(!myBaseLoaded);
|
||||
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
myUpToDateDocument.setReadOnly(false);
|
||||
myUpToDateDocument.replaceString(0, myUpToDateDocument.getTextLength(), StringUtil.convertLineSeparators(upToDateContent));
|
||||
myUpToDateDocument.setReadOnly(true);
|
||||
reinstallRanges();
|
||||
|
||||
if (myDocumentListener == null) {
|
||||
myDocumentListener = new MyDocumentListener();
|
||||
myDocument.addDocumentListener(myDocumentListener);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
myUpToDateDocument.setReadOnly(true);
|
||||
reinstallRanges();
|
||||
|
||||
myDocumentListener = new MyDocumentListener();
|
||||
myDocument.addDocumentListener(myDocumentListener);
|
||||
}
|
||||
finally {
|
||||
myIsInitialized = true;
|
||||
}
|
||||
finally {
|
||||
myBaseLoaded = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private synchronized void reinstallRanges() {
|
||||
reinstallRanges(new RangesBuilder(myDocument, myUpToDateDocument).getRanges());
|
||||
}
|
||||
private void reinstallRanges() {
|
||||
myApplication.assertWriteAccessAllowed();
|
||||
|
||||
private void reinstallRanges(List<Range> ranges) {
|
||||
removeHighlighters();
|
||||
myRanges = ranges;
|
||||
addHighlighters();
|
||||
}
|
||||
|
||||
private void addHighlighters() {
|
||||
for (Range range : myRanges) {
|
||||
if (!range.hasHighlighter()) range.setHighlighter(createHighlighter(range));
|
||||
removeHighlightersFromMarkupModel();
|
||||
myRanges = new RangesBuilder(myDocument, myUpToDateDocument).getRanges();
|
||||
for (final Range range : myRanges) {
|
||||
range.setHighlighter(createHighlighter(range));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"AutoBoxing"})
|
||||
synchronized private RangeHighlighter createHighlighter(Range range) {
|
||||
private RangeHighlighter createHighlighter(final Range range) {
|
||||
int first =
|
||||
range.getOffset1() >= myDocument.getLineCount() ? myDocument.getTextLength() : myDocument.getLineStartOffset(range.getOffset1());
|
||||
|
||||
int second =
|
||||
range.getOffset2() >= myDocument.getLineCount() ? myDocument.getTextLength() : myDocument.getLineStartOffset(range.getOffset2());
|
||||
|
||||
|
||||
RangeHighlighter highlighter = myDocument.getMarkupModel(myProject)
|
||||
final RangeHighlighter highlighter = myDocument.getMarkupModel(myProject)
|
||||
.addRangeHighlighter(first, second, HighlighterLayer.FIRST - 1, null, HighlighterTargetArea.LINES_IN_RANGE);
|
||||
myHighlighterCount++;
|
||||
TextAttributes attr = getAttributesFor(range);
|
||||
final TextAttributes attr = LineStatusTrackerDrawing.getAttributesFor(range);
|
||||
highlighter.setErrorStripeMarkColor(attr.getErrorStripeColor());
|
||||
highlighter.setThinErrorStripeMark(true);
|
||||
highlighter.setGreedyToLeft(true);
|
||||
highlighter.setGreedyToRight(true);
|
||||
highlighter.setLineMarkerRenderer(createRenderer(range));
|
||||
highlighter.setLineMarkerRenderer(LineStatusTrackerDrawing.createRenderer(range, this));
|
||||
highlighter.setEditorFilter(MarkupEditorFilterFactory.createIsNotDiffFilter());
|
||||
final int line1 = myDocument.getLineNumber(first);
|
||||
final int line2 = myDocument.getLineNumber(second);
|
||||
@@ -159,141 +135,17 @@ public class LineStatusTracker {
|
||||
return highlighter;
|
||||
}
|
||||
|
||||
|
||||
private void removeHighlighters() {
|
||||
for (Range oldRange : myRanges) {
|
||||
removeHighlighter(oldRange.getHighlighter());
|
||||
oldRange.setHighlighter(null);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized void removeHighlighter(RangeHighlighter highlighter) {
|
||||
if (highlighter == null) return;
|
||||
MarkupModel markupModel = myDocument.getMarkupModel(myProject);
|
||||
//noinspection ConstantConditions
|
||||
if (markupModel == null) return;
|
||||
markupModel.removeHighlighter(highlighter);
|
||||
myHighlighterCount--;
|
||||
}
|
||||
|
||||
private static TextAttributesKey getDiffColor(Range range) {
|
||||
switch (range.getType()) {
|
||||
case Range.INSERTED:
|
||||
return DiffColors.DIFF_INSERTED;
|
||||
case Range.DELETED:
|
||||
return DiffColors.DIFF_DELETED;
|
||||
case Range.MODIFIED:
|
||||
return DiffColors.DIFF_MODIFIED;
|
||||
default:
|
||||
assert false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static TextAttributesKey getEditorColorNameFor(Range range) {
|
||||
switch (range.getType()) {
|
||||
case Range.MODIFIED:
|
||||
return DiffColors.DIFF_MODIFIED;
|
||||
case Range.DELETED:
|
||||
return DiffColors.DIFF_DELETED;
|
||||
default:
|
||||
return DiffColors.DIFF_INSERTED;
|
||||
}
|
||||
}
|
||||
|
||||
private static TextAttributes getAttributesFor(Range range) {
|
||||
final EditorColorsScheme globalScheme = EditorColorsManager.getInstance().getGlobalScheme();
|
||||
final Color stripeColor = globalScheme.getAttributes(getEditorColorNameFor(range)).getErrorStripeColor();
|
||||
final TextAttributes textAttributes = new TextAttributes(null, stripeColor, null, EffectType.BOXED, Font.PLAIN);
|
||||
textAttributes.setErrorStripeColor(stripeColor);
|
||||
return textAttributes;
|
||||
}
|
||||
|
||||
private static void paintGutterFragment(Editor editor, Graphics g, Rectangle r, TextAttributesKey diffAttributeKey) {
|
||||
final EditorGutterComponentEx gutter = ((EditorEx)editor).getGutterComponentEx();
|
||||
final Color stripeColor = editor.getColorsScheme().getAttributes(diffAttributeKey).getErrorStripeColor();
|
||||
g.setColor(brighter(stripeColor));
|
||||
|
||||
int endX = gutter.getWhitespaceSeparatorOffset();
|
||||
int x = r.x + r.width - 2;
|
||||
int width = endX - x;
|
||||
if (r.height > 0) {
|
||||
g.fillRect(x, r.y + 2, width, r.height - 4);
|
||||
g.setColor(gutter.getOutlineColor(false));
|
||||
UIUtil.drawLine(g, x, r.y + 2, x + width, r.y + 2);
|
||||
UIUtil.drawLine(g, x, r.y + 2, x, r.y + r.height - 3);
|
||||
UIUtil.drawLine(g, x, r.y + r.height - 3, x + width, r.y + r.height - 3);
|
||||
}
|
||||
else {
|
||||
int[] xPoints = new int[]{x,
|
||||
x,
|
||||
x + width - 1};
|
||||
int[] yPoints = new int[]{r.y - 4,
|
||||
r.y + 4,
|
||||
r.y};
|
||||
g.fillPolygon(xPoints, yPoints, 3);
|
||||
|
||||
g.setColor(gutter.getOutlineColor(false));
|
||||
g.drawPolygon(xPoints, yPoints, 3);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Color brighter(final Color color) {
|
||||
if (color == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final float[] hsbStripeColor = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null);
|
||||
|
||||
if (hsbStripeColor[1] < 0.02f) {
|
||||
// color is grey
|
||||
hsbStripeColor[2] = Math.min(1.0f, hsbStripeColor[2] * 1.3f);
|
||||
} else {
|
||||
// let's decrease color saturation
|
||||
hsbStripeColor[1] *= 0.3;
|
||||
|
||||
// max brightness
|
||||
hsbStripeColor[2] = 1.0f;
|
||||
}
|
||||
return Color.getHSBColor(hsbStripeColor[0], hsbStripeColor[1], hsbStripeColor[2]);
|
||||
}
|
||||
|
||||
private LineMarkerRenderer createRenderer(final Range range) {
|
||||
return new ActiveGutterRenderer() {
|
||||
public void paint(Editor editor, Graphics g, Rectangle r) {
|
||||
paintGutterFragment(editor, g, r, getDiffColor(range));
|
||||
public void release() {
|
||||
myApplication.runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (myDocumentListener != null) {
|
||||
myDocument.removeDocumentListener(myDocumentListener);
|
||||
}
|
||||
removeHighlightersFromMarkupModel();
|
||||
myRanges.clear();
|
||||
}
|
||||
|
||||
public void doAction(Editor editor, MouseEvent e) {
|
||||
e.consume();
|
||||
JComponent comp = (JComponent)e.getComponent(); // shall be EditorGutterComponent, cast is safe.
|
||||
JLayeredPane layeredPane = comp.getRootPane().getLayeredPane();
|
||||
Point point = SwingUtilities.convertPoint(comp, ((EditorEx)editor).getGutterComponentEx().getWidth(), e.getY(), layeredPane);
|
||||
showActiveHint(range, editor, point);
|
||||
}
|
||||
|
||||
public boolean canDoAction(final MouseEvent e) {
|
||||
final EditorGutterComponentEx gutter = (EditorGutterComponentEx)e.getComponent();
|
||||
return e.getX() > gutter.getLineMarkerAreaOffset() + gutter.getIconsAreaWidth();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public synchronized void release() {
|
||||
try {
|
||||
if (!myIsInitialized) return;
|
||||
LOG.assertTrue(!myIsReleased);
|
||||
|
||||
removeHighlighters();
|
||||
if (myDocumentListener != null) {
|
||||
myDocument.removeDocumentListener(myDocumentListener);
|
||||
myDocumentListener = null;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
myIsReleased = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Document getDocument() {
|
||||
@@ -305,6 +157,8 @@ public class LineStatusTracker {
|
||||
}
|
||||
|
||||
public List<Range> getRanges() {
|
||||
myApplication.assertReadAccessAllowed();
|
||||
|
||||
return myRanges;
|
||||
}
|
||||
|
||||
@@ -313,19 +167,56 @@ public class LineStatusTracker {
|
||||
}
|
||||
|
||||
public void startBulkUpdate() {
|
||||
myBulkUpdate = true;
|
||||
for (Range oldRange : myRanges) {
|
||||
removeHighlighter(oldRange.getHighlighter());
|
||||
oldRange.setHighlighter(null);
|
||||
myApplication.runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myBulkUpdate = true;
|
||||
removeHighlightersFromMarkupModel();
|
||||
myRanges.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void removeHighlightersFromMarkupModel() {
|
||||
final MarkupModel markupModel = myDocument.getMarkupModel(myProject);
|
||||
for (Range range : myRanges) {
|
||||
markupModel.removeHighlighter(range.getHighlighter());
|
||||
}
|
||||
myRanges.clear();
|
||||
}
|
||||
|
||||
public void finishBulkUpdate() {
|
||||
if (myBulkUpdate) {
|
||||
myBulkUpdate = false;
|
||||
reinstallRanges();
|
||||
}
|
||||
myApplication.runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myBulkUpdate = false;
|
||||
reinstallRanges();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if was cleared and base revision contents load should be started
|
||||
* false -> load was already started; after contents is loaded,
|
||||
*/
|
||||
public boolean resetForBaseRevisionLoad() {
|
||||
return myApplication.runReadAction(new Computable<Boolean>() {
|
||||
@Override
|
||||
public Boolean compute() {
|
||||
if (!myBaseLoaded) return false;
|
||||
return myApplication.runWriteAction(new Computable<Boolean>() {
|
||||
@Override
|
||||
public Boolean compute() {
|
||||
myUpToDateDocument.setReadOnly(false);
|
||||
myUpToDateDocument.setText("");
|
||||
myUpToDateDocument.setReadOnly(true);
|
||||
removeHighlightersFromMarkupModel();
|
||||
myRanges.clear();
|
||||
myBaseLoaded = false;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private class MyDocumentListener extends DocumentAdapter {
|
||||
@@ -336,7 +227,9 @@ public class LineStatusTracker {
|
||||
private int myLinesBeforeChange;
|
||||
|
||||
public void beforeDocumentChange(DocumentEvent e) {
|
||||
if (myBulkUpdate) return;
|
||||
if (myBulkUpdate || (! myBaseLoaded)) return;
|
||||
myApplication.assertWriteAccessAllowed();
|
||||
|
||||
myFirstChangedLine = myDocument.getLineNumber(e.getOffset());
|
||||
myLastChangedLine = myDocument.getLineNumber(e.getOffset() + e.getOldLength());
|
||||
if (StringUtil.endsWithChar(e.getOldFragment(), '\n')) myLastChangedLine++;
|
||||
@@ -382,14 +275,15 @@ public class LineStatusTracker {
|
||||
}
|
||||
|
||||
public void documentChanged(DocumentEvent e) {
|
||||
if (myBulkUpdate) return;
|
||||
if (myBulkUpdate || (! myBaseLoaded)) return;
|
||||
myApplication.assertWriteAccessAllowed();
|
||||
|
||||
int line = myDocument.getLineNumber(e.getOffset() + e.getNewLength());
|
||||
int linesAfterChange = line - myDocument.getLineNumber(e.getOffset());
|
||||
int linesShift = linesAfterChange - myLinesBeforeChange;
|
||||
|
||||
List<Range> rangesAfterChange = getRangesAfter(myLastChangedLine);
|
||||
List<Range> rangesBeforeChange = getRangesBefore(myFirstChangedLine);
|
||||
List<Range> rangesAfterChange = getRangesAfter(myRanges, myLastChangedLine);
|
||||
List<Range> rangesBeforeChange = getRangesBefore(myRanges, myFirstChangedLine);
|
||||
|
||||
List<Range> changedRanges = getChangedRanges(myFirstChangedLine, myLastChangedLine);
|
||||
|
||||
@@ -416,19 +310,10 @@ public class LineStatusTracker {
|
||||
myRanges.addAll(newChangedRanges);
|
||||
myRanges.addAll(rangesAfterChange);
|
||||
|
||||
if (myHighlighterCount != myRanges.size()) {
|
||||
LOG.error("Highlighters: " + myHighlighterCount + ", ranges: " + myRanges.size());
|
||||
}
|
||||
|
||||
myRanges = mergeRanges(myRanges);
|
||||
|
||||
for (Range range : myRanges) {
|
||||
if (!range.hasHighlighter()) range.setHighlighter(createHighlighter(range));
|
||||
|
||||
}
|
||||
|
||||
if (myHighlighterCount != myRanges.size()) {
|
||||
LOG.error("Highlighters: " + myHighlighterCount + ", ranges: " + myRanges.size());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,6 +327,8 @@ public class LineStatusTracker {
|
||||
}
|
||||
|
||||
private List<Range> mergeRanges(List<Range> ranges) {
|
||||
final MarkupModel markupModel = myDocument.getMarkupModel(myProject);
|
||||
|
||||
ArrayList<Range> result = new ArrayList<Range>();
|
||||
Iterator<Range> iterator = ranges.iterator();
|
||||
if (!iterator.hasNext()) return result;
|
||||
@@ -449,6 +336,8 @@ public class LineStatusTracker {
|
||||
while (iterator.hasNext()) {
|
||||
Range range = iterator.next();
|
||||
if (prev.canBeMergedWith(range)) {
|
||||
markupModel.removeHighlighter(range.getHighlighter());
|
||||
markupModel.removeHighlighter(prev.getHighlighter());
|
||||
prev = prev.mergeWith(range, LineStatusTracker.this);
|
||||
}
|
||||
else {
|
||||
@@ -461,8 +350,10 @@ public class LineStatusTracker {
|
||||
}
|
||||
|
||||
private void replaceRanges(List<Range> rangesInChange, List<Range> newRangesInChange) {
|
||||
final MarkupModel markupModel = myDocument.getMarkupModel(myProject);
|
||||
|
||||
for (Range range : rangesInChange) {
|
||||
removeHighlighter(range.getHighlighter());
|
||||
markupModel.removeHighlighter(range.getHighlighter());
|
||||
range.setHighlighter(null);
|
||||
}
|
||||
for (Range range : newRangesInChange) {
|
||||
@@ -491,9 +382,51 @@ public class LineStatusTracker {
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Range> getRangesBefore(int line) {
|
||||
return getRangesBefore(myRanges, line);
|
||||
@Nullable
|
||||
Range getNextRange(final Range range) {
|
||||
final int index = myRanges.indexOf(range);
|
||||
if (index == myRanges.size() - 1) return null;
|
||||
return myRanges.get(index + 1);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
Range getPrevRange(final Range range) {
|
||||
final int index = myRanges.indexOf(range);
|
||||
if (index <= 0) return null;
|
||||
return myRanges.get(index - 1);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Range getNextRange(final int line) {
|
||||
final Range currentRange = getRangeForLine(line);
|
||||
if (currentRange != null) {
|
||||
return getNextRange(currentRange);
|
||||
}
|
||||
|
||||
for (final Range range : myRanges) {
|
||||
if (line > range.getOffset1() || line > range.getOffset2()) {
|
||||
continue;
|
||||
}
|
||||
return range;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Range getPrevRange(final int line) {
|
||||
final Range currentRange = getRangeForLine(line);
|
||||
if (currentRange != null) {
|
||||
return getPrevRange(currentRange);
|
||||
}
|
||||
|
||||
for (ListIterator<Range> iterator = myRanges.listIterator(myRanges.size()); iterator.hasPrevious();) {
|
||||
final Range range = iterator.previous();
|
||||
if (range.getOffset1() > line) {
|
||||
continue;
|
||||
}
|
||||
return range;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<Range> getRangesBefore(List<Range> ranges, int line) {
|
||||
@@ -506,10 +439,6 @@ public class LineStatusTracker {
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<Range> getRangesAfter(int line) {
|
||||
return getRangesAfter(myRanges, line);
|
||||
}
|
||||
|
||||
public static List<Range> getRangesAfter(List<Range> ranges, int line) {
|
||||
ArrayList<Range> result = new ArrayList<Range>();
|
||||
for (Range range : ranges) {
|
||||
@@ -518,69 +447,6 @@ public class LineStatusTracker {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void moveToRange(final Range range, final Editor editor) {
|
||||
final int firstOffset = myDocument.getLineStartOffset(Math.min(range.getOffset1(), myDocument.getLineCount() - 1));
|
||||
editor.getCaretModel().moveToOffset(firstOffset);
|
||||
editor.getScrollingModel().scrollToCaret(ScrollType.CENTER);
|
||||
|
||||
editor.getScrollingModel().runActionOnScrollingFinished(new Runnable() {
|
||||
public void run() {
|
||||
Point p = editor.visualPositionToXY(editor.offsetToVisualPosition(firstOffset));
|
||||
JComponent editorComponent = editor.getContentComponent();
|
||||
JLayeredPane layeredPane = editorComponent.getRootPane().getLayeredPane();
|
||||
p = SwingUtilities.convertPoint(editorComponent, 0, p.y, layeredPane);
|
||||
showActiveHint(range, editor, p);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Range getNextRange(Range range) {
|
||||
int index = myRanges.indexOf(range);
|
||||
if (index == myRanges.size() - 1) return null;
|
||||
return myRanges.get(index + 1);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Range getPrevRange(Range range) {
|
||||
int index = myRanges.indexOf(range);
|
||||
if (index <= 0) return null;
|
||||
return myRanges.get(index - 1);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Range getNextRange(int line) {
|
||||
final Range currentRange = getRangeForLine(line);
|
||||
if (currentRange != null) {
|
||||
return getNextRange(currentRange);
|
||||
}
|
||||
|
||||
for (Range range : myRanges) {
|
||||
if (line > range.getOffset1() || line > range.getOffset2()) {
|
||||
continue;
|
||||
}
|
||||
return range;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Range getPrevRange(int line) {
|
||||
final Range currentRange = getRangeForLine(line);
|
||||
if (currentRange != null) {
|
||||
return getPrevRange(currentRange);
|
||||
}
|
||||
|
||||
for (ListIterator<Range> iterator = myRanges.listIterator(myRanges.size()); iterator.hasPrevious();) {
|
||||
Range range = iterator.previous();
|
||||
if (range.getOffset1() > line) {
|
||||
continue;
|
||||
}
|
||||
return range;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Range getRangeForLine(final int line) {
|
||||
for (final Range range : myRanges) {
|
||||
@@ -594,63 +460,9 @@ public class LineStatusTracker {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static abstract class MyAction extends AnAction implements DumbAware {
|
||||
protected final LineStatusTracker myLineStatusTracker;
|
||||
protected final Range myRange;
|
||||
public void rollbackChanges(final Range range) {
|
||||
myApplication.assertWriteAccessAllowed();
|
||||
|
||||
protected MyAction(String text, Icon icon, LineStatusTracker lineStatusTracker, Range range) {
|
||||
super(text, null, icon);
|
||||
myLineStatusTracker = lineStatusTracker;
|
||||
myRange = range;
|
||||
}
|
||||
|
||||
public void update(AnActionEvent e) {
|
||||
e.getPresentation().setEnabled(isEnabled());
|
||||
}
|
||||
|
||||
public abstract boolean isEnabled();
|
||||
|
||||
protected int getMyRangeIndex() {
|
||||
List<Range> ranges = myLineStatusTracker.getRanges();
|
||||
for (int i = 0; i < ranges.size(); i++) {
|
||||
Range range = ranges.get(i);
|
||||
if (range.getOffset1() == myRange.getOffset1() && range.getOffset2() == myRange.getOffset2()) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public static class RollbackAction extends LineStatusTracker.MyAction {
|
||||
public RollbackAction(LineStatusTracker lineStatusTracker, Range range, Editor editor) {
|
||||
super(VcsBundle.message("action.name.rollback"), IconLoader.getIcon("/actions/reset.png"), lineStatusTracker, range);
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
CommandProcessor.getInstance().executeCommand(myLineStatusTracker.getProject(), new Runnable() {
|
||||
public void run() {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
public void run() {
|
||||
if (!myLineStatusTracker.getDocument().isWritable()) {
|
||||
final ReadonlyStatusHandler.OperationStatus operationStatus = ReadonlyStatusHandler
|
||||
.getInstance(myLineStatusTracker.getProject()).ensureFilesWritable(myLineStatusTracker.getVirtualFile());
|
||||
if (operationStatus.hasReadonlyFiles()) return;
|
||||
}
|
||||
myLineStatusTracker.rollbackChanges(myRange);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, VcsBundle.message("command.name.rollback.change"), null);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void rollbackChanges(Range range) {
|
||||
TextRange currentTextRange = getCurrentTextRange(range);
|
||||
|
||||
if (range.getType() == Range.INSERTED) {
|
||||
@@ -676,81 +488,25 @@ public class LineStatusTracker {
|
||||
return myUpToDateDocument.getCharsSequence().subSequence(startOffset, endOffset).toString();
|
||||
}
|
||||
|
||||
private Project getProject() {
|
||||
Project getProject() {
|
||||
return myProject;
|
||||
}
|
||||
|
||||
public class ShowDiffAction extends LineStatusTracker.MyAction {
|
||||
public ShowDiffAction(LineStatusTracker lineStatusTracker, Range range, Editor editor) {
|
||||
super(VcsBundle.message("action.name.show.difference"), IconLoader.getIcon("/actions/diff.png"), lineStatusTracker, range);
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return isModifiedRange() || isDeletedRange();
|
||||
}
|
||||
|
||||
private boolean isDeletedRange() {
|
||||
return myRange.getType() == Range.DELETED;
|
||||
}
|
||||
|
||||
private boolean isModifiedRange() {
|
||||
return myRange.getType() == Range.MODIFIED;
|
||||
}
|
||||
|
||||
public void actionPerformed(AnActionEvent e) {
|
||||
DiffManager.getInstance().getDiffTool().show(createDiffData());
|
||||
}
|
||||
|
||||
private DiffRequest createDiffData() {
|
||||
return new DiffRequest(myLineStatusTracker.getProject()) {
|
||||
public DiffContent[] getContents() {
|
||||
return new DiffContent[]{createDiffContent(myLineStatusTracker.getUpToDateDocument(),
|
||||
myLineStatusTracker.getUpToDateRange(myRange), null),
|
||||
createDiffContent(myLineStatusTracker.getDocument(), myLineStatusTracker.getCurrentTextRange(myRange),
|
||||
myLineStatusTracker.getVirtualFile())};
|
||||
}
|
||||
|
||||
public String[] getContentTitles() {
|
||||
return new String[]{VcsBundle.message("diff.content.title.up.to.date"),
|
||||
VcsBundle.message("diff.content.title.current.range")};
|
||||
}
|
||||
|
||||
public String getWindowTitle() {
|
||||
return VcsBundle.message("dialog.title.diff.for.range");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private DiffContent createDiffContent(final Document uDocument, TextRange textRange, VirtualFile file) {
|
||||
DiffContent diffContent = new DocumentContent(myProject, uDocument);
|
||||
return new FragmentContent(diffContent, textRange, myLineStatusTracker.getProject(), file);
|
||||
}
|
||||
TextRange getCurrentTextRange(Range range) {
|
||||
return getRange(range.getType(), range.getOffset1(), range.getOffset2(), Range.DELETED, myDocument, false);
|
||||
}
|
||||
|
||||
public class CopyAction extends MyAction {
|
||||
protected CopyAction(LineStatusTracker lineStatusTracker, Range range) {
|
||||
super(VcsBundle.message("action.name.copy.old.text"), IconLoader.getIcon("/actions/copy.png"), lineStatusTracker, range);
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return myRange.getType() == Range.DELETED || myRange.getType() == Range.MODIFIED;
|
||||
}
|
||||
|
||||
public void actionPerformed(final AnActionEvent e) {
|
||||
final String content = myLineStatusTracker.getUpToDateContent(myRange);
|
||||
CopyPasteManager.getInstance().setContents(new StringSelection(content));
|
||||
}
|
||||
TextRange getUpToDateRange(Range range) {
|
||||
return getRange(range.getType(), range.getUOffset1(), range.getUOffset2(), Range.INSERTED, myUpToDateDocument, false);
|
||||
}
|
||||
|
||||
private TextRange getCurrentTextRange(Range range) {
|
||||
return getRange(range.getType(), range.getOffset1(), range.getOffset2(), Range.DELETED, myDocument);
|
||||
// a hack
|
||||
TextRange getUpToDateRangeWithEndSymbol(Range range) {
|
||||
return getRange(range.getType(), range.getUOffset1(), range.getUOffset2(), Range.INSERTED, myUpToDateDocument, true);
|
||||
}
|
||||
|
||||
private TextRange getUpToDateRange(Range range) {
|
||||
return getRange(range.getType(), range.getUOffset1(), range.getUOffset2(), Range.INSERTED, myUpToDateDocument);
|
||||
}
|
||||
|
||||
private static TextRange getRange(byte rangeType, int offset1, int offset2, byte emptyRangeCondition, Document document) {
|
||||
private static TextRange getRange(byte rangeType, int offset1, int offset2, byte emptyRangeCondition, Document document,
|
||||
final boolean keepEnd) {
|
||||
if (rangeType == emptyRangeCondition) {
|
||||
int lineStartOffset;
|
||||
if (offset1 == 0) {
|
||||
@@ -767,104 +523,28 @@ public class LineStatusTracker {
|
||||
int startOffset = document.getLineStartOffset(offset1);
|
||||
int endOffset = document.getLineEndOffset(offset2 - 1);
|
||||
if (startOffset > 0) {
|
||||
startOffset--;
|
||||
endOffset--;
|
||||
-- startOffset;
|
||||
if (! keepEnd) {
|
||||
-- endOffset;
|
||||
}
|
||||
}
|
||||
return new TextRange(startOffset, endOffset);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void showActiveHint(Range range, final Editor editor, Point point) {
|
||||
|
||||
DefaultActionGroup group = new DefaultActionGroup();
|
||||
|
||||
final AnAction globalShowNextAction = ActionManager.getInstance().getAction("VcsShowNextChangeMarker");
|
||||
final AnAction globalShowPrevAction = ActionManager.getInstance().getAction("VcsShowPrevChangeMarker");
|
||||
|
||||
final ShowPrevChangeMarkerAction localShowPrevAction = new ShowPrevChangeMarkerAction(getPrevRange(range), this, editor);
|
||||
final ShowNextChangeMarkerAction localShowNextAction = new ShowNextChangeMarkerAction(getNextRange(range), this, editor);
|
||||
|
||||
JComponent editorComponent = editor.getComponent();
|
||||
|
||||
localShowNextAction.registerCustomShortcutSet(localShowNextAction.getShortcutSet(), editorComponent);
|
||||
localShowPrevAction.registerCustomShortcutSet(localShowPrevAction.getShortcutSet(), editorComponent);
|
||||
|
||||
group.add(localShowPrevAction);
|
||||
group.add(localShowNextAction);
|
||||
|
||||
localShowNextAction.copyFrom(globalShowNextAction);
|
||||
localShowPrevAction.copyFrom(globalShowPrevAction);
|
||||
|
||||
group.add(new RollbackAction(this, range, editor));
|
||||
group.add(new ShowDiffAction(this, range, editor));
|
||||
group.add(new CopyAction(this, range));
|
||||
|
||||
final List<AnAction> actionList = (List<AnAction>)editorComponent.getClientProperty(AnAction.ourClientProperty);
|
||||
|
||||
actionList.remove(globalShowPrevAction);
|
||||
actionList.remove(globalShowNextAction);
|
||||
|
||||
JComponent toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.FILEHISTORY_VIEW_TOOLBAR, group, true).getComponent();
|
||||
|
||||
final Color background = ((EditorEx)editor).getBackgroundColor();
|
||||
final Color foreground = editor.getColorsScheme().getColor(EditorColors.CARET_COLOR);
|
||||
toolbar.setBackground(background);
|
||||
|
||||
toolbar.setBorder(new ColoredSideBorder(foreground, foreground, range.getType() != Range.INSERTED ? null : foreground, foreground, 1));
|
||||
|
||||
JPanel component = new JPanel(new BorderLayout());
|
||||
component.setOpaque(false);
|
||||
|
||||
JPanel toolbarPanel = new JPanel(new BorderLayout());
|
||||
toolbarPanel.setOpaque(false);
|
||||
toolbarPanel.add(toolbar, BorderLayout.WEST);
|
||||
component.add(toolbarPanel, BorderLayout.NORTH);
|
||||
|
||||
if (range.getType() != Range.INSERTED) {
|
||||
DocumentEx doc = (DocumentEx)myUpToDateDocument;
|
||||
EditorEx uEditor = (EditorEx)EditorFactory.getInstance().createViewer(doc, myProject);
|
||||
EditorHighlighter highlighter = EditorHighlighterFactory.getInstance().createEditorHighlighter(myProject, getFileName());
|
||||
uEditor.setHighlighter(highlighter);
|
||||
|
||||
EditorFragmentComponent editorFragmentComponent =
|
||||
EditorFragmentComponent.createEditorFragmentComponent(uEditor, range.getUOffset1(), range.getUOffset2(), false, false);
|
||||
|
||||
component.add(editorFragmentComponent, BorderLayout.CENTER);
|
||||
EditorFactory.getInstance().releaseEditor(uEditor);
|
||||
}
|
||||
|
||||
LightweightHint lightweightHint = new LightweightHint(component);
|
||||
lightweightHint.addHintListener(new HintListener() {
|
||||
public void hintHidden(EventObject event) {
|
||||
actionList.remove(localShowPrevAction);
|
||||
actionList.remove(localShowNextAction);
|
||||
actionList.add(globalShowPrevAction);
|
||||
actionList.add(globalShowNextAction);
|
||||
}
|
||||
});
|
||||
|
||||
HintManagerImpl.getInstanceImpl().showEditorHint(lightweightHint, editor, point, HintManagerImpl.HIDE_BY_ANY_KEY | HintManagerImpl.HIDE_BY_TEXT_CHANGE |
|
||||
HintManagerImpl.HIDE_BY_OTHER_HINT | HintManagerImpl.HIDE_BY_SCROLLING,
|
||||
-1, false);
|
||||
}
|
||||
|
||||
private String getFileName() {
|
||||
VirtualFile file = FileDocumentManager.getInstance().getFile(myDocument);
|
||||
if (file == null) return "";
|
||||
return file.getName();
|
||||
}
|
||||
|
||||
public static LineStatusTracker createOn(Document doc, String upToDateContent, Project project) {
|
||||
Document document = EditorFactory.getInstance().createDocument(StringUtil.convertLineSeparators(upToDateContent));
|
||||
public static LineStatusTracker createOn(final Document doc, final String upToDateContent, final Project project) {
|
||||
final Document document = EditorFactory.getInstance().createDocument(StringUtil.convertLineSeparators(upToDateContent));
|
||||
final LineStatusTracker tracker = new LineStatusTracker(doc, document, project);
|
||||
tracker.initialize(upToDateContent);
|
||||
return tracker;
|
||||
}
|
||||
|
||||
public static LineStatusTracker createOn(Document doc, Project project) {
|
||||
Document document = EditorFactory.getInstance().createDocument("");
|
||||
public static LineStatusTracker createOn(final Document doc, final Project project) {
|
||||
final Document document = EditorFactory.getInstance().createDocument("");
|
||||
return new LineStatusTracker(doc, document, project);
|
||||
}
|
||||
|
||||
public boolean isBaseLoaded() {
|
||||
return myBaseLoaded;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
* Copyright 2000-2010 JetBrains s.r.o.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.openapi.vcs.ex;
|
||||
|
||||
import com.intellij.codeInsight.hint.EditorFragmentComponent;
|
||||
import com.intellij.codeInsight.hint.HintManagerImpl;
|
||||
import com.intellij.openapi.actionSystem.ActionManager;
|
||||
import com.intellij.openapi.actionSystem.ActionPlaces;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup;
|
||||
import com.intellij.openapi.diff.DiffColors;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.editor.ScrollType;
|
||||
import com.intellij.openapi.editor.colors.EditorColors;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsScheme;
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey;
|
||||
import com.intellij.openapi.editor.ex.DocumentEx;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.ex.EditorGutterComponentEx;
|
||||
import com.intellij.openapi.editor.highlighter.EditorHighlighter;
|
||||
import com.intellij.openapi.editor.highlighter.EditorHighlighterFactory;
|
||||
import com.intellij.openapi.editor.markup.ActiveGutterRenderer;
|
||||
import com.intellij.openapi.editor.markup.EffectType;
|
||||
import com.intellij.openapi.editor.markup.LineMarkerRenderer;
|
||||
import com.intellij.openapi.editor.markup.TextAttributes;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.vcs.actions.ShowNextChangeMarkerAction;
|
||||
import com.intellij.openapi.vcs.actions.ShowPrevChangeMarkerAction;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.ui.ColoredSideBorder;
|
||||
import com.intellij.ui.HintListener;
|
||||
import com.intellij.ui.LightweightHint;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.EventObject;
|
||||
|
||||
/**
|
||||
* @author irengrig
|
||||
*/
|
||||
public class LineStatusTrackerDrawing {
|
||||
private LineStatusTrackerDrawing() {
|
||||
}
|
||||
|
||||
static TextAttributes getAttributesFor(final Range range) {
|
||||
final EditorColorsScheme globalScheme = EditorColorsManager.getInstance().getGlobalScheme();
|
||||
final Color stripeColor = globalScheme.getAttributes(getDiffColor(range)).getErrorStripeColor();
|
||||
final TextAttributes textAttributes = new TextAttributes(null, stripeColor, null, EffectType.BOXED, Font.PLAIN);
|
||||
textAttributes.setErrorStripeColor(stripeColor);
|
||||
return textAttributes;
|
||||
}
|
||||
|
||||
private static void paintGutterFragment(final Editor editor, final Graphics g, final Rectangle r, final TextAttributesKey diffAttributeKey) {
|
||||
final EditorGutterComponentEx gutter = ((EditorEx)editor).getGutterComponentEx();
|
||||
final Color stripeColor = editor.getColorsScheme().getAttributes(diffAttributeKey).getErrorStripeColor();
|
||||
g.setColor(brighter(stripeColor));
|
||||
|
||||
final int endX = gutter.getWhitespaceSeparatorOffset();
|
||||
final int x = r.x + r.width - 2;
|
||||
final int width = endX - x;
|
||||
if (r.height > 0) {
|
||||
g.fillRect(x, r.y + 2, width, r.height - 4);
|
||||
g.setColor(gutter.getOutlineColor(false));
|
||||
UIUtil.drawLine(g, x, r.y + 2, x + width, r.y + 2);
|
||||
UIUtil.drawLine(g, x, r.y + 2, x, r.y + r.height - 3);
|
||||
UIUtil.drawLine(g, x, r.y + r.height - 3, x + width, r.y + r.height - 3);
|
||||
}
|
||||
else {
|
||||
final int[] xPoints = new int[]{x,
|
||||
x,
|
||||
x + width - 1};
|
||||
final int[] yPoints = new int[]{r.y - 4,
|
||||
r.y + 4,
|
||||
r.y};
|
||||
g.fillPolygon(xPoints, yPoints, 3);
|
||||
|
||||
g.setColor(gutter.getOutlineColor(false));
|
||||
g.drawPolygon(xPoints, yPoints, 3);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Color brighter(final Color color) {
|
||||
if (color == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final float[] hsbStripeColor = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null);
|
||||
|
||||
if (hsbStripeColor[1] < 0.02f) {
|
||||
// color is grey
|
||||
hsbStripeColor[2] = Math.min(1.0f, hsbStripeColor[2] * 1.3f);
|
||||
} else {
|
||||
// let's decrease color saturation
|
||||
hsbStripeColor[1] *= 0.3;
|
||||
|
||||
// max brightness
|
||||
hsbStripeColor[2] = 1.0f;
|
||||
}
|
||||
return Color.getHSBColor(hsbStripeColor[0], hsbStripeColor[1], hsbStripeColor[2]);
|
||||
}
|
||||
|
||||
public static LineMarkerRenderer createRenderer(final Range range, final LineStatusTracker tracker) {
|
||||
return new ActiveGutterRenderer() {
|
||||
public void paint(final Editor editor, final Graphics g, final Rectangle r) {
|
||||
paintGutterFragment(editor, g, r, getDiffColor(range));
|
||||
}
|
||||
|
||||
public void doAction(final Editor editor, final MouseEvent e) {
|
||||
e.consume();
|
||||
final JComponent comp = (JComponent)e.getComponent(); // shall be EditorGutterComponent, cast is safe.
|
||||
final JLayeredPane layeredPane = comp.getRootPane().getLayeredPane();
|
||||
final Point point = SwingUtilities.convertPoint(comp, ((EditorEx)editor).getGutterComponentEx().getWidth(), e.getY(), layeredPane);
|
||||
showActiveHint(range, editor, point, tracker);
|
||||
}
|
||||
|
||||
public boolean canDoAction(final MouseEvent e) {
|
||||
final EditorGutterComponentEx gutter = (EditorGutterComponentEx)e.getComponent();
|
||||
return e.getX() > gutter.getLineMarkerAreaOffset() + gutter.getIconsAreaWidth();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static void showActiveHint(final Range range, final Editor editor, final Point point, final LineStatusTracker tracker) {
|
||||
|
||||
final DefaultActionGroup group = new DefaultActionGroup();
|
||||
|
||||
final AnAction globalShowNextAction = ActionManager.getInstance().getAction("VcsShowNextChangeMarker");
|
||||
final AnAction globalShowPrevAction = ActionManager.getInstance().getAction("VcsShowPrevChangeMarker");
|
||||
|
||||
final ShowPrevChangeMarkerAction localShowPrevAction = new ShowPrevChangeMarkerAction(tracker.getPrevRange(range), tracker, editor);
|
||||
final ShowNextChangeMarkerAction localShowNextAction = new ShowNextChangeMarkerAction(tracker.getNextRange(range), tracker, editor);
|
||||
|
||||
final JComponent editorComponent = editor.getComponent();
|
||||
|
||||
localShowNextAction.registerCustomShortcutSet(localShowNextAction.getShortcutSet(), editorComponent);
|
||||
localShowPrevAction.registerCustomShortcutSet(localShowPrevAction.getShortcutSet(), editorComponent);
|
||||
|
||||
group.add(localShowPrevAction);
|
||||
group.add(localShowNextAction);
|
||||
|
||||
localShowNextAction.copyFrom(globalShowNextAction);
|
||||
localShowPrevAction.copyFrom(globalShowPrevAction);
|
||||
|
||||
group.add(new RollbackLineStatusRangeAction(tracker, range, editor));
|
||||
group.add(new ShowLineStatusRangeDiffAction(tracker, range, editor));
|
||||
group.add(new CopyLineStatusRangeAction(tracker, range));
|
||||
|
||||
final java.util.List<AnAction> actionList = (java.util.List<AnAction>)editorComponent.getClientProperty(AnAction.ourClientProperty);
|
||||
|
||||
actionList.remove(globalShowPrevAction);
|
||||
actionList.remove(globalShowNextAction);
|
||||
|
||||
final JComponent toolbar = ActionManager.getInstance().createActionToolbar(ActionPlaces.FILEHISTORY_VIEW_TOOLBAR, group, true).getComponent();
|
||||
|
||||
final Color background = ((EditorEx)editor).getBackgroundColor();
|
||||
final Color foreground = editor.getColorsScheme().getColor(EditorColors.CARET_COLOR);
|
||||
toolbar.setBackground(background);
|
||||
|
||||
toolbar.setBorder(new ColoredSideBorder(foreground, foreground, (range.getType() != Range.INSERTED) ? null : foreground, foreground, 1));
|
||||
|
||||
final JPanel component = new JPanel(new BorderLayout());
|
||||
component.setOpaque(false);
|
||||
|
||||
final JPanel toolbarPanel = new JPanel(new BorderLayout());
|
||||
toolbarPanel.setOpaque(false);
|
||||
toolbarPanel.add(toolbar, BorderLayout.WEST);
|
||||
component.add(toolbarPanel, BorderLayout.NORTH);
|
||||
|
||||
if (range.getType() != Range.INSERTED) {
|
||||
final DocumentEx doc = (DocumentEx) tracker.getUpToDateDocument();
|
||||
final EditorEx uEditor = (EditorEx)EditorFactory.getInstance().createViewer(doc, tracker.getProject());
|
||||
final EditorHighlighter highlighter = EditorHighlighterFactory.getInstance().createEditorHighlighter(tracker.getProject(), getFileName(tracker.getDocument()));
|
||||
uEditor.setHighlighter(highlighter);
|
||||
|
||||
final EditorFragmentComponent editorFragmentComponent =
|
||||
EditorFragmentComponent.createEditorFragmentComponent(uEditor, range.getUOffset1(), range.getUOffset2(), false, false);
|
||||
|
||||
component.add(editorFragmentComponent, BorderLayout.CENTER);
|
||||
EditorFactory.getInstance().releaseEditor(uEditor);
|
||||
}
|
||||
|
||||
final LightweightHint lightweightHint = new LightweightHint(component);
|
||||
lightweightHint.addHintListener(new HintListener() {
|
||||
public void hintHidden(final EventObject event) {
|
||||
actionList.remove(localShowPrevAction);
|
||||
actionList.remove(localShowNextAction);
|
||||
actionList.add(globalShowPrevAction);
|
||||
actionList.add(globalShowNextAction);
|
||||
}
|
||||
});
|
||||
|
||||
HintManagerImpl.getInstanceImpl().showEditorHint(lightweightHint, editor, point, HintManagerImpl.HIDE_BY_ANY_KEY | HintManagerImpl.HIDE_BY_TEXT_CHANGE |
|
||||
HintManagerImpl.HIDE_BY_OTHER_HINT | HintManagerImpl.HIDE_BY_SCROLLING,
|
||||
-1, false);
|
||||
}
|
||||
|
||||
private static String getFileName(final Document document) {
|
||||
final VirtualFile file = FileDocumentManager.getInstance().getFile(document);
|
||||
if (file == null) return "";
|
||||
return file.getName();
|
||||
}
|
||||
|
||||
public static void moveToRange(final Range range, final Editor editor, final LineStatusTracker tracker) {
|
||||
final Document document = tracker.getDocument();
|
||||
final int firstOffset = document.getLineStartOffset(Math.min(range.getOffset1(), document.getLineCount() - 1));
|
||||
editor.getCaretModel().moveToOffset(firstOffset);
|
||||
editor.getScrollingModel().scrollToCaret(ScrollType.CENTER);
|
||||
|
||||
editor.getScrollingModel().runActionOnScrollingFinished(new Runnable() {
|
||||
public void run() {
|
||||
Point p = editor.visualPositionToXY(editor.offsetToVisualPosition(firstOffset));
|
||||
final JComponent editorComponent = editor.getContentComponent();
|
||||
final JLayeredPane layeredPane = editorComponent.getRootPane().getLayeredPane();
|
||||
p = SwingUtilities.convertPoint(editorComponent, 0, p.y, layeredPane);
|
||||
showActiveHint(range, editor, p, tracker);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static TextAttributesKey getDiffColor(Range range) {
|
||||
switch (range.getType()) {
|
||||
case Range.INSERTED:
|
||||
return DiffColors.DIFF_INSERTED;
|
||||
case Range.DELETED:
|
||||
return DiffColors.DIFF_DELETED;
|
||||
case Range.MODIFIED:
|
||||
return DiffColors.DIFF_MODIFIED;
|
||||
default:
|
||||
assert false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,12 +154,7 @@ public class Range {
|
||||
}
|
||||
|
||||
public Range mergeWith(Range range, LineStatusTracker tracker) {
|
||||
tracker.removeHighlighter(getHighlighter());
|
||||
setHighlighter(null);
|
||||
tracker.removeHighlighter(range.getHighlighter());
|
||||
range.setHighlighter(null);
|
||||
Range result = new Range(myOffset1, range.myOffset2, myUpToDateOffset1, range.myUpToDateOffset2, mergedStatusWith(range));
|
||||
return result;
|
||||
return new Range(myOffset1, range.myOffset2, myUpToDateOffset1, range.myUpToDateOffset2, mergedStatusWith(range));
|
||||
}
|
||||
|
||||
private byte mergedStatusWith(Range range) {
|
||||
|
||||
@@ -16,11 +16,10 @@
|
||||
package com.intellij.openapi.vcs.ex;
|
||||
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.diff.Diff;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -30,16 +29,12 @@ import java.util.List;
|
||||
public class RangesBuilder {
|
||||
private List<Range> myRanges;
|
||||
|
||||
public RangesBuilder(String current, String upToDate) {
|
||||
this(EditorFactory.getInstance().createDocument(current), EditorFactory.getInstance().createDocument(upToDate));
|
||||
}
|
||||
|
||||
public RangesBuilder(Document current, Document upToDate) {
|
||||
this(new DocumentWrapper(current).getLines(), new DocumentWrapper(upToDate).getLines(), 0, 0);
|
||||
}
|
||||
|
||||
public RangesBuilder(List<String> current, List<String> upToDate, int shift, int uShift) {
|
||||
myRanges = new ArrayList<Range>();
|
||||
myRanges = new LinkedList<Range>();
|
||||
|
||||
int shiftBefore = 0;
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright 2000-2010 JetBrains s.r.o.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.openapi.vcs.ex;
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.command.CommandProcessor;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.util.IconLoader;
|
||||
import com.intellij.openapi.vcs.VcsBundle;
|
||||
import com.intellij.openapi.vfs.ReadonlyStatusHandler;
|
||||
|
||||
/**
|
||||
* @author irengrig
|
||||
*/
|
||||
public class RollbackLineStatusRangeAction extends BaseLineStatusRangeAction {
|
||||
public RollbackLineStatusRangeAction(final LineStatusTracker lineStatusTracker, final Range range, final Editor editor) {
|
||||
super(VcsBundle.message("action.name.rollback"), IconLoader.getIcon("/actions/reset.png"), lineStatusTracker, range);
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void actionPerformed(final AnActionEvent e) {
|
||||
CommandProcessor.getInstance().executeCommand(myLineStatusTracker.getProject(), new Runnable() {
|
||||
public void run() {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
public void run() {
|
||||
if (!myLineStatusTracker.getDocument().isWritable()) {
|
||||
final ReadonlyStatusHandler.OperationStatus operationStatus = ReadonlyStatusHandler
|
||||
.getInstance(myLineStatusTracker.getProject()).ensureFilesWritable(myLineStatusTracker.getVirtualFile());
|
||||
if (operationStatus.hasReadonlyFiles()) return;
|
||||
}
|
||||
myLineStatusTracker.rollbackChanges(myRange);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, VcsBundle.message("command.name.rollback.change"), null);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2000-2010 JetBrains s.r.o.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.openapi.vcs.ex;
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.diff.*;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.IconLoader;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.vcs.VcsBundle;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
|
||||
/**
|
||||
* @author irengrig
|
||||
*/
|
||||
public class ShowLineStatusRangeDiffAction extends BaseLineStatusRangeAction {
|
||||
public ShowLineStatusRangeDiffAction(final LineStatusTracker lineStatusTracker, final Range range, final Editor editor) {
|
||||
super(VcsBundle.message("action.name.show.difference"), IconLoader.getIcon("/actions/diff.png"), lineStatusTracker, range);
|
||||
}
|
||||
|
||||
public boolean isEnabled() {
|
||||
return isModifiedRange() || isDeletedRange();
|
||||
}
|
||||
|
||||
private boolean isDeletedRange() {
|
||||
return Range.DELETED == myRange.getType();
|
||||
}
|
||||
|
||||
private boolean isModifiedRange() {
|
||||
return Range.MODIFIED == myRange.getType();
|
||||
}
|
||||
|
||||
public void actionPerformed(final AnActionEvent e) {
|
||||
DiffManager.getInstance().getDiffTool().show(createDiffData());
|
||||
}
|
||||
|
||||
private DiffRequest createDiffData() {
|
||||
return new DiffRequest(myLineStatusTracker.getProject()) {
|
||||
public DiffContent[] getContents() {
|
||||
return new DiffContent[]{createDiffContent(myLineStatusTracker.getUpToDateDocument(),
|
||||
myLineStatusTracker.getUpToDateRangeWithEndSymbol(myRange), null),
|
||||
createDiffContent(myLineStatusTracker.getDocument(), myLineStatusTracker.getCurrentTextRange(myRange),
|
||||
myLineStatusTracker.getVirtualFile())};
|
||||
}
|
||||
|
||||
public String[] getContentTitles() {
|
||||
return new String[]{VcsBundle.message("diff.content.title.up.to.date"),
|
||||
VcsBundle.message("diff.content.title.current.range")};
|
||||
}
|
||||
|
||||
public String getWindowTitle() {
|
||||
return VcsBundle.message("dialog.title.diff.for.range");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private DiffContent createDiffContent(final Document uDocument, final TextRange textRange, final VirtualFile file) {
|
||||
final Project project = myLineStatusTracker.getProject();
|
||||
final DiffContent diffContent = new DocumentContent(project, uDocument);
|
||||
return new FragmentContent(diffContent, textRange, project, file);
|
||||
}
|
||||
}
|
||||
+239
-202
@@ -24,7 +24,9 @@ package com.intellij.openapi.vcs.impl;
|
||||
|
||||
import com.intellij.lifecycle.PeriodicalTasksCloser;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ModalityState;
|
||||
import com.intellij.openapi.components.ProjectComponent;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
@@ -40,11 +42,14 @@ import com.intellij.openapi.editor.ex.DocumentBulkUpdateListener;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.vcs.AbstractVcs;
|
||||
import com.intellij.openapi.vcs.FileStatus;
|
||||
import com.intellij.openapi.vcs.FileStatusListener;
|
||||
import com.intellij.openapi.vcs.FileStatusManager;
|
||||
import com.intellij.openapi.vcs.changes.committed.AbstractCalledLater;
|
||||
import com.intellij.openapi.vcs.ex.LineStatusTracker;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.vfs.VirtualFileAdapter;
|
||||
@@ -56,50 +61,79 @@ import com.intellij.util.containers.HashMap;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
public class LineStatusTrackerManager implements ProjectComponent {
|
||||
public class LineStatusTrackerManager implements ProjectComponent, LineStatusTrackerManagerI {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.vcs.impl.LineStatusTrackerManager");
|
||||
|
||||
public static LineStatusTrackerManager getInstance(Project project) {
|
||||
return PeriodicalTasksCloser.getInstance().safeGetComponent(project, LineStatusTrackerManager.class);
|
||||
public static LineStatusTrackerManagerI getInstance(final Project project) {
|
||||
if (System.getProperty(IGNORE_CHANGEMARKERS_KEY) != null) {
|
||||
return Dummy.getInstance();
|
||||
}
|
||||
return PeriodicalTasksCloser.getInstance().safeGetComponent(project, LineStatusTrackerManagerI.class);
|
||||
}
|
||||
|
||||
private final Project myProject;
|
||||
|
||||
private HashMap<Document, LineStatusTracker> myLineStatusTrackers =
|
||||
new HashMap<Document, LineStatusTracker>();
|
||||
private final Map<Document, LineStatusTracker> myLineStatusTrackers;
|
||||
// !!! no state queries and self lock for add/remove
|
||||
// removal from here - not under write action
|
||||
private final Map<Document, Alarm> myLineStatusUpdateAlarms;
|
||||
|
||||
private final HashMap<Document, Alarm> myLineStatusUpdateAlarms =
|
||||
new HashMap<Document, Alarm>();
|
||||
|
||||
private final Object TRACKERS_LOCK = new Object();
|
||||
private boolean myIsDisposed = false;
|
||||
@NonNls protected static final String IGNORE_CHANGEMARKERS_KEY = "idea.ignore.changemarkers";
|
||||
|
||||
private final ProjectLevelVcsManagerImpl myVcsManager;
|
||||
private final VcsFileStatusProvider myStatusProvider;
|
||||
private final Application myApplication;
|
||||
private final FileEditorManager myFileEditorManager;
|
||||
private final Disposable myDisposable;
|
||||
|
||||
public LineStatusTrackerManager(final Project project, final ProjectLevelVcsManagerImpl vcsManager, final VcsFileStatusProvider statusProvider) {
|
||||
public LineStatusTrackerManager(final Project project, final ProjectLevelVcsManagerImpl vcsManager, final VcsFileStatusProvider statusProvider,
|
||||
final Application application, final FileEditorManager fileEditorManager) {
|
||||
myProject = project;
|
||||
myVcsManager = vcsManager;
|
||||
myStatusProvider = statusProvider;
|
||||
myApplication = application;
|
||||
myFileEditorManager = fileEditorManager;
|
||||
myLineStatusTrackers = new HashMap<Document, LineStatusTracker>();
|
||||
myLineStatusUpdateAlarms = Collections.synchronizedMap(new HashMap<Document, Alarm>());
|
||||
|
||||
project.getMessageBus().connect().subscribe(DocumentBulkUpdateListener.TOPIC, new DocumentBulkUpdateListener.Adapter() {
|
||||
public void updateStarted(final Document doc) {
|
||||
myApplication.assertWriteAccessAllowed(); // can remove after some testing
|
||||
|
||||
final LineStatusTracker tracker = getLineStatusTracker(doc);
|
||||
if (tracker != null) tracker.startBulkUpdate();
|
||||
}
|
||||
|
||||
public void updateFinished(final Document doc) {
|
||||
myApplication.assertWriteAccessAllowed(); // can remove after some testing
|
||||
|
||||
final LineStatusTracker tracker = getLineStatusTracker(doc);
|
||||
if (tracker != null) tracker.finishBulkUpdate();
|
||||
}
|
||||
});
|
||||
|
||||
myDisposable = new Disposable() {
|
||||
@Override
|
||||
public void dispose() {
|
||||
final Collection<LineStatusTracker> trackers = myLineStatusTrackers.values();
|
||||
final LineStatusTracker[] lineStatusTrackers = trackers.toArray(new LineStatusTracker[trackers.size()]);
|
||||
for (final LineStatusTracker tracker : lineStatusTrackers) {
|
||||
releaseTracker(tracker.getDocument());
|
||||
}
|
||||
|
||||
myLineStatusTrackers.clear();
|
||||
assert myLineStatusUpdateAlarms.isEmpty();
|
||||
myLineStatusUpdateAlarms.clear();
|
||||
}
|
||||
};
|
||||
Disposer.register(myProject, myDisposable);
|
||||
}
|
||||
|
||||
public void projectOpened() {
|
||||
trackAwtThread();
|
||||
final MyFileStatusListener fileStatusListener = new MyFileStatusListener();
|
||||
final EditorFactoryListener editorFactoryListener = new MyEditorFactoryListener();
|
||||
final MyVirtualFileListener virtualFileListener = new MyVirtualFileListener();
|
||||
@@ -109,7 +143,6 @@ public class LineStatusTrackerManager implements ProjectComponent {
|
||||
}
|
||||
};
|
||||
|
||||
myLineStatusTrackers = new HashMap<Document, LineStatusTracker>();
|
||||
final FileStatusManager fsManager = FileStatusManager.getInstance(myProject);
|
||||
fsManager.addFileStatusListener(fileStatusListener, myProject);
|
||||
|
||||
@@ -122,9 +155,8 @@ public class LineStatusTrackerManager implements ProjectComponent {
|
||||
final EditorColorsManager editorColorsManager = EditorColorsManager.getInstance();
|
||||
editorColorsManager.addEditorColorsListener(editorColorsListener);
|
||||
|
||||
Disposer.register(myProject, new Disposable() {
|
||||
Disposer.register(myDisposable, new Disposable() {
|
||||
public void dispose() {
|
||||
trackAwtThread();
|
||||
fsManager.removeFileStatusListener(fileStatusListener);
|
||||
virtualFileManager.removeVirtualFileListener(virtualFileListener);
|
||||
editorColorsManager.removeEditorColorsListener(editorColorsListener);
|
||||
@@ -133,13 +165,6 @@ public class LineStatusTrackerManager implements ProjectComponent {
|
||||
}
|
||||
|
||||
public void projectClosed() {
|
||||
try {
|
||||
trackAwtThread();
|
||||
dispose();
|
||||
}
|
||||
finally {
|
||||
myIsDisposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNls @NotNull
|
||||
@@ -153,213 +178,212 @@ public class LineStatusTrackerManager implements ProjectComponent {
|
||||
public void disposeComponent() {
|
||||
}
|
||||
|
||||
private void dispose() {
|
||||
final Collection<LineStatusTracker> trackers = myLineStatusTrackers.values();
|
||||
final LineStatusTracker[] lineStatusTrackers = trackers.toArray(new LineStatusTracker[trackers.size()]);
|
||||
for (LineStatusTracker tracker : lineStatusTrackers) {
|
||||
releaseTracker(tracker.getDocument());
|
||||
}
|
||||
@Override
|
||||
public LineStatusTracker getLineStatusTracker(final Document document) {
|
||||
myApplication.assertReadAccessAllowed();
|
||||
|
||||
myLineStatusTrackers = null;
|
||||
}
|
||||
|
||||
public LineStatusTracker getLineStatusTracker(Document document) {
|
||||
trackAwtThread();
|
||||
if (myLineStatusTrackers == null) return null;
|
||||
if ((! myProject.isOpen()) || myProject.isDisposed()) return null;
|
||||
return myLineStatusTrackers.get(document);
|
||||
}
|
||||
|
||||
|
||||
public LineStatusTracker setUpToDateContent(final Document document, final String lastUpToDateContent) {
|
||||
trackAwtThread();
|
||||
LineStatusTracker result = myLineStatusTrackers.get(document);
|
||||
if (result == null) {
|
||||
result = LineStatusTracker.createOn(document, lastUpToDateContent, myProject);
|
||||
myLineStatusTrackers.put(document, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private LineStatusTracker createTrackerForDocument(Document document, VirtualFile vf) {
|
||||
LOG.assertTrue(!myLineStatusTrackers.containsKey(document));
|
||||
LineStatusTracker result = LineStatusTracker.createOn(document, myProject);
|
||||
myLineStatusTrackers.put(document, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private void resetTracker(final VirtualFile virtualFile) {
|
||||
if (System.getProperty(IGNORE_CHANGEMARKERS_KEY) != null) return;
|
||||
private void resetTracker(@NotNull final VirtualFile virtualFile) {
|
||||
if ((! myProject.isOpen()) || myProject.isDisposed()) return;
|
||||
|
||||
final Document document = FileDocumentManager.getInstance().getCachedDocument(virtualFile);
|
||||
if (document == null) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Skipping resetTracker() because no cached document for " + virtualFile.getPath());
|
||||
}
|
||||
log("Skipping resetTracker() because no cached document for " + virtualFile.getPath());
|
||||
return;
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("resetting tracker for file " + virtualFile.getPath());
|
||||
}
|
||||
synchronized (TRACKERS_LOCK) {
|
||||
final LineStatusTracker tracker = myLineStatusTrackers.get(document);
|
||||
if (tracker != null) {
|
||||
resetTracker(tracker);
|
||||
}
|
||||
else {
|
||||
if (Arrays.asList(FileEditorManager.getInstance(myProject).getOpenFiles()).contains(virtualFile)) {
|
||||
log("resetting tracker for file " + virtualFile.getPath());
|
||||
|
||||
final LineStatusTracker tracker = myLineStatusTrackers.get(document);
|
||||
final boolean editorOpened = myFileEditorManager.isFileOpen(virtualFile);
|
||||
final boolean shouldBeInstalled = shouldBeInstalled(virtualFile) && editorOpened;
|
||||
|
||||
if (tracker == null && (! shouldBeInstalled)) return;
|
||||
|
||||
myApplication.runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// remove ?
|
||||
if (tracker != null) {
|
||||
if (! shouldBeInstalled) {
|
||||
releaseTracker(document);
|
||||
return;
|
||||
} else if ((! tracker.isBaseLoaded())) {
|
||||
return; // will be recalculated
|
||||
} else {
|
||||
tracker.resetForBaseRevisionLoad();
|
||||
startAlarm(document, virtualFile);
|
||||
}
|
||||
} else if (shouldBeInstalled) {
|
||||
installTracker(virtualFile, document);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean releaseTracker(Document document) {
|
||||
synchronized (TRACKERS_LOCK) {
|
||||
releaseUpdateAlarms(document);
|
||||
if (myLineStatusTrackers == null) return false;
|
||||
if (!myLineStatusTrackers.containsKey(document)) return false;
|
||||
LineStatusTracker tracker = myLineStatusTrackers.remove(document);
|
||||
tracker.release();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
private void releaseTracker(final Document document) {
|
||||
if ((! myProject.isOpen()) || myProject.isDisposed()) return;
|
||||
|
||||
private void releaseUpdateAlarms(final Document document) {
|
||||
if (myLineStatusUpdateAlarms.containsKey(document)) {
|
||||
final Alarm alarm = myLineStatusUpdateAlarms.get(document);
|
||||
if (alarm != null) {
|
||||
alarm.cancelAllRequests();
|
||||
myApplication.runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final Alarm alarm = myLineStatusUpdateAlarms.remove(document);
|
||||
if (alarm != null) {
|
||||
alarm.cancelAllRequests();
|
||||
}
|
||||
final LineStatusTracker tracker = myLineStatusTrackers.remove(document);
|
||||
if (tracker != null) {
|
||||
tracker.release();
|
||||
}
|
||||
}
|
||||
myLineStatusUpdateAlarms.remove(document);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void resetTracker(final LineStatusTracker tracker) {
|
||||
trackAwtThread();
|
||||
if (tracker != null) {
|
||||
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
||||
private boolean shouldBeInstalled(final VirtualFile virtualFile) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
|
||||
if (virtualFile == null || virtualFile instanceof LightVirtualFile) return false;
|
||||
if (! virtualFile.isInLocalFileSystem()) return false;
|
||||
if ((! myProject.isOpen()) || myProject.isDisposed()) return false;
|
||||
final FileStatusManager statusManager = FileStatusManager.getInstance(myProject);
|
||||
if (statusManager == null) return false;
|
||||
final AbstractVcs activeVcs = myVcsManager.getVcsFor(virtualFile);
|
||||
if (activeVcs == null) {
|
||||
log("installTracker() for file " + virtualFile.getPath() + " failed: no active VCS");
|
||||
return false;
|
||||
}
|
||||
final FileStatus status = statusManager.getStatus(virtualFile);
|
||||
if (status == FileStatus.NOT_CHANGED || status == FileStatus.ADDED || status == FileStatus.UNKNOWN || status == FileStatus.IGNORED) {
|
||||
log("installTracker() for file " + virtualFile.getPath() + " failed: status=" + status);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void installTracker(final VirtualFile virtualFile, final Document document) {
|
||||
/*ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
|
||||
if (virtualFile == null || virtualFile instanceof LightVirtualFile) return;
|
||||
if (! virtualFile.isInLocalFileSystem()) return;
|
||||
if ((! myProject.isOpen()) || myProject.isDisposed()) return;
|
||||
final FileStatusManager statusManager = FileStatusManager.getInstance(myProject);
|
||||
if (statusManager == null) return;
|
||||
final AbstractVcs activeVcs = myVcsManager.getVcsFor(virtualFile);
|
||||
if (activeVcs == null) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.info("installTracker() for file " + virtualFile.getPath() + " failed: no active VCS");
|
||||
}
|
||||
return;
|
||||
}
|
||||
final FileStatus status = statusManager.getStatus(virtualFile);
|
||||
if (status == FileStatus.NOT_CHANGED || status == FileStatus.ADDED || status == FileStatus.UNKNOWN || status == FileStatus.IGNORED) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.info("installTracker() for file " + virtualFile.getPath() + " failed: status=" + status);
|
||||
System.out.println("installTracker() for file " + virtualFile.getPath() + " failed: status=" + status);
|
||||
}
|
||||
return;
|
||||
} */
|
||||
|
||||
myApplication.runWriteAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (myLineStatusTrackers.containsKey(document)) return;
|
||||
assert !myLineStatusUpdateAlarms.containsKey(document);
|
||||
|
||||
final LineStatusTracker tracker = LineStatusTracker.createOn(document, myProject);
|
||||
myLineStatusTrackers.put(document, tracker);
|
||||
|
||||
startAlarm(document, virtualFile);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void startAlarm(final Document document, final VirtualFile virtualFile) {
|
||||
myApplication.assertWriteAccessAllowed();
|
||||
|
||||
final Alarm alarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD);
|
||||
myLineStatusUpdateAlarms.put(document, alarm);
|
||||
alarm.addRequest(new BaseRevisionLoader(alarm, document, virtualFile), 10);
|
||||
}
|
||||
|
||||
private class BaseRevisionLoader implements Runnable {
|
||||
private final Alarm myAlarm;
|
||||
private final VirtualFile myVirtualFile;
|
||||
private final Document myDocument;
|
||||
|
||||
private BaseRevisionLoader(final Alarm alarm, final Document document, final VirtualFile virtualFile) {
|
||||
myAlarm = alarm;
|
||||
myDocument = document;
|
||||
myVirtualFile = virtualFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
myAlarm.cancelAllRequests();
|
||||
final Boolean removed = myApplication.runReadAction(new Computable<Boolean>() {
|
||||
@Override
|
||||
public Boolean compute() {
|
||||
final Alarm removed = myLineStatusUpdateAlarms.remove(myDocument);
|
||||
return removed != null;
|
||||
}
|
||||
});
|
||||
if (! Boolean.TRUE.equals(removed)) return;
|
||||
if ((! myProject.isOpen()) || myProject.isDisposed()) return;
|
||||
|
||||
if (! myVirtualFile.isValid()) {
|
||||
log("installTracker() for file " + myVirtualFile.getPath() + " failed: virtual file not valid");
|
||||
return;
|
||||
}
|
||||
|
||||
final String lastUpToDateContent = myStatusProvider.getBaseVersionContent(myVirtualFile);
|
||||
if (lastUpToDateContent == null) {
|
||||
log("installTracker() for file " + myVirtualFile.getPath() + " failed: no up to date content");
|
||||
return;
|
||||
}
|
||||
|
||||
myApplication.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
if (myIsDisposed) return;
|
||||
if (releaseTracker(tracker.getDocument())) {
|
||||
installTracker(tracker.getVirtualFile(), tracker.getDocument());
|
||||
}
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
public void run() {
|
||||
log("initializing tracker for file " + myVirtualFile.getPath());
|
||||
final LineStatusTracker tracker = myLineStatusTrackers.get(myDocument);
|
||||
if (tracker != null) {
|
||||
tracker.initialize(lastUpToDateContent);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}, new Condition() {
|
||||
@Override
|
||||
public boolean value(final Object ignore) {
|
||||
return (! myProject.isOpen()) || myProject.isDisposed();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void installTracker(final VirtualFile virtualFile, final Document document) {
|
||||
if (virtualFile == null || virtualFile instanceof LightVirtualFile) return;
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
|
||||
if (myProject.isDisposed() || myLineStatusTrackers == null) return;
|
||||
final FileStatusManager statusManager = FileStatusManager.getInstance(myProject);
|
||||
if (statusManager == null) return;
|
||||
final FileStatus status = statusManager.getStatus(virtualFile);
|
||||
|
||||
synchronized (TRACKERS_LOCK) {
|
||||
if (myLineStatusTrackers.containsKey(document)) return;
|
||||
|
||||
if (status == FileStatus.NOT_CHANGED ||
|
||||
status == FileStatus.ADDED ||
|
||||
status == FileStatus.UNKNOWN ||
|
||||
status == FileStatus.IGNORED) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("installTracker() for file " + virtualFile.getPath() + " failed: status=" + status);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
AbstractVcs activeVcs = myVcsManager.getVcsFor(virtualFile);
|
||||
|
||||
if (activeVcs == null) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("installTracker() for file " + virtualFile.getPath() + " failed: no active VCS");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!virtualFile.isInLocalFileSystem()) return;
|
||||
|
||||
if (System.getProperty(IGNORE_CHANGEMARKERS_KEY) != null) return;
|
||||
|
||||
final Alarm alarm;
|
||||
|
||||
if (myLineStatusUpdateAlarms.containsKey(document)) {
|
||||
alarm = myLineStatusUpdateAlarms.get(document);
|
||||
alarm.cancelAllRequests();
|
||||
}
|
||||
else {
|
||||
alarm = new Alarm(Alarm.ThreadToUse.SHARED_THREAD);
|
||||
myLineStatusUpdateAlarms.put(document, alarm);
|
||||
}
|
||||
|
||||
final LineStatusTracker tracker = createTrackerForDocument(document, virtualFile);
|
||||
|
||||
alarm.addRequest(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
alarm.cancelAllRequests();
|
||||
if (!virtualFile.isValid()) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("installTracker() for file " + virtualFile.getPath() + " failed: virtual file not valid");
|
||||
}
|
||||
return;
|
||||
}
|
||||
final String lastUpToDateContent = myStatusProvider.getBaseVersionContent(virtualFile);
|
||||
if (lastUpToDateContent == null) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("installTracker() for file " + virtualFile.getPath() + " failed: no up to date content");
|
||||
}
|
||||
return;
|
||||
}
|
||||
ApplicationManager.getApplication().invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
if (!myProject.isDisposed()) {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
public void run() {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("initializing tracker for file " + virtualFile.getPath());
|
||||
}
|
||||
synchronized (TRACKERS_LOCK) {
|
||||
tracker.initialize(lastUpToDateContent);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
finally {
|
||||
// todo guard alarms!!!
|
||||
myLineStatusUpdateAlarms.remove(document);
|
||||
}
|
||||
}
|
||||
}, 10);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void resetTrackersForOpenFiles() {
|
||||
final VirtualFile[] openFiles = FileEditorManager.getInstance(myProject).getOpenFiles();
|
||||
synchronized (TRACKERS_LOCK) {
|
||||
for(VirtualFile openFile: openFiles) {
|
||||
resetTracker(openFile);
|
||||
}
|
||||
myApplication.assertReadAccessAllowed();
|
||||
if ((! myProject.isOpen()) || myProject.isDisposed()) return;
|
||||
|
||||
final VirtualFile[] openFiles = myFileEditorManager.getOpenFiles();
|
||||
for(final VirtualFile openFile: openFiles) {
|
||||
// write action inside is sufficient level
|
||||
resetTracker(openFile);
|
||||
}
|
||||
}
|
||||
|
||||
private class MyFileStatusListener implements FileStatusListener {
|
||||
public void fileStatusesChanged() {
|
||||
if (myProject.isDisposed()) return;
|
||||
LOG.debug("LineStatusTrackerManager: fileStatusesChanged");
|
||||
trackAwtThread();
|
||||
log("LineStatusTrackerManager: fileStatusesChanged");
|
||||
resetTrackersForOpenFiles();
|
||||
}
|
||||
|
||||
public void fileStatusChanged(@NotNull VirtualFile virtualFile) {
|
||||
trackAwtThread();
|
||||
resetTracker(virtualFile);
|
||||
}
|
||||
}
|
||||
@@ -370,33 +394,46 @@ public class LineStatusTrackerManager implements ProjectComponent {
|
||||
// outside of EDT, so the EDT check mustn't be done here
|
||||
Editor editor = event.getEditor();
|
||||
if (editor.getProject() != null && editor.getProject() != myProject) return;
|
||||
Document document = editor.getDocument();
|
||||
VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document);
|
||||
installTracker(virtualFile, document);
|
||||
final Document document = editor.getDocument();
|
||||
final VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document);
|
||||
|
||||
new AbstractCalledLater(myProject, ModalityState.NON_MODAL) {
|
||||
@Override
|
||||
public void run() {
|
||||
if (shouldBeInstalled(virtualFile)) {
|
||||
installTracker(virtualFile, document);
|
||||
}
|
||||
}
|
||||
}.callMe();
|
||||
}
|
||||
|
||||
public void editorReleased(EditorFactoryEvent event) {
|
||||
trackAwtThread();
|
||||
final Editor editor = event.getEditor();
|
||||
if (editor.getProject() != null && editor.getProject() != myProject) return;
|
||||
final Document doc = editor.getDocument();
|
||||
final Editor[] editors = event.getFactory().getEditors(doc, myProject);
|
||||
if (editors.length == 0) {
|
||||
releaseTracker(doc);
|
||||
new AbstractCalledLater(myProject, ModalityState.NON_MODAL) {
|
||||
@Override
|
||||
public void run() {
|
||||
releaseTracker(doc);
|
||||
}
|
||||
}.callMe();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class MyVirtualFileListener extends VirtualFileAdapter {
|
||||
public void beforeContentsChange(VirtualFileEvent event) {
|
||||
trackAwtThread();
|
||||
if (event.isFromRefresh()) {
|
||||
resetTracker(event.getFile());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void trackAwtThread() {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
private static void log(final String s) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2000-2010 JetBrains s.r.o.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.openapi.vcs.impl;
|
||||
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.vcs.ex.LineStatusTracker;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* @author irengrig
|
||||
*/
|
||||
public interface LineStatusTrackerManagerI {
|
||||
@Nullable
|
||||
LineStatusTracker getLineStatusTracker(Document document);
|
||||
|
||||
class Dummy implements LineStatusTrackerManagerI {
|
||||
private final static Dummy ourInstance = new Dummy();
|
||||
|
||||
public static Dummy getInstance() {
|
||||
return ourInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LineStatusTracker getLineStatusTracker(final Document document) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -40,7 +40,7 @@ public class UpToDateLineNumberProviderImpl implements UpToDateLineNumberProvide
|
||||
public int getLineNumber(int currentNumber) {
|
||||
LineStatusTracker tracker = LineStatusTrackerManager.getInstance(myProject).getLineStatusTracker(myDocument);
|
||||
if (tracker == null) {
|
||||
tracker = LineStatusTrackerManager.getInstance(myProject).setUpToDateContent(myDocument, myUpToDateContent);
|
||||
return currentNumber;
|
||||
}
|
||||
return calcLineNumber(tracker, currentNumber);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user