From 086d2083e8ecf39edb4cc10b1e6a5dd93f2c949d Mon Sep 17 00:00:00 2001 From: peter Date: Thu, 23 Oct 2014 11:40:23 +0200 Subject: [PATCH] don't expose whole document text through subsequences in document change events (IDEA-131780) --- .../openapi/editor/impl/DocumentImpl.java | 15 +++++++++------ .../src/com/intellij/util/text/ImmutableText.java | 11 +++++++++-- 2 files changed, 18 insertions(+), 8 deletions(-) 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 918ebaa22425..34512a6fc714 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 @@ -493,7 +493,9 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { throwGuardedFragment(marker, offset, null, s.toString()); } - updateText(myText.insert(offset, ImmutableText.valueOf(s)), offset, null, s, false, LocalTimeCounter.currentTime()); + myText = myText.ensureChunked(); + ImmutableText newText = myText.insert(offset, ImmutableText.valueOf(s)); + updateText(newText, offset, null, newText.subtext(offset, offset + s.length()), false, LocalTimeCounter.currentTime()); trimToSize(); } @@ -511,14 +513,13 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { if (!isWritable()) throw new ReadOnlyModificationException(this); if (startOffset == endOffset) return; - CharSequence sToDelete = myText.subSequence(startOffset, endOffset); - RangeMarker marker = getRangeGuard(startOffset, endOffset); if (marker != null) { - throwGuardedFragment(marker, startOffset, sToDelete.toString(), null); + throwGuardedFragment(marker, startOffset, myText.subSequence(startOffset, endOffset).toString(), null); } - updateText(myText.delete(startOffset, endOffset), startOffset, sToDelete, null, false, LocalTimeCounter.currentTime()); + myText = myText.ensureChunked(); + updateText(myText.delete(startOffset, endOffset), startOffset, myText.subtext(startOffset, endOffset), null, false, LocalTimeCounter.currentTime()); } @Override @@ -582,7 +583,7 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { } CharSequence changedPart = s.subSequence(newStartInString, newEndInString); - CharSequence sToDelete = myText.subSequence(startOffset, endOffset); + CharSequence sToDelete = myText.subtext(startOffset, endOffset); RangeMarker guard = getRangeGuard(startOffset, endOffset); if (guard != null) { throwGuardedFragment(guard, startOffset, sToDelete.toString(), changedPart.toString()); @@ -593,7 +594,9 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx { newText = (ImmutableText)s; } else { + myText = myText.ensureChunked(); newText = myText.delete(startOffset, endOffset).insert(startOffset, changedPart); + changedPart = newText.subtext(startOffset, startOffset + changedPart.length()); } updateText(newText, startOffset, sToDelete, changedPart, wholeTextReplaced, newModificationStamp); trimToSize(); diff --git a/platform/util/src/com/intellij/util/text/ImmutableText.java b/platform/util/src/com/intellij/util/text/ImmutableText.java index 4ec4b93ec2b2..2dde3eb60293 100644 --- a/platform/util/src/com/intellij/util/text/ImmutableText.java +++ b/platform/util/src/com/intellij/util/text/ImmutableText.java @@ -96,7 +96,14 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr return new ImmutableText(new LeafNode(chars)); } - private ImmutableText ensureChunked() { + /** + * When first loaded, ImmutableText contents are stored as a single large array. This saves memory but isn't + * modification-friendly as it disallows slightly changed texts to retain most of the internal structure of the + * original document. Whoever retains old non-chunked version will use more memory than really needed. + * + * @return a copy of this text better prepared for small modifications to fully enable structure-sharing capabilities + */ + public ImmutableText ensureChunked() { if (length() > BLOCK_SIZE && myNode instanceof LeafNode) { return new ImmutableText(nodeOf(((LeafNode)myNode)._data, 0, length())); } @@ -292,7 +299,7 @@ public final class ImmutableText extends ImmutableCharSequence implements CharAr * @throws IndexOutOfBoundsException if (start < 0) || (end < 0) || * (start > end) || (end > this.length()) */ - private ImmutableText subtext(int start, int end) { + public ImmutableText subtext(int start, int end) { if ((start < 0) || (start > end) || (end > length())) throw new IndexOutOfBoundsException(); if ((start == 0) && (end == length()))