mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
range markers made weakly referenced
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -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);
|
||||
|
||||
+1
-2
@@ -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);
|
||||
}
|
||||
|
||||
+384
-185
@@ -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() ? "]" : ")");
|
||||
}
|
||||
}
|
||||
+42
-126
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
+13
-12
@@ -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;
|
||||
|
||||
+43
-34
@@ -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
|
||||
|
||||
+32
-8
@@ -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 {
|
||||
|
||||
+55
-47
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user