range markers made weakly referenced

This commit is contained in:
Alexey Kudravtsev
2011-01-31 16:30:07 +03:00
parent 880b984dc0
commit 1a71d0a512
22 changed files with 838 additions and 823 deletions
@@ -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() {
@@ -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();
}
}
}
@@ -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);
@@ -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);
}
@@ -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();
@@ -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<DocumentListener> myDocumentListeners = new ArrayList<DocumentListener>();
private final List<DocumentListener> myDocumentListeners = ContainerUtil.createEmptyCOWList();
private final RangeMarkerTree<RangeMarkerEx> myRangeMarkers = new RangeMarkerTree<RangeMarkerEx>(this);
private final List<RangeMarker> myGuardedBlocks = new ArrayList<RangeMarker>();
private ReadonlyFragmentModificationHandler myReadonlyFragmentModificationHandler;
@@ -73,7 +74,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
private volatile MarkupModelEx myMarkupModel;
private DocumentListener[] myCachedDocumentListeners;
private final List<EditReadOnlyListener> myReadOnlyListeners = new ArrayList<EditReadOnlyListener>(1);
private final List<EditReadOnlyListener> 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);
}
}
@@ -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) {
@@ -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;
}
@@ -26,7 +26,6 @@ public interface IntervalTree<T extends Interval> extends Iterable<T> {
boolean processOverlappingWith(int start, int end, @NotNull Processor<? super T> processor);
boolean processOverlappingWith(int offset, @NotNull Processor<? super T> 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);
}
@@ -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<T extends MutableInterval> extends RedBlackTree<T> implements IntervalTree<T> {
protected final ReadWriteLock l = new ReentrantReadWriteLock();
private final EqualStartIntervalComparator<T> comparator;
public class MyNode extends DeltaNode<T> {
public MyNode(T key) {
super(key);
protected abstract EqualStartIntervalComparator<IntervalNode> getComparator();
private final ReferenceQueue<T> myReferenceQueue = new ReferenceQueue<T>();
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<T> interval;
public MyNode(@NotNull T key, int start, int end) {
// maxEnd == 0 so to not disrupt existing maxes
interval = new WeakReference<T>(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<T extends MutableInterval> extends RedBlackTree.Node<T> {
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<T> node = this;
while (node != null) {
if (key.isValid()) {
delta += node.delta;
}
node = (DeltaNode<T>)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<T> lookupNode(T key, Node<T> 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<T> interval) {
return ((RangeMarkerImpl)key).myNode;
//int delta = 0;
//int delta1 = ((RangeMarkerImpl)key).myNode == null ? 0 : ((RangeMarkerImpl)key).myNode.computeDeltaUpToRoot();
//List<IntervalNode> invalid = new ArrayList<IntervalNode>();
//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<IntervalNode> 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<T extends MutableInterval> {
int compare(T i1, T i2);
}
public IntervalTreeImpl(EqualStartIntervalComparator<T> comparator) {
super(null);
this.comparator = comparator;
}
protected MyNode getRoot() {
return (MyNode)root;
protected IntervalTreeImpl.IntervalNode getRoot() {
return (IntervalNode)root;
}
public boolean process(@NotNull Processor<? super T> processor) {
@@ -147,12 +196,15 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
}
}
private boolean process(Node<T> root, Processor<? super T> processor, int modCountBefore) {
private boolean process(IntervalNode root, Processor<? super T> 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<T extends MutableInterval> extends RedBla
}
}
private boolean processOverlappingWith(MyNode root, int start, int end, Processor<? super T> processor, int modCountBefore, int deltaUpToRootExclusive) {
private boolean processOverlappingWith(IntervalNode root,
int start,
int end,
Processor<? super T> 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<T extends MutableInterval> 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<T extends MutableInterval> extends RedBla
}
}
private boolean processOverlapping(MyNode root, int offset, Processor<? super T> processor, int modCountBefore, int deltaUpToRootExclusive) {
private boolean processOverlapping(IntervalNode root,
int offset,
Processor<? super T> 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<T extends MutableInterval> 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<IntervalNode> gced = new ArrayList<IntervalNode>();
//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<IntervalNode> 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<T extends MutableInterval> 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<Boolean> allValid = new Ref<Boolean>(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<Integer,Integer,Integer> checkMax(MyNode root, int deltaUpToRootExclusive, boolean assertInvalid) {
protected Trinity<Integer,Integer,Integer> checkMax(IntervalNode root,
int deltaUpToRootExclusive,
boolean assertInvalid,
Ref<Boolean> 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<Integer, Integer, Integer> l = checkMax(root.getLeft(), delta, assertInvalid);
counter.getAndIncrement();
int delta = deltaUpToRootExclusive + (root.isValid() ? root.delta : 0);
Trinity<Integer, Integer, Integer> l = checkMax(root.getLeft(), delta, assertInvalid, allValid, counter);
int minLeftStart = l.first;
int maxLeftStart = l.second;
int maxLeftEnd = l.third;
Trinity<Integer, Integer, Integer> r = checkMax(root.getRight(), delta, assertInvalid);
Trinity<Integer, Integer, Integer> 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<IntervalNode>());
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<T extends MutableInterval> extends RedBla
@Override
protected Node<T> maximumNode(Node<T> 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<T extends MutableInterval> 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<T extends MutableInterval> 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<T> 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<T extends MutableInterval> extends RedBla
incDelta(root.getRight(), delta);
}
}
@Override
protected MyNode swapWithMaxPred(Node<T> root, Node<T> maxPred) {
protected IntervalNode swapWithMaxPred(Node<T> root, Node<T> 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<T extends MutableInterval> 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<T extends MutableInterval> 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<T extends MutableInterval> extends RedBla
@Override
protected void rotateRight(Node<T> 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<T extends MutableInterval> extends RedBla
@Override
protected void rotateLeft(Node<T> 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<T extends MutableInterval> extends RedBla
@Override
protected void replaceNode(@NotNull Node<T> node, Node<T> 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<T> 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<T extends MutableInterval> extends RedBla
@NotNull
Iterator<T> 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<T> createIteratorFrom(final MyNode firstNode) {
private Iterator<T> createIteratorFrom(@NotNull final IntervalNode firstNode) {
checkMax(true);
normalize();
final int modCountBefore = modCount;
return new Iterator<T>() {
Node<T> 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<T> 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<T> getNext(Node<T> element) {
Node<T> 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<T> 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<T extends MutableInterval> 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<T extends MutableInterval> 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<IntervalNode> gced = new ArrayList<IntervalNode>();
collectGced(getRoot(), gced);
deleteNodes(gced);
}
private static void collectGced(IntervalNode root, List<IntervalNode> 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());
}
@@ -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<RangeHighlighterEx> list = new ArrayList<RangeHighlighterEx>();
myHighlighterTree.process(new CommonProcessors.CollectProcessor<RangeHighlighterEx>(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);
}
@@ -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() ? "]" : ")");
}
}
@@ -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<RangeHighlighterEx> 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;
}
}
@@ -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();
}
}
@@ -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;
@@ -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<RangeHighlighterEx> 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
@@ -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<RangeHighlighterEx> {
public RangeHighlighterTree(Document document) {
super(document, COMPARATOR);
}
private static final EqualStartIntervalComparator<RangeHighlighterEx> COMPARATOR = new EqualStartIntervalComparator<RangeHighlighterEx>() {
public int compare(RangeHighlighterEx o1, RangeHighlighterEx o2) {
if (o1.getLayer() != o2.getLayer()) {
return o2.getLayer() - o1.getLayer();
private final EqualStartIntervalComparator<IntervalNode> myComparator = new EqualStartIntervalComparator<IntervalNode>() {
@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<RangeHighlighterEx> {
}
};
public RangeHighlighterTree(Document document) {
super(document);
}
@Override
protected EqualStartIntervalComparator<IntervalNode> 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;
}
}
}
@@ -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<RangeMarkerEx>.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();
}
}
}
@@ -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<T extends RangeMarkerEx> extends IntervalTreeImpl<T> {
private final PrioritizedDocumentListener myListener;
private final Document myDocument;
private static final EqualStartIntervalComparator<RangeMarkerEx> RANGEMARKER_COMPARATOR = new EqualStartIntervalComparator<RangeMarkerEx>() {
public int compare(RangeMarkerEx o1, RangeMarkerEx o2) {
private final EqualStartIntervalComparator<IntervalNode> myEqualStartIntervalComparator = new EqualStartIntervalComparator<IntervalNode>() {
@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<T extends RangeMarkerEx> extends IntervalTreeImpl<T
}
};
protected RangeMarkerTree(Document document, EqualStartIntervalComparator<T> comparator) {
super(comparator);
@Override
protected EqualStartIntervalComparator<IntervalNode> getComparator() {
return myEqualStartIntervalComparator;
}
protected RangeMarkerTree(Document document) {
myDocument = document;
myListener = new PrioritizedDocumentListener() {
public int getPriority() {
@@ -72,41 +83,70 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
document.addDocumentListener(myListener);
}
public RangeMarkerTree(Document document) {
this(document, (EqualStartIntervalComparator<T>)RANGEMARKER_COMPARATOR);
}
public void dispose() {
myDocument.removeDocumentListener(myListener);
}
@Override
protected Trinity<Integer, Integer, Integer> checkMax(MyNode root, int deltaUpToRootExclusive, boolean assertInvalid) {
protected Trinity<Integer, Integer, Integer> checkMax(IntervalNode root,
int deltaUpToRootExclusive,
boolean assertInvalid,
Ref<Boolean> 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<T extends RangeMarkerEx> extends IntervalTreeImpl<T
normalized = false;
modCount++;
List<MyNode> affected = new ArrayList<MyNode>();
List<IntervalNode> affected = new ArrayList<IntervalNode>();
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<MyNode> affected) {
private static void updateMarkersOnChange(IntervalNode root,
@NotNull DocumentEvent e,
@NotNull List<IntervalNode> 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<T extends RangeMarkerEx> extends IntervalTreeImpl<T
correctMax(root, 0);
}
else {
if (offset <= interval.getEndOffset()) {
if (offset <= root.intervalEnd()) {
// unlucky enough so that change affects the interval
affected.add(root);
root.key.setValid(false); //make invisible
if (key != null) affected.add(root); // otherwise we've already added it
root.setValid(false); //make invisible
}
updateMarkersOnChange(root.getLeft(), e, affected);
@@ -17,42 +17,23 @@ package com.intellij.openapi.editor.impl;
import org.jetbrains.annotations.NotNull;
import java.util.Comparator;
/**
* User: cdr
*/
public class RedBlackTree<K> {
public static final boolean VERIFY = false;
public abstract class RedBlackTree<K> {
public static boolean VERIFY = false;
private static final int INDENT_STEP = 4;
protected int size;
protected int modCount;
public Node<K> root;
protected Node<K> root;
private final Comparator<K> comparator;
public RedBlackTree(Comparator<K> comparator) {
this.comparator = comparator;
public RedBlackTree() {
root = null;
verifyProperties();
}
protected Node<K> lookupNode(K key, Node<K> 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<K> lookupNode(@NotNull K key, Node<K> root);
protected void rotateLeft(Node<K> n) {
Node<K> r = n.getRight();
@@ -97,49 +78,7 @@ public class RedBlackTree<K> {
//oldn.right = null;
}
protected Node<K> createNewNode(K key) {
return new Node<K>(key);
}
public Node<K> insert(Node<K> newNode) {
modCount++;
newNode.color = Color.RED;
K key = newNode.key;
if (root == null) {
root = newNode;
}
else {
Node<K> 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<K> createNewNode(K key, int start, int end, Object data);
protected void insertCase1(Node<K> n) {
if (n.getParent() == null) {
@@ -157,7 +96,7 @@ public class RedBlackTree<K> {
// Tree is still valid
}
void insertCase3(Node<K> n) {
private void insertCase3(Node<K> n) {
if (nodeColor(n.uncle()) == Color.RED) {
n.getParent().color = Color.BLACK;
n.uncle().color = Color.BLACK;
@@ -169,7 +108,7 @@ public class RedBlackTree<K> {
}
}
void insertCase4(Node<K> n) {
private void insertCase4(Node<K> n) {
if (n == n.getParent().getRight() && n.getParent() == n.grandparent().getLeft()) {
rotateLeft(n.getParent());
n = n.getLeft();
@@ -181,7 +120,7 @@ public class RedBlackTree<K> {
insertCase5(n);
}
void insertCase5(Node<K> n) {
private void insertCase5(Node<K> 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<K> {
assert node1 == null || node1.getParent() == null || node1.getParent().getLeft() == node1 || node1.getParent().getRight() == node1;
}
//returns parent of deleted node
public Node<K> delete(K key) {
modCount++;
public Node<K> delete(@NotNull K key) {
Node<K> n = lookupNode(key, root);
deleteNode(n);
return n;
}
protected void deleteNode(Node<K> n) {
modCount++;
if (n == null) return; // Key not found, do nothing
Node<K> 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<K> pred = maximumNode(n.getLeft());
@@ -236,11 +179,7 @@ public class RedBlackTree<K> {
verifyProperties();
}
protected Node<K> swapWithMaxPred(Node<K> nowAscendant, Node<K> nowDescendant) {
nowAscendant.key = nowDescendant.key;
//nowAscendant.value = nowDescendant.value;
return nowDescendant;
}
protected abstract Node<K> swapWithMaxPred(Node<K> nowAscendant, Node<K> nowDescendant);
protected Node<K> maximumNode(Node<K> n) {
assert n != null;
@@ -347,93 +286,23 @@ public class RedBlackTree<K> {
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<Integer> t = new RedBlackTree<Integer>();
for (int i = 0; i < adds.length; i++) {
int key = adds[i];
Node<Integer> 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<Integer> t = new RedBlackTree<Integer>();
List<Integer> adds = new ArrayList<Integer>();
List<Integer> dels = new ArrayList<Integer>();
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<Integer> node = t.createNewNode(x);
node.value = x;
t.insert(node);
Integer lookup = t.lookupNode(x).value;
assert lookup.equals(i) : lookup;
}
List<Integer> candidates = new ArrayList<Integer>(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> {
K key;
public abstract static class Node<K> {
private Node<K> left;
private Node<K> right;
private Node<K> parent;
protected Color color;
private Node<K> parent = null;
protected Color color = Color.RED;
public Node(K key) {
this.key = key;
color = Color.RED;
setParent(null);
public Node() {
}
public Node<K> grandparent() {
@@ -477,6 +346,8 @@ public class RedBlackTree<K> {
public void setParent(Node<K> parent) {
this.parent = parent;
}
public abstract K getKey();
}
protected static enum Color {
@@ -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<SelectionListener> mySelectionListeners = ContainerUtil.createEmptyCOWList();
private final AtomicReference<MyRangeMarker> mySelectionMarker = new AtomicReference<MyRangeMarker>();
private final List<SelectionListener> 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;
@@ -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