range marker lazy creation

This commit is contained in:
Alexey Kudravtsev
2011-03-09 12:54:10 +03:00
parent 9ba4d4bb8a
commit ba9e4f9bf4
16 changed files with 506 additions and 324 deletions
@@ -526,10 +526,6 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
return myDelegate.removeRangeMarker(((RangeMarkerWindow)rangeMarker).getDelegate());
}
public void addRangeMarker(@NotNull RangeMarkerEx rangeMarker, int start, int end) {
myDelegate.addRangeMarker(((RangeMarkerWindow)rangeMarker).getDelegate(), start, end);
}
public boolean isInBulkUpdate() {
return false;
}
@@ -50,7 +50,6 @@ public interface DocumentEx extends Document {
void clearLineModificationFlags();
boolean removeRangeMarker(@NotNull RangeMarkerEx rangeMarker);
void addRangeMarker(@NotNull RangeMarkerEx rangeMarker, int start, int end);
boolean isInBulkUpdate();
@@ -213,15 +213,19 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
return myRangeMarkers.removeInterval(rangeMarker);
}
public void addRangeMarker(@NotNull RangeMarkerEx rangeMarker, int start, int end) {
public void addRangeMarker(@NotNull RangeMarkerEx rangeMarker, int start, int end, boolean greedyToLeft, boolean greedyToRight, int layer) {
ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed();
myRangeMarkers.addInterval(rangeMarker, start, end, null);
myRangeMarkers.addInterval(rangeMarker, start, end, greedyToLeft, greedyToRight, layer);
}
@TestOnly
public int getRangeMarkersSize() {
return myRangeMarkers.size();
}
@TestOnly
public int getRangeMarkersNodeSize() {
return myRangeMarkers.nodeSize();
}
@NotNull
public RangeMarker createGuardedBlock(int startOffset, int endOffset) {
@@ -0,0 +1,31 @@
/*
* Copyright 2000-2011 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 java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
/**
* User: cdr
*/
interface Getable<T> {
T get();
}
class WeakReferencedGetable<T> extends WeakReference<T> implements Getable<T> {
public WeakReferencedGetable(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}
@@ -26,6 +26,5 @@ 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 addInterval(@NotNull T interval, int start, int end, Object data);
boolean removeInterval(@NotNull T interval);
}
@@ -15,17 +15,16 @@
*/
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.SmartList;
import com.intellij.util.containers.ContainerUtil;
import gnu.trove.TLongHashSet;
import org.jetbrains.annotations.NotNull;
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;
@@ -35,13 +34,14 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
* User: cdr
*/
public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBlackTree<T> implements IntervalTree<T> {
protected int keySize; // number of all keys
protected final ReadWriteLock l = new ReentrantReadWriteLock();
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 {
public abstract class IntervalNode extends RedBlackTree.Node<T> 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();
@@ -53,28 +53,98 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
public IntervalNode getRight() {
return (IntervalNode)super.getRight();
}
@Override
public IntervalNode getParent() {
return (IntervalNode)super.getParent();
}
}
protected class MyNode extends IntervalNode {
protected class MyNode extends IntervalNode {
private volatile int myStart;
private volatile int myEnd;
private volatile boolean isValid = true;
private final Reference<T> interval;
protected final List<Getable<T>> intervals;
public MyNode(@NotNull T key, int start, int end) {
// maxEnd == 0 so to not disrupt existing maxes
interval = new WeakReference<T>(key,myReferenceQueue);
intervals = new SmartList<Getable<T>>(createGetable(key));
myStart = start;
myEnd = end;
}
@Override
public T getKey() {
return interval.get();
public boolean processAliveKeys(@NotNull Processor<? super T> processor) {
for (Getable<T> interval : intervals) {
T key = interval.get();
if (key != null && !processor.process(key)) return false;
}
return true;
}
@NotNull
@Override
public List<T> getAliveKeys() {
List<T> result = null;
for (Getable<T> ref : intervals) {
T interval = ref.get();
if (interval == null) continue;
if (result == null) {
result = new ArrayList<T>(intervals.size());
}
result.add(interval);
}
return result == null ? Collections.<T>emptyList() : result;
}
public boolean hasAliveKey(boolean purgeDead) {
for (int i = intervals.size() - 1; i >= 0; i--) {
Getable<T> interval = intervals.get(i);
if (interval.get() != null) return true;
if (purgeDead) {
assertUnderWriteLock();
intervals.remove(i);
assert keySize > 0 : keySize;
keySize--;
}
}
return false;
}
// removes interval and the node, if node become empty
public boolean removeInterval(@NotNull T key) {
checkBelongsToTheTree(key, true);
assertUnderWriteLock();
for (int i = intervals.size() - 1; i >= 0; i--) {
Getable<T> interval = intervals.get(i);
T t = interval.get();
if (t == key) {
intervals.remove(i);
assert keySize > 0 : keySize;
keySize--;
if (intervals.isEmpty()) {
removeNode(this);
return true;
}
return false;
}
}
List<T> aliveKeys = getAliveKeys();
assert false: "not found interval: "+key +"; "+ aliveKeys;
return false;
}
private void assertUnderWriteLock() {
if (!VERIFY) return;
String s = l.writeLock().toString();
assert s.contains("Locked by thread") : s;
}
public void addInterval(@NotNull T interval) {
assertUnderWriteLock();
intervals.add(createGetable(interval));
keySize++;
}
protected Getable<T> createGetable(@NotNull T interval) {
return new WeakReferencedGetable<T>(interval, myReferenceQueue);
}
@Override
@@ -134,38 +204,12 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
}
}
@Override
protected MyNode createNewNode(T key, int start, int end, Object data) {
return new MyNode(key, start, end);
}
@NotNull
protected abstract MyNode createNewNode(@NotNull T key, int start, int end, boolean greedyToLeft, boolean greedyToRight, int layer);
protected abstract MyNode lookupNode(@NotNull T key);
@Override
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 compareNodes(@NotNull IntervalNode i1, int delta1, @NotNull IntervalNode i2, int delta2, List<IntervalNode> invalid) {
T i2Key = (T)i2.getKey();
if (i2Key == null) {
private int compareNodes(@NotNull IntervalNode i1, int delta1, @NotNull IntervalNode i2, int delta2, @NotNull List<IntervalNode> invalid) {
if (!i2.hasAliveKey(false)) {
invalid.add(i2); //gced
}
int start1 = i1.intervalStart() + delta1;
@@ -179,8 +223,10 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
protected interface EqualStartIntervalComparator<T extends MutableInterval> {
int compare(T i1, T i2);
}
protected IntervalTreeImpl.IntervalNode getRoot() {
protected IntervalNode getRoot() {
return (IntervalNode)root;
}
@@ -200,11 +246,8 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
if (root == null) return true;
if (!process(root.getLeft(), processor, modCountBefore)) return false;
T key = (T)root.getKey();
if (key != null) {
if (!processor.process(key)) return false;
assert modCount == modCountBefore;
}
if (!root.processAliveKeys(processor)) return false;
assert modCount == modCountBefore;
return process(root.getRight(), processor, modCountBefore);
}
@@ -241,9 +284,8 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
int myStartOffset = root.intervalStart() + delta;
int myEndOffset = root.intervalEnd() + delta;
boolean overlaps = Math.max(myStartOffset, start) <= Math.min(myEndOffset, end);
T key = (T)root.getKey();
if (key != null) {
if (overlaps && !processor.process(key)) return false;
if (overlaps) {
if (!root.processAliveKeys(processor)) return false;
assert modCount == modCountBefore;
}
@@ -285,9 +327,8 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
int myEndOffset = root.intervalEnd() + delta;
boolean overlaps = myStartOffset <= offset && offset < myEndOffset;
T key = (T)root.getKey();
if (key != null) {
if (overlaps && !processor.process(key)) return false;
if (overlaps) {
if (!root.processAliveKeys(processor)) return false;
assert modCount == modCountBefore;
}
@@ -298,7 +339,7 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
return processOverlapping(root.getRight(), offset, processor, modCountBefore, delta);
}
protected void insert(@NotNull IntervalNode node) {
protected MyNode findOrInsert(@NotNull IntervalNode node) {
node.color = Color.RED;
node.setParent(null);
node.setValid(true);
@@ -308,8 +349,6 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
node.setRight(null);
List<IntervalNode> gced = new ArrayList<IntervalNode>();
//T nodeKey = (T)node.getKey();
//assert nodeKey != null;
if (root == null) {
root = node;
}
@@ -320,6 +359,9 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
while (true) {
delta += current.delta;
int compResult = compareNodes(node, 0, current, delta, gced);
if (compResult == 0) {
return (MyNode)current;
}
if (compResult < 0) {
if (current.getLeft() == null) {
current.setLeft(node);
@@ -327,44 +369,25 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
}
current = current.getLeft();
}
else if (compResult > 0) {
else /*if (compResult > 0)*/ {
if (current.getRight() == null) {
current.setRight(node);
break loop;
}
current = current.getRight();
}
else {
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);
return;
}
}
node.delta = -delta;
node.setParent(current);
}
size++;
correctMaxUp(node);
onInsertNode();
keySize += ((MyNode)node).intervals.size();
insertCase1(node);
verifyProperties();
deleteNodes(gced);
return (MyNode)node;
}
private void deleteNodes(List<IntervalNode> collectedAway) {
@@ -382,18 +405,21 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
}
}
@Override
public IntervalNode addInterval(@NotNull T interval, int start, int end, Object data) {
public MyNode addInterval(@NotNull T interval, int start, int end, boolean greedyToLeft, boolean greedyToRight, int layer) {
try {
l.writeLock().lock();
checkMax(true);
processReferenceQueue();
modCount++;
IntervalNode newNode = createNewNode(interval, start, end, data);
insert(newNode);
IntervalNode newNode = createNewNode(interval, start, end, greedyToLeft, greedyToRight, layer);
MyNode insertedNode = findOrInsert(newNode);
if (insertedNode != newNode) {
// merged
insertedNode.addInterval(interval);
}
checkMax(true); // myNode assigned
return newNode;
checkMax(true);
return insertedNode;
}
finally {
l.writeLock().unlock();
@@ -404,10 +430,13 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
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);
AtomicInteger keyCounter = new AtomicInteger();
AtomicInteger nodeCounter = new AtomicInteger();
TLongHashSet ids = new TLongHashSet();
checkMax(getRoot(), 0, assertInvalid, allValid, keyCounter, nodeCounter, ids);
if (assertInvalid) {
assert size == counter.get() : "size: "+size+"; actual: "+counter;
assert nodeSize() == nodeCounter.get() : "node size: "+ nodeSize() +"; actual: "+nodeCounter;
assert keySize == keyCounter.get() : "key size: "+ keySize +"; actual: "+keyCounter;
}
return allValid.get();
}
@@ -416,15 +445,23 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
protected Trinity<Integer,Integer,Integer> checkMax(IntervalNode root,
int deltaUpToRootExclusive,
boolean assertInvalid,
Ref<Boolean> allValid, AtomicInteger counter) {
Ref<Boolean> allValid,
AtomicInteger keyCounter,
AtomicInteger nodeCounter,
TLongHashSet ids) {
if (root == null) return Trinity.create(Integer.MAX_VALUE,Integer.MIN_VALUE,Integer.MIN_VALUE);
counter.getAndIncrement();
for (T t : root.getAliveKeys()) {
checkBelongsToTheTree(t, assertInvalid);
assert ids.add(((RangeMarkerImpl)t).getId()) : t;
}
keyCounter.addAndGet(((MyNode)root).intervals.size());
nodeCounter.incrementAndGet();
int delta = deltaUpToRootExclusive + (root.isValid() ? root.delta : 0);
Trinity<Integer, Integer, Integer> l = checkMax(root.getLeft(), delta, assertInvalid, allValid, counter);
Trinity<Integer, Integer, Integer> l = checkMax(root.getLeft(), delta, assertInvalid, allValid, keyCounter, nodeCounter, ids);
int minLeftStart = l.first;
int maxLeftStart = l.second;
int maxLeftEnd = l.third;
Trinity<Integer, Integer, Integer> r = checkMax(root.getRight(), delta, assertInvalid, allValid, counter);
Trinity<Integer, Integer, Integer> r = checkMax(root.getRight(), delta, assertInvalid, allValid, keyCounter, nodeCounter, ids);
int maxRightEnd = r.third;
int minRightStart = r.first;
int maxRightStart = r.second;
@@ -434,8 +471,7 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
return Trinity.create(Math.min(minLeftStart, minRightStart), Math.max(maxLeftStart, maxRightStart), Math.max(maxRightEnd, maxLeftEnd));
}
IntervalNode parent = root.getParent();
T rootKey = (T)root.getKey();
if (parent != null && assertInvalid && rootKey != null) {
if (parent != null && assertInvalid && root.hasAliveKey(false)) {
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;
@@ -465,17 +501,39 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
return root;
}
protected void checkBelongsToTheTree(T interval, boolean assertInvalid) {
if (!VERIFY) return;
MyNode root = lookupNode(interval);
if (root == null) return;
assert !root.intervals.isEmpty();
assert root.getTree() == this;
List<T> keys = root.getAliveKeys();
assert keys.contains(interval) : keys + "; " + interval;
for (T key : keys) {
MyNode node = lookupNode(key);
assert assertInvalid && node == root || !assertInvalid && (node == null || node == root) : node;
assert assertInvalid && node.getTree() == this || !assertInvalid && (node == null || node.getTree() == this) : node;
}
IntervalNode e = root;
while (e.getParent() != null) e = e.getParent();
assert e == this.root; // assert the node belongs to our tree
}
@Override
public boolean removeInterval(@NotNull T interval) {
if (!interval.isValid()) return false;
try {
l.writeLock().lock();
checkBelongsToTheTree(interval, true);
checkMax(true);
processReferenceQueue();
IntervalNode node = lookupNode(interval, root);
MyNode node = lookupNode(interval);
if (node == null) return false;
removeNode(node);
node.removeInterval(interval);
checkMax(true);
return true;
}
@@ -493,33 +551,51 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
@Override
protected void deleteNode(Node<T> n) {
pushDeltaFromRoot((IntervalNode)n);
MyNode node = (MyNode)n;
pushDeltaFromRoot(node);
super.deleteNode(n);
keySize -= node.intervals.size();
assert keySize >= 0 : keySize;
}
protected static void pushDelta(IntervalNode root) {
if (root == null || !root.isValid()) return;
@Override
public int size() {
return keySize;
}
// returns true if some delta was or became not null
protected boolean pushDelta(IntervalNode root) {
if (root == null || !root.isValid()) return false;
int delta = root.delta;
if (delta != 0) {
root.setIntervalStart(root.intervalStart() + delta);
root.setIntervalEnd(root.intervalEnd() + delta);
root.maxEnd += delta;
root.delta = 0;
incDelta(root.getLeft(), delta);
//noinspection NonShortCircuitBooleanExpression
return
incDelta(root.getLeft(), delta) |
incDelta(root.getRight(), delta);
}
return false;
}
// returns true if some delta was or became not null
private boolean incDelta(IntervalNode root, int delta) {
if (root == null) return false;
if (root.isValid()) {
int newDelta = root.delta += delta;
return newDelta != 0;
}
else {
//noinspection NonShortCircuitBooleanExpression
return
incDelta(root.getLeft(), delta) |
incDelta(root.getRight(), delta);
}
}
private static void incDelta(IntervalNode root, int delta) {
if (root == null) return;
if (root.isValid()) {
root.delta += delta;
}
else {
incDelta(root.getLeft(), delta);
incDelta(root.getRight(), delta);
}
}
@Override
protected IntervalNode swapWithMaxPred(Node<T> root, Node<T> maxPred) {
checkMax(false);
@@ -546,7 +622,6 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
checkMax(false);
return a;
}
private void swapNodes(IntervalNode n1, IntervalNode n2) {
IntervalNode l1 = n1.getLeft();
IntervalNode r1 = n1.getRight();
@@ -580,7 +655,7 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
if (r2 != null) r2.setParent(n1);
}
protected static int maxEndOf(IntervalNode node, int deltaUpToRootExclusive) {
private int maxEndOf(IntervalNode node, int deltaUpToRootExclusive) {
if (node == null) {
return 0;
}
@@ -591,14 +666,14 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
}
// max of n.left's maxend, n.right's maxend and its own interval endOffset
protected static void correctMax(@NotNull IntervalNode node, int deltaUpToRoot) {
protected 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.intervalEnd());
node.maxEnd = realMax - deltaUpToRoot;
}
private static void correctMaxUp(IntervalNode node) {
private void correctMaxUp(IntervalNode node) {
int delta = node == null ? 0 : node.computeDeltaUpToRoot();
while (node != null) {
if (node.isValid()) {
@@ -720,37 +795,39 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
final int modCountBefore = modCount;
return new Iterator<T>() {
private IntervalNode node = firstNode;
private T element = (T)firstNode.getKey();
private Iterator<T> iteratorInCurrentList = firstNode.getAliveKeys().iterator();
{
// find first non-null key
while (element == null) {
while (!iteratorInCurrentList.hasNext()) {
moveNext();
if (node == null) break;
}
}
public boolean hasNext() {
return element != null;
Iterator<T> it = iteratorInCurrentList;
return it != null && it.hasNext();
}
public T next() {
assert modCount == modCountBefore : "Must not modify range markers during iterate";
if (node == null || element == null) throw new NoSuchElementException();
T current = element;
if (node == null || iteratorInCurrentList == null || !iteratorInCurrentList.hasNext()) throw new NoSuchElementException();
T current = iteratorInCurrentList.next();
moveNext();
return current;
}
private void moveNext() {
if (iteratorInCurrentList != null && iteratorInCurrentList.hasNext()) return;
while (true) {
node = getNextNode();
if (node == null) {
element = null;
iteratorInCurrentList = null;
break;
}
element = (T)node.getKey();
if (element != null) break;
iteratorInCurrentList = node.getAliveKeys().iterator();
if (iteratorInCurrentList.hasNext()) break;
}
}
@@ -793,14 +870,38 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
l.writeLock().unlock();
}
}
private static void normalize(IntervalNode root) {
private void normalize(IntervalNode root) {
if (root == null) return;
pushDelta(root);
normalize(root.getLeft());
normalize(root.getRight());
}
public void changeData(T interval, int start, int end, boolean greedyToLeft, boolean greedyToRight, int layer) {
try {
l.writeLock().lock();
MyNode node = lookupNode(interval);
if (node == null) return;
int before = size();
boolean nodeRemoved = node.removeInterval(interval);
assert nodeRemoved || !node.intervals.isEmpty();
MyNode insertedNode = addInterval(interval, start, end, greedyToLeft, greedyToRight, layer);
assert node != insertedNode;
int after = size();
// can be gced
assert before >= after : before +";" + after;
checkBelongsToTheTree(interval, true);
checkMax(true);
}
finally {
l.writeLock().unlock();
}
}
// called under write lock
private void processReferenceQueue() {
int dead = 0;
@@ -821,35 +922,19 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
deleteNodes(gced);
}
private static void collectGced(IntervalNode root, List<IntervalNode> gced) {
private void collectGced(IntervalNode root, List<IntervalNode> gced) {
if (root == null) return;
if (root.getKey() == null) gced.add(root);
if (!root.hasAliveKey(true)) 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 static void printSorted(IntervalNode root) {
private void printSorted(IntervalNode root) {
if (root == null) return;
printSorted(root.getLeft());
System.out.println(root);
printSorted(root.getRight());
}
}
@@ -35,9 +35,7 @@ import com.intellij.openapi.editor.markup.HighlighterTargetArea;
import com.intellij.openapi.editor.markup.RangeHighlighter;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.util.UserDataHolderBase;
import com.intellij.util.CommonProcessors;
import com.intellij.util.Consumer;
import com.intellij.util.Processor;
import com.intellij.util.*;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -99,6 +97,7 @@ public class MarkupModelImpl extends UserDataHolderBase implements MarkupModelEx
return offset;
}
// NB: Can return invalid highlighters
@NotNull
public RangeHighlighter[] getAllHighlighters() {
ApplicationManager.getApplication().assertIsDispatchThread();
@@ -123,16 +122,11 @@ public class MarkupModelImpl extends UserDataHolderBase implements MarkupModelEx
ApplicationManager.getApplication().assertIsDispatchThread();
RangeHighlighterEx highlighter = isPersistent
? new PersistentRangeHighlighterImpl(this, startOffset, layer, targetArea, textAttributes)
: new RangeHighlighterImpl(this, startOffset, endOffset, layer, targetArea, textAttributes);
: new RangeHighlighterImpl(this, startOffset, endOffset, layer, targetArea, textAttributes, false, false);
myCachedHighlighters = null;
if (changeAttributesAction != null) {
if (isPersistent) {
((PersistentRangeHighlighterImpl)highlighter).changeAttributesInBatch(changeAttributesAction);
}
else {
((RangeHighlighterImpl)highlighter).changeAttributesInBatch(changeAttributesAction);
}
((RangeHighlighterImpl)highlighter).changeAttributesNoEvents(changeAttributesAction);
}
fireAfterAdded(highlighter);
return highlighter;
@@ -141,17 +135,20 @@ public class MarkupModelImpl extends UserDataHolderBase implements MarkupModelEx
@Override
public void changeAttributesInBatch(@NotNull RangeHighlighterEx highlighter, @NotNull Consumer<RangeHighlighterEx> changeAttributesAction) {
ApplicationManager.getApplication().assertIsDispatchThread();
boolean changed = highlighter instanceof PersistentRangeHighlighterImpl
? ((PersistentRangeHighlighterImpl)highlighter).changeAttributesInBatch(changeAttributesAction)
: ((RangeHighlighterImpl)highlighter).changeAttributesInBatch(changeAttributesAction);
boolean changed = ((RangeHighlighterImpl)highlighter).changeAttributesNoEvents(changeAttributesAction);
if (changed) {
fireAttributesChanged(highlighter);
}
}
void addRangeHighlighter(RangeHighlighterEx marker, int start, int end, RangeHighlighterData data) {
IntervalTreeImpl<RangeHighlighterEx>.IntervalNode addRangeHighlighter(RangeHighlighterEx marker,
int start,
int end,
boolean greedyToLeft,
boolean greedyToRight,
int layer) {
ApplicationManager.getApplication().assertIsDispatchThread();
myHighlighterTree.addInterval(marker, start, end, data);
return myHighlighterTree.addInterval(marker, start, end, greedyToLeft, greedyToRight, layer);
}
@NotNull
@@ -34,7 +34,8 @@ class PersistentRangeHighlighterImpl extends RangeHighlighterImpl implements Ran
int layer,
@NotNull HighlighterTargetArea target,
TextAttributes textAttributes) {
super(model, model.getDocument().getLineStartOffset(model.getDocument().getLineNumber(offset)), model.getDocument().getLineEndOffset(model.getDocument().getLineNumber(offset)),layer, target, textAttributes);
super(model, model.getDocument().getLineStartOffset(model.getDocument().getLineNumber(offset)), model.getDocument().getLineEndOffset(model.getDocument().getLineNumber(offset)),layer, target, textAttributes,
false, false);
setLine(model.getDocument().getLineNumber(offset));
}
@@ -15,6 +15,7 @@
*/
package com.intellij.openapi.editor.impl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.ex.RangeHighlighterEx;
import com.intellij.openapi.editor.markup.*;
@@ -30,7 +31,6 @@ import java.awt.*;
*/
abstract class RangeHighlighterData {
private final MarkupModel myModel;
private final int myLayer;
private final HighlighterTargetArea myTargetArea;
private TextAttributes myTextAttributes;
private LineMarkerRenderer myLineMarkerRenderer;
@@ -46,12 +46,10 @@ abstract class RangeHighlighterData {
int myLine; // for PersistentRangeHighlighterImpl only
RangeHighlighterData(@NotNull MarkupModel model,
int layer,
@NotNull HighlighterTargetArea target,
TextAttributes textAttributes) {
myTextAttributes = textAttributes;
myTargetArea = target;
myLayer = layer;
myModel = model;
if (textAttributes != null) {
myErrorStripeColor = textAttributes.getErrorStripeColor();
@@ -73,10 +71,6 @@ abstract class RangeHighlighterData {
}
}
public int getLayer() {
return myLayer;
}
public HighlighterTargetArea getTargetArea() {
return myTargetArea;
}
@@ -127,6 +121,7 @@ abstract class RangeHighlighterData {
}
public void setErrorStripeTooltip(Object tooltipObject) {
ApplicationManager.getApplication().assertIsDispatchThread();
Object old = myErrorStripeTooltip;
myErrorStripeTooltip = tooltipObject;
if (!Comparing.equal(old, tooltipObject)) {
@@ -139,6 +134,7 @@ abstract class RangeHighlighterData {
}
public void setThinErrorStripeMark(boolean value) {
ApplicationManager.getApplication().assertIsDispatchThread();
boolean old = myErrorStripeMarkIsThin;
myErrorStripeMarkIsThin = value;
if (old != value) {
@@ -223,16 +219,10 @@ abstract class RangeHighlighterData {
return Math.min(textLength, document.getLineEndOffset(document.getLineNumber(endOffset)) + 1);
}
public void registerMe(int start, int end) {
((MarkupModelImpl)myModel).addRangeHighlighter(getRangeHighlighter(), start, end,this);
}
public void unregisterMe() {
myModel.removeHighlighter(getRangeHighlighter());
}
// returns true if change was detected
boolean changeAttributesInBatch(@NotNull Consumer<RangeHighlighterEx> change) {
assert !inBatchChange;
assert !changed;
inBatchChange = true;
boolean result;
try {
@@ -245,4 +235,8 @@ abstract class RangeHighlighterData {
}
return result;
}
public MarkupModel getMarkupModel() {
return myModel;
}
}
@@ -28,43 +28,64 @@ import java.awt.*;
* Implementation of the markup element for the editor and document.
* @author max
*/
class RangeHighlighterImpl extends RangeMarkerImpl implements RangeHighlighterEx {
class RangeHighlighterImpl extends RangeMarkerImpl implements RangeHighlighterEx, Getable<RangeHighlighterImpl> {
private final RangeHighlighterData data;
RangeHighlighterImpl(@NotNull MarkupModel model,
int start,
int end,
int layer,
@NotNull HighlighterTargetArea target,
TextAttributes textAttributes) {
TextAttributes textAttributes, boolean greedyToLeft, boolean greedyToRight) {
super((DocumentEx)model.getDocument(), start, end,false);
RangeHighlighterData data = new RangeHighlighterData(model, layer, target, textAttributes) {
data = new RangeHighlighterData(model, target, textAttributes) {
@NotNull
@Override
public RangeHighlighterEx getRangeHighlighter() {
return RangeHighlighterImpl.this;
}
};
data.registerMe(start, end);
registerInTree(start, end, greedyToLeft, greedyToRight, layer);
}
@Override
public void setGreedyToLeft(boolean greedy) {
if (!isValid() || greedy == isGreedyToLeft()) return;
myNode.getTree().changeData(this, getStartOffset(), getEndOffset(), greedy, isGreedyToRight(), getLayer());
}
@Override
public void setGreedyToRight(boolean greedy) {
if (!isValid() || greedy == isGreedyToRight()) return;
myNode.getTree().changeData(this, getStartOffset(), getEndOffset(), isGreedyToLeft(), greedy, getLayer());
}
protected RangeHighlighterData getData() {
return ((RangeHighlighterTree.RHNode)myNode).data;
return data;
}
@Override
protected void registerInDocument(int start, int end) {
protected void registerInTree(int start, int end, boolean greedyToLeft, boolean greedyToRight, int layer) {
// we store highlighters in MarkupModel
((MarkupModelImpl)data.getMarkupModel()).addRangeHighlighter(this, start, end, greedyToLeft, greedyToRight, layer);
}
@Override
protected boolean unregisterInDocument() {
protected boolean unregisterInTree() {
if (myNode == null) return false;
// we store highlighters in MarkupModel
getData().unregisterMe();
getData().getMarkupModel().removeHighlighter(this);
myNode = null;
return true;
}
@Override
public RangeHighlighterImpl get() {
return this;
}
// delegates
public TextAttributes getTextAttributes() {
return getData().getTextAttributes();
@@ -74,12 +95,13 @@ class RangeHighlighterImpl extends RangeMarkerImpl implements RangeHighlighterEx
getData().setTextAttributes(textAttributes);
}
boolean changeAttributesInBatch(@NotNull Consumer<RangeHighlighterEx> change) {
boolean changeAttributesNoEvents(@NotNull Consumer<RangeHighlighterEx> change) {
return getData().changeAttributesInBatch(change);
}
public int getLayer() {
return getData().getLayer();
RangeHighlighterTree.RHNode node = (RangeHighlighterTree.RHNode)myNode;
return node == null ? -1 : node.myLayer;
}
public HighlighterTargetArea getTargetArea() {
@@ -28,8 +28,8 @@ public class RangeHighlighterTree extends RangeMarkerTree<RangeHighlighterEx> {
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();
if (o1.myLayer != o2.myLayer) {
return o2.myLayer - o1.myLayer;
}
boolean greedyL1 = o1.isGreedyToLeft();
boolean greedyL2 = o2.isGreedyToLeft();
@@ -42,9 +42,7 @@ public class RangeHighlighterTree extends RangeMarkerTree<RangeHighlighterEx> {
boolean greedyR2 = o2.isGreedyToRight();
if (greedyR1 != greedyR2) return greedyR1 ? -1 : 1;
// for now we tolerate equal range range markers (till lazy creation impl)
d = (int)(o1.getId() - o2.getId());
return d;
return 0;
}
};
@@ -57,19 +55,29 @@ public class RangeHighlighterTree extends RangeMarkerTree<RangeHighlighterEx> {
return myComparator;
}
@NotNull
@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;
protected RHNode createNewNode(@NotNull RangeHighlighterEx key, int start, int end, boolean greedyToLeft, boolean greedyToRight, int layer) {
return new RHNode(key, start, end, greedyToLeft, greedyToRight,layer);
}
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;
final int myLayer;
public RHNode(@NotNull final RangeHighlighterEx key,
int start,
int end,
boolean greedyToLeft,
boolean greedyToRight,
int layer) {
super(key, start, end, greedyToLeft, greedyToRight);
myLayer = layer;
}
// range highlighters are strongly referenced
@Override
protected Getable<RangeHighlighterEx> createGetable(@NotNull RangeHighlighterEx interval) {
return (Getable)interval;
}
}
}
@@ -23,13 +23,22 @@ 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, MutableInterval {
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.RangeMarkerImpl");
protected final DocumentEx myDocument;
RangeMarkerTree.RMNode myNode;
private final long myId;
//private static long counter;
private static final AtomicLong counter = new AtomicLong();
protected RangeMarkerImpl(@NotNull DocumentEx document, int start, int end, boolean register) {
this(document, start, end, register, false, false);
}
private RangeMarkerImpl(@NotNull DocumentEx document, int start, int end, boolean register, boolean greedyToLeft, boolean greedyToRight) {
if (start < 0) {
throw new IllegalArgumentException("Wrong start: " + start+"; end="+end);
}
@@ -41,29 +50,38 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx
}
myDocument = document;
myId = counter.getAndIncrement();
if (register) {
registerInDocument(start, end);
registerInTree(start, end, greedyToLeft, greedyToRight, 0);
}
}
protected void registerInDocument(int start, int end) {
myNode = null;
myDocument.addRangeMarker(this, start, end);
assert myNode != null;
private static Object createData(boolean greedyToLeft, boolean greedyToRight) {
return (greedyToLeft ? 2 : 0) | (greedyToRight ? 1 : 0);
}
protected boolean unregisterInDocument() {
protected void registerInTree(int start, int end, boolean greedyToLeft, boolean greedyToRight, int layer) {
((DocumentImpl)myDocument).addRangeMarker(this, start, end, greedyToLeft, greedyToRight, layer);
}
protected boolean unregisterInTree() {
IntervalTreeImpl tree = myNode.getTree();
tree.checkMax(true);
boolean b = myDocument.removeRangeMarker(this);
myNode = null;
tree.checkMax(true);
return b;
}
public long getId() {
return myNode.getId();
return myId;
}
@Override
public void dispose() {
unregisterInDocument();
if(isValid()) {
unregisterInTree();
}
}
public int getStartOffset() {
@@ -85,22 +103,13 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx
public void setGreedyToLeft(final boolean greedy) {
if (!isValid() || greedy == isGreedyToLeft()) return;
myNode.getTree().changeAttribute(myNode, new Runnable() {
@Override
public void run() {
myNode.setGreedyToLeft(greedy);
}
});
myNode.getTree().changeData(this, getStartOffset(), getEndOffset(), greedy, isGreedyToRight(), 0);
}
public void setGreedyToRight(final boolean greedy) {
if (!isValid() || greedy == isGreedyToRight()) return;
myNode.getTree().changeAttribute(myNode, new Runnable() {
@Override
public void run() {
myNode.setGreedyToRight(greedy);
}
});
myNode.getTree().changeData(this, getStartOffset(), getEndOffset(), isGreedyToLeft(), greedy, 0);
}
public boolean isGreedyToLeft() {
@@ -210,7 +219,7 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx
@NonNls
public String toString() {
return "RangeMarker" + (isGreedyToLeft() ? "[" : "(") + (isValid() ? "valid" : "invalid") + "," + getStartOffset() + "," + getEndOffset() + (
isGreedyToRight() ? "]" : ")");
isGreedyToRight() ? "]" : ")") + " " + getId();
}
@Override
@@ -241,9 +250,7 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx
if (node == null) {
return -1;
}
else {
return node.intervalStart();
}
return node.intervalStart();
}
@Override
@@ -252,8 +259,6 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx
if (node == null) {
return -1;
}
else {
return node.intervalEnd();
}
return node.intervalEnd();
}
}
@@ -20,22 +20,18 @@ 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
*/
public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T> {
private final PrioritizedDocumentListener myListener;
private final Document myDocument;
final Document myDocument;
private final EqualStartIntervalComparator<IntervalNode> myEqualStartIntervalComparator = new EqualStartIntervalComparator<IntervalNode>() {
@Override
public int compare(IntervalNode i1, IntervalNode i2) {
@@ -54,9 +50,7 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
boolean greedyR2 = o2.isGreedyToRight();
if (greedyR1 != greedyR2) return greedyR1 ? -1 : 1;
// for now we tolerate equal range range markers (till lazy creation impl)
d = (int)(o1.getId() - o2.getId());
return d;
return 0;
}
};
@@ -88,53 +82,49 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
}
@Override
protected Trinity<Integer, Integer, Integer> checkMax(IntervalNode root,
int deltaUpToRootExclusive,
boolean assertInvalid,
Ref<Boolean> allValid, AtomicInteger counter) {
if (root != null) {
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, allValid, counter);
}
@Override
public IntervalNode addInterval(@NotNull T interval, int start, int end, Object data) {
public MyNode addInterval(@NotNull T interval, int start, int end, boolean greedyToLeft, boolean greedyToRight, int layer) {
RangeMarkerImpl marker = (RangeMarkerImpl)interval;
marker.setValid(true);
return super.addInterval(interval, start, end, data);
}
RMNode node = (RMNode)super.addInterval(interval, start, end, greedyToLeft, greedyToRight, layer);
((RangeMarkerImpl)interval).myNode = node;
checkBelongsToTheTree(interval, true);
@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;
}
//private static long counter;
private static final AtomicLong counter = new AtomicLong();
@NotNull
@Override
protected RMNode createNewNode(@NotNull T key, int start, int end, boolean greedyToLeft, boolean greedyToRight, int layer) {
return new RMNode(key, start, end, greedyToLeft, greedyToRight);
}
@Override
protected void checkBelongsToTheTree(T interval, boolean assertInvalid) {
if (!VERIFY) return;
assert ((RangeMarkerImpl)interval).myDocument == myDocument;
super.checkBelongsToTheTree(interval, assertInvalid);
}
@Override
protected MyNode lookupNode(@NotNull T key) {
return ((RangeMarkerImpl)key).myNode;
}
public class RMNode extends MyNode {
private boolean isExpandToLeft = false;
private boolean isExpandToRight = false;
private final long myId;
private final boolean isExpandToLeft;
private final boolean isExpandToRight;
public RMNode(@NotNull T key, int start, int end) {
public RMNode(@NotNull T key, int start, int end, boolean greedyToLeft, boolean greedyToRight) {
super(key, start, end);
myId = counter.getAndIncrement();
//myId = counter++;
isExpandToLeft = greedyToLeft;
isExpandToRight = greedyToRight;
}
public void setGreedyToLeft(boolean greedy) {
isExpandToLeft = greedy;
}
public void setGreedyToRight(boolean greedy) {
isExpandToRight = greedy;
@Override
public boolean processAliveKeys(@NotNull Processor processor) {
return super.processAliveKeys(processor);
}
public boolean isGreedyToLeft() {
@@ -145,8 +135,18 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
return isExpandToRight;
}
public long getId() {
return myId;
@Override
public void addInterval(@NotNull T interval) {
super.addInterval(interval);
((RangeMarkerImpl)interval).myNode = this;
checkBelongsToTheTree(interval, true);
}
@Override
public boolean removeInterval(@NotNull T key) {
boolean removedNode = super.removeInterval(key);
((RangeMarkerImpl)key).myNode = null;
return removedNode;
}
@Override
@@ -159,11 +159,10 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
try {
l.writeLock().lock();
checkMax(true);
normalized = false;
modCount++;
List<IntervalNode> affected = new ArrayList<IntervalNode>();
updateMarkersOnChange(getRoot(), e, affected);
normalized &= !collectAffectedMarkers(getRoot(), e, affected);
checkMax(false);
if (!affected.isEmpty()) {
@@ -172,7 +171,7 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
int startOffset = node.intervalStart();
int endOffset = node.intervalEnd();
removeNode(node);
node.delta = 0;
node.delta = 0; // we can do it because all the deltas in the way were cleared in the collectAffectedMarkers
node.setParent(null);
node.setLeft(null);
node.setRight(null);
@@ -181,17 +180,26 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
}
checkMax(true);
for (IntervalNode node : affected) {
RangeMarkerImpl marker = (RangeMarkerImpl)node.getKey();
if (marker == null) continue; // collected
List<T> aliveKeys = node.getAliveKeys();
if (aliveKeys.isEmpty()) continue; // collected
RangeMarkerImpl marker = (RangeMarkerImpl)aliveKeys.get(0);
marker.setValid(true);
//marker.myNode = null;
marker.documentChanged(e);
if (marker.isValid()) {
insert(node);
RMNode insertedNode = (RMNode)findOrInsert(node);
// can change if two range become the one
if (insertedNode != node) {
// merge happened
for (T interval : aliveKeys) {
insertedNode.addInterval(interval);
}
}
assert marker.isValid();
}
}
checkMax(true);
}
checkMax(true);
IntervalNode root = getRoot();
assert root == null || root.maxEnd + root.delta <= myDocument.getTextLength();
@@ -201,19 +209,20 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
}
}
private static void updateMarkersOnChange(IntervalNode root,
@NotNull DocumentEvent e,
@NotNull List<IntervalNode> affected) {
if (root == null) return;
pushDelta(root);
// returns true if some delta was or became not null
private boolean collectAffectedMarkers(IntervalNode root,
@NotNull DocumentEvent e,
@NotNull List<IntervalNode> affected) {
if (root == null) return false;
boolean denorm = pushDelta(root);
int maxEnd = root.maxEnd;
assert root.isValid();
int offset = e.getOffset();
int affectedEndOffset = offset + e.getOldLength();
Object key = root.getKey();
if (key == null) {
boolean hasAliveKeys = root.hasAliveKey(false);
if (!hasAliveKeys) {
// marker was garbage collected
affected.add(root);
}
@@ -222,28 +231,31 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
}
else if (affectedEndOffset < root.intervalStart()) {
int lengthDelta = e.getNewLength() - e.getOldLength();
root.delta += lengthDelta;
if (root.getLeft() != null) {
root.getLeft().delta -= lengthDelta;
int newD = root.delta += lengthDelta;
denorm |= newD != 0;
IntervalNode left = root.getLeft();
if (left != null) {
int newL = left.delta -= lengthDelta;
denorm |= newL != 0;
}
pushDelta(root);
updateMarkersOnChange(root.getLeft(), e, affected);
denorm |= pushDelta(root);
denorm |= collectAffectedMarkers(left, e, affected);
correctMax(root, 0);
}
else {
if (offset <= root.intervalEnd()) {
// unlucky enough so that change affects the interval
if (key != null) affected.add(root); // otherwise we've already added it
if (hasAliveKeys) affected.add(root); // otherwise we've already added it
root.setValid(false); //make invisible
}
updateMarkersOnChange(root.getLeft(), e, affected);
updateMarkersOnChange(root.getRight(), e, affected);
denorm |= collectAffectedMarkers(root.getLeft(), e, affected);
denorm |= collectAffectedMarkers(root.getRight(), e, affected);
correctMax(root,0);
}
return denorm;
}
public boolean sweep(final int start, final int end, @NotNull final MarkupModelEx.SweepProcessor<T> sweepProcessor) {
normalize();
return sweep(new Generator<T>() {
@@ -252,7 +264,6 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
return processOverlappingWith(start, end, processor);
}
}, sweepProcessor);
}
public interface Generator<T> {
@@ -15,8 +15,11 @@
*/
package com.intellij.openapi.editor.impl;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* User: cdr
@@ -24,7 +27,7 @@ import org.jetbrains.annotations.NotNull;
public abstract class RedBlackTree<K> {
public static boolean VERIFY = false;
private static final int INDENT_STEP = 4;
protected int size;
private int nodeSize; // number of nodes
protected int modCount;
protected Node<K> root;
@@ -33,8 +36,6 @@ public abstract class RedBlackTree<K> {
verifyProperties();
}
protected abstract Node<K> lookupNode(@NotNull K key, Node<K> root);
protected void rotateLeft(Node<K> n) {
Node<K> r = n.getRight();
replaceNode(n, r);
@@ -78,7 +79,9 @@ public abstract class RedBlackTree<K> {
//oldn.right = null;
}
protected abstract Node<K> createNewNode(K key, int start, int end, Object data);
protected void onInsertNode() {
nodeSize++;
}
protected void insertCase1(Node<K> n) {
if (n.getParent() == null) {
@@ -137,12 +140,6 @@ public abstract class RedBlackTree<K> {
assert node1 == null || node1.getParent() == null || node1.getParent().getLeft() == node1 || node1.getParent().getRight() == node1;
}
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
@@ -175,7 +172,9 @@ public abstract class RedBlackTree<K> {
if (nodeColor(root) == Color.RED) {
root.color = Color.BLACK;
}
size--;
assert nodeSize > 0 : nodeSize;
nodeSize--;
verifyProperties();
}
@@ -286,10 +285,10 @@ public abstract class RedBlackTree<K> {
System.err.print(" ");
}
if (n.color == Color.BLACK) {
System.err.println(n.getKey());
System.err.println(n);
}
else {
System.err.println("<" + n.getKey() + ">");
System.err.println("<" + n + ">");
}
if (n.getLeft() != null) {
printHelper(n.getLeft(), indent + INDENT_STEP);
@@ -347,7 +346,10 @@ public abstract class RedBlackTree<K> {
this.parent = parent;
}
public abstract K getKey();
public abstract boolean processAliveKeys(@NotNull Processor<? super K> processor);
@NotNull
public abstract List<K> getAliveKeys();
public abstract boolean hasAliveKey(boolean purgeDead);
}
protected static enum Color {
@@ -355,7 +357,10 @@ public abstract class RedBlackTree<K> {
}
public int size() {
return size;
return nodeSize;
}
public int nodeSize() {
return nodeSize;
}
public void verifyProperties() {
@@ -426,6 +431,6 @@ public abstract class RedBlackTree<K> {
public void clear() {
modCount++;
root = null;
size = 0;
nodeSize = 0;
}
}
@@ -287,10 +287,6 @@ public class MockDocument implements DocumentEx {
return false;
}
@Override
public void addRangeMarker(@NotNull RangeMarkerEx rangeMarker, int start, int end) {
}
@Override
public boolean isInBulkUpdate() {
return false;
@@ -768,11 +768,29 @@ public class ContainerUtil {
}
return result;
}
@NotNull
public static <T,V> List<V> map(@NotNull Collection<? extends T> iterable, @NotNull Function<T, V> mapping) {
List<V> result = new ArrayList<V>(iterable.size());
for (T t : iterable) {
result.add(mapping.fun(t));
}
return result;
}
@NotNull
public static <T, V> List<V> mapNotNull(@NotNull T[] array, Function<T, V> mapping) {
return mapNotNull(Arrays.asList(array), mapping);
}
public static <T, V> V[] mapNotNull(@NotNull T[] arr, @NotNull Function<T, V> mapping, @NotNull V[] emptyArray) {
List<V> result = new ArrayList<V>(arr.length);
for (T t : arr) {
V v = mapping.fun(t);
if (v != null) {
result.add(v);
}
}
return result.toArray(emptyArray);
}
@NotNull
public static <T, V> List<V> mapNotNull(Iterable<? extends T> iterable, Function<T, V> mapping) {
@@ -785,6 +803,17 @@ public class ContainerUtil {
}
return result;
}
@NotNull
public static <T, V> List<V> mapNotNull(Collection<? extends T> iterable, Function<T, V> mapping) {
List<V> result = new ArrayList<V>(iterable.size());
for (T t : iterable) {
final V o = mapping.fun(t);
if (o != null) {
result.add(o);
}
}
return result;
}
@NotNull
public static <T> List<T> packNullables(@NotNull T... elements) {