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);
    }
  }
 private void commitToOriginalInner() {
   final String text = myNewDocument.getText();
   final Map<
           PsiLanguageInjectionHost,
           Set<Trinity<RangeMarker, RangeMarker, SmartPsiElementPointer>>>
       map =
           ContainerUtil.classify(
               myMarkers.iterator(),
               new Convertor<
                   Trinity<RangeMarker, RangeMarker, SmartPsiElementPointer>,
                   PsiLanguageInjectionHost>() {
                 @Override
                 public PsiLanguageInjectionHost convert(
                     final Trinity<RangeMarker, RangeMarker, SmartPsiElementPointer> o) {
                   final PsiElement element = o.third.getElement();
                   return (PsiLanguageInjectionHost) element;
                 }
               });
   PsiDocumentManager documentManager = PsiDocumentManager.getInstance(myProject);
   documentManager.commitDocument(myOrigDocument); // commit here and after each manipulator update
   int localInsideFileCursor = 0;
   for (PsiLanguageInjectionHost host : map.keySet()) {
     if (host == null) continue;
     String hostText = host.getText();
     ProperTextRange insideHost = null;
     StringBuilder sb = new StringBuilder();
     for (Trinity<RangeMarker, RangeMarker, SmartPsiElementPointer> entry : map.get(host)) {
       RangeMarker origMarker = entry.first; // check for validity?
       int hostOffset = host.getTextRange().getStartOffset();
       ProperTextRange localInsideHost =
           new ProperTextRange(
               origMarker.getStartOffset() - hostOffset, origMarker.getEndOffset() - hostOffset);
       RangeMarker rangeMarker = entry.second;
       ProperTextRange localInsideFile =
           new ProperTextRange(
               Math.max(localInsideFileCursor, rangeMarker.getStartOffset()),
               rangeMarker.getEndOffset());
       if (insideHost != null) {
         // append unchanged inter-markers fragment
         sb.append(
             hostText.substring(insideHost.getEndOffset(), localInsideHost.getStartOffset()));
       }
       sb.append(
           localInsideFile.getEndOffset() <= text.length() && !localInsideFile.isEmpty()
               ? localInsideFile.substring(text)
               : "");
       localInsideFileCursor = localInsideFile.getEndOffset();
       insideHost = insideHost == null ? localInsideHost : insideHost.union(localInsideHost);
     }
     assert insideHost != null;
     ElementManipulators.getManipulator(host).handleContentChange(host, insideHost, sb.toString());
     documentManager.commitDocument(myOrigDocument);
   }
 }
 public int compareTo(PostprocessFormattingTask o) {
   RangeMarker o1 = myRange;
   RangeMarker o2 = o.myRange;
   if (o1.equals(o2)) return 0;
   final int diff = o2.getEndOffset() - o1.getEndOffset();
   if (diff == 0) {
     if (o1.getStartOffset() == o2.getStartOffset()) return 0;
     if (o1.getStartOffset() == o1.getEndOffset()) return -1; // empty ranges first
     if (o2.getStartOffset() == o2.getEndOffset()) return 1; // empty ranges first
     return o1.getStartOffset() - o2.getStartOffset();
   }
   return diff;
 }
  public void initMarkers(Place shreds) {
    SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(myProject);
    int curOffset = -1;
    for (PsiLanguageInjectionHost.Shred shred : shreds) {
      final RangeMarker rangeMarker =
          myNewDocument.createRangeMarker(
              shred.getRange().getStartOffset() + shred.getPrefix().length(),
              shred.getRange().getEndOffset() - shred.getSuffix().length());
      final TextRange rangeInsideHost = shred.getRangeInsideHost();
      PsiLanguageInjectionHost host = shred.getHost();
      RangeMarker origMarker =
          myOrigDocument.createRangeMarker(
              rangeInsideHost.shiftRight(host.getTextRange().getStartOffset()));
      SmartPsiElementPointer<PsiLanguageInjectionHost> elementPointer =
          smartPointerManager.createSmartPsiElementPointer(host);
      Trinity<RangeMarker, RangeMarker, SmartPsiElementPointer> markers =
          Trinity.<RangeMarker, RangeMarker, SmartPsiElementPointer>create(
              origMarker, rangeMarker, elementPointer);
      myMarkers.add(markers);

      origMarker.setGreedyToRight(true);
      rangeMarker.setGreedyToRight(true);
      if (origMarker.getStartOffset() > curOffset) {
        origMarker.setGreedyToLeft(true);
        rangeMarker.setGreedyToLeft(true);
      }
      curOffset = origMarker.getEndOffset();
    }
    initGuardedBlocks(shreds);
  }
 public TextRange surroundExpression(
     final Project project, final Editor editor, PsiExpression expr)
     throws IncorrectOperationException {
   assert expr.isValid();
   PsiType[] types = GuessManager.getInstance(project).guessTypeToCast(expr);
   final boolean parenthesesNeeded =
       expr instanceof PsiPolyadicExpression
           || expr instanceof PsiConditionalExpression
           || expr instanceof PsiAssignmentExpression;
   String exprText = parenthesesNeeded ? "(" + expr.getText() + ")" : expr.getText();
   final Template template = generateTemplate(project, exprText, types);
   TextRange range;
   if (expr.isPhysical()) {
     range = expr.getTextRange();
   } else {
     final RangeMarker rangeMarker = expr.getUserData(ElementToWorkOn.TEXT_RANGE);
     if (rangeMarker == null) return null;
     range = new TextRange(rangeMarker.getStartOffset(), rangeMarker.getEndOffset());
   }
   editor.getDocument().deleteString(range.getStartOffset(), range.getEndOffset());
   editor.getCaretModel().moveToOffset(range.getStartOffset());
   editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
   TemplateManager.getInstance(project).startTemplate(editor, template);
   return null;
 }
  public void testCreation() throws Exception {
    RangeMarker marker = createMarker("0123456789", 2, 5);

    assertEquals(2, marker.getStartOffset());
    assertEquals(5, marker.getEndOffset());
    assertTrue(marker.isValid());
  }
  public void testInsertIntoEnd() throws Exception {
    RangeMarker marker = createMarker("0123456789", 2, 5);

    marker.getDocument().insertString(5, "xxx");

    assertEquals(2, marker.getStartOffset());
    assertEquals(5, marker.getEndOffset());
    assertTrue(marker.isValid());
  }
  public void testDeleteBeforeStart() throws Exception {
    RangeMarker marker = createMarker("01[234]56789");

    marker.getDocument().deleteString(0, 1);

    assertEquals(1, marker.getStartOffset());
    assertEquals(4, marker.getEndOffset());
    assertTrue(marker.isValid());
  }
  public void testNested3() {
    RangeMarker marker1 = createMarker("01[23]4567890123");
    DocumentEx document = (DocumentEx) marker1.getDocument();
    RangeMarker marker2 = document.createRangeMarker(9, 11);
    RangeMarker marker3 = document.createRangeMarker(1, 12);
    marker3.dispose();

    document.deleteString(marker1.getEndOffset(), marker2.getStartOffset());
  }
Example #10
0
  private void doEnter(PsiElement atCaret, Editor editor) throws IncorrectOperationException {
    final PsiFile psiFile = atCaret.getContainingFile();

    final RangeMarker rangeMarker = createRangeMarker(atCaret);
    if (myFirstErrorOffset != Integer.MAX_VALUE) {
      editor.getCaretModel().moveToOffset(myFirstErrorOffset);
      reformat(atCaret);
      return;
    }

    reformat(atCaret);
    commit(editor);

    atCaret =
        GroovyRefactoringUtil.findElementInRange(
            ((GroovyFileBase) psiFile),
            rangeMarker.getStartOffset(),
            rangeMarker.getEndOffset(),
            atCaret.getClass());

    //    atCaret = CodeInsightUtil.findElementInRange(psiFile, rangeMarker.getStartOffset(),
    // rangeMarker.getEndOffset(), atCaret.getClass());

    for (EnterProcessor processor : ourEnterProcessors) {
      if (atCaret == null) {
        // Can't restore element at caret after enter processor execution!
        break;
      }

      if (processor.doEnter(editor, atCaret, isModified(editor))) return;
    }

    if (!isModified(editor)) {
      plainEnter(editor);
    } else {
      if (myFirstErrorOffset == Integer.MAX_VALUE) {
        editor.getCaretModel().moveToOffset(rangeMarker.getEndOffset());
      } else {
        editor.getCaretModel().moveToOffset(myFirstErrorOffset);
      }
    }
  }
  public void testInsertIntoStartExpandToLeft() throws Exception {
    RangeMarker marker = createMarker("0123456789", 2, 5);

    marker.setGreedyToLeft(true);

    marker.getDocument().insertString(2, "xxx");

    assertEquals(2, marker.getStartOffset());
    assertEquals(8, marker.getEndOffset());
    assertTrue(marker.isValid());
  }
 static TextRange processDocument(
     Document document, RangeMarker marker, Commenter commenter, boolean escape) {
   if (commenter instanceof EscapingCommenter) {
     if (escape) {
       ((EscapingCommenter) commenter).escape(document, marker);
     } else {
       ((EscapingCommenter) commenter).unescape(document, marker);
     }
   }
   return TextRange.create(marker.getStartOffset(), marker.getEndOffset());
 }
  public void testUpdateInvalid() throws Exception {
    RangeMarker marker = createMarker("01[]23456789");

    marker.getDocument().deleteString(1, 3);
    assertFalse(marker.isValid());

    marker.getDocument().insertString(2, "xxx");

    assertEquals(2, marker.getStartOffset());
    assertEquals(2, marker.getEndOffset());
    assertFalse(marker.isValid());
  }
 public boolean changesRange(TextRange range) {
   if (myAltFullRange != null) {
     return range.intersects(myAltFullRange.getStartOffset(), myAltFullRange.getEndOffset());
   } else if (!myMarkers.isEmpty()) {
     TextRange hostRange =
         TextRange.create(
             myMarkers.get(0).first.getStartOffset(),
             myMarkers.get(myMarkers.size() - 1).first.getEndOffset());
     return range.intersects(hostRange);
   }
   return false;
 }
 protected Document createCopy() {
   final Document originalDocument = myRangeMarker.getDocument();
   String textInRange =
       originalDocument
           .getCharsSequence()
           .subSequence(myRangeMarker.getStartOffset(), myRangeMarker.getEndOffset())
           .toString();
   final Document result = EditorFactory.getInstance().createDocument(textInRange);
   result.setReadOnly(!originalDocument.isWritable());
   result.putUserData(ORIGINAL_DOCUMENT, originalDocument);
   return result;
 }
Example #16
0
 private void collectRangeMarker(
     RangeMarker rangeMarker,
     int lineOffset,
     Collection<RangeMarker> leftGreedyMarkers,
     Collection<RangeMarker> emptyMarkers) {
   if (rangeMarker.getStartOffset() == lineOffset && rangeMarker.isGreedyToLeft()) {
     leftGreedyMarkers.add(rangeMarker);
   }
   if (rangeMarker.getStartOffset() == lineOffset
       && rangeMarker.getEndOffset() == lineOffset
       && !rangeMarker.isGreedyToRight()) {
     emptyMarkers.add(rangeMarker);
   }
 }
 public void execute(FileViewProvider viewProvider) {
   final Document document = viewProvider.getDocument();
   final PsiFile psiFile = viewProvider.getPsi(viewProvider.getBaseLanguage());
   for (Pair<Integer, RangeMarker> integerRangeMarkerPair : myRangesToReindent) {
     RangeMarker marker = integerRangeMarkerPair.second;
     final CharSequence charsSequence =
         document.getCharsSequence().subSequence(marker.getStartOffset(), marker.getEndOffset());
     final int oldIndent = integerRangeMarkerPair.first;
     final TextRange[] whitespaces =
         CharArrayUtil.getIndents(charsSequence, marker.getStartOffset());
     final int indentAdjustment = getNewIndent(psiFile, marker.getStartOffset()) - oldIndent;
     if (indentAdjustment != 0)
       adjustIndentationInRange(psiFile, document, whitespaces, indentAdjustment);
   }
 }
  private void surroundWithCodeBlock(@NotNull final MoveInfo info, final boolean down) {
    try {
      final Document document =
          PsiDocumentManager.getInstance(statementToSurroundWithCodeBlock.getProject())
              .getDocument(statementToSurroundWithCodeBlock.getContainingFile());
      int startOffset = document.getLineStartOffset(info.toMove.startLine);
      int endOffset = getLineStartSafeOffset(document, info.toMove.endLine);
      if (document.getText().charAt(endOffset - 1) == '\n') endOffset--;
      final RangeMarker lineRangeMarker = document.createRangeMarker(startOffset, endOffset);

      final PsiElementFactory factory =
          JavaPsiFacade.getInstance(statementToSurroundWithCodeBlock.getProject())
              .getElementFactory();
      PsiCodeBlock codeBlock = factory.createCodeBlock();
      codeBlock.add(statementToSurroundWithCodeBlock);
      final PsiBlockStatement blockStatement =
          (PsiBlockStatement)
              factory.createStatementFromText("{}", statementToSurroundWithCodeBlock);
      blockStatement.getCodeBlock().replace(codeBlock);
      PsiBlockStatement newStatement =
          (PsiBlockStatement) statementToSurroundWithCodeBlock.replace(blockStatement);
      newStatement = CodeInsightUtilBase.forcePsiPostprocessAndRestoreElement(newStatement);
      info.toMove =
          new LineRange(
              document.getLineNumber(lineRangeMarker.getStartOffset()),
              document.getLineNumber(lineRangeMarker.getEndOffset()) + 1);
      PsiCodeBlock newCodeBlock = newStatement.getCodeBlock();
      if (down) {
        PsiElement blockChild = firstNonWhiteElement(newCodeBlock.getFirstBodyElement(), true);
        if (blockChild == null) blockChild = newCodeBlock.getRBrace();
        info.toMove2 =
            new LineRange(
                info.toMove2
                    .startLine, // document.getLineNumber(newCodeBlock.getParent().getTextRange().getStartOffset()),
                document.getLineNumber(blockChild.getTextRange().getStartOffset()));
      } else {
        int start =
            document.getLineNumber(newCodeBlock.getRBrace().getTextRange().getStartOffset());
        int end = info.toMove.startLine;
        if (start > end) end = start;
        info.toMove2 = new LineRange(start, end);
      }
    } catch (IncorrectOperationException e) {
      LOG.error(e);
    }
  }
  private static void indentLinesIn(
      final Editor editor,
      final PsiFile file,
      final Document document,
      final Project project,
      RangeMarker range) {
    final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
    int line1 = editor.offsetToLogicalPosition(range.getStartOffset()).line;
    int line2 = editor.offsetToLogicalPosition(range.getEndOffset()).line;

    while (!lineContainsNonSpaces(document, line1) && line1 < line2) line1++;
    while (!lineContainsNonSpaces(document, line2) && line1 < line2) line2--;

    final FileViewProvider provider = file.getViewProvider();
    PsiFile rootToAdjustIndentIn = provider.getPsi(provider.getBaseLanguage());
    codeStyleManager.adjustLineIndent(
        rootToAdjustIndentIn,
        new TextRange(document.getLineStartOffset(line1), document.getLineStartOffset(line2)));
  }
 private static boolean isOffsetInsideHighlightInfo(
     int offset, HighlightInfo info, boolean includeFixRange) {
   RangeHighlighterEx highlighter = info.highlighter;
   if (highlighter == null || !highlighter.isValid()) return false;
   int startOffset = highlighter.getStartOffset();
   int endOffset = highlighter.getEndOffset();
   if (startOffset <= offset && offset <= endOffset) {
     return true;
   }
   if (!includeFixRange) return false;
   RangeMarker fixMarker = info.fixMarker;
   if (fixMarker != null) { // null means its range is the same as highlighter
     if (!fixMarker.isValid()) return false;
     startOffset = fixMarker.getStartOffset();
     endOffset = fixMarker.getEndOffset();
     return startOffset <= offset && offset <= endOffset;
   }
   return false;
 }
 private void refreshLineMarkers() {
   for (Map.Entry<RangeMarker, LineMarkerInfo> entry : myLineMarkerInfos.entrySet()) {
     RangeMarker rangeMarker = entry.getKey();
     int startOffset = rangeMarker.getStartOffset();
     int endOffset = rangeMarker.getEndOffset();
     LineMarkerInfo value = entry.getValue();
     PsiElement element = value.getElement();
     assert element != null : value;
     TextRange range = new TextRange(startOffset, endOffset);
     final String tooltip = value.getLineMarkerTooltip();
     LineMarkerInfo markerInfo =
         new LineMarkerInfo<>(
             element,
             range,
             null,
             value.updatePass,
             e -> tooltip,
             null,
             GutterIconRenderer.Alignment.RIGHT);
     entry.setValue(markerInfo);
   }
 }
 private void refreshLineMarkers() {
   for (Map.Entry<RangeMarker, LineMarkerInfo> entry : lineMarkerInfos.entrySet()) {
     RangeMarker rangeMarker = entry.getKey();
     int startOffset = rangeMarker.getStartOffset();
     int endOffset = rangeMarker.getEndOffset();
     final LineMarkerInfo value = entry.getValue();
     LineMarkerInfo markerInfo =
         new LineMarkerInfo<PsiElement>(
             value.getElement(),
             new TextRange(startOffset, endOffset),
             null,
             value.updatePass,
             new Function<PsiElement, String>() {
               @Override
               public String fun(PsiElement psiElement) {
                 return value.getLineMarkerTooltip();
               }
             },
             null,
             GutterIconRenderer.Alignment.RIGHT);
     entry.setValue(markerInfo);
   }
 }
  private static void indentLinesIn(
      final Editor editor,
      final PsiFile file,
      final Document document,
      final Project project,
      RangeMarker range) {
    final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(project);
    int line1 = editor.offsetToLogicalPosition(range.getStartOffset()).line;
    int line2 = editor.offsetToLogicalPosition(range.getEndOffset()).line;

    /*    if (PsiUtil.isInJspFile(file)) {
      // This version is slow because of each moved line cause commit
      // and right now we unable to fix JSP formatter quickly
      // TODO: remove this code
      for (int line = line1; line <= line2; line++) {
        if (lineContainsNonSpaces(document, line)) {
          int lineStart = document.getLineStartOffset(line);
          codeStyleManager.adjustLineIndent(document, lineStart);
        }
      }
    }
    else {*/

    while (!lineContainsNonSpaces(document, line1) && line1 <= line2) line1++;
    while (!lineContainsNonSpaces(document, line2) && line2 > line1) line2--;

    try {
      final FileViewProvider provider = file.getViewProvider();
      PsiFile rootToAdjustIndentIn = provider.getPsi(provider.getBaseLanguage());
      codeStyleManager.adjustLineIndent(
          rootToAdjustIndentIn,
          new TextRange(document.getLineStartOffset(line1), document.getLineStartOffset(line2)));
    } catch (IncorrectOperationException ex) {
      throw new RuntimeException(ex);
    }
    /* } */
  }
 public int getEndOffset() {
   return myRange.getEndOffset();
 }
 /**
  * Allows to check if text range defined by the given range marker completely contains text range
  * of the given fold region.
  *
  * @param rangeMarker range marker to check
  * @param foldRegion fold region to check
  * @return <code>true</code> if text range defined by the given range marker completely contains
  *     text range of the given fold region; <code>false</code> otherwise
  */
 private static boolean contains(
     @NotNull RangeMarker rangeMarker, @NotNull FoldRegion foldRegion) {
   return rangeMarker.getStartOffset() <= foldRegion.getStartOffset()
       && rangeMarker.getEndOffset() >= foldRegion.getEndOffset();
 }
  @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(), "!");
      }
    }
  }
  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;
  }
  @Override
  public void writeExternal(Element element) throws WriteExternalException {
    PsiDocumentManager.getInstance(myProject).commitAllDocuments();

    if (myPsiElements.isEmpty() && myRangeMarkers.isEmpty() && mySerializedElements.isEmpty()) {
      throw new WriteExternalException();
    }

    if (mySerializedElements.isEmpty()) {
      for (SmartPsiElementPointer<PsiElement> ptr : myPsiElements) {
        PsiElement psiElement = ptr.getElement();
        if (psiElement == null || !psiElement.isValid()) {
          continue;
        }
        FoldingInfo fi = psiElement.getUserData(FOLDING_INFO_KEY);
        boolean state = fi != null && fi.expanded;
        String signature = FoldingPolicy.getSignature(psiElement);
        if (signature == null) {
          continue;
        }

        PsiFile containingFile = psiElement.getContainingFile();
        PsiElement restoredElement = FoldingPolicy.restoreBySignature(containingFile, signature);
        if (!psiElement.equals(restoredElement)) {
          StringBuilder trace = new StringBuilder();
          PsiElement restoredAgain =
              FoldingPolicy.restoreBySignature(containingFile, signature, trace);
          LOG.error(
              "element: "
                  + psiElement
                  + "("
                  + psiElement.getText()
                  + "); restoredElement: "
                  + restoredElement
                  + "; signature: '"
                  + signature
                  + "'; file: "
                  + containingFile
                  + "; injected: "
                  + InjectedLanguageManager.getInstance(myProject)
                      .isInjectedFragment(containingFile)
                  + "; languages: "
                  + containingFile.getViewProvider().getLanguages()
                  + "; restored again: "
                  + restoredAgain
                  + "; restore produces same results: "
                  + (restoredAgain == restoredElement)
                  + "; trace:\n"
                  + trace);
        }

        Element e = new Element(ELEMENT_TAG);
        e.setAttribute(SIGNATURE_ATT, signature);
        e.setAttribute(EXPANDED_ATT, Boolean.toString(state));
        element.addContent(e);
      }
    } else {
      // get back postponed state (before folding initialization)
      for (SerializedPsiElement entry : mySerializedElements) {
        Element e = new Element(ELEMENT_TAG);
        e.setAttribute(SIGNATURE_ATT, entry.mySerializedElement);
        e.setAttribute(EXPANDED_ATT, Boolean.toString(entry.myFoldingInfo.getExpanded()));
        element.addContent(e);
      }
    }
    String date = null;
    for (RangeMarker marker : myRangeMarkers) {
      FoldingInfo fi = marker.getUserData(FOLDING_INFO_KEY);
      boolean state = fi != null && fi.expanded;

      Element e = new Element(MARKER_TAG);
      if (date == null) {
        date = getTimeStamp();
      }
      if (date.isEmpty()) {
        continue;
      }

      e.setAttribute(DATE_ATT, date);
      e.setAttribute(EXPANDED_ATT, Boolean.toString(state));
      String signature =
          Integer.valueOf(marker.getStartOffset()) + ":" + Integer.valueOf(marker.getEndOffset());
      e.setAttribute(SIGNATURE_ATT, signature);
      String placeHolderText = fi == null ? DEFAULT_PLACEHOLDER : fi.placeHolder;
      e.setAttribute(PLACEHOLDER_ATT, placeHolderText);
      element.addContent(e);
    }
  }
  @Override
  public void setToEditor(@NotNull final Editor editor) {
    assertDispatchThread();
    final PsiManager psiManager = PsiManager.getInstance(myProject);
    if (psiManager.isDisposed()) return;

    if (!myFile.isValid()) return;
    final PsiFile psiFile = psiManager.findFile(myFile);
    if (psiFile == null) return;

    if (!mySerializedElements.isEmpty()) {
      // Restore postponed state
      assert myPsiElements.isEmpty() : "Sequential deserialization";
      for (SerializedPsiElement entry : mySerializedElements) {
        PsiElement restoredElement =
            FoldingPolicy.restoreBySignature(psiFile, entry.mySerializedElement);
        if (restoredElement != null && restoredElement.isValid()) {
          myPsiElements.add(
              SmartPointerManager.getInstance(myProject)
                  .createSmartPsiElementPointer(restoredElement));
          restoredElement.putUserData(FOLDING_INFO_KEY, entry.myFoldingInfo);
        }
      }
      mySerializedElements.clear();
    }

    Map<PsiElement, FoldingDescriptor> ranges = null;
    for (SmartPsiElementPointer<PsiElement> ptr : myPsiElements) {
      PsiElement element = ptr.getElement();
      if (element == null || !element.isValid()) {
        continue;
      }

      if (ranges == null) {
        ranges = buildRanges(editor, psiFile);
      }
      FoldingDescriptor descriptor = ranges.get(element);
      if (descriptor == null) {
        continue;
      }

      TextRange range = descriptor.getRange();
      FoldRegion region =
          FoldingUtil.findFoldRegion(editor, range.getStartOffset(), range.getEndOffset());
      if (region != null) {
        FoldingInfo fi = element.getUserData(FOLDING_INFO_KEY);
        boolean state = fi != null && fi.expanded;
        region.setExpanded(state);
      }
    }
    for (RangeMarker marker : myRangeMarkers) {
      if (!marker.isValid()) {
        continue;
      }
      FoldRegion region =
          FoldingUtil.findFoldRegion(editor, marker.getStartOffset(), marker.getEndOffset());
      FoldingInfo info = marker.getUserData(FOLDING_INFO_KEY);
      if (region == null) {
        if (info != null) {
          region =
              editor
                  .getFoldingModel()
                  .addFoldRegion(marker.getStartOffset(), marker.getEndOffset(), info.placeHolder);
        }
        if (region == null) {
          return;
        }
      }

      boolean state = info != null && info.expanded;
      region.setExpanded(state);
    }
  }
  @Override
  protected void invokeImpl(PsiClass targetClass) {
    final PsiFile callSite = myMethodCall.getContainingFile();
    final Project project = myMethodCall.getProject();
    PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory();

    IdeDocumentHistory.getInstance(project).includeCurrentPlaceAsChangePlace();

    try {
      PsiMethod constructor = elementFactory.createConstructor();
      constructor = (PsiMethod) targetClass.add(constructor);

      final TemplateBuilderImpl templateBuilder = new TemplateBuilderImpl(constructor);
      CreateFromUsageUtils.setupMethodParameters(
          constructor,
          templateBuilder,
          myMethodCall.getArgumentList(),
          getTargetSubstitutor(myMethodCall));

      final PsiFile psiFile = myMethodCall.getContainingFile();

      templateBuilder.setEndVariableAfter(constructor.getBody().getLBrace());
      final RangeMarker rangeMarker =
          psiFile.getViewProvider().getDocument().createRangeMarker(myMethodCall.getTextRange());

      constructor = CodeInsightUtilCore.forcePsiPostprocessAndRestoreElement(constructor);

      targetClass = constructor.getContainingClass();
      myMethodCall =
          CodeInsightUtil.findElementInRange(
              psiFile,
              rangeMarker.getStartOffset(),
              rangeMarker.getEndOffset(),
              myMethodCall.getClass());
      rangeMarker.dispose();

      Template template = templateBuilder.buildTemplate();
      final Editor editor = positionCursor(project, targetClass.getContainingFile(), targetClass);
      if (editor == null) return;
      final TextRange textRange = constructor.getTextRange();
      final PsiFile file = targetClass.getContainingFile();
      editor.getDocument().deleteString(textRange.getStartOffset(), textRange.getEndOffset());
      editor.getCaretModel().moveToOffset(textRange.getStartOffset());

      startTemplate(
          editor,
          template,
          project,
          new TemplateEditingAdapter() {
            @Override
            public void templateFinished(Template template, boolean brokenOff) {
              ApplicationManager.getApplication()
                  .runWriteAction(
                      new Runnable() {
                        @Override
                        public void run() {
                          try {
                            PsiDocumentManager.getInstance(project)
                                .commitDocument(editor.getDocument());
                            final int offset = editor.getCaretModel().getOffset();
                            PsiMethod constructor =
                                PsiTreeUtil.findElementOfClassAtOffset(
                                    file, offset, PsiMethod.class, false);
                            CreateFromUsageUtils.setupMethodBody(constructor);
                            CreateFromUsageUtils.setupEditor(constructor, editor);

                            UndoUtil.markPsiFileForUndo(callSite);
                          } catch (IncorrectOperationException e) {
                            LOG.error(e);
                          }
                        }
                      });
            }
          });
    } catch (IncorrectOperationException e) {
      LOG.error(e);
    }
  }