private void updateFragments(int start, int end, @NotNull String replace) { int docStart = psiToDocumentOffset(start); int docEnd = psiToDocumentOffset(end); TextRange startRange = findFragment(docStart); TextRange endRange = findFragment(docEnd); myPsiText = myPsiText.delete(start, end).insert(start, replace); TextRange newFragment = new TextRange( startRange != null ? startRange.getStartOffset() : docStart, endRange != null ? endRange.getEndOffset() : docEnd); CharSequence newReplacement = myPsiText.subSequence( documentToPsiOffset(newFragment.getStartOffset(), false), documentToPsiOffset(newFragment.getEndOffset(), true) + replace.length() - (end - start)); for (Iterator<TextRange> iterator = myAffectedFragments.keySet().iterator(); iterator.hasNext(); ) { if (iterator.next().intersects(newFragment)) { iterator.remove(); } } myAffectedFragments.put(newFragment, newReplacement); }
public LazyParseableElement(@NotNull IElementType type, @Nullable CharSequence text) { super(type); if (text != null) { synchronized (lock) { myText = ImmutableCharSequence.asImmutable(text); } setCachedLength(text.length()); } }
@NotNull @Override public CharSequence getImmutableCharSequence() { return ImmutableCharSequence.asImmutable(getText()); }
public void replace( int psiStart, int length, @NotNull String replace, @Nullable PsiElement replacement) { // calculating fragment // minimize replace int start = 0; int end = start + length; final CharSequence chars = myPsiText.subSequence(psiStart, psiStart + length); if (StringUtil.equals(chars, replace)) return; int newStartInReplace = 0; final int replaceLength = replace.length(); while (newStartInReplace < replaceLength && start < end && replace.charAt(newStartInReplace) == chars.charAt(start)) { start++; newStartInReplace++; } int newEndInReplace = replaceLength; while (start < end && newStartInReplace < newEndInReplace && replace.charAt(newEndInReplace - 1) == chars.charAt(end - 1)) { newEndInReplace--; end--; } // increase the changed range to start and end on PSI token boundaries // this will help to survive smart pointers with the same boundaries if (replacement != null && (newStartInReplace > 0 || newEndInReplace < replaceLength)) { PsiElement startLeaf = replacement.findElementAt(newStartInReplace); PsiElement endLeaf = replacement.findElementAt(newEndInReplace - 1); if (startLeaf != null && endLeaf != null) { int leafStart = startLeaf.getTextRange().getStartOffset() - replacement.getTextRange().getStartOffset(); int leafEnd = endLeaf.getTextRange().getEndOffset() - replacement.getTextRange().getStartOffset(); start += leafStart - newStartInReplace; end += leafEnd - newEndInReplace; newStartInReplace = leafStart; newEndInReplace = leafEnd; } } // optimization: when delete fragment from the middle of the text, prefer split at the line // boundaries if (newStartInReplace == newEndInReplace && start > 0 && start < end && StringUtil.indexOf(chars, '\n', start, end) != -1) { // try to align to the line boundaries while (start > 0 && newStartInReplace > 0 && chars.charAt(start - 1) == chars.charAt(end - 1) && chars.charAt(end - 1) != '\n') { start--; end--; newStartInReplace--; newEndInReplace--; } } start += psiStart; end += psiStart; // [mike] dirty hack for xml: // make sure that deletion of <t> in: <tag><t/><tag> doesn't remove t/>< // which is perfectly valid but invalidates range markers final CharSequence charsSequence = myPsiText; while (start < charsSequence.length() && end < charsSequence.length() && start > 0 && charsSequence.subSequence(start, end).toString().endsWith("><") && charsSequence.charAt(start - 1) == '<') { start--; newStartInReplace--; end--; newEndInReplace--; } updateFragments(start, end, replace.substring(newStartInReplace, newEndInReplace)); }