From 1a71d0a512ccfd70ae66c4980254ec2bd485cd34 Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Mon, 31 Jan 2011 16:25:03 +0300 Subject: [PATCH] range markers made weakly referenced --- .../injected/editor/DocumentWindowImpl.java | 4 +- .../injected/editor/SelectionModelWindow.java | 8 +- .../tree/injected/MultiHostRegistrarImpl.java | 1 + .../incrementalMerge/DiffRangeMarker.java | 3 +- .../openapi/editor/ex/DocumentEx.java | 2 +- .../openapi/editor/impl/DocumentImpl.java | 30 +- .../openapi/editor/impl/FoldRegionImpl.java | 4 +- .../openapi/editor/impl/FoldingModelImpl.java | 1 - .../openapi/editor/impl/IntervalTree.java | 5 +- .../openapi/editor/impl/IntervalTreeImpl.java | 569 ++++++++++++------ .../openapi/editor/impl/MarkupModelImpl.java | 10 +- .../editor/impl/PersistentLineMarker.java | 65 -- .../impl/PersistentRangeHighlighterImpl.java | 168 ++---- .../editor/impl/PersistentRangeMarker.java | 10 +- .../editor/impl/RangeHighlighterData.java | 25 +- .../editor/impl/RangeHighlighterImpl.java | 77 +-- .../editor/impl/RangeHighlighterTree.java | 40 +- .../openapi/editor/impl/RangeMarkerImpl.java | 183 +++--- .../openapi/editor/impl/RangeMarkerTree.java | 173 ++++-- .../openapi/editor/impl/RedBlackTree.java | 179 +----- .../editor/impl/SelectionModelImpl.java | 102 ++-- .../src/com/intellij/mock/MockDocument.java | 2 +- 22 files changed, 838 insertions(+), 823 deletions(-) delete mode 100644 platform/platform-impl/src/com/intellij/openapi/editor/impl/PersistentLineMarker.java diff --git a/platform/lang-impl/src/com/intellij/injected/editor/DocumentWindowImpl.java b/platform/lang-impl/src/com/intellij/injected/editor/DocumentWindowImpl.java index 0ba92d9eaff1..18337b955264 100644 --- a/platform/lang-impl/src/com/intellij/injected/editor/DocumentWindowImpl.java +++ b/platform/lang-impl/src/com/intellij/injected/editor/DocumentWindowImpl.java @@ -527,8 +527,8 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable return myDelegate.removeRangeMarker(((RangeMarkerWindow)rangeMarker).getDelegate()); } - public void addRangeMarker(@NotNull RangeMarkerEx rangeMarker) { - myDelegate.addRangeMarker(((RangeMarkerWindow)rangeMarker).getDelegate()); + public void addRangeMarker(@NotNull RangeMarkerEx rangeMarker, int start, int end) { + myDelegate.addRangeMarker(((RangeMarkerWindow)rangeMarker).getDelegate(), start, end); } public boolean isInBulkUpdate() { diff --git a/platform/lang-impl/src/com/intellij/injected/editor/SelectionModelWindow.java b/platform/lang-impl/src/com/intellij/injected/editor/SelectionModelWindow.java index 78fcf1afec8f..6cd3fd69b43c 100644 --- a/platform/lang-impl/src/com/intellij/injected/editor/SelectionModelWindow.java +++ b/platform/lang-impl/src/com/intellij/injected/editor/SelectionModelWindow.java @@ -152,11 +152,13 @@ public class SelectionModelWindow implements SelectionModel { } public LogicalPosition getBlockStart() { - return myInjectedEditor.hostToInjected(myHostModel.getBlockStart()); + LogicalPosition hostBlock = myHostModel.getBlockStart(); + return hostBlock == null ? null : myInjectedEditor.hostToInjected(hostBlock); } public LogicalPosition getBlockEnd() { - return myInjectedEditor.hostToInjected(myHostModel.getBlockEnd()); + LogicalPosition hostBlock = myHostModel.getBlockEnd(); + return hostBlock == null ? null : myInjectedEditor.hostToInjected(hostBlock); } public boolean isBlockSelectionGuarded() { @@ -170,4 +172,4 @@ public class SelectionModelWindow implements SelectionModel { public TextAttributes getTextAttributes() { return myHostModel.getTextAttributes(); } -} \ No newline at end of file +} diff --git a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java index 2dd318b8cf07..88494a37acad 100644 --- a/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java +++ b/platform/lang-impl/src/com/intellij/psi/impl/source/tree/injected/MultiHostRegistrarImpl.java @@ -417,6 +417,7 @@ public class MultiHostRegistrarImpl implements MultiHostRegistrar { TextRange newRange = InjectedLanguageUtil.toTextRange(newMarker); for (int i = 0; i < injectedRegions.size(); i++) { RangeMarker stored = injectedRegions.get(i); + if (!stored.isValid()) continue; TextRange storedRange = InjectedLanguageUtil.toTextRange(stored); if (storedRange.intersects(newRange)) { injectedRegions.set(i, newMarker); diff --git a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/DiffRangeMarker.java b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/DiffRangeMarker.java index 301788d55f56..6e6d8665b1d9 100644 --- a/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/DiffRangeMarker.java +++ b/platform/platform-impl/src/com/intellij/openapi/diff/impl/incrementalMerge/DiffRangeMarker.java @@ -31,8 +31,7 @@ class DiffRangeMarker extends RangeMarkerImpl { private RangeInvalidListener myListener; DiffRangeMarker(DocumentEx document, TextRange range, RangeInvalidListener listener) { - super(document, range.getStartOffset(), range.getEndOffset()); - registerInDocument(); + super(document, range.getStartOffset(), range.getEndOffset(),true); myListener = listener; if (myListener != null) InvalidRangeDispatcher.addClient(document); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/ex/DocumentEx.java b/platform/platform-impl/src/com/intellij/openapi/editor/ex/DocumentEx.java index 1aa39b8e864b..1d7cff61ee85 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/ex/DocumentEx.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/ex/DocumentEx.java @@ -50,7 +50,7 @@ public interface DocumentEx extends Document { void clearLineModificationFlags(); boolean removeRangeMarker(@NotNull RangeMarkerEx rangeMarker); - void addRangeMarker(@NotNull RangeMarkerEx rangeMarker); + void addRangeMarker(@NotNull RangeMarkerEx rangeMarker, int start, int end); boolean isInBulkUpdate(); diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java index b77a0c3e2550..d158bae6c458 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java @@ -41,6 +41,7 @@ import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.util.LocalTimeCounter; import com.intellij.util.Processor; import com.intellij.util.containers.ConcurrentHashMap; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.text.CharArrayUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -50,14 +51,14 @@ import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Collections; +import java.util.Arrays; import java.util.List; import java.util.concurrent.ConcurrentMap; public class DocumentImpl extends UserDataHolderBase implements DocumentEx { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.DocumentImpl"); - private final List myDocumentListeners = new ArrayList(); + private final List myDocumentListeners = ContainerUtil.createEmptyCOWList(); private final RangeMarkerTree myRangeMarkers = new RangeMarkerTree(this); private final List myGuardedBlocks = new ArrayList(); private ReadonlyFragmentModificationHandler myReadonlyFragmentModificationHandler; @@ -73,7 +74,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { private volatile MarkupModelEx myMarkupModel; private DocumentListener[] myCachedDocumentListeners; - private final List myReadOnlyListeners = new ArrayList(1); + private final List myReadOnlyListeners = ContainerUtil.createEmptyCOWList(); private int myCheckGuardedBlocks = 0; private boolean myGuardsSuppressed = false; @@ -209,12 +210,12 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { public boolean removeRangeMarker(@NotNull RangeMarkerEx rangeMarker) { ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed(); - return myRangeMarkers.remove(rangeMarker); + return myRangeMarkers.removeInterval(rangeMarker); } - public void addRangeMarker(@NotNull RangeMarkerEx rangeMarker) { + public void addRangeMarker(@NotNull RangeMarkerEx rangeMarker, int start, int end) { ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed(); - myRangeMarkers.add(rangeMarker); + myRangeMarkers.addInterval(rangeMarker, start, end, null); } @TestOnly @@ -293,11 +294,9 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { if (!(0 <= startOffset && startOffset <= endOffset && endOffset <= getTextLength())) { LOG.error("Incorrect offsets startOffset=" + startOffset + ", endOffset=" + endOffset + ", text length=" + getTextLength()); } - RangeMarkerImpl rangeMarker = surviveOnExternalChange - ? new PersistentRangeMarker(this, startOffset, endOffset) - : new RangeMarkerImpl(this, startOffset, endOffset); - rangeMarker.registerInDocument(); - return rangeMarker; + return surviveOnExternalChange + ? new PersistentRangeMarker(this, startOffset, endOffset,true) + : new RangeMarkerImpl(this, startOffset, endOffset,true); } public long getModificationStamp() { @@ -607,8 +606,9 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { private DocumentListener[] getCachedListeners() { if (myCachedDocumentListeners == null) { - Collections.sort(myDocumentListeners, PrioritizedDocumentListener.COMPARATOR); - myCachedDocumentListeners = myDocumentListeners.toArray(new DocumentListener[myDocumentListeners.size()]); + DocumentListener[] listeners = myDocumentListeners.toArray(new DocumentListener[myDocumentListeners.size()]); + Arrays.sort(listeners, PrioritizedDocumentListener.COMPARATOR); + myCachedDocumentListeners = listeners; } return myCachedDocumentListeners; @@ -616,9 +616,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { public void fireReadOnlyModificationAttempt() { ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed(); - EditReadOnlyListener[] listeners = myReadOnlyListeners.toArray( - new EditReadOnlyListener[myReadOnlyListeners.size()]); - for (EditReadOnlyListener listener : listeners) { + for (EditReadOnlyListener listener : myReadOnlyListeners) { listener.readOnlyModificationAttempt(this); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldRegionImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldRegionImpl.java index 903ce99efcc1..96329e81e785 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldRegionImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldRegionImpl.java @@ -44,7 +44,7 @@ public class FoldRegionImpl extends RangeMarkerImpl implements FoldRegion { @NotNull String placeholder, FoldingGroup group, boolean shouldNeverExpand) { - super((DocumentEx)editor.getDocument(), startOffset, endOffset); + super((DocumentEx)editor.getDocument(), startOffset, endOffset,true); myGroup = group; myShouldNeverExpand = shouldNeverExpand; myIsExpanded = true; @@ -77,7 +77,7 @@ public class FoldRegionImpl extends RangeMarkerImpl implements FoldRegion { } public boolean isValid() { - return super.isValid() && myStart + 1 < myEnd; + return super.isValid() && intervalStart() + 1 < intervalEnd(); } public void setExpandedInternal(boolean toExpand) { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldingModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldingModelImpl.java index f46d773a9f57..53920ca66252 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldingModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/FoldingModelImpl.java @@ -458,7 +458,6 @@ public class FoldingModelImpl implements FoldingModelEx, PrioritizedDocumentList public FoldRegion createFoldRegion(int startOffset, int endOffset, @NotNull String placeholder, FoldingGroup group, boolean neverExpands) { FoldRegionImpl region = new FoldRegionImpl(myEditor, startOffset, endOffset, placeholder, group, neverExpands); - region.registerInDocument(); LOG.assertTrue(region.isValid()); return region; } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/IntervalTree.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/IntervalTree.java index 649af003ad6e..120878b3c521 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/IntervalTree.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/IntervalTree.java @@ -26,7 +26,6 @@ public interface IntervalTree extends Iterable { boolean processOverlappingWith(int start, int end, @NotNull Processor processor); boolean processOverlappingWith(int offset, @NotNull Processor processor); - Object add(@NotNull T interval); - boolean remove(@NotNull T interval); - + Object addInterval(@NotNull T interval, int start, int end, Object data); + boolean removeInterval(@NotNull T interval); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/IntervalTreeImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/IntervalTreeImpl.java index 02d90e64dc35..76a499692d42 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/IntervalTreeImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/IntervalTreeImpl.java @@ -16,14 +16,18 @@ package com.intellij.openapi.editor.impl; import com.intellij.openapi.editor.ex.RangeMarkerEx; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.Trinity; import com.intellij.util.IncorrectOperationException; import com.intellij.util.Processor; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; -import java.util.Iterator; -import java.util.NoSuchElementException; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -32,107 +36,152 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; */ public abstract class IntervalTreeImpl extends RedBlackTree implements IntervalTree { protected final ReadWriteLock l = new ReentrantReadWriteLock(); - private final EqualStartIntervalComparator comparator; - public class MyNode extends DeltaNode { - public MyNode(T key) { - super(key); + protected abstract EqualStartIntervalComparator getComparator(); + private final ReferenceQueue myReferenceQueue = new ReferenceQueue(); + private int deadReferenceCount; + + public abstract static class IntervalNode extends RedBlackTree.Node implements MutableInterval { + protected int maxEnd; // max of all intervalEnd()s among all children. + protected int delta; // delta of startOffset. getStartOffset() = myStartOffset + Sum of deltas up to root + protected abstract int computeDeltaUpToRoot(); + @Override + public IntervalNode getLeft() { + return (IntervalNode)super.getLeft(); } @Override - public MyNode getLeft() { - return (MyNode)super.getLeft(); + public IntervalNode getRight() { + return (IntervalNode)super.getRight(); } @Override - public MyNode getRight() { - return (MyNode)super.getRight(); + public IntervalNode getParent() { + return (IntervalNode)super.getParent(); } + } + protected class MyNode extends IntervalNode { + + private volatile int myStart; + private volatile int myEnd; + private volatile boolean isValid = true; + private final Reference interval; + public MyNode(@NotNull T key, int start, int end) { + // maxEnd == 0 so to not disrupt existing maxes + interval = new WeakReference(key,myReferenceQueue); + myStart = start; + myEnd = end; + } @Override - public MyNode getParent() { - return (MyNode)super.getParent(); + public T getKey() { + return interval.get(); } @Override protected int computeDeltaUpToRoot() { if (normalized) return 0; - return super.computeDeltaUpToRoot(); + int delta = 0; + IntervalNode node = this; + while (node != null) { + if (node.isValid()) { + delta += node.delta; + } + node = node.getParent(); + } + return delta; } - } - private void pushDeltaFromRoot(MyNode node) { + @Override + public int setIntervalStart(int start) { + return myStart = start; + } + + @Override + public int setIntervalEnd(int end) { + return myEnd = end; + } + + @Override + public boolean isValid() { + return isValid; + } + + @Override + public boolean setValid(boolean value) { + return isValid = value; + } + + @Override + public int intervalStart() { + return myStart; + } + + @Override + public int intervalEnd() { + return myEnd; + } + + public IntervalTreeImpl getTree() { + return IntervalTreeImpl.this; + } + + } + private void pushDeltaFromRoot(IntervalNode node) { + if (normalized) return; if (node != null) { pushDeltaFromRoot(node.getParent()); pushDelta(node); } } - protected static class DeltaNode extends RedBlackTree.Node { - protected int maxEnd; // max of all intervalEnd()s among all children. - protected int delta; // delta of startOffset. getStartOffset() = myStartOffset + Sum of deltas up to root - public DeltaNode(T key) { - // maxEnd == 0 so to not disrupt existing maxes - super(key); - } - - protected int computeDeltaUpToRoot() { - int delta = 0; - DeltaNode node = this; - while (node != null) { - if (key.isValid()) { - delta += node.delta; - } - node = (DeltaNode)node.getParent(); - } - return delta; - } - } - - @Override - protected MyNode createNewNode(T key) { - return new MyNode(key); + protected MyNode createNewNode(T key, int start, int end, Object data) { + return new MyNode(key, start, end); } @Override - protected Node lookupNode(T key, Node root) { - int delta = 0; - int delta1 = ((RangeMarkerImpl)key).myNode == null ? 0 : ((RangeMarkerImpl)key).myNode.computeDeltaUpToRoot(); - while (root != null) { - delta += ((MyNode)root).delta; - int compResult = compare(key, delta1, root.key, delta); - if (compResult < 0) { - root = root.getLeft(); - } - else if (compResult > 0) { - root = root.getRight(); - } - else { - return root; - } - } - return root; + protected IntervalNode lookupNode(@NotNull T key, Node interval) { + return ((RangeMarkerImpl)key).myNode; + //int delta = 0; + //int delta1 = ((RangeMarkerImpl)key).myNode == null ? 0 : ((RangeMarkerImpl)key).myNode.computeDeltaUpToRoot(); + //List invalid = new ArrayList(); + //IntervalNode root = (IntervalNode)interval; + //while (root != null) { + // delta += root.delta; + // int compResult = compareNodes(((RangeMarkerImpl)key).myNode, delta1, root, delta, invalid); + // if (compResult < 0) { + // root = root.getLeft(); + // } + // else if (compResult > 0) { + // root = root.getRight(); + // } + // else { + // return root; + // } + //} + //deleteNodes(invalid); + //return root; } - private int compare(T i1, int delta1, T i2, int delta2) { + private int compareNodes(@NotNull IntervalNode i1, int delta1, @NotNull IntervalNode i2, int delta2, List invalid) { + T i2Key = (T)i2.getKey(); + if (i2Key == null) { + invalid.add(i2); //gced + } int start1 = i1.intervalStart() + delta1; int start2 = i2.intervalStart() + delta2; if (start1 != start2) return start1 - start2; - int equalStartCompare = comparator.compare(i1, i2); + //if (i2Key == null) return 1; // by default insert to the right to the gced node + int equalStartCompare = getComparator().compare(i1, i2); return equalStartCompare; } protected interface EqualStartIntervalComparator { + int compare(T i1, T i2); } - - public IntervalTreeImpl(EqualStartIntervalComparator comparator) { - super(null); - this.comparator = comparator; - } - - protected MyNode getRoot() { - return (MyNode)root; + protected IntervalTreeImpl.IntervalNode getRoot() { + return (IntervalNode)root; } public boolean process(@NotNull Processor processor) { @@ -147,12 +196,15 @@ public abstract class IntervalTreeImpl extends RedBla } } - private boolean process(Node root, Processor processor, int modCountBefore) { + private boolean process(IntervalNode root, Processor processor, int modCountBefore) { if (root == null) return true; if (!process(root.getLeft(), processor, modCountBefore)) return false; - if (!processor.process(root.key)) return false; - assert modCount == modCountBefore; + T key = (T)root.getKey(); + if (key != null) { + if (!processor.process(key)) return false; + assert modCount == modCountBefore; + } return process(root.getRight(), processor, modCountBefore); } @@ -169,11 +221,16 @@ public abstract class IntervalTreeImpl extends RedBla } } - private boolean processOverlappingWith(MyNode root, int start, int end, Processor processor, int modCountBefore, int deltaUpToRootExclusive) { + private boolean processOverlappingWith(IntervalNode root, + int start, + int end, + Processor processor, + int modCountBefore, + int deltaUpToRootExclusive) { if (root == null) { return true; } - assert root.key.isValid(); + assert root.isValid(); int delta = deltaUpToRootExclusive + root.delta; if (start > maxEndOf(root, deltaUpToRootExclusive)) { @@ -181,11 +238,14 @@ public abstract class IntervalTreeImpl extends RedBla } if (!processOverlappingWith(root.getLeft(), start, end, processor, modCountBefore, delta)) return false; - int myStartOffset = root.key.intervalStart() + delta; - int myEndOffset = root.key.intervalEnd() + delta; + int myStartOffset = root.intervalStart() + delta; + int myEndOffset = root.intervalEnd() + delta; boolean overlaps = Math.max(myStartOffset, start) <= Math.min(myEndOffset, end); - if (overlaps && !processor.process(root.key)) return false; - assert modCount == modCountBefore; + T key = (T)root.getKey(); + if (key != null) { + if (overlaps && !processor.process(key)) return false; + assert modCount == modCountBefore; + } if (end < myStartOffset) { return true; // left of the root, cant be in the right subtree @@ -206,23 +266,30 @@ public abstract class IntervalTreeImpl extends RedBla } } - private boolean processOverlapping(MyNode root, int offset, Processor processor, int modCountBefore, int deltaUpToRootExclusive) { + private boolean processOverlapping(IntervalNode root, + int offset, + Processor processor, + int modCountBefore, + int deltaUpToRootExclusive) { if (root == null) { return true; } - assert root.key.isValid(); + assert root.isValid(); int delta = deltaUpToRootExclusive + root.delta; if (offset > maxEndOf(root, deltaUpToRootExclusive)) { return true; // right of the rightmost interval in the subtree } if (!processOverlapping(root.getLeft(), offset, processor, modCountBefore, delta)) return false; - int myStartOffset = root.key.intervalStart() + delta; - int myEndOffset = root.key.intervalEnd() + delta; + int myStartOffset = root.intervalStart() + delta; + int myEndOffset = root.intervalEnd() + delta; boolean overlaps = myStartOffset <= offset && offset < myEndOffset; - if (overlaps && !processor.process(root.key)) return false; - assert modCount == modCountBefore; + T key = (T)root.getKey(); + if (key != null) { + if (overlaps && !processor.process(key)) return false; + assert modCount == modCountBefore; + } if (offset < myStartOffset) { return true; // left of the root, cant be in the right subtree @@ -231,61 +298,101 @@ public abstract class IntervalTreeImpl extends RedBla return processOverlapping(root.getRight(), offset, processor, modCountBefore, delta); } - protected void insert(MyNode newNode) { - T key = newNode.key; + protected void insert(@NotNull IntervalNode node) { + node.color = Color.RED; + node.setParent(null); + node.setValid(true); + node.maxEnd = 0; + node.delta = 0; + node.setLeft(null); + node.setRight(null); + + List gced = new ArrayList(); + //T nodeKey = (T)node.getKey(); + //assert nodeKey != null; if (root == null) { - root = newNode; + root = node; } else { - MyNode n = (MyNode)root; + IntervalNode current = (IntervalNode)root; int delta = 0; loop: while (true) { - assert n.key.isValid(); - delta += n.delta; - int compResult = compare(key, 0, n.key, delta); + delta += current.delta; + int compResult = compareNodes(node, 0, current, delta, gced); if (compResult < 0) { - if (n.getLeft() == null) { - n.setLeft(newNode); + if (current.getLeft() == null) { + current.setLeft(node); break loop; } - n = n.getLeft(); + current = current.getLeft(); } else if (compResult > 0) { - if (n.getRight() == null) { - n.setRight(newNode); + if (current.getRight() == null) { + current.setRight(node); break loop; } - n = n.getRight(); + current = current.getRight(); } else { - T i1 = key; - T i2 = n.key; + T i1 = (T)node.getKey(); + T i2 = (T)current.getKey(); int delta1 = 0; int delta2 = delta; - assert false : "already inserted: " + i1 + ":" + delta1 + "; "+ ((RangeMarkerEx)i1).getId() +" <-> " + i2 + ":" + delta2 + "; "+ ((RangeMarkerEx)i2).getId()+ " iden=" + (i1 == i2); + assert false : "already inserted: " + + i1 + + ":" + + delta1 + + "; " + + ((RangeMarkerEx)i1).getId() + + " <-> " + + i2 + + ":" + + delta2 + + "; " + + ((RangeMarkerEx)i2).getId() + + " iden=" + + (i1 == i2); return; } } - newNode.delta = -delta; - newNode.setParent(n); + node.delta = -delta; + node.setParent(current); } size++; - correctMaxUp(newNode); - insertCase1(newNode); + correctMaxUp(node); + insertCase1(node); verifyProperties(); + + deleteNodes(gced); } - public MyNode add(@NotNull T interval) { + private void deleteNodes(List collectedAway) { + if (collectedAway.isEmpty()) return; try { l.writeLock().lock(); checkMax(true); + for (IntervalNode node : collectedAway) { + removeNode(node); + } + checkMax(true); + } + finally { + l.writeLock().unlock(); + } + } + @Override + public IntervalNode addInterval(@NotNull T interval, int start, int end, Object data) { + try { + l.writeLock().lock(); + checkMax(true); + processReferenceQueue(); modCount++; - MyNode newNode = createNewNode(interval); + IntervalNode newNode = createNewNode(interval, start, end, data); insert(newNode); - checkMax(false); // myNode still not assigned + checkMax(true); // myNode assigned return newNode; } finally { @@ -293,35 +400,48 @@ public abstract class IntervalTreeImpl extends RedBla } } - public void checkMax(boolean assertInvalid) { - if (!VERIFY) return; - checkMax(getRoot(), 0, assertInvalid); + // returns true if all markers are valid + public boolean checkMax(boolean assertInvalid) { + if (!VERIFY) return false; + Ref allValid = new Ref(true); + AtomicInteger counter = new AtomicInteger(); + checkMax(getRoot(), 0, assertInvalid, allValid, counter); + if (assertInvalid) { + assert size == counter.get() : "size: "+size+"; actual: "+counter; + } + return allValid.get(); } // returns real (minStart, maxStart, maxEnd) - protected Trinity checkMax(MyNode root, int deltaUpToRootExclusive, boolean assertInvalid) { + protected Trinity checkMax(IntervalNode root, + int deltaUpToRootExclusive, + boolean assertInvalid, + Ref allValid, AtomicInteger counter) { if (root == null) return Trinity.create(Integer.MAX_VALUE,Integer.MIN_VALUE,Integer.MIN_VALUE); - int delta = deltaUpToRootExclusive + (root.key.isValid() ? root.delta : 0); - Trinity l = checkMax(root.getLeft(), delta, assertInvalid); + counter.getAndIncrement(); + int delta = deltaUpToRootExclusive + (root.isValid() ? root.delta : 0); + Trinity l = checkMax(root.getLeft(), delta, assertInvalid, allValid, counter); int minLeftStart = l.first; int maxLeftStart = l.second; int maxLeftEnd = l.third; - Trinity r = checkMax(root.getRight(), delta, assertInvalid); + Trinity r = checkMax(root.getRight(), delta, assertInvalid, allValid, counter); int maxRightEnd = r.third; int minRightStart = r.first; int maxRightStart = r.second; - if (!root.key.isValid()) { - if (assertInvalid) assert false : root.key; + if (!root.isValid()) { + allValid.set(false); + if (assertInvalid) assert false : (T)root; return Trinity.create(Math.min(minLeftStart, minRightStart), Math.max(maxLeftStart, maxRightStart), Math.max(maxRightEnd, maxLeftEnd)); } - MyNode parent = root.getParent(); - if (parent != null && assertInvalid) { - int c = compare(root.key, delta, parent.key, delta - root.delta); + IntervalNode parent = root.getParent(); + T rootKey = (T)root.getKey(); + if (parent != null && assertInvalid && rootKey != null) { + int c = compareNodes(root, delta, parent, delta - root.delta, new ArrayList()); assert c != 0; assert c < 0 && parent.getLeft() == root || c > 0 && parent.getRight() == root; } - assert delta + root.maxEnd == Math.max(maxLeftEnd, Math.max(maxRightEnd, delta + root.key.intervalEnd())); - int myStartOffset = delta + root.key.intervalStart(); + assert delta + root.maxEnd == Math.max(maxLeftEnd, Math.max(maxRightEnd, delta + root.intervalEnd())); + int myStartOffset = delta + root.intervalStart(); assert maxLeftStart <= myStartOffset; assert minRightStart >= myStartOffset; assert myStartOffset >= 0; @@ -335,7 +455,7 @@ public abstract class IntervalTreeImpl extends RedBla @Override protected Node maximumNode(Node n) { - MyNode root = (MyNode)n; + IntervalNode root = (IntervalNode)n; pushDelta(root.getParent()); pushDelta(root); while (root.getRight() != null) { @@ -345,15 +465,17 @@ public abstract class IntervalTreeImpl extends RedBla return root; } - public boolean remove(@NotNull T interval) { + @Override + public boolean removeInterval(@NotNull T interval) { + if (!interval.isValid()) return false; try { l.writeLock().lock(); checkMax(true); + processReferenceQueue(); - MyNode node = (MyNode)delete(interval); + IntervalNode node = lookupNode(interval, root); if (node == null) return false; - MyNode parent = node.getParent(); - correctMaxUp(parent); + removeNode(node); checkMax(true); return true; } @@ -362,27 +484,35 @@ public abstract class IntervalTreeImpl extends RedBla } } + // run under write lock + public void removeNode(@NotNull IntervalNode node) { + deleteNode(node); + IntervalNode parent = node.getParent(); + correctMaxUp(parent); + } + @Override protected void deleteNode(Node n) { - pushDeltaFromRoot((MyNode)n); + pushDeltaFromRoot((IntervalNode)n); super.deleteNode(n); } - protected void pushDelta(MyNode root) { - if (root == null || !root.key.isValid()) return; + protected static void pushDelta(IntervalNode root) { + if (root == null || !root.isValid()) return; int delta = root.delta; if (delta != 0) { - root.key.setIntervalStart(root.key.intervalStart() + delta); - root.key.setIntervalEnd(root.key.intervalEnd() + delta); + root.setIntervalStart(root.intervalStart() + delta); + root.setIntervalEnd(root.intervalEnd() + delta); root.maxEnd += delta; root.delta = 0; incDelta(root.getLeft(), delta); incDelta(root.getRight(), delta); } } - private void incDelta(MyNode root, int delta) { + + private static void incDelta(IntervalNode root, int delta) { if (root == null) return; - if (root.key.isValid()) { + if (root.isValid()) { root.delta += delta; } else { @@ -390,22 +520,21 @@ public abstract class IntervalTreeImpl extends RedBla incDelta(root.getRight(), delta); } } - @Override - protected MyNode swapWithMaxPred(Node root, Node maxPred) { + protected IntervalNode swapWithMaxPred(Node root, Node maxPred) { checkMax(false); - MyNode a = (MyNode)root; - MyNode d = (MyNode)maxPred; + IntervalNode a = (IntervalNode)root; + IntervalNode d = (IntervalNode)maxPred; Color acolor = a.color; Color dcolor = d.color; - assert !a.key.isValid() || a.delta == 0 : a.delta; - for (MyNode n = a.getLeft(); n != null; n = n.getRight()) { - assert !n.key.isValid() || n.delta == 0 : n.delta; + assert !a.isValid() || a.delta == 0 : a.delta; + for (IntervalNode n = a.getLeft(); n != null; n = n.getRight()) { + assert !n.isValid() || n.delta == 0 : n.delta; } swapNodes(a, d); // set range of the key to be deleted so it wont disrupt maxes - a.key.setValid(false); + a.setValid(false); //a.key.setIntervalStart(d.key.intervalStart()); //a.key.setIntervalEnd(d.key.intervalEnd()); @@ -418,13 +547,13 @@ public abstract class IntervalTreeImpl extends RedBla return a; } - private void swapNodes(MyNode n1, MyNode n2) { - MyNode l1 = n1.getLeft(); - MyNode r1 = n1.getRight(); - MyNode p1 = n1.getParent(); - MyNode l2 = n2.getLeft(); - MyNode r2 = n2.getRight(); - MyNode p2 = n2.getParent(); + private void swapNodes(IntervalNode n1, IntervalNode n2) { + IntervalNode l1 = n1.getLeft(); + IntervalNode r1 = n1.getRight(); + IntervalNode p1 = n1.getParent(); + IntervalNode l2 = n2.getLeft(); + IntervalNode r2 = n2.getRight(); + IntervalNode p2 = n2.getParent(); if (p1 != null) { if (p1.getLeft() == n1) p1.setLeft(n2); else p1.setRight(n2); @@ -451,28 +580,28 @@ public abstract class IntervalTreeImpl extends RedBla if (r2 != null) r2.setParent(n1); } - protected int maxEndOf(MyNode node, int deltaUpToRootExclusive) { + protected static int maxEndOf(IntervalNode node, int deltaUpToRootExclusive) { if (node == null) { return 0; } - if (node.key.isValid()) { + if (node.isValid()) { return node.maxEnd + node.delta + deltaUpToRootExclusive; } return Math.max(maxEndOf(node.getLeft(), deltaUpToRootExclusive), maxEndOf(node.getRight(), deltaUpToRootExclusive)); } // max of n.left's maxend, n.right's maxend and its own interval endOffset - protected void correctMax(@NotNull MyNode node, int deltaUpToRoot) { - if (!node.key.isValid()) return; + protected static void correctMax(@NotNull IntervalNode node, int deltaUpToRoot) { + if (!node.isValid()) return; int realMax = Math.max(Math.max(maxEndOf(node.getLeft(), deltaUpToRoot), maxEndOf(node.getRight(), deltaUpToRoot)), - deltaUpToRoot + node.key.intervalEnd()); + deltaUpToRoot + node.intervalEnd()); node.maxEnd = realMax - deltaUpToRoot; } - private void correctMaxUp(MyNode node) { + private static void correctMaxUp(IntervalNode node) { int delta = node == null ? 0 : node.computeDeltaUpToRoot(); while (node != null) { - if (node.key.isValid()) { + if (node.isValid()) { int d = node.delta; correctMax(node, delta); delta -= d; @@ -485,11 +614,11 @@ public abstract class IntervalTreeImpl extends RedBla @Override protected void rotateRight(Node n) { checkMax(false); - MyNode node1 = (MyNode)n; - MyNode node2 = node1.getLeft(); - MyNode node3 = node1.getRight(); + IntervalNode node1 = (IntervalNode)n; + IntervalNode node2 = node1.getLeft(); + IntervalNode node3 = node1.getRight(); - MyNode parent = node1.getParent(); + IntervalNode parent = node1.getParent(); int deltaUp = parent == null ? 0 : parent.computeDeltaUpToRoot(); pushDelta(node1); pushDelta(node2); @@ -508,11 +637,11 @@ public abstract class IntervalTreeImpl extends RedBla @Override protected void rotateLeft(Node n) { checkMax(false); - MyNode node1 = (MyNode)n; - MyNode node2 = node1.getLeft(); - MyNode node3 = node1.getRight(); + IntervalNode node1 = (IntervalNode)n; + IntervalNode node2 = node1.getLeft(); + IntervalNode node3 = node1.getRight(); - MyNode parent = node1.getParent(); + IntervalNode parent = node1.getParent(); int deltaUp = parent == null ? 0 : parent.computeDeltaUpToRoot(); pushDelta(node1); pushDelta(node2); @@ -531,35 +660,39 @@ public abstract class IntervalTreeImpl extends RedBla @Override protected void replaceNode(@NotNull Node node, Node child) { super.replaceNode(node, child); - if (child != null && node.key.isValid()) { - ((MyNode)child).delta += ((MyNode)node).delta; + IntervalNode myNode = (IntervalNode)node; + if (child != null && myNode.isValid()) { + ((IntervalNode)child).delta += myNode.delta; //todo correct max up to root?? } } public Iterator iterator() { - MyNode firstNode = getRoot(); + IntervalNode firstNode = getRoot(); while (firstNode != null && firstNode.getLeft() != null) { firstNode = firstNode.getLeft(); } + if (firstNode == null) { + return ContainerUtil.emptyIterator(); + } return createIteratorFrom(firstNode); } - private MyNode findMinOverlappingWith(MyNode root, Interval interval, int modCountBefore, int deltaUpToRootExclusive) { + private IntervalNode findMinOverlappingWith(IntervalNode root, Interval interval, int modCountBefore, int deltaUpToRootExclusive) { if (root == null) { return null; } - assert root.key.isValid(); + assert root.isValid(); int delta = deltaUpToRootExclusive + root.delta; if (interval.intervalStart() > maxEndOf(root, deltaUpToRootExclusive)) { return null; // right of the rightmost interval in the subtree } - MyNode inLeft = findMinOverlappingWith(root.getLeft(), interval, modCountBefore, delta); + IntervalNode inLeft = findMinOverlappingWith(root.getLeft(), interval, modCountBefore, delta); if (inLeft != null) return inLeft; - int myStartOffset = root.key.intervalStart() + delta; - int myEndOffset = root.key.intervalEnd() + delta; + int myStartOffset = root.intervalStart() + delta; + int myEndOffset = root.intervalEnd() + delta; boolean overlaps = Math.max(myStartOffset, interval.intervalStart()) <= Math.min(myEndOffset, interval.intervalEnd()); if (overlaps) return root; assert modCount == modCountBefore; @@ -573,48 +706,70 @@ public abstract class IntervalTreeImpl extends RedBla @NotNull Iterator iteratorFrom(@NotNull Interval interval) { - MyNode firstOverlap = findMinOverlappingWith(getRoot(), interval, modCount, 0); + IntervalNode firstOverlap = findMinOverlappingWith(getRoot(), interval, modCount, 0); if (firstOverlap == null) { return ContainerUtil.emptyIterator(); } return createIteratorFrom(firstOverlap); } - private Iterator createIteratorFrom(final MyNode firstNode) { + private Iterator createIteratorFrom(@NotNull final IntervalNode firstNode) { checkMax(true); normalize(); final int modCountBefore = modCount; return new Iterator() { - Node element = firstNode; + private IntervalNode node = firstNode; + private T element = (T)firstNode.getKey(); + + { + // find first non-null key + while (element == null) { + moveNext(); + if (node == null) break; + } + } public boolean hasNext() { return element != null; } public T next() { - assert modCount == modCountBefore; - if (element == null) throw new NoSuchElementException(); - Node prev = element; - element = getNext(element); - return prev.key; + assert modCount == modCountBefore : "Must not modify range markers during iterate"; + if (node == null || element == null) throw new NoSuchElementException(); + T current = element; + moveNext(); + return current; } - private Node getNext(Node element) { - Node n = element.getRight(); + private void moveNext() { + while (true) { + node = getNextNode(); + if (node == null) { + element = null; + break; + } + element = (T)node.getKey(); + if (element != null) break; + } + } + + private IntervalNode getNextNode() { + IntervalNode n = node.getRight(); if (n != null) { while (n.getLeft()!= null) { n = n.getLeft(); } return n; } - Node parent = element.getParent(); + IntervalNode parent = node.getParent(); + IntervalNode current = node; while (parent != null) { - if (parent.getLeft() == element) return parent; - element = parent; + if (parent.getLeft() == current) return parent; + current = parent; parent = parent.getParent(); } - return parent; + return null; } public void remove() { @@ -629,6 +784,7 @@ public abstract class IntervalTreeImpl extends RedBla if (normalized) return; try { l.writeLock().lock(); + processReferenceQueue(); if (normalized) return; normalize(getRoot()); normalized = true; @@ -638,18 +794,61 @@ public abstract class IntervalTreeImpl extends RedBla } } - private void normalize(MyNode root) { + private static void normalize(IntervalNode root) { if (root == null) return; pushDelta(root); normalize(root.getLeft()); normalize(root.getRight()); } + // called under write lock + private void processReferenceQueue() { + int dead = 0; + while (myReferenceQueue.poll() != null) { + dead++; + } + + deadReferenceCount += dead; + if (deadReferenceCount > Math.max(1, size() / 3)) { + purgeDeadNodes(); + deadReferenceCount = 0; + } + } + + private void purgeDeadNodes() { + List gced = new ArrayList(); + collectGced(getRoot(), gced); + deleteNodes(gced); + } + + private static void collectGced(IntervalNode root, List gced) { + if (root == null) return; + if (root.getKey() == null) gced.add(root); + collectGced(root.getLeft(), gced); + collectGced(root.getRight(), gced); + } + + public void changeAttribute(@NotNull IntervalNode node, @NotNull Runnable changeAttributes) { + normalize(); + try { + l.writeLock().lock(); + checkMax(true); + removeNode(node); + changeAttributes.run(); + insert(node); + checkMax(true); + } + finally { + l.writeLock().unlock(); + } + } + + private void printSorted() { printSorted(getRoot());} - private void printSorted(MyNode root) { + private static void printSorted(IntervalNode root) { if (root == null) return; printSorted(root.getLeft()); - System.out.println(root.key); + System.out.println(root); printSorted(root.getRight()); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/MarkupModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/MarkupModelImpl.java index 193990d041be..ecce8d321218 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/MarkupModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/MarkupModelImpl.java @@ -99,6 +99,7 @@ public class MarkupModelImpl extends UserDataHolderBase implements MarkupModelEx @NotNull public RangeHighlighter[] getAllHighlighters() { if (myCachedHighlighters == null) { + if (myHighlighterTree.size() == 0) return RangeHighlighter.EMPTY_ARRAY; List list = new ArrayList(); myHighlighterTree.process(new CommonProcessors.CollectProcessor(list)); myCachedHighlighters = list.toArray(new RangeHighlighter[list.size()]); @@ -118,9 +119,6 @@ public class MarkupModelImpl extends UserDataHolderBase implements MarkupModelEx ? new PersistentRangeHighlighterImpl(this, startOffset, layer, targetArea, textAttributes) : new RangeHighlighterImpl(this, startOffset, endOffset, layer, targetArea, textAttributes); - RangeMarkerImpl marker = (RangeMarkerImpl)highlighter; - marker.registerInDocument(); - myCachedHighlighters = null; if (changeAttributesAction != null) { if (isPersistent) { @@ -144,8 +142,8 @@ public class MarkupModelImpl extends UserDataHolderBase implements MarkupModelEx } } - void addRangeHighlighter(RangeHighlighterEx marker) { - myHighlighterTree.add(marker); + void addRangeHighlighter(RangeHighlighterEx marker, int start, int end, RangeHighlighterData data) { + myHighlighterTree.addInterval(marker, start, end, data); } @NotNull @@ -163,7 +161,7 @@ public class MarkupModelImpl extends UserDataHolderBase implements MarkupModelEx fireBeforeRemoved((RangeHighlighterEx)segmentHighlighter); - boolean removed = myHighlighterTree.remove((RangeHighlighterEx)segmentHighlighter); + boolean removed = myHighlighterTree.removeInterval((RangeHighlighterEx)segmentHighlighter); LOG.assertTrue(removed); } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/PersistentLineMarker.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/PersistentLineMarker.java deleted file mode 100644 index 0c99f8a8eb1d..000000000000 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/PersistentLineMarker.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2000-2009 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.editor.impl; - -import com.intellij.openapi.editor.event.DocumentEvent; -import com.intellij.openapi.editor.ex.DocumentEx; -import com.intellij.openapi.editor.impl.event.DocumentEventImpl; - -/** - * @author max - */ -public class PersistentLineMarker extends RangeMarkerImpl { - private int myLine; - public PersistentLineMarker(DocumentEx document, int offset) { - super(document, document.getLineStartOffset(document.getLineNumber(offset)), document.getLineEndOffset(document.getLineNumber(offset))); - myLine = document.getLineNumber(offset); - } - - @Override - protected void changedUpdateImpl(DocumentEvent e) { - DocumentEventImpl event = (DocumentEventImpl)e; - if (PersistentRangeMarkerUtil.shouldTranslateViaDiff(event, this)) { - myLine = event.translateLineViaDiff(myLine); - if (myLine < 0 || myLine >= getDocument().getLineCount()) { - invalidate(); - } - else { - DocumentEx document = getDocument(); - myStart = document.getLineStartOffset(myLine); - myEnd = document.getLineEndOffset(myLine); - } - } - else { - super.changedUpdateImpl(e); - if (isValid()) { - myLine = getDocument().getLineNumber(myStart); - int endLine = getDocument().getLineNumber(myEnd); - if (endLine != myLine) { - myEnd = getDocument().getLineEndOffset(myLine); - } - } - } - } - - @Override - public String toString() { - return "PersistentLineMarker" + - (isGreedyToLeft() ? "[" : "(") + - (isValid() ? "valid" : "invalid") + "," + getStartOffset() + "," + getEndOffset() + " - " + myLine + - (isGreedyToRight() ? "]" : ")"); - } -} diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/PersistentRangeHighlighterImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/PersistentRangeHighlighterImpl.java index 8a87b9cf76e0..30b1aedf7f52 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/PersistentRangeHighlighterImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/PersistentRangeHighlighterImpl.java @@ -15,153 +15,69 @@ */ package com.intellij.openapi.editor.impl; +import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.ex.DocumentEx; import com.intellij.openapi.editor.ex.RangeHighlighterEx; -import com.intellij.openapi.editor.markup.*; -import com.intellij.util.Consumer; +import com.intellij.openapi.editor.impl.event.DocumentEventImpl; +import com.intellij.openapi.editor.markup.HighlighterTargetArea; +import com.intellij.openapi.editor.markup.MarkupModel; +import com.intellij.openapi.editor.markup.TextAttributes; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.awt.*; /** * Implementation of the markup element for the editor and document. * @author max */ -public class PersistentRangeHighlighterImpl extends PersistentLineMarker implements RangeHighlighterEx { - private final RangeHighlighterData data; +class PersistentRangeHighlighterImpl extends RangeHighlighterImpl implements RangeHighlighterEx { PersistentRangeHighlighterImpl(@NotNull MarkupModel model, - int start, + int offset, int layer, @NotNull HighlighterTargetArea target, - TextAttributes textAttributes - ) { - super((DocumentEx)model.getDocument(), start); - data = new RangeHighlighterData(model, layer, target, textAttributes, this); + TextAttributes textAttributes) { + super(model, model.getDocument().getLineStartOffset(model.getDocument().getLineNumber(offset)), model.getDocument().getLineEndOffset(model.getDocument().getLineNumber(offset)),layer, target, textAttributes); + setLine(model.getDocument().getLineNumber(offset)); } @Override - protected void registerInDocument() { - // we store highlighters in MarkupModel - data.registerMe(); + protected void changedUpdateImpl(DocumentEvent e) { + DocumentEventImpl event = (DocumentEventImpl)e; + if (PersistentRangeMarkerUtil.shouldTranslateViaDiff(event, this)) { + setLine(event.translateLineViaDiff(getLine())); + if (getLine() < 0 || getLine() >= getDocument().getLineCount()) { + invalidate(); + } + else { + DocumentEx document = getDocument(); + setIntervalStart(document.getLineStartOffset(getLine())); + setIntervalEnd(document.getLineEndOffset(getLine())); + } + } + else { + super.changedUpdateImpl(e); + if (isValid()) { + setLine(getDocument().getLineNumber(getStartOffset())); + int endLine = getDocument().getLineNumber(getEndOffset()); + if (endLine != getLine()) { + setIntervalEnd(getDocument().getLineEndOffset(getLine())); + } + } + } } @Override - protected boolean unregisterInDocument() { - // we store highlighters in MarkupModel - data.unregisterMe(); - myNode = null; - return true; + public String toString() { + return "PersistentRangeHighlighter" + + (isGreedyToLeft() ? "[" : "(") + + (isValid() ? "valid" : "invalid") + "," + getStartOffset() + "," + getEndOffset() + " - " + getLine() + + (isGreedyToRight() ? "]" : ")"); } // delegates - - public TextAttributes getTextAttributes() { - return data.getTextAttributes(); + public int getLine() { + return getData().myLine; } - public void setTextAttributes(TextAttributes textAttributes) { - data.setTextAttributes(textAttributes); - } - - boolean changeAttributesInBatch(@NotNull Consumer change) { - return data.changeAttributesInBatch(change); - } - - public int getLayer() { - return data.getLayer(); - } - - public HighlighterTargetArea getTargetArea() { - return data.getTargetArea(); - } - - public LineMarkerRenderer getLineMarkerRenderer() { - return data.getLineMarkerRenderer(); - } - - public void setLineMarkerRenderer(LineMarkerRenderer renderer) { - data.setLineMarkerRenderer(renderer); - } - - public CustomHighlighterRenderer getCustomRenderer() { - return data.getCustomRenderer(); - } - - public void setCustomRenderer(CustomHighlighterRenderer renderer) { - data.setCustomRenderer(renderer); - } - - public GutterIconRenderer getGutterIconRenderer() { - return data.getGutterIconRenderer(); - } - - public void setGutterIconRenderer(GutterIconRenderer renderer) { - data.setGutterIconRenderer(renderer); - } - - public Color getErrorStripeMarkColor() { - return data.getErrorStripeMarkColor(); - } - - public void setErrorStripeMarkColor(Color color) { - data.setErrorStripeMarkColor(color); - } - - public Object getErrorStripeTooltip() { - return data.getErrorStripeTooltip(); - } - - public void setErrorStripeTooltip(Object tooltipObject) { - data.setErrorStripeTooltip(tooltipObject); - } - - public boolean isThinErrorStripeMark() { - return data.isThinErrorStripeMark(); - } - - public void setThinErrorStripeMark(boolean value) { - data.setThinErrorStripeMark(value); - } - - public Color getLineSeparatorColor() { - return data.getLineSeparatorColor(); - } - - public void setLineSeparatorColor(Color color) { - data.setLineSeparatorColor(color); - } - - public SeparatorPlacement getLineSeparatorPlacement() { - return data.getLineSeparatorPlacement(); - } - - public void setLineSeparatorPlacement(@Nullable SeparatorPlacement placement) { - data.setLineSeparatorPlacement(placement); - } - - public void setEditorFilter(@NotNull MarkupEditorFilter filter) { - data.setEditorFilter(filter); - } - - @NotNull - public MarkupEditorFilter getEditorFilter() { - return data.getEditorFilter(); - } - - public boolean isAfterEndOfLine() { - return data.isAfterEndOfLine(); - } - - public void setAfterEndOfLine(boolean afterEndOfLine) { - data.setAfterEndOfLine(afterEndOfLine); - } - - public int getAffectedAreaStartOffset() { - return data.getAffectedAreaStartOffset(); - } - - public int getAffectedAreaEndOffset() { - return data.getAffectedAreaEndOffset(); + public void setLine(int line) { + getData().myLine = line; } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/PersistentRangeMarker.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/PersistentRangeMarker.java index 521628828717..1c7d4a464300 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/PersistentRangeMarker.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/PersistentRangeMarker.java @@ -34,8 +34,8 @@ class PersistentRangeMarker extends RangeMarkerImpl { private int myEndLine; private int myEndColumn; - PersistentRangeMarker(DocumentEx document, int startOffset, int endOffset) { - super(document, startOffset, endOffset); + PersistentRangeMarker(DocumentEx document, int startOffset, int endOffset, boolean register) { + super(document, startOffset, endOffset, register); storeLinesAndCols(); } @@ -66,7 +66,7 @@ class PersistentRangeMarker extends RangeMarkerImpl { invalidate(); } else{ - myStart = getDocument().getLineStartOffset(myStartLine) + myStartColumn; + setIntervalStart(getDocument().getLineStartOffset(myStartLine) + myStartColumn); } myEndLine = event.translateLineViaDiffStrict(myEndLine); @@ -74,7 +74,7 @@ class PersistentRangeMarker extends RangeMarkerImpl { invalidate(); } else{ - myEnd = getDocument().getLineStartOffset(myEndLine) + myEndColumn; + setIntervalEnd(getDocument().getLineStartOffset(myEndLine) + myEndColumn); } } else { @@ -83,7 +83,7 @@ class PersistentRangeMarker extends RangeMarkerImpl { storeLinesAndCols(); } } - if (myEnd < myStart || myEnd > getDocument().getTextLength()) { + if (getEndOffset() < getStartOffset() || getEndOffset() > getDocument().getTextLength()) { invalidate(); } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeHighlighterData.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeHighlighterData.java index e7c5c5a3c6b8..cb0fa21f512a 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeHighlighterData.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeHighlighterData.java @@ -27,7 +27,7 @@ import java.awt.*; /** * User: cdr */ -class RangeHighlighterData { +abstract class RangeHighlighterData { private final MarkupModel myModel; private final int myLayer; private final HighlighterTargetArea myTargetArea; @@ -42,14 +42,12 @@ class RangeHighlighterData { private Object myErrorStripeTooltip; private MarkupEditorFilter myFilter = MarkupEditorFilter.EMPTY; private CustomHighlighterRenderer myCustomRenderer; - private final RangeHighlighterEx rangeHighlighter; + int myLine; // for PersistentRangeHighlighterImpl only RangeHighlighterData(@NotNull MarkupModel model, int layer, @NotNull HighlighterTargetArea target, - TextAttributes textAttributes, - @NotNull RangeHighlighterEx rangeHighlighter) { - this.rangeHighlighter = rangeHighlighter; + TextAttributes textAttributes) { myTextAttributes = textAttributes; myTargetArea = target; myLayer = layer; @@ -59,6 +57,9 @@ class RangeHighlighterData { } } + @NotNull + public abstract RangeHighlighterEx getRangeHighlighter(); + public TextAttributes getTextAttributes() { return myTextAttributes; } @@ -198,32 +199,32 @@ class RangeHighlighterData { changed = true; } else { - ((MarkupModelImpl)myModel).fireAttributesChanged(rangeHighlighter); + ((MarkupModelImpl)myModel).fireAttributesChanged(getRangeHighlighter()); } } } public int getAffectedAreaStartOffset() { - int startOffset = rangeHighlighter.getStartOffset(); + int startOffset = getRangeHighlighter().getStartOffset(); if (getTargetArea() == HighlighterTargetArea.EXACT_RANGE) return startOffset; if (startOffset == myModel.getDocument().getTextLength()) return startOffset; return myModel.getDocument().getLineStartOffset(myModel.getDocument().getLineNumber(startOffset)); } public int getAffectedAreaEndOffset() { - int endOffset = rangeHighlighter.getEndOffset(); + int endOffset = getRangeHighlighter().getEndOffset(); if (getTargetArea() == HighlighterTargetArea.EXACT_RANGE) return endOffset; int textLength = myModel.getDocument().getTextLength(); if (endOffset == textLength) return endOffset; return Math.min(textLength, myModel.getDocument().getLineEndOffset(myModel.getDocument().getLineNumber(endOffset)) + 1); } - public void registerMe() { - ((MarkupModelImpl)myModel).addRangeHighlighter(rangeHighlighter); + public void registerMe(int start, int end) { + ((MarkupModelImpl)myModel).addRangeHighlighter(getRangeHighlighter(), start, end,this); } public void unregisterMe() { - myModel.removeHighlighter(rangeHighlighter); + myModel.removeHighlighter(getRangeHighlighter()); } // returns true if change was detected @@ -231,7 +232,7 @@ class RangeHighlighterData { inBatchChange = true; boolean result; try { - change.consume(rangeHighlighter); + change.consume(getRangeHighlighter()); } finally { inBatchChange = false; diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeHighlighterImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeHighlighterImpl.java index f8db3b7045ac..02f5ff65a0d5 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeHighlighterImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeHighlighterImpl.java @@ -29,140 +29,149 @@ import java.awt.*; * @author max */ class RangeHighlighterImpl extends RangeMarkerImpl implements RangeHighlighterEx { - private final RangeHighlighterData data; RangeHighlighterImpl(@NotNull MarkupModel model, int start, int end, int layer, @NotNull HighlighterTargetArea target, TextAttributes textAttributes) { - super((DocumentEx)model.getDocument(), start, end); - data = new RangeHighlighterData(model, layer, target, textAttributes, this); + super((DocumentEx)model.getDocument(), start, end,false); + + RangeHighlighterData data = new RangeHighlighterData(model, layer, target, textAttributes) { + @NotNull + @Override + public RangeHighlighterEx getRangeHighlighter() { + return RangeHighlighterImpl.this; + } + }; + data.registerMe(start, end); + } + + protected RangeHighlighterData getData() { + return ((RangeHighlighterTree.RHNode)myNode).data; } @Override - protected void registerInDocument() { + protected void registerInDocument(int start, int end) { // we store highlighters in MarkupModel - data.registerMe(); } @Override protected boolean unregisterInDocument() { // we store highlighters in MarkupModel - data.unregisterMe(); + getData().unregisterMe(); myNode = null; return true; } // delegates - public TextAttributes getTextAttributes() { - return data.getTextAttributes(); + return getData().getTextAttributes(); } public void setTextAttributes(TextAttributes textAttributes) { - data.setTextAttributes(textAttributes); + getData().setTextAttributes(textAttributes); } boolean changeAttributesInBatch(@NotNull Consumer change) { - return data.changeAttributesInBatch(change); + return getData().changeAttributesInBatch(change); } public int getLayer() { - return data.getLayer(); + return getData().getLayer(); } public HighlighterTargetArea getTargetArea() { - return data.getTargetArea(); + return getData().getTargetArea(); } public LineMarkerRenderer getLineMarkerRenderer() { - return data.getLineMarkerRenderer(); + return getData().getLineMarkerRenderer(); } public void setLineMarkerRenderer(LineMarkerRenderer renderer) { - data.setLineMarkerRenderer(renderer); + getData().setLineMarkerRenderer(renderer); } public CustomHighlighterRenderer getCustomRenderer() { - return data.getCustomRenderer(); + return getData().getCustomRenderer(); } public void setCustomRenderer(CustomHighlighterRenderer renderer) { - data.setCustomRenderer(renderer); + getData().setCustomRenderer(renderer); } public GutterIconRenderer getGutterIconRenderer() { - return data.getGutterIconRenderer(); + return getData().getGutterIconRenderer(); } public void setGutterIconRenderer(GutterIconRenderer renderer) { - data.setGutterIconRenderer(renderer); + getData().setGutterIconRenderer(renderer); } public Color getErrorStripeMarkColor() { - return data.getErrorStripeMarkColor(); + return getData().getErrorStripeMarkColor(); } public void setErrorStripeMarkColor(Color color) { - data.setErrorStripeMarkColor(color); + getData().setErrorStripeMarkColor(color); } public Object getErrorStripeTooltip() { - return data.getErrorStripeTooltip(); + return getData().getErrorStripeTooltip(); } public void setErrorStripeTooltip(Object tooltipObject) { - data.setErrorStripeTooltip(tooltipObject); + getData().setErrorStripeTooltip(tooltipObject); } public boolean isThinErrorStripeMark() { - return data.isThinErrorStripeMark(); + return getData().isThinErrorStripeMark(); } public void setThinErrorStripeMark(boolean value) { - data.setThinErrorStripeMark(value); + getData().setThinErrorStripeMark(value); } public Color getLineSeparatorColor() { - return data.getLineSeparatorColor(); + return getData().getLineSeparatorColor(); } public void setLineSeparatorColor(Color color) { - data.setLineSeparatorColor(color); + getData().setLineSeparatorColor(color); } public SeparatorPlacement getLineSeparatorPlacement() { - return data.getLineSeparatorPlacement(); + return getData().getLineSeparatorPlacement(); } public void setLineSeparatorPlacement(@Nullable SeparatorPlacement placement) { - data.setLineSeparatorPlacement(placement); + getData().setLineSeparatorPlacement(placement); } public void setEditorFilter(@NotNull MarkupEditorFilter filter) { - data.setEditorFilter(filter); + getData().setEditorFilter(filter); } @NotNull public MarkupEditorFilter getEditorFilter() { - return data.getEditorFilter(); + return getData().getEditorFilter(); } public boolean isAfterEndOfLine() { - return data.isAfterEndOfLine(); + return getData().isAfterEndOfLine(); } public void setAfterEndOfLine(boolean afterEndOfLine) { - data.setAfterEndOfLine(afterEndOfLine); + getData().setAfterEndOfLine(afterEndOfLine); } public int getAffectedAreaStartOffset() { - return data.getAffectedAreaStartOffset(); + return getData().getAffectedAreaStartOffset(); } public int getAffectedAreaEndOffset() { - return data.getAffectedAreaEndOffset(); + return getData().getAffectedAreaEndOffset(); } @Override diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeHighlighterTree.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeHighlighterTree.java index 61b1cc70483a..fcf92a4c720e 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeHighlighterTree.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeHighlighterTree.java @@ -17,19 +17,19 @@ package com.intellij.openapi.editor.impl; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.ex.RangeHighlighterEx; +import org.jetbrains.annotations.NotNull; /** * User: cdr */ public class RangeHighlighterTree extends RangeMarkerTree { - public RangeHighlighterTree(Document document) { - super(document, COMPARATOR); - } - - private static final EqualStartIntervalComparator COMPARATOR = new EqualStartIntervalComparator() { - public int compare(RangeHighlighterEx o1, RangeHighlighterEx o2) { - if (o1.getLayer() != o2.getLayer()) { - return o2.getLayer() - o1.getLayer(); + private final EqualStartIntervalComparator myComparator = new EqualStartIntervalComparator() { + @Override + public int compare(IntervalNode i1, IntervalNode i2) { + RHNode o1 = (RHNode)i1; + RHNode o2 = (RHNode)i2; + if (o1.data.getLayer() != o2.data.getLayer()) { + return o2.data.getLayer() - o1.data.getLayer(); } boolean greedyL1 = o1.isGreedyToLeft(); boolean greedyL2 = o2.isGreedyToLeft(); @@ -48,4 +48,28 @@ public class RangeHighlighterTree extends RangeMarkerTree { } }; + public RangeHighlighterTree(Document document) { + super(document); + } + + @Override + protected EqualStartIntervalComparator getComparator() { + return myComparator; + } + + @Override + protected RHNode createNewNode(RangeHighlighterEx key, int start, int end, Object data) { + RHNode node = new RHNode(key, start, end, (RangeHighlighterData)data); + ((RangeMarkerImpl)key).myNode = node; + return node; + } + + class RHNode extends RMNode { + final RangeHighlighterData data; + public RHNode(@NotNull final RangeHighlighterEx key, int start, int end, RangeHighlighterData data) { + super(key, start, end); + this.data = data; + } + } + } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerImpl.java index f428f8a7b0ac..88e05ac03d34 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerImpl.java @@ -23,25 +23,13 @@ import com.intellij.openapi.util.UserDataHolderBase; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; -import java.util.concurrent.atomic.AtomicLong; - -public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx { +public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx, MutableInterval { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.RangeMarkerImpl"); protected final DocumentEx myDocument; - protected volatile int myStart; - protected volatile int myEnd; - private volatile boolean isValid = true; - private boolean isExpandToLeft = false; - private boolean isExpandToRight = false; + RangeMarkerTree.RMNode myNode; - private static final AtomicLong counter = new AtomicLong(); - //private static long counter; - private final long myId; - private volatile int modCount; - IntervalTreeImpl.MyNode myNode; - - protected RangeMarkerImpl(@NotNull DocumentEx document, int start, int end) { + protected RangeMarkerImpl(@NotNull DocumentEx document, int start, int end, boolean register) { if (start < 0) { throw new IllegalArgumentException("Wrong start: " + start+"; end="+end); } @@ -53,15 +41,14 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx } myDocument = document; - myStart = start; - myEnd = end; - myId = counter.getAndIncrement(); - //myId = counter++; + if (register) { + registerInDocument(start, end); + } } - protected void registerInDocument() { + protected void registerInDocument(int start, int end) { myNode = null; - myDocument.addRangeMarker(this); + myDocument.addRangeMarker(this, start, end); assert myNode != null; } protected boolean unregisterInDocument() { @@ -71,28 +58,24 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx } public long getId() { - return myId; + return myNode.getId(); } @Override public void dispose() { - myDocument.removeRangeMarker(this); + unregisterInDocument(); } public int getStartOffset() { - return myStart + (myNode == null ? 0 : myNode.computeDeltaUpToRoot()); + return intervalStart() + (myNode == null ? 0 : myNode.computeDeltaUpToRoot()); } public int getEndOffset() { - return myEnd + (myNode == null ? 0 : myNode.computeDeltaUpToRoot()); - } - - public boolean isValid() { - return isValid; + return intervalEnd() + (myNode == null ? 0 : myNode.computeDeltaUpToRoot()); } public void invalidate() { - isValid = false; + setValid(false); } @NotNull @@ -100,63 +83,67 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx return myDocument; } - public void setGreedyToLeft(boolean greedy) { - if (!isValid()) return; - boolean b = unregisterInDocument(); - assert b; - isExpandToLeft = greedy; - registerInDocument(); + public void setGreedyToLeft(final boolean greedy) { + if (!isValid() || greedy == isGreedyToLeft()) return; + myNode.getTree().changeAttribute(myNode, new Runnable() { + @Override + public void run() { + myNode.setGreedyToLeft(greedy); + } + }); } - public void setGreedyToRight(boolean greedy) { - if (!isValid()) return; - boolean b = unregisterInDocument(); - assert b; - isExpandToRight = greedy; - registerInDocument(); + public void setGreedyToRight(final boolean greedy) { + if (!isValid() || greedy == isGreedyToRight()) return; + myNode.getTree().changeAttribute(myNode, new Runnable() { + @Override + public void run() { + myNode.setGreedyToRight(greedy); + } + }); } public boolean isGreedyToLeft() { - return isExpandToLeft; + RangeMarkerTree.RMNode node = myNode; + return node != null && node.isGreedyToLeft(); } public boolean isGreedyToRight() { - return isExpandToRight; + RangeMarkerTree.RMNode node = myNode; + return node != null && node.isGreedyToRight(); } public final void documentChanged(DocumentEvent e) { - int modCount = ++this.modCount; - int oldStart = myStart; - int oldEnd = myEnd; + int oldStart = intervalStart(); + int oldEnd = intervalEnd(); int docLength = myDocument.getTextLength(); - if (!isValid) { - LOG.error("Invalid range marker "+ (isExpandToLeft ? "[" : "(") + oldStart + ", " + oldEnd + (isExpandToRight ? "]" : ")") + + if (!isValid()) { + LOG.error("Invalid range marker "+ (isGreedyToLeft() ? "[" : "(") + oldStart + ", " + oldEnd + (isGreedyToRight() ? "]" : ")") + ". Event = " + e + ". Doc length=" + docLength + "; "+getClass()); return; } - if (myStart > myEnd || myStart < 0 || myEnd > docLength - e.getNewLength() + e.getOldLength()) { - LOG.error("RangeMarker" + (isExpandToLeft ? "[" : "(") + oldStart + ", " + oldEnd + (isExpandToRight ? "]" : ")") + + if (intervalStart() > intervalEnd() || intervalStart() < 0 || intervalEnd() > docLength - e.getNewLength() + e.getOldLength()) { + LOG.error("RangeMarker" + (isGreedyToLeft() ? "[" : "(") + oldStart + ", " + oldEnd + (isGreedyToRight() ? "]" : ")") + " is invalid before update. Event = " + e + ". Doc length=" + docLength + "; "+getClass()); - isValid = false; + invalidate(); return; } changedUpdateImpl(e); - if (isValid && (myStart > myEnd || myStart < 0 || myEnd > docLength)) { + if (isValid() && (intervalStart() > intervalEnd() || intervalStart() < 0 || intervalEnd() > docLength)) { String markerBefore = toString(); LOG.error("Update failed. Event = " + e + ". " + "old doc length=" + docLength + "; real doc length = "+myDocument.getTextLength()+ - "; old mod count="+modCount+"; mod count="+this.modCount+ "; "+getClass()+"." + " Before update: '"+markerBefore+"'; After update: '"+this+"'"); - isValid = false; + invalidate(); } } protected void changedUpdateImpl(DocumentEvent e) { - if (!isValid) return; + if (!isValid()) return; // Process if one point. - if (myStart == myEnd) { + if (intervalStart() == intervalEnd()) { processIfOnePoint(e); return; } @@ -166,34 +153,34 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx final int newLength = e.getNewLength(); // changes after the end. - if (myEnd < offset || !isExpandToRight && myEnd == offset) { + if (intervalEnd() < offset || !isGreedyToRight() && intervalEnd() == offset) { return; } // changes before start - if (myStart > offset + oldLength || !isExpandToLeft && myStart == offset + oldLength) { - myStart += newLength - oldLength; - myEnd += newLength - oldLength; + if (intervalStart() > offset + oldLength || !isGreedyToLeft() && intervalStart() == offset + oldLength) { + setIntervalStart(intervalStart() + newLength - oldLength); + setIntervalEnd(intervalEnd() + newLength - oldLength); return; } // Changes inside marker's area. Expand/collapse. - if (myStart <= offset && myEnd >= offset + oldLength) { - myEnd += newLength - oldLength; + if (intervalStart() <= offset && intervalEnd() >= offset + oldLength) { + setIntervalEnd(intervalEnd() + newLength - oldLength); return; } // At this point we either have (myStart xor myEnd inside changed area) or whole area changed. // Replacing prefix or suffix... - if (myStart >= offset && myStart <= offset + oldLength && myEnd > offset + oldLength) { - myEnd += newLength - oldLength; - myStart = offset + newLength; + if (intervalStart() >= offset && intervalStart() <= offset + oldLength && intervalEnd() > offset + oldLength) { + setIntervalEnd(intervalEnd() + newLength - oldLength); + setIntervalStart(offset + newLength); return; } - if (myEnd >= offset && myEnd <= offset + oldLength && myStart < offset) { - myEnd = offset; + if (intervalEnd() >= offset && intervalEnd() <= offset + oldLength && intervalStart() < offset) { + setIntervalEnd(offset); return; } @@ -204,47 +191,69 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx int offset = e.getOffset(); int oldLength = e.getOldLength(); int oldEnd = offset + oldLength; - if (offset < myStart && myStart < oldEnd) { + if (offset < intervalStart() && intervalStart() < oldEnd) { invalidate(); return; } - if (offset == myStart && oldLength == 0 && isExpandToRight) { - myEnd += e.getNewLength(); + if (offset == intervalStart() && oldLength == 0 && isGreedyToRight()) { + setIntervalEnd(intervalEnd() + e.getNewLength()); return; } - if (myStart > oldEnd || myStart == oldEnd && oldLength > 0) { - myStart += e.getNewLength() - oldLength; - myEnd += e.getNewLength() - oldLength; + if (intervalStart() > oldEnd || intervalStart() == oldEnd && oldLength > 0) { + setIntervalStart(intervalStart() + e.getNewLength() - oldLength); + setIntervalEnd(intervalEnd() + e.getNewLength() - oldLength); } } @NonNls public String toString() { - return "RangeMarker" + (isGreedyToLeft() ? "[" : "(") + (isValid ? "valid" : "invalid") + "," + getStartOffset() + "," + getEndOffset() + ( + return "RangeMarker" + (isGreedyToLeft() ? "[" : "(") + (isValid() ? "valid" : "invalid") + "," + getStartOffset() + "," + getEndOffset() + ( isGreedyToRight() ? "]" : ")"); } - public int intervalStart() { - return myStart; - } - - public int intervalEnd() { - return myEnd; - } - + @Override public int setIntervalStart(int start) { - return myStart = start; + return myNode.setIntervalStart(start); } + @Override public int setIntervalEnd(int end) { - return myEnd = end; + return myNode.setIntervalEnd(end); } + @Override + public boolean isValid() { + RangeMarkerTree.RMNode node = myNode; + return node != null && node.isValid(); + } + + @Override public boolean setValid(boolean value) { - isValid = value; - return value; + RangeMarkerTree.RMNode node = myNode; + return node == null || node.setValid(value); } + @Override + public int intervalStart() { + RangeMarkerTree.RMNode node = myNode; + if (node == null) { + return -1; + } + else { + return node.intervalStart(); + } + } + + @Override + public int intervalEnd() { + RangeMarkerTree.RMNode node = myNode; + if (node == null) { + return -1; + } + else { + return node.intervalEnd(); + } + } } diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerTree.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerTree.java index 073b3fdcd7ce..8e31f8327bb8 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerTree.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/RangeMarkerTree.java @@ -20,12 +20,15 @@ import com.intellij.openapi.editor.event.DocumentEvent; import com.intellij.openapi.editor.ex.MarkupModelEx; import com.intellij.openapi.editor.ex.PrioritizedDocumentListener; import com.intellij.openapi.editor.ex.RangeMarkerEx; +import com.intellij.openapi.util.Ref; import com.intellij.openapi.util.Segment; import com.intellij.openapi.util.Trinity; import com.intellij.util.Processor; import org.jetbrains.annotations.NotNull; import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; /** * User: cdr @@ -33,14 +36,18 @@ import java.util.*; public class RangeMarkerTree extends IntervalTreeImpl { private final PrioritizedDocumentListener myListener; private final Document myDocument; - - private static final EqualStartIntervalComparator RANGEMARKER_COMPARATOR = new EqualStartIntervalComparator() { - public int compare(RangeMarkerEx o1, RangeMarkerEx o2) { + private final EqualStartIntervalComparator myEqualStartIntervalComparator = new EqualStartIntervalComparator() { + @Override + public int compare(IntervalNode i1, IntervalNode i2) { + RMNode o1 = (RMNode)i1; + RMNode o2 = (RMNode)i2; boolean greedyL1 = o1.isGreedyToLeft(); boolean greedyL2 = o2.isGreedyToLeft(); if (greedyL1 != greedyL2) return greedyL1 ? -1 : 1; - int d = o1.intervalEnd() - o1.intervalStart() - (o2.intervalEnd() - o2.intervalStart()); + int o1Length = o1.intervalEnd() - o1.intervalStart(); + int o2Length = o2.intervalEnd() - o2.intervalStart(); + int d = o1Length - o2Length; if (d != 0) return d; boolean greedyR1 = o1.isGreedyToRight(); @@ -53,9 +60,13 @@ public class RangeMarkerTree extends IntervalTreeImpl comparator) { - super(comparator); + @Override + protected EqualStartIntervalComparator getComparator() { + return myEqualStartIntervalComparator; + } + + protected RangeMarkerTree(Document document) { myDocument = document; myListener = new PrioritizedDocumentListener() { public int getPriority() { @@ -72,41 +83,70 @@ public class RangeMarkerTree extends IntervalTreeImpl)RANGEMARKER_COMPARATOR); - } - public void dispose() { myDocument.removeDocumentListener(myListener); } @Override - protected Trinity checkMax(MyNode root, int deltaUpToRootExclusive, boolean assertInvalid) { + protected Trinity checkMax(IntervalNode root, + int deltaUpToRootExclusive, + boolean assertInvalid, + Ref allValid, AtomicInteger counter) { if (root != null) { - RangeMarkerImpl r = (RangeMarkerImpl)root.key; - assert r.myNode == null || r.myNode == root; + RangeMarkerImpl r = (RangeMarkerImpl)root.getKey(); + if (r != null) { + assert /*r.myNode == null || */r.myNode == root; + assert r.myNode.getTree() == this; + } } - return super.checkMax(root, deltaUpToRootExclusive, assertInvalid); + return super.checkMax(root, deltaUpToRootExclusive, assertInvalid, allValid, counter); } @Override - public MyNode add(@NotNull T interval) { - try { - l.writeLock().lock(); - checkMax(true); - RangeMarkerImpl marker = (RangeMarkerImpl)interval; - marker.setValid(true); + public IntervalNode addInterval(@NotNull T interval, int start, int end, Object data) { + RangeMarkerImpl marker = (RangeMarkerImpl)interval; + marker.setValid(true); + return super.addInterval(interval, start, end, data); + } - modCount++; - MyNode newNode = createNewNode(interval); - insert(newNode); + @Override + protected RMNode createNewNode(T key, int start, int end, Object data) { + RMNode node = new RMNode(key, start, end); + ((RangeMarkerImpl)key).myNode = node; + return node; + } - marker.myNode = (IntervalTreeImpl.MyNode)newNode; - checkMax(true); // myNode already assigned - return newNode; + //private static long counter; + private static final AtomicLong counter = new AtomicLong(); + public class RMNode extends MyNode { + private boolean isExpandToLeft = false; + private boolean isExpandToRight = false; + private final long myId; + + public RMNode(@NotNull T key, int start, int end) { + super(key, start, end); + myId = counter.getAndIncrement(); + //myId = counter++; } - finally { - l.writeLock().unlock(); + + public void setGreedyToLeft(boolean greedy) { + isExpandToLeft = greedy; + } + + public void setGreedyToRight(boolean greedy) { + isExpandToRight = greedy; + } + + public boolean isGreedyToLeft() { + return isExpandToLeft; + } + + public boolean isGreedyToRight() { + return isExpandToRight; + } + + public long getId() { + return myId; } } @@ -117,59 +157,66 @@ public class RangeMarkerTree extends IntervalTreeImpl affected = new ArrayList(); + List affected = new ArrayList(); updateMarkersOnChange(getRoot(), e, affected); checkMax(false); - for (MyNode node : affected) { - // assumption: interval.getEndOffset() will never be accessed during remove() - RangeMarkerEx marker = node.key; - int startOffset = marker.getStartOffset(); - int endOffset = marker.getEndOffset(); - deleteNode(node); - node.delta = 0; - node.setParent(null); - node.setLeft(null); - node.setRight(null); - assert marker.intervalStart() == startOffset; - assert marker.intervalEnd() == endOffset; - //marker.setIntervalStart(startOffset); - //marker.setIntervalEnd(endOffset); //might have been changed by delete - } - checkMax(true); - for (MyNode node : affected) { - RangeMarkerImpl marker = (RangeMarkerImpl)node.key; - marker.setValid(true); - marker.myNode = null; - marker.documentChanged(e); - if (marker.isValid()) { - marker.registerInDocument(); + if (!affected.isEmpty()) { + for (IntervalNode node : affected) { + // assumption: interval.getEndOffset() will never be accessed during remove() + int startOffset = node.intervalStart(); + int endOffset = node.intervalEnd(); + removeNode(node); + node.delta = 0; + node.setParent(null); + node.setLeft(null); + node.setRight(null); + assert node.intervalStart() == startOffset; + assert node.intervalEnd() == endOffset; } + checkMax(true); + for (IntervalNode node : affected) { + RangeMarkerImpl marker = (RangeMarkerImpl)node.getKey(); + if (marker == null) continue; // collected + marker.setValid(true); + //marker.myNode = null; + marker.documentChanged(e); + if (marker.isValid()) { + insert(node); + } + } + checkMax(true); } - checkMax(true); + + IntervalNode root = getRoot(); + assert root == null || root.maxEnd + root.delta <= myDocument.getTextLength(); } finally { l.writeLock().unlock(); } } - private void updateMarkersOnChange(MyNode root, - DocumentEvent e, - List affected) { + private static void updateMarkersOnChange(IntervalNode root, + @NotNull DocumentEvent e, + @NotNull List affected) { if (root == null) return; pushDelta(root); int maxEnd = root.maxEnd; - RangeMarkerEx interval = root.key; - assert interval.isValid(); + assert root.isValid(); int offset = e.getOffset(); int affectedEndOffset = offset + e.getOldLength(); - int lengthDelta = e.getNewLength() - e.getOldLength(); + Object key = root.getKey(); + if (key == null) { + // marker was garbage collected + affected.add(root); + } if (offset > maxEnd) { } - else if (affectedEndOffset < interval.intervalStart()) { + else if (affectedEndOffset < root.intervalStart()) { + int lengthDelta = e.getNewLength() - e.getOldLength(); root.delta += lengthDelta; if (root.getLeft() != null) { root.getLeft().delta -= lengthDelta; @@ -179,10 +226,10 @@ public class RangeMarkerTree extends IntervalTreeImpl { - public static final boolean VERIFY = false; +public abstract class RedBlackTree { + public static boolean VERIFY = false; private static final int INDENT_STEP = 4; protected int size; protected int modCount; - public Node root; + protected Node root; - private final Comparator comparator; - - public RedBlackTree(Comparator comparator) { - this.comparator = comparator; + public RedBlackTree() { root = null; verifyProperties(); } - protected Node lookupNode(K key, Node root) { - while (root != null) { - int compResult = comparator.compare(key, root.key); - if (compResult < 0) { - root = root.getLeft(); - } - else if (compResult > 0) { - root = root.getRight(); - } - else { - return root; - } - } - return root; - } + protected abstract Node lookupNode(@NotNull K key, Node root); protected void rotateLeft(Node n) { Node r = n.getRight(); @@ -97,49 +78,7 @@ public class RedBlackTree { //oldn.right = null; } - protected Node createNewNode(K key) { - return new Node(key); - } - - public Node insert(Node newNode) { - modCount++; - newNode.color = Color.RED; - K key = newNode.key; - - if (root == null) { - root = newNode; - } - else { - Node n = root; - loop: - while (true) { - int compResult = comparator.compare(key, n.key); - if (compResult < 0) { - if (n.getLeft() == null) { - n.setLeft(newNode); - break loop; - } - n = n.getLeft(); - } - else if (compResult > 0) { - if (n.getRight() == null) { - n.setRight(newNode); - break loop; - } - n = n.getRight(); - } - else { - assert false : "already inserted"; - return root; - } - } - newNode.setParent(n); - } - size++; - insertCase1(newNode); - verifyProperties(); - return newNode; - } + protected abstract Node createNewNode(K key, int start, int end, Object data); protected void insertCase1(Node n) { if (n.getParent() == null) { @@ -157,7 +96,7 @@ public class RedBlackTree { // Tree is still valid } - void insertCase3(Node n) { + private void insertCase3(Node n) { if (nodeColor(n.uncle()) == Color.RED) { n.getParent().color = Color.BLACK; n.uncle().color = Color.BLACK; @@ -169,7 +108,7 @@ public class RedBlackTree { } } - void insertCase4(Node n) { + private void insertCase4(Node n) { if (n == n.getParent().getRight() && n.getParent() == n.grandparent().getLeft()) { rotateLeft(n.getParent()); n = n.getLeft(); @@ -181,7 +120,7 @@ public class RedBlackTree { insertCase5(n); } - void insertCase5(Node n) { + private void insertCase5(Node n) { n.getParent().color = Color.BLACK; n.grandparent().color = Color.RED; if (n == n.getParent().getLeft() && n.getParent() == n.grandparent().getLeft()) { @@ -198,16 +137,20 @@ public class RedBlackTree { assert node1 == null || node1.getParent() == null || node1.getParent().getLeft() == node1 || node1.getParent().getRight() == node1; } - //returns parent of deleted node - public Node delete(K key) { - modCount++; + public Node delete(@NotNull K key) { Node n = lookupNode(key, root); deleteNode(n); return n; } protected void deleteNode(Node n) { + modCount++; if (n == null) return; // Key not found, do nothing + + Node e = n; + while (e.getParent() != null) e = e.getParent(); + assert e == root; // assert the node belongs to our tree + if (n.getLeft() != null && n.getRight() != null) { // Copy key/value from predecessor and then delete it instead Node pred = maximumNode(n.getLeft()); @@ -236,11 +179,7 @@ public class RedBlackTree { verifyProperties(); } - protected Node swapWithMaxPred(Node nowAscendant, Node nowDescendant) { - nowAscendant.key = nowDescendant.key; - //nowAscendant.value = nowDescendant.value; - return nowDescendant; - } + protected abstract Node swapWithMaxPred(Node nowAscendant, Node nowDescendant); protected Node maximumNode(Node n) { assert n != null; @@ -347,93 +286,23 @@ public class RedBlackTree { System.err.print(" "); } if (n.color == Color.BLACK) { - System.err.println(n.key); + System.err.println(n.getKey()); } else { - System.err.println("<" + n.key + ">"); + System.err.println("<" + n.getKey() + ">"); } if (n.getLeft() != null) { printHelper(n.getLeft(), indent + INDENT_STEP); } } - /* - private static void test (int... keys) { - testDel(keys,keys); - } - private static void testDel (int[] adds, int[] dels) { - RedBlackTree t = new RedBlackTree(); - for (int i = 0; i < adds.length; i++) { - int key = adds[i]; - Node node = t.createNewNode(key); - node.value = i; - t.insert(node); - } - for (int key : dels) { - t.delete(key); - } - } - - public static void main(String[] args) { - test(0, 2, 1, 2); - test(4, 2, 0, 9, 6, 1, 3, 7); - testDel(new int[] {7, 2, 5, 0, 4}, new int[]{2,7}); - - - - - //t.insert(0, 0); - //t.insert(2, 1); //assert t.lookup(1).equals(1) : t.lookup(1); - //t.insert(1, 0); //assert t.lookup(0).equals(0) : t.lookup(0); - //t.insert(2, 1); //assert t.lookup(1).equals(1) : t.lookup(1); - - Random gen = new Random(); - - while (true) { - RedBlackTree t = new RedBlackTree(); - List adds = new ArrayList(); - List dels = new ArrayList(); - assert t.size() == 0 : t.size(); - try { - for (int i = 0; i < 500; i++) { - int x = gen.nextInt(500); - if (adds.contains(x)) continue; - adds.add(x); - Node node = t.createNewNode(x); - node.value = x; - t.insert(node); - Integer lookup = t.lookupNode(x).value; - assert lookup.equals(i) : lookup; - } - List candidates = new ArrayList(adds); - for (int i = 0; i < candidates.size(); i++) { - int x = gen.nextInt(candidates.size()); - Integer r = candidates.get(x); - candidates.remove((Object)r); - dels.add(r); - t.delete(r); - } - } - catch (AssertionError e) { - System.err.println("adds = " + adds); - System.err.println("dels = " + dels); - throw e; - } - } - } - */ - - public static class Node { - K key; + public abstract static class Node { private Node left; private Node right; - private Node parent; - protected Color color; + private Node parent = null; + protected Color color = Color.RED; - public Node(K key) { - this.key = key; - color = Color.RED; - setParent(null); + public Node() { } public Node grandparent() { @@ -477,6 +346,8 @@ public class RedBlackTree { public void setParent(Node parent) { this.parent = parent; } + + public abstract K getKey(); } protected static enum Color { diff --git a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SelectionModelImpl.java b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SelectionModelImpl.java index 98de2ccac0ea..72444b824403 100644 --- a/platform/platform-impl/src/com/intellij/openapi/editor/impl/SelectionModelImpl.java +++ b/platform/platform-impl/src/com/intellij/openapi/editor/impl/SelectionModelImpl.java @@ -56,14 +56,13 @@ import org.jetbrains.annotations.Nullable; import java.awt.*; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.StringSelection; -import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.atomic.AtomicReference; +import java.util.List; public class SelectionModelImpl implements SelectionModel, PrioritizedDocumentListener { private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.SelectionModelImpl"); - private final CopyOnWriteArrayList mySelectionListeners = ContainerUtil.createEmptyCOWList(); - private final AtomicReference mySelectionMarker = new AtomicReference(); + private final List mySelectionListeners = ContainerUtil.createEmptyCOWList(); + private volatile MyRangeMarker mySelectionMarker; private final EditorImpl myEditor; private int myLastSelectionStart; private LogicalPosition myBlockStart; @@ -72,19 +71,19 @@ public class SelectionModelImpl implements SelectionModel, PrioritizedDocumentLi private DocumentEvent myIsInUpdate; private class MyRangeMarker extends RangeMarkerImpl { - private VisualPosition myStartPosition; private VisualPosition myEndPosition; private boolean myEndPositionIsLead; private boolean myIsReleased; MyRangeMarker(DocumentEx document, int start, int end) { - super(document, start, end); + super(document, start, end,true); myIsReleased = false; } public void release() { myIsReleased = true; + dispose(); } @Nullable @@ -115,33 +114,20 @@ public class SelectionModelImpl implements SelectionModel, PrioritizedDocumentLi myEndPositionIsLead = endPositionIsLead; } + int startBefore; + int endBefore; @Override protected void changedUpdateImpl(DocumentEvent e) { if (myIsReleased) return; - int startBefore = getStartOffset(); - int endBefore = getEndOffset(); + startBefore = getStartOffset(); + endBefore = getEndOffset(); super.changedUpdateImpl(e); - - if (!isValid()) { - myLastSelectionStart = myEditor.getCaretModel().getOffset(); - release(); - mySelectionMarker.set(null); - fireSelectionChanged(startBefore, endBefore, myLastSelectionStart, myLastSelectionStart); - return; - } - - if (startBefore != getStartOffset() || endBefore != getStartOffset()) { - fireSelectionChanged(startBefore, endBefore, getStartOffset(), getEndOffset()); - } } - protected void registerInDocument() { - } - private void invalidateVisualPositions() { SoftWrapModelImpl model = myEditor.getSoftWrapModel(); - if ((!myEditor.offsetToVisualPosition(myStart).equals(myStartPosition) && model.getSoftWrap(myStart) == null) - || (!myEditor.offsetToVisualPosition(myEnd).equals(myEndPosition) && model.getSoftWrap(myEnd) == null)) + if (!myEditor.offsetToVisualPosition(getStartOffset()).equals(myStartPosition) && model.getSoftWrap(getStartOffset()) == null + || !myEditor.offsetToVisualPosition(getEndOffset()).equals(myEndPosition) && model.getSoftWrap(getEndOffset()) == null) { myStartPosition = null; myEndPosition = null; @@ -149,16 +135,38 @@ public class SelectionModelImpl implements SelectionModel, PrioritizedDocumentLi } } + private int startBefore; + private int endBefore; public void beforeDocumentChange(DocumentEvent event) { myIsInUpdate = event; + MyRangeMarker marker = mySelectionMarker; + if (marker != null && marker.isValid()) { + startBefore = marker.getStartOffset(); + endBefore = marker.getEndOffset(); + } } public void documentChanged(DocumentEvent event) { if (myIsInUpdate == event) { myIsInUpdate = null; - MyRangeMarker marker = mySelectionMarker.get(); - if (marker != null && marker.isValid()) { - marker.documentChanged(event); + MyRangeMarker marker = mySelectionMarker; + if (marker != null) { + int endAfter; + int startAfter; + if (marker.isValid()) { + startAfter = marker.getStartOffset(); + endAfter = marker.getEndOffset(); + } + else { + myLastSelectionStart = myEditor.getCaretModel().getOffset(); + marker.release(); + mySelectionMarker = null; + startAfter = endAfter = myLastSelectionStart; + } + + if (startBefore != startAfter || endBefore != endAfter) { + fireSelectionChanged(startBefore, endBefore, startAfter, endAfter); + } } } } @@ -174,7 +182,7 @@ public class SelectionModelImpl implements SelectionModel, PrioritizedDocumentLi public int getSelectionStart() { validateContext(false); if (hasSelection()) { - MyRangeMarker marker = mySelectionMarker.get(); + MyRangeMarker marker = mySelectionMarker; if (marker != null) { return marker.getStartOffset(); } @@ -190,7 +198,7 @@ public class SelectionModelImpl implements SelectionModel, PrioritizedDocumentLi return defaultPosition; } - MyRangeMarker marker = mySelectionMarker.get(); + MyRangeMarker marker = mySelectionMarker; if (marker == null) { return defaultPosition; } @@ -207,15 +215,15 @@ public class SelectionModelImpl implements SelectionModel, PrioritizedDocumentLi ApplicationManager.getApplication().assertReadAccessAllowed(); } - if (myIsInUpdate != null) { - documentChanged(myIsInUpdate); - } + //if (myIsInUpdate != null) { + // documentChanged(myIsInUpdate); + //} } public int getSelectionEnd() { validateContext(false); if (hasSelection()) { - MyRangeMarker marker = mySelectionMarker.get(); + MyRangeMarker marker = mySelectionMarker; if (marker != null) { return marker.getEndOffset(); } @@ -231,7 +239,7 @@ public class SelectionModelImpl implements SelectionModel, PrioritizedDocumentLi return defaultPosition; } - MyRangeMarker marker = mySelectionMarker.get(); + MyRangeMarker marker = mySelectionMarker; if (marker == null) { return defaultPosition; } @@ -242,12 +250,12 @@ public class SelectionModelImpl implements SelectionModel, PrioritizedDocumentLi public boolean hasSelection() { validateContext(false); - MyRangeMarker marker = mySelectionMarker.get(); - if (marker != null && !marker.isValid()) { - removeSelection(); - } + MyRangeMarker marker = mySelectionMarker; + //if (marker != null && !marker.isValid()) { + // removeSelection(); + //} - return marker != null; + return marker != null && marker.isValid(); } public void setSelection(int startOffset, int endOffset) { @@ -267,7 +275,7 @@ public class SelectionModelImpl implements SelectionModel, PrioritizedDocumentLi } @Override - public void setSelection(final @Nullable VisualPosition startPosition, int startOffset, final @Nullable VisualPosition endPosition, + public void setSelection(@Nullable final VisualPosition startPosition, int startOffset, @Nullable final VisualPosition endPosition, int endOffset) { VisualPosition startPositionToUse = startPosition == null ? myEditor.offsetToVisualPosition(startOffset) : startPosition; @@ -332,7 +340,7 @@ public class SelectionModelImpl implements SelectionModel, PrioritizedDocumentLi oldSelectionStart = oldSelectionEnd = myEditor.getCaretModel().getOffset(); } - MyRangeMarker marker = mySelectionMarker.get(); + MyRangeMarker marker = mySelectionMarker; if (marker != null) { marker.release(); } @@ -350,7 +358,7 @@ public class SelectionModelImpl implements SelectionModel, PrioritizedDocumentLi marker.setEndPositionIsLead(true); } } - mySelectionMarker.set(marker); + mySelectionMarker = marker; fireSelectionChanged(oldSelectionStart, oldSelectionEnd, startOffset, endOffset); @@ -422,12 +430,12 @@ public class SelectionModelImpl implements SelectionModel, PrioritizedDocumentLi validateContext(true); removeBlockSelection(); myLastSelectionStart = myEditor.getCaretModel().getOffset(); - MyRangeMarker marker = mySelectionMarker.get(); + MyRangeMarker marker = mySelectionMarker; if (marker != null) { int startOffset = marker.getStartOffset(); int endOffset = marker.getEndOffset(); marker.release(); - mySelectionMarker.set(null); + mySelectionMarker = null; fireSelectionChanged(startOffset, endOffset, myLastSelectionStart, myLastSelectionStart); } } @@ -585,7 +593,7 @@ public class SelectionModelImpl implements SelectionModel, PrioritizedDocumentLi validateContext(false); int caretOffset = myEditor.getCaretModel().getOffset(); if (hasSelection()) { - MyRangeMarker marker = mySelectionMarker.get(); + MyRangeMarker marker = mySelectionMarker; if (marker != null) { int startOffset = marker.getStartOffset(); int endOffset = marker.getEndOffset(); @@ -617,7 +625,7 @@ public class SelectionModelImpl implements SelectionModel, PrioritizedDocumentLi @NotNull @Override public VisualPosition getLeadSelectionPosition() { - MyRangeMarker marker = mySelectionMarker.get(); + MyRangeMarker marker = mySelectionMarker; VisualPosition caretPosition = myEditor.getCaretModel().getVisualPosition(); if (marker == null) { return caretPosition; diff --git a/platform/testFramework/src/com/intellij/mock/MockDocument.java b/platform/testFramework/src/com/intellij/mock/MockDocument.java index f0c32447cafa..2156531a7f69 100644 --- a/platform/testFramework/src/com/intellij/mock/MockDocument.java +++ b/platform/testFramework/src/com/intellij/mock/MockDocument.java @@ -288,7 +288,7 @@ public class MockDocument implements DocumentEx { } @Override - public void addRangeMarker(@NotNull RangeMarkerEx rangeMarker) { + public void addRangeMarker(@NotNull RangeMarkerEx rangeMarker, int start, int end) { } @Override