diff --git a/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java b/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java index ed8630232770..7fa3e8d3f52a 100644 --- a/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java +++ b/platform/core-impl/src/com/intellij/openapi/editor/impl/DocumentImpl.java @@ -38,7 +38,7 @@ import com.intellij.util.*; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.IntArrayList; import com.intellij.util.text.CharArrayUtil; -import com.intellij.util.text.ImmutableText; +import com.intellij.util.text.ImmutableCharSequence; import gnu.trove.TIntObjectHashMap; import gnu.trove.TObjectProcedure; import org.jetbrains.annotations.NonNls; @@ -66,7 +66,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { @SuppressWarnings("RedundantStringConstructorCall") private final Object myLineSetLock = new String("line set lock"); private volatile LineSet myLineSet; - private volatile ImmutableText myText; + private volatile ImmutableCharSequence myText; private volatile SoftReference myTextString; private volatile FrozenDocument myFrozen; @@ -133,7 +133,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { public DocumentImpl(@NotNull CharSequence chars, boolean acceptSlashR, boolean forUseInNonAWTThread) { setAcceptSlashR(acceptSlashR); assertValidSeparators(chars); - myText = ImmutableText.valueOf(chars); + myText = CharArrayUtil.createImmutableCharSequence(chars); setCyclicBufferSize(0); setModificationStamp(LocalTimeCounter.currentTime()); myAssertThreading = !forUseInNonAWTThread; @@ -472,12 +472,12 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { RangeMarker marker = getRangeGuard(offset, offset); if (marker != null) { - throwGuardedFragment(marker, offset, null, s.toString()); + throwGuardedFragment(marker, offset, "", s); } - myText = myText.ensureChunked(); - ImmutableText newText = myText.insert(offset, ImmutableText.valueOf(s)); - updateText(newText, offset, null, newText.subtext(offset, offset + s.length()), false, LocalTimeCounter.currentTime(), offset, 0); + ImmutableCharSequence newText = myText.insert(offset, s); + ImmutableCharSequence newString = newText.subtext(offset, offset + s.length()); + updateText(newText, offset, "", newString, false, LocalTimeCounter.currentTime(), offset, 0); trimToSize(); } @@ -497,11 +497,12 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { RangeMarker marker = getRangeGuard(startOffset, endOffset); if (marker != null) { - throwGuardedFragment(marker, startOffset, myText.subSequence(startOffset, endOffset).toString(), null); + throwGuardedFragment(marker, startOffset, myText.subSequence(startOffset, endOffset), ""); } - myText = myText.ensureChunked(); - updateText(myText.delete(startOffset, endOffset), startOffset, myText.subtext(startOffset, endOffset), null, false, LocalTimeCounter.currentTime(), startOffset, endOffset - startOffset); + ImmutableCharSequence newText = myText.delete(startOffset, endOffset); + ImmutableCharSequence oldString = myText.subtext(startOffset, endOffset); + updateText(newText, startOffset, oldString, "", false, LocalTimeCounter.currentTime(), startOffset, endOffset - startOffset); } @Override @@ -575,15 +576,14 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { CharSequence sToDelete = myText.subtext(startOffset, endOffset); RangeMarker guard = getRangeGuard(startOffset, endOffset); if (guard != null) { - throwGuardedFragment(guard, startOffset, sToDelete.toString(), changedPart.toString()); + throwGuardedFragment(guard, startOffset, sToDelete, changedPart); } - ImmutableText newText; - if (wholeTextReplaced && s instanceof ImmutableText) { - newText = (ImmutableText)s; + ImmutableCharSequence newText; + if (wholeTextReplaced && s instanceof ImmutableCharSequence) { + newText = (ImmutableCharSequence)s; } else { - myText = myText.ensureChunked(); newText = myText.delete(startOffset, endOffset).insert(startOffset, changedPart); changedPart = newText.subtext(startOffset, startOffset + changedPart.length()); } @@ -649,7 +649,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { } } - private void throwGuardedFragment(@NotNull RangeMarker guard, int offset, String oldString, String newString) { + private void throwGuardedFragment(@NotNull RangeMarker guard, int offset, @NotNull CharSequence oldString, @NotNull CharSequence newString) { if (myCheckGuardedBlocks > 0 && !myGuardsSuppressed) { DocumentEvent event = new DocumentEventImpl(this, offset, oldString, newString, myModificationStamp, false); throw new ReadOnlyFragmentModificationException(event, guard); @@ -698,10 +698,10 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { myFrozen = null; } - private void updateText(@NotNull ImmutableText newText, + private void updateText(@NotNull ImmutableCharSequence newText, int offset, - @Nullable CharSequence oldString, - @Nullable CharSequence newString, + @NotNull CharSequence oldString, + @NotNull CharSequence newString, boolean wholeTextReplaced, long newModificationStamp, int initialStartOffset, @@ -712,7 +712,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { DocumentEvent event = new DocumentEventImpl(this, offset, oldString, newString, myModificationStamp, wholeTextReplaced, initialStartOffset, initialOldLength); doBeforeChangedUpdate(event); myTextString = null; - ImmutableText prevText = myText; + ImmutableCharSequence prevText = myText; myText = newText; sequence.incrementAndGet(); // increment sequence before firing events so that modification sequence on commit will match this sequence now changedUpdate(event, newModificationStamp, prevText); @@ -764,7 +764,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { } } - private void changedUpdate(@NotNull DocumentEvent event, long newModificationStamp, ImmutableText prevText) { + private void changedUpdate(@NotNull DocumentEvent event, long newModificationStamp, @NotNull CharSequence prevText) { try { if (LOG.isDebugEnabled()) LOG.debug(event.toString()); diff --git a/platform/core-impl/src/com/intellij/openapi/editor/impl/FrozenDocument.java b/platform/core-impl/src/com/intellij/openapi/editor/impl/FrozenDocument.java index c1a68481882f..d0afcec67ac6 100644 --- a/platform/core-impl/src/com/intellij/openapi/editor/impl/FrozenDocument.java +++ b/platform/core-impl/src/com/intellij/openapi/editor/impl/FrozenDocument.java @@ -28,7 +28,7 @@ import com.intellij.openapi.util.TextRange; import com.intellij.reference.SoftReference; import com.intellij.util.Processor; import com.intellij.util.text.CharArrayUtil; -import com.intellij.util.text.ImmutableText; +import com.intellij.util.text.ImmutableCharSequence; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -40,12 +40,12 @@ import java.util.List; * @author peter */ public class FrozenDocument implements DocumentEx { - private final ImmutableText myText; + private final ImmutableCharSequence myText; private final LineSet myLineSet; private final long myStamp; private volatile SoftReference myTextString; - public FrozenDocument(@NotNull ImmutableText text, @NotNull LineSet lineSet, long stamp, @Nullable String textString) { + FrozenDocument(@NotNull ImmutableCharSequence text, @NotNull LineSet lineSet, long stamp, @Nullable String textString) { myText = text; myLineSet = lineSet; myStamp = stamp; @@ -55,8 +55,8 @@ public class FrozenDocument implements DocumentEx { public FrozenDocument applyEvent(DocumentEvent event, int newStamp) { final int offset = event.getOffset(); final int oldEnd = offset + event.getOldLength(); - final ImmutableText newText = myText.delete(offset, oldEnd).insert(offset, event.getNewFragment()); - final LineSet newLineSet = myLineSet.update(myText, offset, oldEnd, event.getNewFragment(), event.isWholeTextReplaced()); + ImmutableCharSequence newText = myText.delete(offset, oldEnd).insert(offset, event.getNewFragment()); + LineSet newLineSet = myLineSet.update(myText, offset, oldEnd, event.getNewFragment(), event.isWholeTextReplaced()); return new FrozenDocument(newText, newLineSet, newStamp, null); } diff --git a/platform/core-impl/src/com/intellij/openapi/editor/impl/event/DocumentEventImpl.java b/platform/core-impl/src/com/intellij/openapi/editor/impl/event/DocumentEventImpl.java index 78f3e58fd03a..45bd99062c75 100644 --- a/platform/core-impl/src/com/intellij/openapi/editor/impl/event/DocumentEventImpl.java +++ b/platform/core-impl/src/com/intellij/openapi/editor/impl/event/DocumentEventImpl.java @@ -17,7 +17,6 @@ package com.intellij.openapi.editor.impl.event; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.event.DocumentEvent; -import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.diff.Diff; import com.intellij.util.diff.FilesTooBigForDiffException; import org.jetbrains.annotations.NotNull; @@ -29,38 +28,27 @@ public class DocumentEventImpl extends DocumentEvent { private final CharSequence myNewString; private final int myNewLength; - private boolean isOnlyOneLineChangedCalculated = false; - private boolean isOnlyOneLineChanged; - - private boolean isStartOldIndexCalculated = false; - private int myStartOldIndex; - private final long myOldTimeStamp; private final boolean myIsWholeDocReplaced; private Diff.Change myChange; private static final Diff.Change TOO_BIG_FILE = new Diff.Change(0, 0, 0, 0, null) { }; - private int myOptimizedLineShift = -1; - private boolean myOptimizedLineShiftCalculated; - - private int myOptimizedOldLineShift = -1; - private boolean myOptimizedOldLineShiftCalculated; private final int myInitialStartOffset; private final int myInitialOldLength; public DocumentEventImpl(@NotNull Document document, int offset, - CharSequence oldString, - CharSequence newString, + @NotNull CharSequence oldString, + @NotNull CharSequence newString, long oldTimeStamp, boolean wholeTextReplaced) { - this(document, offset, oldString, newString, oldTimeStamp, wholeTextReplaced, offset, oldString == null ? 0 : oldString.length()); + this(document, offset, oldString, newString, oldTimeStamp, wholeTextReplaced, offset, oldString.length()); } public DocumentEventImpl(@NotNull Document document, int offset, - CharSequence oldString, - CharSequence newString, + @NotNull CharSequence oldString, + @NotNull CharSequence newString, long oldTimeStamp, boolean wholeTextReplaced, int initialStartOffset, @@ -68,25 +56,18 @@ public class DocumentEventImpl extends DocumentEvent { super(document); myOffset = offset; - myOldString = oldString == null ? "" : oldString; - myOldLength = myOldString.length(); + myOldString = oldString; + myOldLength = oldString.length(); - myNewString = newString == null ? "" : newString; - myNewLength = myNewString.length(); + myNewString = newString; + myNewLength = newString.length(); myInitialStartOffset = initialStartOffset; myInitialOldLength = initialOldLength; myOldTimeStamp = oldTimeStamp; - if (getDocument().getTextLength() == 0) { - isOnlyOneLineChangedCalculated = true; - isOnlyOneLineChanged = false; - myIsWholeDocReplaced = false; - } - else { - myIsWholeDocReplaced = wholeTextReplaced; - } + myIsWholeDocReplaced = getDocument().getTextLength() != 0 && wholeTextReplaced; } @Override @@ -138,44 +119,12 @@ public class DocumentEventImpl extends DocumentEvent { return myInitialOldLength; } - public int getStartOldIndex() { - if (isStartOldIndexCalculated) return myStartOldIndex; - - isStartOldIndexCalculated = true; - myStartOldIndex = getDocument().getLineNumber(myOffset); - return myStartOldIndex; - } - - public boolean isOnlyOneLineChanged() { - if (isOnlyOneLineChangedCalculated) return isOnlyOneLineChanged; - - isOnlyOneLineChangedCalculated = true; - isOnlyOneLineChanged = true; - - for (int i = 0; i < myOldString.length(); i++) { - if (myOldString.charAt(i) == '\n') { - isOnlyOneLineChanged = false; - break; - } - } - - if (isOnlyOneLineChanged) { - for (int i = 0; i < myNewString.length(); i++) { - if (myNewString.charAt(i) == '\n') { - isOnlyOneLineChanged = false; - break; - } - } - } - return isOnlyOneLineChanged; - } - @Override public long getOldTimeStamp() { return myOldTimeStamp; } - @SuppressWarnings({"HardCodedStringLiteral"}) + @SuppressWarnings("HardCodedStringLiteral") public String toString() { return "DocumentEventImpl[myOffset=" + myOffset + ", myOldLength=" + myOldLength + ", myNewLength=" + myNewLength + ", myOldString='" + myOldString + "', myNewString='" + myNewString + "']" + (isWholeTextReplaced() ? " Whole." : "."); @@ -229,29 +178,5 @@ public class DocumentEventImpl extends DocumentEvent { return myChange; } - public int getOptimizedLineShift() { - if (!myOptimizedLineShiftCalculated) { - myOptimizedLineShiftCalculated = true; - if (myOldLength == 0) { - int lineShift = StringUtil.countNewLines(myNewString); - - myOptimizedLineShift = lineShift == 0 ? -1 : lineShift; - } - } - return myOptimizedLineShift; - } - - public int getOptimizedOldLineShift() { - if (!myOptimizedOldLineShiftCalculated) { - myOptimizedOldLineShiftCalculated = true; - - if (myNewLength == 0) { - int lineShift = StringUtil.countNewLines(myOldString); - - myOptimizedOldLineShift = lineShift == 0 ? -1 : lineShift; - } - } - return myOptimizedOldLineShift; - } } diff --git a/platform/core-impl/src/com/intellij/psi/impl/PsiToDocumentSynchronizer.java b/platform/core-impl/src/com/intellij/psi/impl/PsiToDocumentSynchronizer.java index b80900871065..5438d8480ed4 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/PsiToDocumentSynchronizer.java +++ b/platform/core-impl/src/com/intellij/psi/impl/PsiToDocumentSynchronizer.java @@ -31,7 +31,8 @@ import com.intellij.psi.*; import com.intellij.psi.impl.source.tree.ForeignLeafPsiElement; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.messages.MessageBus; -import com.intellij.util.text.ImmutableText; +import com.intellij.util.text.CharArrayUtil; +import com.intellij.util.text.ImmutableCharSequence; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.TestOnly; @@ -48,7 +49,7 @@ public class PsiToDocumentSynchronizer extends PsiTreeChangeAdapter { private volatile Document mySyncDocument; - public PsiToDocumentSynchronizer(PsiDocumentManagerBase psiDocumentManager, MessageBus bus) { + PsiToDocumentSynchronizer(PsiDocumentManagerBase psiDocumentManager, MessageBus bus) { myPsiDocumentManager = psiDocumentManager; myBus = bus; } @@ -317,13 +318,11 @@ public class PsiToDocumentSynchronizer extends PsiTreeChangeAdapter { } }); private final PsiFile myChangeScope; - private final ImmutableText myDocText; - private ImmutableText myPsiText; + private ImmutableCharSequence myPsiText; - public DocumentChangeTransaction(@NotNull Document doc, @NotNull PsiFile scope) { + DocumentChangeTransaction(@NotNull Document doc, @NotNull PsiFile scope) { myChangeScope = scope; - myDocText = ImmutableText.valueOf(doc.getImmutableCharSequence()); - myPsiText = myDocText; + myPsiText = CharArrayUtil.createImmutableCharSequence(doc.getImmutableCharSequence()); } @NotNull diff --git a/platform/util/src/com/intellij/util/text/CharArrayUtil.java b/platform/util/src/com/intellij/util/text/CharArrayUtil.java index b2adcad7df50..9ee6d632aeb8 100644 --- a/platform/util/src/com/intellij/util/text/CharArrayUtil.java +++ b/platform/util/src/com/intellij/util/text/CharArrayUtil.java @@ -571,4 +571,9 @@ public class CharArrayUtil { //noinspection IOResourceOpenedButNotSafelyClosed return chars == null ? new CharSequenceReader(text.toString()) : new UnsyncCharArrayReader(chars, 0, text.length()); } + + @NotNull + public static ImmutableCharSequence createImmutableCharSequence(@NotNull CharSequence sequence) { + return ImmutableText.valueOf(sequence); + } } diff --git a/platform/util/src/com/intellij/util/text/ImmutableCharSequence.java b/platform/util/src/com/intellij/util/text/ImmutableCharSequence.java index 6c2823ab261c..92e29972463c 100644 --- a/platform/util/src/com/intellij/util/text/ImmutableCharSequence.java +++ b/platform/util/src/com/intellij/util/text/ImmutableCharSequence.java @@ -25,8 +25,14 @@ public abstract class ImmutableCharSequence implements CharSequence { public static boolean isImmutable(@NotNull final CharSequence cs) { if (cs instanceof ImmutableCharSequence) return true; - if (cs instanceof CharSequenceSubSequence) return isImmutable(((CharSequenceSubSequence)cs).getBaseSequence()); - return false; + return cs instanceof CharSequenceSubSequence && isImmutable(((CharSequenceSubSequence)cs).getBaseSequence()); } - + + public abstract ImmutableCharSequence concat(CharSequence sequence); + + public abstract ImmutableCharSequence insert(int index, CharSequence seq); + + public abstract ImmutableCharSequence delete(int start, int end); + + public abstract ImmutableCharSequence subtext(int start, int end); } diff --git a/platform/util/src/com/intellij/util/text/ImmutableText.java b/platform/util/src/com/intellij/util/text/ImmutableText.java index d8ad05bd03c4..691976e16438 100644 --- a/platform/util/src/com/intellij/util/text/ImmutableText.java +++ b/platform/util/src/com/intellij/util/text/ImmutableText.java @@ -55,7 +55,7 @@ import org.jetbrains.annotations.Nullable; * @version 5.3, January 10, 2007 */ @SuppressWarnings({"AssignmentToForLoopParameter","UnnecessaryThis"}) -public final class ImmutableText extends ImmutableCharSequence implements CharArrayExternalizable, CharSequenceWithStringHash { +final class ImmutableText extends ImmutableCharSequence implements CharArrayExternalizable, CharSequenceWithStringHash { /** * Holds the default size for primitive blocks of characters. */ @@ -78,7 +78,7 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr * @param obj the object to represent as text. * @return the textual representation of the specified object. */ - public static ImmutableText valueOf(@NotNull Object obj) { + static ImmutableText valueOf(@NotNull Object obj) { if (obj instanceof ImmutableText) return (ImmutableText)obj; if (obj instanceof CharSequence) return ((CharSequence)obj).length() == 0 ? EMPTY : valueOf((CharSequence)obj); return valueOf(String.valueOf(obj)); @@ -129,7 +129,7 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr * * @return a copy of this text better prepared for small modifications to fully enable structure-sharing capabilities */ - public ImmutableText ensureChunked() { + private ImmutableText ensureChunked() { if (length() > BLOCK_SIZE && myNode instanceof LeafNode) { return new ImmutableText(nodeOf((LeafNode)myNode, 0, length())); } @@ -145,22 +145,6 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr return new CompositeNode(nodeOf(node, offset, half), nodeOf(node, offset + half, length - half)); } - /** - * Returns the text representation of the boolean argument. - * - * @param b a boolean. - * @return if the argument is true, the text - * "true" is returned; otherwise, the text - * "false" is returned. - */ - public static ImmutableText valueOf(boolean b) { - return b ? TRUE : FALSE; - } - - private static final ImmutableText TRUE = valueOf("true"); - - private static final ImmutableText FALSE = valueOf("false"); - private static final LeafNode EMPTY_NODE = new Leaf8BitNode(ArrayUtil.EMPTY_BYTE_ARRAY); private static final ImmutableText EMPTY = new ImmutableText(EMPTY_NODE); @@ -183,10 +167,15 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr * @param that the text that is concatenated. * @return this + that */ - public ImmutableText concat(ImmutableText that) { + private ImmutableText concat(ImmutableText that) { return that.length() == 0 ? this : length() == 0 ? that : new ImmutableText(concatNodes(ensureChunked().myNode, that.ensureChunked().myNode)); } + @Override + public ImmutableText concat(CharSequence sequence) { + return concat(valueOf(sequence)); + } + /** * Returns a portion of this text. * @@ -209,10 +198,11 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr * @throws IndexOutOfBoundsException if (index < 0) || * (index > this.length()) */ - public ImmutableText insert(int index, ImmutableText txt) { + private ImmutableText insert(int index, ImmutableText txt) { return subtext(0, index).concat(txt).concat(subtext(index)); } + @Override public ImmutableText insert(int index, CharSequence seq) { return insert(index, valueOf(seq)); } @@ -226,6 +216,7 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr * @throws IndexOutOfBoundsException if (start < 0) || (end < 0) || * (start > end) || (end > this.length() */ + @Override public ImmutableText delete(int start, int end) { if (start == end) return this; if (start > end) { @@ -333,11 +324,12 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr * @throws IndexOutOfBoundsException if (start < 0) || (end < 0) || * (start > end) || (end > this.length()) */ + @Override public ImmutableText subtext(int start, int end) { - if ((start < 0) || (start > end) || (end > length())) { + if (start < 0 || start > end || end > length()) { throw new IndexOutOfBoundsException(); } - if ((start == 0) && (end == length())) { + if (start == 0 && end == length()) { return this; } if (start == end) { @@ -405,7 +397,7 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr Node head = node1; Node tail = node2; - if (((head.length() << 1) < tail.length()) && tail instanceof CompositeNode) { + if ((head.length() << 1) < tail.length() && tail instanceof CompositeNode) { // head too small, returns (head + tail/2) + (tail/2) if (((CompositeNode)tail).head.length() > ((CompositeNode)tail).tail.length()) { // Rotates to concatenate with smaller part. @@ -414,7 +406,7 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr head = concatNodes(head, ((CompositeNode)tail).head); tail = ((CompositeNode)tail).tail; } - else if (((tail.length() << 1) < head.length()) && head instanceof CompositeNode) { + else if ((tail.length() << 1) < head.length() && head instanceof CompositeNode) { // tail too small, returns (head/2) + (head/2 concat tail) if (((CompositeNode)head).tail.length() > ((CompositeNode)head).head.length()) { // Rotates to concatenate with smaller part. @@ -441,7 +433,7 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr @Override void getChars(int start, int end, @NotNull char[] dest, int destPos) { - if ((start < 0) || (end > length()) || (start > end)) { + if (start < 0 || end > length() || start > end) { throw new IndexOutOfBoundsException(); } System.arraycopy(data, start, dest, destPos, end - start); @@ -480,7 +472,7 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr @Override void getChars(int start, int end, @NotNull char[] dest, int destPos) { - if ((start < 0) || (end > length()) || (start > end)) { + if (start < 0 || end > length() || start > end) { throw new IndexOutOfBoundsException(); } for (int i=start;i= cesure) { return tail.subNode(start - cesure, end - cesure); } - if ((start == 0) && (end == count)) { + if (start == 0 && end == count) { return this; } // Overlaps head and tail.