private void altCommitToOriginal(@NotNull DocumentEvent e) {
    final PsiFile origPsiFile =
        PsiDocumentManager.getInstance(myProject).getPsiFile(myOrigDocument);
    String newText = myNewDocument.getText();
    // prepare guarded blocks
    LinkedHashMap<String, String> replacementMap = new LinkedHashMap<String, String>();
    int count = 0;
    for (RangeMarker o : ContainerUtil.reverse(((DocumentEx) myNewDocument).getGuardedBlocks())) {
      String replacement = o.getUserData(REPLACEMENT_KEY);
      String tempText = "REPLACE" + (count++) + Long.toHexString(StringHash.calc(replacement));
      newText =
          newText.substring(0, o.getStartOffset()) + tempText + newText.substring(o.getEndOffset());
      replacementMap.put(tempText, replacement);
    }
    // run preformat processors
    final int hostStartOffset = myAltFullRange.getStartOffset();
    myEditor.getCaretModel().moveToOffset(hostStartOffset);
    for (CopyPastePreProcessor preProcessor :
        Extensions.getExtensions(CopyPastePreProcessor.EP_NAME)) {
      newText = preProcessor.preprocessOnPaste(myProject, origPsiFile, myEditor, newText, null);
    }
    myOrigDocument.replaceString(hostStartOffset, myAltFullRange.getEndOffset(), newText);
    // replace temp strings for guarded blocks
    for (String tempText : replacementMap.keySet()) {
      int idx =
          CharArrayUtil.indexOf(
              myOrigDocument.getCharsSequence(),
              tempText,
              hostStartOffset,
              myAltFullRange.getEndOffset());
      myOrigDocument.replaceString(idx, idx + tempText.length(), replacementMap.get(tempText));
    }
    // JAVA: fix occasional char literal concatenation
    fixDocumentQuotes(myOrigDocument, hostStartOffset - 1);
    fixDocumentQuotes(myOrigDocument, myAltFullRange.getEndOffset());

    // reformat
    PsiDocumentManager.getInstance(myProject).commitDocument(myOrigDocument);
    Runnable task =
        () -> {
          try {
            CodeStyleManager.getInstance(myProject)
                .reformatRange(origPsiFile, hostStartOffset, myAltFullRange.getEndOffset(), true);
          } catch (IncorrectOperationException e1) {
            // LOG.error(e);
          }
        };
    DocumentUtil.executeInBulk(myOrigDocument, true, task);

    PsiElement newInjected =
        InjectedLanguageManager.getInstance(myProject)
            .findInjectedElementAt(origPsiFile, hostStartOffset);
    DocumentWindow documentWindow =
        newInjected == null ? null : InjectedLanguageUtil.getDocumentWindow(newInjected);
    if (documentWindow != null) {
      myEditor.getCaretModel().moveToOffset(documentWindow.injectedToHost(e.getOffset()));
      myEditor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
    }
  }
  @Override
  public void handleInsert(InsertionContext context) {
    final Document document = context.getEditor().getDocument();
    document.replaceString(context.getStartOffset(), context.getTailOffset(), ";");
    final InsertionContext qualifierContext =
        CompletionUtil.emulateInsertion(context, context.getStartOffset(), myQualifier);
    OffsetKey oldStart = context.trackOffset(context.getStartOffset(), false);

    int start =
        CharArrayUtil.shiftForward(
            context.getDocument().getCharsSequence(), context.getStartOffset(), " \t\n");
    if (shouldParenthesizeQualifier(context.getFile(), start, qualifierContext.getTailOffset())) {
      final String space =
          CodeStyleSettingsManager.getSettings(qualifierContext.getProject())
                  .SPACE_WITHIN_PARENTHESES
              ? " "
              : "";
      document.insertString(start, "(" + space);
      document.insertString(qualifierContext.getTailOffset(), space + ")");
    }

    final char atTail = document.getCharsSequence().charAt(context.getTailOffset() - 1);
    if (atTail != ';') {
      LOG.error(
          LogMessageEx.createEvent(
              "Unexpected character",
              "atTail="
                  + atTail
                  + "\n"
                  + "offset="
                  + context.getTailOffset()
                  + "\n"
                  + "item="
                  + this
                  + "\n"
                  + "item.class="
                  + this.getClass()
                  + "\n"
                  + DebugUtil.currentStackTrace(),
              AttachmentFactory.createAttachment(context.getDocument())));
    }
    document.replaceString(context.getTailOffset() - 1, context.getTailOffset(), ".");

    CompletionUtil.emulateInsertion(getDelegate(), context.getTailOffset(), context);
    context.commitDocument();

    int formatStart = context.getOffset(oldStart);
    int formatEnd = context.getTailOffset();
    if (formatStart >= 0 && formatEnd >= 0) {
      CodeStyleManager.getInstance(context.getProject())
          .reformatText(context.getFile(), formatStart, formatEnd);
    }
  }
  private static void replaceByTagContent(Project project, XmlTag tagToReplace, XmlTag tagToInline)
      throws AndroidRefactoringErrorException {
    final ASTNode node = tagToInline.getNode();

    if (node == null) {
      throw new AndroidRefactoringErrorException();
    }
    final ASTNode startTagEnd = XmlChildRole.START_TAG_END_FINDER.findChild(node);
    final ASTNode closingTagStart = XmlChildRole.CLOSING_TAG_START_FINDER.findChild(node);

    if (startTagEnd == null || closingTagStart == null) {
      throw new AndroidRefactoringErrorException();
    }
    final int contentStart = startTagEnd.getTextRange().getEndOffset();
    final int contentEnd = closingTagStart.getTextRange().getStartOffset();

    if (contentStart < 0 || contentEnd < 0 || contentStart >= contentEnd) {
      throw new AndroidRefactoringErrorException();
    }
    final PsiFile file = tagToInline.getContainingFile();

    if (file == null) {
      throw new AndroidRefactoringErrorException();
    }
    final String textToInline = file.getText().substring(contentStart, contentEnd).trim();
    final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
    final Document document = documentManager.getDocument(tagToReplace.getContainingFile());

    if (document == null) {
      throw new AndroidRefactoringErrorException();
    }
    final TextRange range = tagToReplace.getTextRange();
    document.replaceString(range.getStartOffset(), range.getEndOffset(), textToInline);
    documentManager.commitDocument(document);
  }
  private void doIndentCommenting(Commenter commenter) {
    CharSequence chars = myDocument.getCharsSequence();
    final FileType fileType = myFile.getFileType();
    Indent minIndent =
        computeMinIndent(myStartLine, myEndLine, chars, myCodeStyleManager, fileType);

    for (int line = myEndLine; line >= myStartLine; line--) {
      int lineStart = myDocument.getLineStartOffset(line);
      int offset = lineStart;
      final StringBuilder buffer = StringBuilderSpinAllocator.alloc();
      try {
        while (true) {
          String space = buffer.toString();
          Indent indent = myCodeStyleManager.getIndent(space, fileType);
          if (indent.isGreaterThan(minIndent) || indent.equals(minIndent)) break;
          char c = chars.charAt(offset);
          if (c != ' ' && c != '\t') {
            String newSpace = myCodeStyleManager.fillIndent(minIndent, fileType);
            myDocument.replaceString(lineStart, offset, newSpace);
            offset = lineStart + newSpace.length();
            break;
          }
          buffer.append(c);
          offset++;
        }
      } finally {
        StringBuilderSpinAllocator.dispose(buffer);
      }
      commentLine(line, offset, commenter);
    }
  }
  private void handleNoSelection(Editor editor) {
    CaretModel caretModel = editor.getCaretModel();
    Document doc = editor.getDocument();
    if ((caretModel == null) || (doc == null) || (doc.getTextLength() == 0)) {
      return;
    }

    char[] allChars = doc.getChars();
    int maxOffset = allChars.length;

    int startOffset = caretModel.getOffset();

    while ((startOffset < maxOffset) && (!Character.isLetterOrDigit(allChars[startOffset]))) {
      startOffset++;
    }

    StringBuffer word = new StringBuffer();
    int i = startOffset;
    while ((i < maxOffset) && (Character.isLetterOrDigit(allChars[i]))) {
      word.append(allChars[i]);

      i++;
    }

    if (word.length() > 0) {
      this.m_transformer.transform(word);
      int newOffset = startOffset + word.length();
      doc.replaceString(startOffset, newOffset, word.toString());

      caretModel.moveToOffset(newOffset);
    }
  }
示例#6
0
  public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
    PsiElement element = descriptor.getPsiElement();
    PsiStatement anchorStatement = PsiTreeUtil.getParentOfType(element, PsiStatement.class);
    LOG.assertTrue(anchorStatement != null);
    Editor editor = getEditor(project, element);
    if (editor == null) return;
    PsiFile file = element.getContainingFile();
    PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
    Document document = documentManager.getDocument(file);
    if (!CodeInsightUtilBase.prepareFileForWrite(file)) return;
    PsiElement[] elements = {anchorStatement};
    PsiElement prev = PsiTreeUtil.skipSiblingsBackward(anchorStatement, PsiWhiteSpace.class);
    if (prev instanceof PsiComment
        && SuppressManager.getInstance().getSuppressedInspectionIdsIn(prev) != null) {
      elements = new PsiElement[] {prev, anchorStatement};
    }
    try {
      TextRange textRange = new JavaWithIfSurrounder().surroundElements(project, editor, elements);
      if (textRange == null) return;

      @NonNls String newText = myText + " != null";
      document.replaceString(textRange.getStartOffset(), textRange.getEndOffset(), newText);
      editor.getCaretModel().moveToOffset(textRange.getEndOffset() + newText.length());
      editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);

    } catch (IncorrectOperationException e) {
      LOG.error(e);
    }
  }
 private void updateDocumentFromPropertyValue(
     final String value, final Document document, final PropertiesFile propertiesFile) {
   @NonNls String text = value;
   if (myBackSlashPressed.contains(propertiesFile)) {
     text += "\\";
   }
   document.replaceString(0, document.getTextLength(), text);
 }
  public void testPersistentMarkerDoesntImpactNormalMarkers() {
    Document doc = new DocumentImpl("text");
    RangeMarker normal = doc.createRangeMarker(1, 3);
    RangeMarker persistent = doc.createRangeMarker(1, 3, true);

    doc.replaceString(0, 4, "before\ntext\nafter");

    assertTrue(persistent.isValid());
    assertFalse(normal.isValid());
  }
 @Override
 public void reparseRange(PsiFile file, int startOffset, int endOffset, CharSequence newTextS)
     throws IncorrectOperationException {
   LOG.assertTrue(file.isValid());
   final PsiFileImpl psiFile = (PsiFileImpl) file;
   final Document document = psiFile.getViewProvider().getDocument();
   assert document != null;
   document.replaceString(startOffset, endOffset, newTextS);
   PsiDocumentManager.getInstance(psiFile.getProject()).commitDocument(document);
 }
  public void testPersistent() throws Exception {
    String text = "xxx\nzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
    Document document = EditorFactory.getInstance().createDocument(text);
    int startOffset = text.indexOf('z');
    int endOffset = text.lastIndexOf('z');
    RangeMarker marker = document.createRangeMarker(startOffset, endOffset, true);

    document.replaceString(startOffset + 1, endOffset - 1, "ccc");

    assertTrue(marker.isValid());
  }
  private void extractExpectedLineMarkerSet(Document document) {
    String text = document.getText();

    String pat = ".*?((<" + LINE_MARKER + ")(?: descr=\"((?:[^\"\\\\]|\\\\\")*)\")?>)(.*)";
    final Pattern p = Pattern.compile(pat, Pattern.DOTALL);
    final Pattern pat2 = Pattern.compile("(.*?)(</" + LINE_MARKER + ">)(.*)", Pattern.DOTALL);

    while (true) {
      Matcher m = p.matcher(text);
      if (!m.matches()) break;
      int startOffset = m.start(1);
      final String descr = m.group(3) != null ? m.group(3) : ANY_TEXT;
      String rest = m.group(4);

      document.replaceString(startOffset, m.end(1), "");

      final Matcher matcher2 = pat2.matcher(rest);
      LOG.assertTrue(matcher2.matches(), "Cannot find closing </" + LINE_MARKER + ">");
      String content = matcher2.group(1);
      int endOffset = startOffset + matcher2.start(3);
      String endTag = matcher2.group(2);

      document.replaceString(startOffset, endOffset, content);
      endOffset -= endTag.length();

      LineMarkerInfo markerInfo =
          new LineMarkerInfo<PsiElement>(
              myFile,
              new TextRange(startOffset, endOffset),
              null,
              Pass.LINE_MARKERS,
              new ConstantFunction<>(descr),
              null,
              GutterIconRenderer.Alignment.RIGHT);

      myLineMarkerInfos.put(document.createRangeMarker(startOffset, endOffset), markerInfo);
      text = document.getText();
    }
  }
  protected void runOverEditor(
      @NotNull final Project project,
      @NotNull final Editor editor,
      @NotNull final PsiFile psiFile) {
    final Document document = editor.getDocument();
    if (!ReadonlyStatusHandler.ensureDocumentWritable(project, document)) return;

    final Runnable runnable =
        () -> {
          final int caretOffset = editor.getCaretModel().getOffset();
          final int lineLength = getRightMargin(project);

          DartAnalysisServerService.getInstance().updateFilesContent();
          DartAnalysisServerService.FormatResult formatResult =
              DartAnalysisServerService.getInstance()
                  .edit_format(psiFile.getVirtualFile(), caretOffset, 0, lineLength);

          if (formatResult == null) {
            showHintLater(editor, DartBundle.message("dart.style.hint.failed"), true);
            LOG.warn("Unexpected response from edit_format, formatResult is null");
            return;
          }

          final List<SourceEdit> edits = formatResult.getEdits();
          if (edits == null || edits.size() == 0) {
            showHintLater(editor, DartBundle.message("dart.style.hint.already.good"), false);
          } else if (edits.size() == 1) {
            final String replacement =
                StringUtil.convertLineSeparators(edits.get(0).getReplacement());
            document.replaceString(0, document.getTextLength(), replacement);
            final int offset =
                DartAnalysisServerService.getInstance()
                    .getConvertedOffset(psiFile.getVirtualFile(), formatResult.getOffset());
            editor.getCaretModel().moveToOffset(offset);
            showHintLater(editor, DartBundle.message("dart.style.hint.success"), false);
          } else {
            showHintLater(editor, DartBundle.message("dart.style.hint.failed"), true);
            LOG.warn(
                "Unexpected response from edit_format, formatResult.getEdits().size() = "
                    + edits.size());
          }
        };

    ApplicationManager.getApplication()
        .runWriteAction(
            () ->
                CommandProcessor.getInstance()
                    .executeCommand(
                        project, runnable, DartBundle.message("dart.style.action.name"), null));
  }
 @Override
 protected void doFix(Project project, ProblemDescriptor descriptor) {
   final TextRange textRange = descriptor.getTextRangeInElement();
   final PsiElement element = descriptor.getPsiElement();
   if (!(element instanceof PsiFile)) {
     return;
   }
   final PsiFile file = (PsiFile) element;
   final Document document = PsiDocumentManager.getInstance(project).getDocument(file);
   if (document == null) {
     return;
   }
   document.replaceString(
       textRange.getStartOffset(), textRange.getEndOffset(), String.valueOf(c));
 }
示例#14
0
 public boolean process(
     @NotNull final Project project,
     @NotNull final Editor editor,
     @NotNull final PsiFile psiFile) {
   final Document document = editor.getDocument();
   final String textForRollback = document.getText();
   try {
     editor.putUserData(SMART_ENTER_TIMESTAMP, editor.getDocument().getModificationStamp());
     myFirstErrorOffset = Integer.MAX_VALUE;
     process(project, editor, psiFile, 0);
   } catch (TooManyAttemptsException e) {
     document.replaceString(0, document.getTextLength(), textForRollback);
   } finally {
     editor.putUserData(SMART_ENTER_TIMESTAMP, null);
   }
   return true;
 }
 private void replaceString(
     Project project, Document document, String replaceText, int startOffset, int endOffset) {
   CommandProcessor.getInstance()
       .executeCommand(
           project,
           (Runnable)
               () -> {
                 ApplicationManager.getApplication()
                     .runWriteAction(
                         (Runnable)
                             () -> {
                               document.replaceString(startOffset, endOffset, replaceText);
                             });
               },
           "Auto-Align",
           this);
 }
 private static void adjustIndentationInRange(
     final PsiFile file,
     final Document document,
     final TextRange[] indents,
     final int indentAdjustment) {
   final CharSequence charsSequence = document.getCharsSequence();
   for (final TextRange indent : indents) {
     final String oldIndentStr =
         charsSequence.subSequence(indent.getStartOffset() + 1, indent.getEndOffset()).toString();
     final int oldIndent =
         IndentHelperImpl.getIndent(file.getProject(), file.getFileType(), oldIndentStr, true);
     final String newIndentStr =
         IndentHelperImpl.fillIndent(
             file.getProject(), file.getFileType(), Math.max(oldIndent + indentAdjustment, 0));
     document.replaceString(indent.getStartOffset() + 1, indent.getEndOffset(), newIndentStr);
   }
 }
  public static void renameNonCodeUsages(
      @NotNull Project project, @NotNull NonCodeUsageInfo[] usages) {
    PsiDocumentManager.getInstance(project).commitAllDocuments();
    Map<Document, List<UsageOffset>> docsToOffsetsMap = new HashMap<Document, List<UsageOffset>>();
    final PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(project);
    for (NonCodeUsageInfo usage : usages) {
      PsiElement element = usage.getElement();

      if (element == null) continue;
      element = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(element, true);
      if (element == null) continue;

      final ProperTextRange rangeInElement = usage.getRangeInElement();
      if (rangeInElement == null) continue;

      final PsiFile containingFile = element.getContainingFile();
      final Document document = psiDocumentManager.getDocument(containingFile);

      final Segment segment = usage.getSegment();
      LOG.assertTrue(segment != null);
      int fileOffset = segment.getStartOffset();

      List<UsageOffset> list = docsToOffsetsMap.get(document);
      if (list == null) {
        list = new ArrayList<UsageOffset>();
        docsToOffsetsMap.put(document, list);
      }

      list.add(new UsageOffset(fileOffset, fileOffset + rangeInElement.getLength(), usage.newText));
    }

    for (Document document : docsToOffsetsMap.keySet()) {
      List<UsageOffset> list = docsToOffsetsMap.get(document);
      LOG.assertTrue(list != null, document);
      UsageOffset[] offsets = list.toArray(new UsageOffset[list.size()]);
      Arrays.sort(offsets);

      for (int i = offsets.length - 1; i >= 0; i--) {
        UsageOffset usageOffset = offsets[i];
        document.replaceString(usageOffset.startOffset, usageOffset.endOffset, usageOffset.newText);
      }
      PsiDocumentManager.getInstance(project).commitDocument(document);
    }
    PsiDocumentManager.getInstance(project).commitAllDocuments();
  }
示例#18
0
  public static void addImportDirectiveOrChangeToFqName(
      @NotNull FqName importFqn,
      @NotNull JetFile file,
      int refOffset,
      @NotNull PsiElement targetElement) {
    PsiReference reference = file.findReferenceAt(refOffset);
    if (reference instanceof JetPsiReference) {
      PsiElement target = reference.resolve();
      if (target != null) {
        boolean same = file.getManager().areElementsEquivalent(target, targetElement);

        if (!same) {
          same =
              target instanceof PsiClass
                  && importFqn.getFqName().equals(((PsiClass) target).getQualifiedName());
        }

        if (!same) {
          if (target instanceof PsiMethod) {
            PsiMethod method = (PsiMethod) target;
            same =
                (method.isConstructor()
                    && file.getManager()
                        .areElementsEquivalent(method.getContainingClass(), targetElement));
          }
        }

        if (!same) {
          if (target instanceof JetObjectDeclarationName) {
            same = file.getManager().areElementsEquivalent(target.getParent(), targetElement);
          }
        }

        if (!same) {
          Document document = PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
          TextRange refRange = reference.getElement().getTextRange();
          document.replaceString(
              refRange.getStartOffset(), refRange.getEndOffset(), importFqn.getFqName());
        }
        return;
      }
    }
    addImportDirective(new ImportPath(importFqn, false), null, file);
  }
 private int doBoundCommentingAndGetShift(
     int offset,
     String commented,
     int skipLength,
     String toInsert,
     boolean skipBrace,
     TextRange selection) {
   if (commented == null
       && (offset == selection.getStartOffset()
           || offset + (skipBrace ? skipLength : 0) == selection.getEndOffset())) {
     return 0;
   }
   if (commented == null) {
     myDocument.insertString(offset + (skipBrace ? skipLength : 0), toInsert);
     return toInsert.length();
   } else {
     myDocument.replaceString(offset, offset + skipLength, commented);
     return commented.length() - skipLength;
   }
 }
  private void handleSelection(Editor editor, SelectionModel selectionModel) {
    Document doc = editor.getDocument();

    if (doc == null) {
      return;
    }

    int start = selectionModel.getSelectionStart();
    int end = selectionModel.getSelectionEnd();
    char[] allChars = doc.getChars();

    StringBuffer selection = new StringBuffer();

    selection.append(allChars, start, end - start);

    this.m_transformer.transform(selection);

    doc.replaceString(start, end, selection.toString());
    selectionModel.setSelection(start, start + selection.length());
  }
  @NotNull
  protected String doExtract(
      final PsiDirectory targetDirectory,
      final String targetfileName,
      final T first,
      final T last,
      final Language includingLanguage)
      throws IncorrectOperationException {
    final PsiFile file = targetDirectory.createFile(targetfileName);
    Project project = targetDirectory.getProject();
    final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
    final Document document = documentManager.getDocument(file);
    document.replaceString(0, document.getTextLength(), first.getText().trim());
    documentManager.commitDocument(document);
    CodeStyleManager.getInstance(PsiManager.getInstance(project).getProject())
        .reformat(file); // TODO: adjustLineIndent

    final String relativePath =
        PsiFileSystemItemUtil.getRelativePath(first.getContainingFile(), file);
    if (relativePath == null) throw new IncorrectOperationException("Cannot extract!");
    return relativePath;
  }
  private static void doAddImport(GoFile file, Editor editor, String pathToImport) {
    GoImportDeclarations[] ids = file.getImportDeclarations();
    Document document = editor.getDocument();

    if (ids.length == 0) {
      addImportUnderPackage(file, document, pathToImport);
      return;
    }

    GoImportDeclarations importDeclarations = ids[ids.length - 1];
    GoImportDeclaration[] imports = importDeclarations.getDeclarations();

    if (imports.length == 0) {
      addImportUnderPackage(file, document, pathToImport);
      return;
    }

    GoImportDeclaration lastImport = imports[imports.length - 1];

    PsiElement lastChild =
        getPrevSiblingIfItsWhiteSpaceOrComment(importDeclarations.getLastChild());
    if (lastChild == null) {
      addImportUnderPackage(file, document, pathToImport);
      return;
    }

    if (")".equals(lastChild.getText())) {
      document.insertString(lastChild.getTextOffset(), "\"" + pathToImport + "\"\n");
      int line = document.getLineNumber(lastChild.getTextOffset());
      reformatLines(file, editor, line, line);
    } else {
      String oldImport = lastImport.getText();
      int start = lastImport.getTextOffset();
      int end = start + lastImport.getTextLength();
      String declarations = String.format("(\n%s\n\"%s\"\n)", oldImport, pathToImport);
      document.replaceString(start, end, declarations);
      reformatPositions(file, start, start + declarations.length());
    }
  }
  private static VirtualFile getCopyWithAnswers(
      @NotNull final VirtualFile taskDir,
      @NotNull final VirtualFile file,
      @NotNull final TaskFile source,
      @NotNull final TaskFile target) {
    VirtualFile copy = null;
    try {

      copy =
          file.copy(
              taskDir,
              taskDir,
              file.getNameWithoutExtension()
                  + EduNames.ANSWERS_POSTFIX
                  + "."
                  + file.getExtension());
      final FileDocumentManager documentManager = FileDocumentManager.getInstance();
      final Document document = documentManager.getDocument(copy);
      if (document != null) {
        TaskFile.copy(source, target);
        EduDocumentListener listener = new EduDocumentListener(target);
        document.addDocumentListener(listener);
        for (AnswerPlaceholder answerPlaceholder : target.getAnswerPlaceholders()) {
          final int start = answerPlaceholder.getOffset();
          final int end = start + answerPlaceholder.getRealLength();
          final String text = answerPlaceholder.getPossibleAnswer();
          document.replaceString(start, end, text);
        }
        ApplicationManager.getApplication()
            .runWriteAction(() -> documentManager.saveDocument(document));
      }
    } catch (IOException e) {
      LOG.error(e);
    }
    return copy;
  }
  @Override
  public void handleInsert(InsertionContext context) {
    PsiVariable variable = getObject();

    Document document = context.getDocument();
    document.replaceString(context.getStartOffset(), context.getTailOffset(), variable.getName());
    context.commitDocument();

    if (variable instanceof PsiField) {
      if (willBeImported()) {
        RangeMarker toDelete =
            JavaCompletionUtil.insertTemporary(context.getTailOffset(), document, " ");
        context.commitDocument();
        final PsiReferenceExpression ref =
            PsiTreeUtil.findElementOfClassAtOffset(
                context.getFile(), context.getStartOffset(), PsiReferenceExpression.class, false);
        if (ref != null) {
          ref.bindToElementViaStaticImport(((PsiField) variable).getContainingClass());
          PostprocessReformattingAspect.getInstance(ref.getProject()).doPostponedFormatting();
        }
        if (toDelete.isValid()) {
          document.deleteString(toDelete.getStartOffset(), toDelete.getEndOffset());
        }
        context.commitDocument();
      } else if (shouldQualify((PsiField) variable, context)) {
        qualifyFieldReference(context, (PsiField) variable);
      }
    }

    PsiReferenceExpression ref =
        PsiTreeUtil.findElementOfClassAtOffset(
            context.getFile(), context.getTailOffset() - 1, PsiReferenceExpression.class, false);
    if (ref != null) {
      JavaCodeStyleManager.getInstance(context.getProject()).shortenClassReferences(ref);
    }

    ref =
        PsiTreeUtil.findElementOfClassAtOffset(
            context.getFile(), context.getTailOffset() - 1, PsiReferenceExpression.class, false);
    makeVariableFinalIfNeeded(context, ref);

    final char completionChar = context.getCompletionChar();
    if (completionChar == '=') {
      context.setAddCompletionChar(false);
      TailType.EQ.processTail(context.getEditor(), context.getTailOffset());
    } else if (completionChar == ','
        && getAttribute(LookupItem.TAIL_TYPE_ATTR) != TailType.UNKNOWN) {
      context.setAddCompletionChar(false);
      TailType.COMMA.processTail(context.getEditor(), context.getTailOffset());
      AutoPopupController.getInstance(context.getProject())
          .autoPopupParameterInfo(context.getEditor(), null);
    } else if (completionChar == ':') {
      context.setAddCompletionChar(false);
      TailType.COND_EXPR_COLON.processTail(context.getEditor(), context.getTailOffset());
    } else if (completionChar == '.') {
      AutoPopupController.getInstance(context.getProject())
          .autoPopupMemberLookup(context.getEditor(), null);
    } else if (completionChar == '!' && PsiType.BOOLEAN.isAssignableFrom(variable.getType())) {
      context.setAddCompletionChar(false);
      if (ref != null) {
        FeatureUsageTracker.getInstance()
            .triggerFeatureUsed(CodeCompletionFeatures.EXCLAMATION_FINISH);
        document.insertString(ref.getTextRange().getStartOffset(), "!");
      }
    }
  }
 private static void fixDocumentQuotes(Document doc, int offset) {
   if (doc.getCharsSequence().charAt(offset) == '\'') {
     doc.replaceString(offset, offset + 1, "\"");
   }
 }
  public void patch(final FlooPatch res) {
    final TextBuf b = this;
    Flog.info("Got _on_patch");

    String text;
    String md5FromDoc;
    final Document d;

    String oldText = buf;
    VirtualFile virtualFile = b.getVirtualFile();
    if (virtualFile == null) {
      Flog.warn("VirtualFile is null, no idea what do do. Aborting everything %s", this);
      getBuf();
      return;
    }
    d = Buf.getDocumentForVirtualFile(virtualFile);
    if (d == null) {
      Flog.warn("Document not found for %s", virtualFile);
      getBuf();
      return;
    }
    String viewText;
    if (virtualFile.exists()) {
      viewText = d.getText();
      if (viewText.equals(oldText)) {
        b.forced_patch = false;
      } else if (!b.forced_patch) {
        b.forced_patch = true;
        oldText = viewText;
        b.send_patch(viewText);
        Flog.warn("Sending force patch for %s. this is dangerous!", b.path);
      }
    } else {
      viewText = oldText;
    }

    b.cancelTimeout();

    String md5Before = DigestUtils.md5Hex(viewText);
    if (!md5Before.equals(res.md5_before)) {
      Flog.warn("starting md5s don't match for %s. this is dangerous!", b.path);
    }

    List<diff_match_patch.Patch> patches = dmp.patch_fromText(res.patch);
    final Object[] results = dmp.patch_apply((LinkedList<diff_match_patch.Patch>) patches, oldText);
    final String patchedContents = (String) results[0];
    final boolean[] patchesClean = (boolean[]) results[1];
    final FlooPatchPosition[] positions = (FlooPatchPosition[]) results[2];

    for (boolean clean : patchesClean) {
      if (!clean) {
        Flog.log("Patch not clean for %s. Sending get_buf and setting readonly.", d);
        getBuf();
        return;
      }
    }
    // XXX: If patchedContents have carriage returns this will be a problem:
    String md5After = DigestUtils.md5Hex(patchedContents);
    if (!md5After.equals(res.md5_after)) {
      Flog.info("MD5 after mismatch (ours %s remote %s)", md5After, res.md5_after);
    }

    if (!d.isWritable()) {
      d.setReadOnly(false);
    }
    if (!ReadonlyStatusHandler.ensureDocumentWritable(context.project, d)) {
      Flog.info("Document: %s is not writable.", d);
      return;
    }

    final Editor[] editors = EditorFactory.getInstance().getEditors(d, context.project);
    final HashMap<ScrollingModel, Integer[]> original = new HashMap<ScrollingModel, Integer[]>();
    for (Editor editor : editors) {
      if (editor.isDisposed()) {
        continue;
      }
      ScrollingModel scrollingModel = editor.getScrollingModel();
      original.put(
          scrollingModel,
          new Integer[] {
            scrollingModel.getHorizontalScrollOffset(), scrollingModel.getVerticalScrollOffset()
          });
    }
    for (FlooPatchPosition flooPatchPosition : positions) {
      int start = Math.max(0, flooPatchPosition.start);
      int end_ld = Math.max(start + flooPatchPosition.end, start);
      end_ld = Math.min(end_ld, d.getTextLength());
      String contents = NEW_LINE.matcher(flooPatchPosition.text).replaceAll("\n");
      Throwable e = null;
      try {
        Listener.flooDisable();
        d.replaceString(start, end_ld, contents);
      } catch (Throwable exception) {
        e = exception;
      } finally {
        Listener.flooEnable();
      }

      if (e != null) {
        Flog.warn(e);
        getBuf();
        return;
      }
    }
    text = d.getText();
    md5FromDoc = DigestUtils.md5Hex(text);
    if (!md5FromDoc.equals(res.md5_after)) {
      Flog.info("md5FromDoc mismatch (ours %s remote %s)", md5FromDoc, res.md5_after);
      b.setGetBufTimeout();
    }

    for (Map.Entry<ScrollingModel, Integer[]> entry : original.entrySet()) {
      ScrollingModel model = entry.getKey();
      Integer[] offsets = entry.getValue();
      model.scrollHorizontally(offsets[0]);
      model.scrollVertically(offsets[1]);
    }

    b.set(text, md5FromDoc);
    Flog.log("Patched %s", res.path);
  }
  static void commentNestedComments(
      @NotNull Document document, TextRange range, Commenter commenter) {
    final int offset = range.getStartOffset();
    final IntArrayList toReplaceWithComments = new IntArrayList();
    final IntArrayList prefixes = new IntArrayList();

    final String text =
        document
            .getCharsSequence()
            .subSequence(range.getStartOffset(), range.getEndOffset())
            .toString();
    final String commentedPrefix = commenter.getCommentedBlockCommentPrefix();
    final String commentedSuffix = commenter.getCommentedBlockCommentSuffix();
    final String commentPrefix = commenter.getBlockCommentPrefix();
    final String commentSuffix = commenter.getBlockCommentSuffix();

    int nearestSuffix = getNearest(text, commentedSuffix, 0);
    int nearestPrefix = getNearest(text, commentedPrefix, 0);
    int level = 0;
    int lastSuffix = -1;
    for (int i = Math.min(nearestPrefix, nearestSuffix);
        i < text.length();
        i = Math.min(nearestPrefix, nearestSuffix)) {
      if (i > nearestPrefix) {
        nearestPrefix = getNearest(text, commentedPrefix, i);
        continue;
      }
      if (i > nearestSuffix) {
        nearestSuffix = getNearest(text, commentedSuffix, i);
        continue;
      }
      if (i == nearestPrefix) {
        if (level <= 0) {
          if (lastSuffix != -1) {
            toReplaceWithComments.add(lastSuffix);
          }
          level = 1;
          lastSuffix = -1;
          toReplaceWithComments.add(i);
          prefixes.add(i);
        } else {
          level++;
        }
        nearestPrefix = getNearest(text, commentedPrefix, nearestPrefix + 1);
      } else {
        lastSuffix = i;
        level--;
        nearestSuffix = getNearest(text, commentedSuffix, nearestSuffix + 1);
      }
    }
    if (lastSuffix != -1) {
      toReplaceWithComments.add(lastSuffix);
    }

    int prefixIndex = prefixes.size() - 1;
    for (int i = toReplaceWithComments.size() - 1; i >= 0; i--) {
      int position = toReplaceWithComments.get(i);
      if (prefixIndex >= 0 && position == prefixes.get(prefixIndex)) {
        prefixIndex--;
        document.replaceString(
            offset + position, offset + position + commentedPrefix.length(), commentPrefix);
      } else {
        document.replaceString(
            offset + position, offset + position + commentedSuffix.length(), commentSuffix);
      }
    }
  }
  public static int insertClassReference(
      PsiClass psiClass, PsiFile file, int startOffset, int endOffset) {
    final Project project = file.getProject();
    PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
    documentManager.commitAllDocuments();

    final PsiManager manager = file.getManager();

    final Document document =
        FileDocumentManager.getInstance().getDocument(file.getViewProvider().getVirtualFile());

    final PsiReference reference = file.findReferenceAt(startOffset);
    if (reference != null) {
      final PsiElement resolved = reference.resolve();
      if (resolved instanceof PsiClass) {
        if (((PsiClass) resolved).getQualifiedName() == null
            || manager.areElementsEquivalent(psiClass, resolved)) {
          return endOffset;
        }
      }
    }

    String name = psiClass.getName();
    if (name == null) {
      return endOffset;
    }

    assert document != null;
    document.replaceString(startOffset, endOffset, name);

    int newEndOffset = startOffset + name.length();
    final RangeMarker toDelete = insertTemporary(newEndOffset, document, " ");

    documentManager.commitAllDocuments();

    PsiElement element = file.findElementAt(startOffset);
    if (element instanceof PsiIdentifier) {
      PsiElement parent = element.getParent();
      if (parent instanceof PsiJavaCodeReferenceElement
          && !((PsiJavaCodeReferenceElement) parent).isQualified()
          && !(parent.getParent() instanceof PsiPackageStatement)) {
        PsiJavaCodeReferenceElement ref = (PsiJavaCodeReferenceElement) parent;

        if (psiClass.isValid()
            && !psiClass.getManager().areElementsEquivalent(psiClass, resolveReference(ref))) {
          final boolean staticImport = ref instanceof PsiImportStaticReferenceElement;
          PsiElement newElement;
          try {
            newElement =
                staticImport
                    ? ((PsiImportStaticReferenceElement) ref).bindToTargetClass(psiClass)
                    : ref.bindToElement(psiClass);
          } catch (IncorrectOperationException e) {
            return endOffset; // can happen if fqn contains reserved words, for example
          }

          final RangeMarker rangeMarker = document.createRangeMarker(newElement.getTextRange());
          documentManager.doPostponedOperationsAndUnblockDocument(document);
          documentManager.commitDocument(document);

          newElement =
              CodeInsightUtilCore.findElementInRange(
                  file,
                  rangeMarker.getStartOffset(),
                  rangeMarker.getEndOffset(),
                  PsiJavaCodeReferenceElement.class,
                  JavaLanguage.INSTANCE);
          rangeMarker.dispose();
          if (newElement != null) {
            newEndOffset = newElement.getTextRange().getEndOffset();
            if (!(newElement instanceof PsiReferenceExpression)) {
              PsiReferenceParameterList parameterList =
                  ((PsiJavaCodeReferenceElement) newElement).getParameterList();
              if (parameterList != null) {
                newEndOffset = parameterList.getTextRange().getStartOffset();
              }
            }

            if (!staticImport
                && !psiClass
                    .getManager()
                    .areElementsEquivalent(psiClass, resolveReference((PsiReference) newElement))
                && !PsiUtil.isInnerClass(psiClass)) {
              final String qName = psiClass.getQualifiedName();
              if (qName != null) {
                document.replaceString(
                    newElement.getTextRange().getStartOffset(), newEndOffset, qName);
                newEndOffset = newElement.getTextRange().getStartOffset() + qName.length();
              }
            }
          }
        }
      }
    }

    if (toDelete.isValid()) {
      document.deleteString(toDelete.getStartOffset(), toDelete.getEndOffset());
    }

    return newEndOffset;
  }
  private void commentLine(int line, int offset, @Nullable Commenter commenter) {
    if (commenter == null) commenter = findCommenter(line);
    if (commenter == null) return;
    if (commenter instanceof SelfManagingCommenter) {
      final SelfManagingCommenter selfManagingCommenter = (SelfManagingCommenter) commenter;
      selfManagingCommenter.commentLine(
          line, offset, myDocument, myCommenterStateMap.get(selfManagingCommenter));
      return;
    }

    String prefix = commenter.getLineCommentPrefix();
    if (prefix != null) {
      if (commenter instanceof CommenterWithLineSuffix) {
        int endOffset = myDocument.getLineEndOffset(line);
        endOffset = CharArrayUtil.shiftBackward(myDocument.getCharsSequence(), endOffset, " \t");
        int shiftedStartOffset =
            CharArrayUtil.shiftForward(myDocument.getCharsSequence(), offset, " \t");
        String lineSuffix = ((CommenterWithLineSuffix) commenter).getLineCommentSuffix();
        if (!CharArrayUtil.regionMatches(
            myDocument.getCharsSequence(), shiftedStartOffset, prefix)) {
          if (!CharArrayUtil.regionMatches(
              myDocument.getCharsSequence(), endOffset - lineSuffix.length(), lineSuffix)) {
            myDocument.insertString(endOffset, lineSuffix);
          }
          myDocument.insertString(offset, prefix);
        }
      } else {
        myDocument.insertString(offset, prefix);
      }
    } else {
      prefix = commenter.getBlockCommentPrefix();
      String suffix = commenter.getBlockCommentSuffix();
      if (prefix == null || suffix == null) return;
      int endOffset = myDocument.getLineEndOffset(line);
      if (endOffset == offset && myStartLine != myEndLine) return;
      final int textLength = myDocument.getTextLength();
      final CharSequence chars = myDocument.getCharsSequence();
      offset = CharArrayUtil.shiftForward(chars, offset, " \t");
      if (endOffset == textLength) {
        final int shifted = CharArrayUtil.shiftBackward(chars, textLength - 1, " \t");
        if (shifted < textLength - 1) endOffset = shifted;
      } else {
        endOffset = CharArrayUtil.shiftBackward(chars, endOffset, " \t");
      }
      if (endOffset < offset || offset == textLength - 1 && line != myDocument.getLineCount() - 1) {
        return;
      }
      final String text = chars.subSequence(offset, endOffset).toString();
      final IntArrayList prefixes = new IntArrayList();
      final IntArrayList suffixes = new IntArrayList();
      final String commentedSuffix = commenter.getCommentedBlockCommentSuffix();
      final String commentedPrefix = commenter.getCommentedBlockCommentPrefix();
      for (int position = 0; position < text.length(); ) {
        int nearestPrefix = text.indexOf(prefix, position);
        if (nearestPrefix == -1) {
          nearestPrefix = text.length();
        }
        int nearestSuffix = text.indexOf(suffix, position);
        if (nearestSuffix == -1) {
          nearestSuffix = text.length();
        }
        if (Math.min(nearestPrefix, nearestSuffix) == text.length()) {
          break;
        }
        if (nearestPrefix < nearestSuffix) {
          prefixes.add(nearestPrefix);
          position = nearestPrefix + prefix.length();
        } else {
          suffixes.add(nearestSuffix);
          position = nearestSuffix + suffix.length();
        }
      }
      if (!(commentedSuffix == null
          && !suffixes.isEmpty()
          && offset + suffixes.get(suffixes.size() - 1) + suffix.length() >= endOffset)) {
        myDocument.insertString(endOffset, suffix);
      }
      int nearestPrefix = prefixes.size() - 1;
      int nearestSuffix = suffixes.size() - 1;
      while (nearestPrefix >= 0 || nearestSuffix >= 0) {
        if (nearestSuffix == -1
            || nearestPrefix != -1 && prefixes.get(nearestPrefix) > suffixes.get(nearestSuffix)) {
          final int position = prefixes.get(nearestPrefix);
          nearestPrefix--;
          if (commentedPrefix != null) {
            myDocument.replaceString(
                offset + position, offset + position + prefix.length(), commentedPrefix);
          } else if (position != 0) {
            myDocument.insertString(offset + position, suffix);
          }
        } else {
          final int position = suffixes.get(nearestSuffix);
          nearestSuffix--;
          if (commentedSuffix != null) {
            myDocument.replaceString(
                offset + position, offset + position + suffix.length(), commentedSuffix);
          } else if (offset + position + suffix.length() < endOffset) {
            myDocument.insertString(offset + position + suffix.length(), prefix);
          }
        }
      }
      if (!(commentedPrefix == null && !prefixes.isEmpty() && prefixes.get(0) == 0)) {
        myDocument.insertString(offset, prefix);
      }
    }
  }