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));
    }