@Override
 public void documentChanged(DocumentEvent e) {
   UndoManager undoManager = UndoManager.getInstance(myProject);
   boolean undoOrRedo = undoManager.isUndoInProgress() || undoManager.isRedoInProgress();
   if (undoOrRedo) {
     // allow undo/redo up until 'creation stamp' back in time
     // and check it after action is completed
     if (e.getDocument() == myOrigDocument) {
       //noinspection SSBasedInspection
       SwingUtilities.invokeLater(
           () -> {
             if (myOrigCreationStamp > myOrigDocument.getModificationStamp()) {
               closeEditor();
             }
           });
     }
   } else if (e.getDocument() == myNewDocument) {
     commitToOriginal(e);
     if (!isValid()) {
       ApplicationManager.getApplication()
           .invokeLater(() -> closeEditor(), myProject.getDisposed());
     }
   } else if (e.getDocument() == myOrigDocument) {
     if (myCommittingToOriginal || myAltFullRange != null && myAltFullRange.isValid()) return;
     ApplicationManager.getApplication().invokeLater(() -> closeEditor(), myProject.getDisposed());
   }
 }
  @Override
  public void contentsChanged(VirtualFileEvent event) {
    if (event.isFromSave()) return;
    final VirtualFile file = event.getFile();
    final Document document = getCachedDocument(file);
    if (document == null) {
      myMultiCaster.fileWithNoDocumentChanged(file);
      return;
    }

    if (isBinaryWithDecompiler(file)) {
      myMultiCaster.fileWithNoDocumentChanged(
          file); // This will generate PSI event at FileManagerImpl
    }

    long documentStamp = document.getModificationStamp();
    long oldFileStamp = event.getOldModificationStamp();
    if (documentStamp != oldFileStamp) {
      LOG.info("reload from disk?");
      LOG.info("  documentStamp:" + documentStamp);
      LOG.info("  oldFileStamp:" + oldFileStamp);

      if (file.isValid() && askReloadFromDisk(file, document)) {
        reloadFromDisk(document);
      }
    } else {
      reloadFromDisk(document);
    }
  }
 public boolean isErrorAnalyzingFinished(PsiFile file) {
   if (myDisposed) return false;
   Document document = PsiDocumentManager.getInstance(myProject).getCachedDocument(file);
   return document != null
       && document.getModificationStamp() == file.getModificationStamp()
       && myFileStatusMap.getFileDirtyScope(document, Pass.UPDATE_ALL) == null;
 }
 @Override
 public boolean isFileModified(@NotNull VirtualFile file) {
   final Document doc = getCachedDocument(file);
   return doc != null
       && isDocumentUnsaved(doc)
       && doc.getModificationStamp() != file.getModificationStamp();
 }
 public boolean isAllAnalysisFinished(@NotNull PsiFile file) {
   if (myDisposed) return false;
   Document document = PsiDocumentManager.getInstance(myProject).getCachedDocument(file);
   return document != null
       && document.getModificationStamp() == file.getModificationStamp()
       && myFileStatusMap.allDirtyScopesAreNull(document);
 }
 protected TextEditorHighlightingPass(
     @NotNull final Project project,
     @Nullable final Document document,
     boolean runIntentionPassAfter) {
   myDocument = document;
   myProject = project;
   myRunIntentionPassAfter = runIntentionPassAfter;
   myInitialStamp = document == null ? 0 : document.getModificationStamp();
 }
  private boolean isValid() {
    if (isDumbMode() && !DumbService.isDumbAware(this)) {
      return false;
    }

    if (myDocument != null && myDocument.getModificationStamp() != myInitialStamp) return false;
    if (myProject != null && myDocument != null) {
      PsiFile file = PsiDocumentManager.getInstance(myProject).getPsiFile(myDocument);
      if (file == null || !file.isValid()) return false;
    }

    return true;
  }
  @Override
  public void beforeDocumentChange(@NotNull DocumentEvent event) {
    if (myStopTrackingDocuments) return;

    final Document document = event.getDocument();
    if (!(document instanceof DocumentWindow) && !myLastCommittedTexts.containsKey(document)) {
      myLastCommittedTexts.put(
          document,
          Pair.create(document.getImmutableCharSequence(), document.getModificationStamp()));
    }

    VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document);
    boolean isRelevant = virtualFile != null && isRelevant(virtualFile);

    final FileViewProvider viewProvider = getCachedViewProvider(document);
    boolean inMyProject = viewProvider != null && viewProvider.getManager() == myPsiManager;
    if (!isRelevant || !inMyProject) {
      return;
    }

    final List<PsiFile> files = viewProvider.getAllFiles();
    PsiFile psiCause = null;
    for (PsiFile file : files) {
      if (file == null) {
        throw new AssertionError(
            "View provider "
                + viewProvider
                + " ("
                + viewProvider.getClass()
                + ") returned null in its files array: "
                + files
                + " for file "
                + viewProvider.getVirtualFile());
      }

      if (mySynchronizer.isInsideAtomicChange(file)) {
        psiCause = file;
      }
    }

    if (psiCause == null) {
      beforeDocumentChangeOnUnlockedDocument(viewProvider);
    }

    ((SingleRootFileViewProvider) viewProvider).beforeDocumentChanged(psiCause);
  }
Example #9
0
  protected void reportStubAstMismatch(String message, StubTree stubTree, Document cachedDocument) {
    rebuildStub();
    clearStub("stub-psi mismatch");
    scheduleDropCachesWithInvalidStubPsi();

    String msg = message;
    msg += "\n file=" + this;
    msg += ", modStamp=" + getModificationStamp();
    msg += "\n stub debugInfo=" + stubTree.getDebugInfo();
    msg += "\n document before=" + cachedDocument;

    ObjectStubTree latestIndexedStub =
        StubTreeLoader.getInstance().readFromVFile(getProject(), getVirtualFile());
    msg += "\nlatestIndexedStub=" + latestIndexedStub;
    if (latestIndexedStub != null) {
      msg +=
          "\n   same size="
              + (stubTree.getPlainList().size() == latestIndexedStub.getPlainList().size());
      msg += "\n   debugInfo=" + latestIndexedStub.getDebugInfo();
    }

    FileViewProvider viewProvider = getViewProvider();
    msg += "\n viewProvider=" + viewProvider;
    msg += "\n viewProvider stamp: " + viewProvider.getModificationStamp();

    VirtualFile file = viewProvider.getVirtualFile();
    msg += "; file stamp: " + file.getModificationStamp();
    msg += "; file modCount: " + file.getModificationCount();

    Document document = FileDocumentManager.getInstance().getCachedDocument(file);
    if (document != null) {
      msg += "\n doc saved: " + !FileDocumentManager.getInstance().isDocumentUnsaved(document);
      msg += "; doc stamp: " + document.getModificationStamp();
      msg += "; doc size: " + document.getTextLength();
      msg += "; committed: " + PsiDocumentManager.getInstance(getProject()).isCommitted(document);
    }

    throw new AssertionError(msg + "\n------------\n");
  }
  private void doSaveDocumentInWriteAction(@NotNull Document document, @NotNull VirtualFile file)
      throws IOException {
    if (!file.isValid()) {
      removeFromUnsaved(document);
      return;
    }

    if (!file.equals(getFile(document))) {
      registerDocument(document, file);
    }

    if (!isSaveNeeded(document, file)) {
      if (document instanceof DocumentEx) {
        ((DocumentEx) document).setModificationStamp(file.getModificationStamp());
      }
      removeFromUnsaved(document);
      updateModifiedProperty(file);
      return;
    }

    myMultiCaster.beforeDocumentSaving(document);

    LOG.assertTrue(file.isValid());

    String text = document.getText();
    String lineSeparator = getLineSeparator(document, file);
    if (!lineSeparator.equals("\n")) {
      text = StringUtil.convertLineSeparators(text, lineSeparator);
    }

    Project project = ProjectLocator.getInstance().guessProjectForFile(file);
    LoadTextUtil.write(project, file, this, text, document.getModificationStamp());

    myUnsavedDocuments.remove(document);
    LOG.assertTrue(!myUnsavedDocuments.contains(document));
    myTrailingSpacesStripper.clearLineModificationFlags(document);
  }
 @Override
 public long getLastCommittedStamp(@NotNull Document document) {
   Pair<CharSequence, Long> pair = myLastCommittedTexts.get(document);
   return pair != null ? pair.second : document.getModificationStamp();
 }
 @Override
 public long getModificationStamp() {
   Document document = myDocument == null ? null : myDocument.get();
   if (document != null) return document.getModificationStamp();
   return myVirtualFile.getModificationStamp();
 }
  @Nullable("returns runnable to execute under write action in AWT to finish the commit")
  private Processor<Document> doCommit(
      @NotNull final Document document,
      @NotNull final PsiFile file,
      @NotNull ProgressIndicator indicator,
      final boolean synchronously,
      @NotNull PsiDocumentManager documentManager) {
    ((PsiDocumentManagerImpl) documentManager).clearTreeHardRef(document);
    final TextBlock textBlock = TextBlock.get(file);
    if (textBlock.isEmpty()) return null;
    final long startPsiModificationTimeStamp = file.getModificationStamp();
    final long startDocModificationTimeStamp = document.getModificationStamp();
    final FileElement myTreeElementBeingReparsedSoItWontBeCollected =
        ((PsiFileImpl) file).calcTreeElement();
    if (textBlock.isEmpty())
      return null; // if tree was just loaded above textBlock will be cleared by contentsLoaded
    final CharSequence chars = document.getCharsSequence();
    final Boolean data = document.getUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY);
    if (data != null) {
      document.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, null);
      file.putUserData(BlockSupport.DO_NOT_REPARSE_INCREMENTALLY, data);
    }
    final String oldPsiText =
        ApplicationManagerEx.getApplicationEx().isInternal()
                && !ApplicationManagerEx.getApplicationEx().isUnitTestMode()
            ? myTreeElementBeingReparsedSoItWontBeCollected.getText()
            : null;
    int startOffset;
    int endOffset;
    int lengthShift;
    if (file.getViewProvider().supportsIncrementalReparse(file.getLanguage())) {
      startOffset = textBlock.getStartOffset();
      int psiEndOffset = textBlock.getPsiEndOffset();
      endOffset = psiEndOffset;
      lengthShift = textBlock.getTextEndOffset() - psiEndOffset;
    } else {
      startOffset = 0;
      endOffset = document.getTextLength();
      lengthShift =
          document.getTextLength() - myTreeElementBeingReparsedSoItWontBeCollected.getTextLength();
    }
    assertBeforeCommit(
        document,
        file,
        textBlock,
        chars,
        oldPsiText,
        myTreeElementBeingReparsedSoItWontBeCollected);
    BlockSupport blockSupport = BlockSupport.getInstance(file.getProject());
    final DiffLog diffLog =
        blockSupport.reparseRange(file, startOffset, endOffset, lengthShift, chars, indicator);

    return new Processor<Document>() {
      @Override
      public boolean process(Document document) {
        ApplicationManager.getApplication().assertWriteAccessAllowed();
        log(
            "Finishing",
            document,
            synchronously,
            document.getModificationStamp(),
            startDocModificationTimeStamp);
        // if (file.getModificationStamp() != startPsiModificationTimeStamp) return; // optimistic
        // locking failed
        if (document.getModificationStamp() != startDocModificationTimeStamp) {
          return false; // optimistic locking failed
        }

        try {
          textBlock.performAtomically(
              new Runnable() {
                @Override
                public void run() {
                  CodeStyleManager.getInstance(file.getProject())
                      .performActionWithFormatterDisabled(
                          new Runnable() {
                            @Override
                            public void run() {
                              synchronized (PsiLock.LOCK) {
                                doActualPsiChange(file, diffLog);
                              }
                            }
                          });
                }
              });

          assertAfterCommit(
              document, file, oldPsiText, myTreeElementBeingReparsedSoItWontBeCollected);
        } finally {
          textBlock.clear();
          SmartPointerManagerImpl.synchronizePointers(file);
        }

        return true;
      }
    };
  }
  QuickEditHandler(
      Project project,
      @NotNull PsiFile injectedFile,
      final PsiFile origFile,
      Editor editor,
      QuickEditAction action) {
    myProject = project;
    myEditor = editor;
    myAction = action;
    myOrigDocument = editor.getDocument();
    Place shreds = InjectedLanguageUtil.getShreds(injectedFile);
    FileType fileType = injectedFile.getFileType();
    Language language = injectedFile.getLanguage();
    PsiLanguageInjectionHost.Shred firstShred = ContainerUtil.getFirstItem(shreds);

    PsiFileFactory factory = PsiFileFactory.getInstance(project);
    String text = InjectedLanguageManager.getInstance(project).getUnescapedText(injectedFile);
    String newFileName =
        StringUtil.notNullize(language.getDisplayName(), "Injected")
            + " Fragment "
            + "("
            + origFile.getName()
            + ":"
            + firstShred.getHost().getTextRange().getStartOffset()
            + ")"
            + "."
            + fileType.getDefaultExtension();

    // preserve \r\n as it is done in MultiHostRegistrarImpl
    myNewFile = factory.createFileFromText(newFileName, language, text, true, false);
    myNewVirtualFile = ObjectUtils.assertNotNull((LightVirtualFile) myNewFile.getVirtualFile());
    myNewVirtualFile.setOriginalFile(origFile.getVirtualFile());

    assert myNewFile != null : "PSI file is null";
    assert myNewFile.getTextLength() == myNewVirtualFile.getContent().length()
        : "PSI / Virtual file text mismatch";

    myNewVirtualFile.setOriginalFile(origFile.getVirtualFile());
    // suppress possible errors as in injected mode
    myNewFile.putUserData(
        InjectedLanguageUtil.FRANKENSTEIN_INJECTION,
        injectedFile.getUserData(InjectedLanguageUtil.FRANKENSTEIN_INJECTION));
    myNewFile.putUserData(FileContextUtil.INJECTED_IN_ELEMENT, shreds.getHostPointer());
    myNewDocument = PsiDocumentManager.getInstance(project).getDocument(myNewFile);
    assert myNewDocument != null;
    EditorActionManager.getInstance()
        .setReadonlyFragmentModificationHandler(myNewDocument, new MyQuietHandler());
    myOrigCreationStamp =
        myOrigDocument.getModificationStamp(); // store creation stamp for UNDO tracking
    myOrigDocument.addDocumentListener(this, this);
    myNewDocument.addDocumentListener(this, this);
    EditorFactory editorFactory = ObjectUtils.assertNotNull(EditorFactory.getInstance());
    // not FileEditorManager listener because of RegExp checker and alike
    editorFactory.addEditorFactoryListener(
        new EditorFactoryAdapter() {
          int useCount;

          @Override
          public void editorCreated(@NotNull EditorFactoryEvent event) {
            if (event.getEditor().getDocument() != myNewDocument) return;
            useCount++;
          }

          @Override
          public void editorReleased(@NotNull EditorFactoryEvent event) {
            if (event.getEditor().getDocument() != myNewDocument) return;
            if (--useCount > 0) return;
            if (Boolean.TRUE.equals(
                myNewVirtualFile.getUserData(FileEditorManagerImpl.CLOSING_TO_REOPEN))) return;

            Disposer.dispose(QuickEditHandler.this);
          }
        },
        this);

    if ("JAVA".equals(firstShred.getHost().getLanguage().getID())) {
      PsiLanguageInjectionHost.Shred lastShred = ContainerUtil.getLastItem(shreds);
      myAltFullRange =
          myOrigDocument.createRangeMarker(
              firstShred.getHostRangeMarker().getStartOffset(),
              lastShred.getHostRangeMarker().getEndOffset());
      myAltFullRange.setGreedyToLeft(true);
      myAltFullRange.setGreedyToRight(true);

      initGuardedBlocks(shreds);
      myInjectedFile = null;
    } else {
      initMarkers(shreds);
      myAltFullRange = null;
      myInjectedFile = injectedFile;
    }
  }
Example #15
0
    public void execute(
        final Change change,
        final FilePath filePath,
        final SLRUMap<Pair<Long, String>, List<BeforeAfter<TextRange>>> cache,
        final LineStatusTrackerManagerI lstManager) {
      try {
        myDocument = null;
        myOldDocument = documentFromRevision(change.getBeforeRevision());
        final String convertedPath = FilePathsHelper.convertPath(filePath);
        if (filePath.getVirtualFile() != null) {
          myDocument =
              FileStatus.DELETED.equals(change.getFileStatus())
                  ? new DocumentImpl("")
                  : FileDocumentManager.getInstance().getDocument(filePath.getVirtualFile());
          if (myDocument != null) {
            final List<BeforeAfter<TextRange>> cached =
                cache.get(new Pair<Long, String>(myDocument.getModificationStamp(), convertedPath));
            if (cached != null) {
              myRanges = cached;
              return;
            }
          }
        }

        if (myDocument == null) {
          myDocument = documentFromRevision(change.getAfterRevision());
          final List<BeforeAfter<TextRange>> cached =
              cache.get(new Pair<Long, String>(-1L, convertedPath));
          if (cached != null) {
            myRanges = cached;
            return;
          }
        }

        ComparisonPolicy comparisonPolicy = DiffManagerImpl.getInstanceEx().getComparisonPolicy();
        if (comparisonPolicy == null) {
          comparisonPolicy = ComparisonPolicy.DEFAULT;
        }
        final TextCompareProcessor processor = new TextCompareProcessor(comparisonPolicy);
        final List<LineFragment> lineFragments =
            processor.process(myOldDocument.getText(), myDocument.getText());
        myRanges = new ArrayList<BeforeAfter<TextRange>>(lineFragments.size());
        for (LineFragment lineFragment : lineFragments) {
          if (!lineFragment.isEqual()) {
            final TextRange oldRange = lineFragment.getRange(FragmentSide.SIDE1);
            final TextRange newRange = lineFragment.getRange(FragmentSide.SIDE2);
            int beforeBegin = myOldDocument.getLineNumber(oldRange.getStartOffset());
            int beforeEnd =
                myOldDocument.getLineNumber(
                    correctRangeEnd(oldRange.getEndOffset(), myOldDocument));
            int afterBegin = myDocument.getLineNumber(newRange.getStartOffset());
            int afterEnd =
                myDocument.getLineNumber(correctRangeEnd(newRange.getEndOffset(), myDocument));
            if (oldRange.isEmpty()) {
              beforeEnd = beforeBegin - 1;
            }
            if (newRange.isEmpty()) {
              afterEnd = afterBegin - 1;
            }
            myRanges.add(
                new BeforeAfter<TextRange>(
                    new UnfairTextRange(beforeBegin, beforeEnd),
                    new UnfairTextRange(afterBegin, afterEnd)));
          }
        }
        cache.put(
            new Pair<Long, String>(myDocument.getModificationStamp(), convertedPath),
            new ArrayList<BeforeAfter<TextRange>>(myRanges));
      } catch (VcsException e) {
        myException = e;
      } catch (FilesTooBigForDiffException e) {
        myException = new VcsException(e);
      }
    }
  @Override
  public Collection<LineExtensionInfo> getLineExtensions(
      @NotNull Project project, @NotNull VirtualFile file, int lineNumber) {
    if (!Registry.is("ide.debugger.inline")) {
      return null;
    }

    final Map<Pair<VirtualFile, Integer>, Set<XValueNodeImpl>> map =
        project.getUserData(XVariablesView.DEBUG_VARIABLES);
    final Map<VirtualFile, Long> timestamps =
        project.getUserData(XVariablesView.DEBUG_VARIABLES_TIMESTAMPS);
    final Document doc = FileDocumentManager.getInstance().getDocument(file);

    if (map == null || timestamps == null || doc == null) {
      return null;
    }

    Map<Variable, VariableValue> oldValues = project.getUserData(CACHE);
    if (oldValues == null) {
      oldValues = new HashMap<Variable, VariableValue>();
      project.putUserData(CACHE, oldValues);
    }
    final Long timestamp = timestamps.get(file);
    if (timestamp == null || timestamp < doc.getModificationStamp()) {
      return null;
    }
    Set<XValueNodeImpl> values = map.get(Pair.create(file, lineNumber));
    if (values != null && !values.isEmpty()) {
      ArrayList<LineExtensionInfo> result = new ArrayList<LineExtensionInfo>();
      for (XValueNodeImpl value : values) {
        SimpleColoredText text = new SimpleColoredText();
        XValueTextRendererImpl renderer = new XValueTextRendererImpl(text);
        final XValuePresentation presentation = value.getValuePresentation();
        if (presentation == null) continue;
        try {
          if (presentation instanceof XValueCompactPresentation) {
            ((XValueCompactPresentation) presentation).renderValue(renderer, value);
          } else {
            presentation.renderValue(renderer);
          }
        } catch (Exception e) {
          continue;
        }
        final Color color = getForeground();
        final String name = value.getName();
        result.add(new LineExtensionInfo("  " + name + ": ", color, null, null, Font.PLAIN));

        Variable var = new Variable(name, lineNumber);
        VariableValue variableValue = oldValues.get(var);
        if (variableValue == null) {
          variableValue = new VariableValue(text.toString(), null, value.hashCode());
          oldValues.put(var, variableValue);
        }
        if (variableValue.valueNodeHashCode != value.hashCode()) {
          variableValue.old = variableValue.actual;
          variableValue.actual = text.toString();
          variableValue.valueNodeHashCode = value.hashCode();
        }

        if (!variableValue.isChanged()) {
          for (String s : text.getTexts()) {
            result.add(new LineExtensionInfo(s, color, null, null, Font.PLAIN));
          }
        } else {
          variableValue.produceChangedParts(result);
        }
      }
      return result;
    }

    return null;
  }
  /**
   * Inserts the <code>rBracesCount</code> of '}' at the <code>rBracesInsertOffset</code> position
   * and formats the code block.
   *
   * @param file target PSI file
   * @param editor target editor
   * @param caretOffset target caret offset
   * @param rBracesInsertOffset target position to insert
   * @param rBracesCount count of '}' to insert
   * @return true for success
   */
  protected boolean insertRBraces(
      @NotNull PsiFile file,
      @NotNull Editor editor,
      int caretOffset,
      int rBracesInsertOffset,
      int rBracesCount) {
    final Document document = editor.getDocument();
    document.insertString(rBracesInsertOffset, "\n" + StringUtil.repeatSymbol('}', rBracesCount));
    // We need to adjust indents of the text that will be moved, hence, need to insert preliminary
    // line feed.
    // Example:
    //     if (test1()) {
    //     } else {<caret> if (test2()) {
    //         foo();
    //     }
    // We insert here '\n}' after 'foo();' and have the following:
    //     if (test1()) {
    //     } else { if (test2()) {
    //         foo();
    //         }
    //     }
    // That is formatted incorrectly because line feed between 'else' and 'if' is not inserted yet
    // (whole 'if' block is indent anchor
    // to 'if' code block('{}')). So, we insert temporary line feed between 'if' and 'else', correct
    // indent and remove that temporary
    // line feed.
    document.insertString(caretOffset, "\n");

    Project project = file.getProject();
    long stamp = document.getModificationStamp();
    boolean closingBraceIndentAdjusted;
    try {
      PsiDocumentManager.getInstance(project).commitDocument(document);
      CodeStyleManager.getInstance(project)
          .adjustLineIndent(file, new TextRange(caretOffset, rBracesInsertOffset + 2));
    } catch (IncorrectOperationException e) {
      LOG.error(e);
    } finally {
      closingBraceIndentAdjusted = stamp != document.getModificationStamp();
      document.deleteString(caretOffset, caretOffset + 1);
    }

    // There is a possible case that formatter was unable to adjust line indent for the closing
    // brace (that is the case for plain text
    // document for example). Hence, we're trying to do the manually.
    if (!closingBraceIndentAdjusted) {
      int line = document.getLineNumber(rBracesInsertOffset);
      StringBuilder buffer = new StringBuilder();
      int start = document.getLineStartOffset(line);
      int end = document.getLineEndOffset(line);
      final CharSequence text = document.getCharsSequence();
      for (int i = start; i < end; i++) {
        char c = text.charAt(i);
        if (c != ' ' && c != '\t') {
          break;
        } else {
          buffer.append(c);
        }
      }
      if (buffer.length() > 0) {
        document.insertString(rBracesInsertOffset + 1, buffer);
      }
    }
    return true;
  }
  @SuppressWarnings("AssignmentToStaticFieldFromInstanceMethod")
  @Override
  public Result beforeCharTyped(
      final char charTyped, Project project, final Editor editor, PsiFile file, FileType fileType) {
    assert !inside;
    inside = true;
    try {
      final LookupImpl lookup = (LookupImpl) LookupManager.getActiveLookup(editor);
      if (lookup == null) {
        return Result.CONTINUE;
      }

      if (charTyped == ' '
          && ChooseItemReplaceAction.hasTemplatePrefix(lookup, TemplateSettings.SPACE_CHAR)) {
        return Result.CONTINUE;
      }

      final CharFilter.Result result = getLookupAction(charTyped, lookup);
      if (lookup.isLookupDisposed()) {
        return Result.CONTINUE;
      }

      if (!lookup.performGuardedChange(
          new Runnable() {
            @Override
            public void run() {
              EditorModificationUtil.deleteSelectedText(editor);
            }
          })) {
        return Result.STOP;
      }
      if (result == CharFilter.Result.ADD_TO_PREFIX) {
        Document document = editor.getDocument();
        long modificationStamp = document.getModificationStamp();

        if (!lookup.performGuardedChange(
            new Runnable() {
              @Override
              public void run() {
                EditorModificationUtil.typeInStringAtCaretHonorBlockSelection(
                    editor, String.valueOf(charTyped), true);
              }
            })) {
          return Result.STOP;
        }
        lookup.appendPrefix(charTyped);
        if (lookup.isStartCompletionWhenNothingMatches() && lookup.getItems().isEmpty()) {
          final CompletionProgressIndicator completion =
              CompletionServiceImpl.getCompletionService().getCurrentCompletion();
          if (completion != null) {
            completion.scheduleRestart();
          } else {
            AutoPopupController.getInstance(editor.getProject()).scheduleAutoPopup(editor, null);
          }
        }

        AutoHardWrapHandler.getInstance()
            .wrapLineIfNecessary(
                editor,
                DataManager.getInstance().getDataContext(editor.getContentComponent()),
                modificationStamp);

        final CompletionProgressIndicator completion =
            CompletionServiceImpl.getCompletionService().getCurrentCompletion();
        if (completion != null) {
          completion.prefixUpdated();
        }
        return Result.STOP;
      }

      if (result == CharFilter.Result.SELECT_ITEM_AND_FINISH_LOOKUP && lookup.isFocused()) {
        LookupElement item = lookup.getCurrentItem();
        if (item != null) {
          if (completeTillTypedCharOccurrence(charTyped, lookup, item)) {
            return Result.STOP;
          }

          inside = false;
          ((CommandProcessorEx) CommandProcessor.getInstance()).enterModal();
          try {
            finishLookup(charTyped, lookup);
          } finally {
            ((CommandProcessorEx) CommandProcessor.getInstance()).leaveModal();
          }
          return Result.STOP;
        }
      }

      lookup.hide();
      TypedHandler.autoPopupCompletion(editor, charTyped, project);
      return Result.CONTINUE;
    } finally {
      inside = false;
    }
  }