private static void reportInconsistentLength(
     PsiFile file, CharSequence newFileText, ASTNode node, int start, int end) {
   String message =
       "Index out of bounds: type="
           + node.getElementType()
           + "; file="
           + file
           + "; file.class="
           + file.getClass()
           + "; start="
           + start
           + "; end="
           + end
           + "; length="
           + node.getTextLength();
   String newTextBefore = newFileText.subSequence(0, start).toString();
   String oldTextBefore = file.getText().subSequence(0, start).toString();
   if (oldTextBefore.equals(newTextBefore)) {
     message += "; oldTextBefore==newTextBefore";
   }
   LOG.error(
       message,
       new Attachment(file.getName() + "_oldNodeText.txt", node.getText()),
       new Attachment(file.getName() + "_oldFileText.txt", file.getText()),
       new Attachment(file.getName() + "_newFileText.txt", newFileText.toString()));
 }
  private static void autoImport(final PsiFile file, int offset, final Editor editor) {
    final CharSequence text = editor.getDocument().getCharsSequence();
    while (offset > 0 && Character.isJavaIdentifierPart(text.charAt(offset))) offset--;
    if (offset <= 0) return;

    while (offset > 0 && Character.isWhitespace(text.charAt(offset))) offset--;
    if (offset <= 0 || text.charAt(offset) != '.') return;

    offset--;

    while (offset > 0 && Character.isWhitespace(text.charAt(offset))) offset--;
    if (offset <= 0) return;

    PsiJavaCodeReferenceElement element =
        extractReference(
            PsiTreeUtil.findElementOfClassAtOffset(file, offset, PsiExpression.class, false));
    if (element == null) return;

    while (true) {
      final PsiJavaCodeReferenceElement qualifier = extractReference(element.getQualifier());
      if (qualifier == null) break;

      element = qualifier;
    }
    if (!(element.getParent() instanceof PsiMethodCallExpression)
        && element.multiResolve(true).length == 0) {
      new ImportClassFix(element).doFix(editor, false, false);
    }
  }
  public void commentRange(
      int startOffset,
      int endOffset,
      String commentPrefix,
      String commentSuffix,
      Commenter commenter) {
    final CharSequence chars = myDocument.getCharsSequence();
    LogicalPosition caretPosition = myCaret.getLogicalPosition();

    if (startOffset == 0 || chars.charAt(startOffset - 1) == '\n') {
      if (endOffset == myDocument.getTextLength()
          || endOffset > 0 && chars.charAt(endOffset - 1) == '\n') {
        CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(myProject);
        CommonCodeStyleSettings settings =
            CodeStyleSettingsManager.getSettings(myProject).getCommonSettings(myFile.getLanguage());
        String space;
        if (!settings.BLOCK_COMMENT_AT_FIRST_COLUMN) {
          final FileType fileType = myFile.getFileType();
          int line1 = myEditor.offsetToLogicalPosition(startOffset).line;
          int line2 = myEditor.offsetToLogicalPosition(endOffset - 1).line;
          Indent minIndent =
              CommentUtil.getMinLineIndent(myProject, myDocument, line1, line2, fileType);
          if (minIndent == null) {
            minIndent = codeStyleManager.zeroIndent();
          }
          space = codeStyleManager.fillIndent(minIndent, fileType);
        } else {
          space = "";
        }
        final StringBuilder nestingPrefix = new StringBuilder(space).append(commentPrefix);
        if (!commentPrefix.endsWith("\n")) {
          nestingPrefix.append("\n");
        }
        final StringBuilder nestingSuffix = new StringBuilder(space);
        nestingSuffix.append(
            commentSuffix.startsWith("\n") ? commentSuffix.substring(1) : commentSuffix);
        nestingSuffix.append("\n");
        TextRange range =
            insertNestedComments(
                startOffset,
                endOffset,
                nestingPrefix.toString(),
                nestingSuffix.toString(),
                commenter);
        myCaret.setSelection(range.getStartOffset(), range.getEndOffset());
        LogicalPosition pos = new LogicalPosition(caretPosition.line + 1, caretPosition.column);
        myCaret.moveToLogicalPosition(pos);
        return;
      }
    }

    TextRange range =
        insertNestedComments(startOffset, endOffset, commentPrefix, commentSuffix, commenter);
    myCaret.setSelection(range.getStartOffset(), range.getEndOffset());
    LogicalPosition pos =
        new LogicalPosition(caretPosition.line, caretPosition.column + commentPrefix.length());
    myCaret.moveToLogicalPosition(pos);
  }
  @NotNull
  private static DiffLog makeFullParse(
      @NotNull PsiFileImpl fileImpl,
      @NotNull CharSequence newFileText,
      @NotNull ProgressIndicator indicator,
      @NotNull CharSequence lastCommittedText) {
    if (fileImpl instanceof PsiCodeFragment) {
      FileElement parent = fileImpl.getTreeElement();
      final FileElement holderElement =
          new DummyHolder(fileImpl.getManager(), null).getTreeElement();
      holderElement.rawAddChildren(
          fileImpl.createContentLeafElement(
              holderElement.getCharTable().intern(newFileText, 0, newFileText.length())));
      DiffLog diffLog = new DiffLog();
      diffLog.appendReplaceFileElement(parent, (FileElement) holderElement.getFirstChildNode());

      return diffLog;
    } else {
      FileViewProvider viewProvider = fileImpl.getViewProvider();
      viewProvider.getLanguages();
      FileType fileType = viewProvider.getVirtualFile().getFileType();
      String fileName = fileImpl.getName();
      final LightVirtualFile lightFile =
          new LightVirtualFile(
              fileName,
              fileType,
              newFileText,
              viewProvider.getVirtualFile().getCharset(),
              fileImpl.getViewProvider().getModificationStamp());
      lightFile.setOriginalFile(viewProvider.getVirtualFile());

      FileViewProvider copy = viewProvider.createCopy(lightFile);
      if (copy.isEventSystemEnabled()) {
        throw new AssertionError(
            "Copied view provider must be non-physical for reparse to deliver correct events: "
                + viewProvider);
      }
      copy.getLanguages();
      SingleRootFileViewProvider.doNotCheckFileSizeLimit(
          lightFile); // optimization: do not convert file contents to bytes to determine if we
                      // should codeinsight it
      PsiFileImpl newFile = getFileCopy(fileImpl, copy);

      newFile.setOriginalFile(fileImpl);

      final FileElement newFileElement = (FileElement) newFile.getNode();
      final FileElement oldFileElement = (FileElement) fileImpl.getNode();
      if (!lastCommittedText.toString().equals(oldFileElement.getText())) {
        throw new IncorrectOperationException();
      }
      DiffLog diffLog =
          mergeTrees(fileImpl, oldFileElement, newFileElement, indicator, lastCommittedText);

      ((PsiManagerEx) fileImpl.getManager()).getFileManager().setViewProvider(lightFile, null);
      return diffLog;
    }
  }
 private static int getNewIndent(final PsiFile psiFile, final int firstWhitespace) {
   final Document document = psiFile.getViewProvider().getDocument();
   final int startOffset = document.getLineStartOffset(document.getLineNumber(firstWhitespace));
   int endOffset = startOffset;
   final CharSequence charsSequence = document.getCharsSequence();
   while (Character.isWhitespace(charsSequence.charAt(endOffset++))) ;
   final String newIndentStr = charsSequence.subSequence(startOffset, endOffset - 1).toString();
   return IndentHelperImpl.getIndent(
       psiFile.getProject(), psiFile.getFileType(), newIndentStr, true);
 }
  private static void highlightTodos(
      @NotNull PsiFile file,
      @NotNull CharSequence text,
      int startOffset,
      int endOffset,
      @NotNull ProgressIndicator progress,
      @NotNull ProperTextRange priorityRange,
      @NotNull Collection<HighlightInfo> result,
      @NotNull Collection<HighlightInfo> outsideResult) {
    PsiSearchHelper helper = PsiSearchHelper.SERVICE.getInstance(file.getProject());
    TodoItem[] todoItems = helper.findTodoItems(file, startOffset, endOffset);
    if (todoItems.length == 0) return;

    for (TodoItem todoItem : todoItems) {
      progress.checkCanceled();
      TextRange range = todoItem.getTextRange();
      String description =
          text.subSequence(range.getStartOffset(), range.getEndOffset()).toString();
      TextAttributes attributes = todoItem.getPattern().getAttributes().getTextAttributes();
      HighlightInfo info =
          HighlightInfo.createHighlightInfo(
              HighlightInfoType.TODO, range, description, description, attributes);
      assert info != null;
      if (priorityRange.containsRange(info.getStartOffset(), info.getEndOffset())) {
        result.add(info);
      } else {
        outsideResult.add(info);
      }
    }
  }
 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 RangeMarker insertTemporary(
     final int endOffset, final Document document, final String temporary) {
   final CharSequence chars = document.getCharsSequence();
   final int length = chars.length();
   final RangeMarker toDelete;
   if (endOffset < length && Character.isJavaIdentifierPart(chars.charAt(endOffset))) {
     document.insertString(endOffset, temporary);
     toDelete = document.createRangeMarker(endOffset, endOffset + 1);
   } else if (endOffset >= length) {
     toDelete = document.createRangeMarker(length, length);
   } else {
     toDelete = document.createRangeMarker(endOffset, endOffset);
   }
   toDelete.setGreedyToLeft(true);
   toDelete.setGreedyToRight(true);
   return toDelete;
 }
    private static void removeTrailingSpaces(final Document document, final int startOffset) {
      int endOffset = startOffset;

      final CharSequence charsSequence = document.getCharsSequence();

      for (int i = startOffset; i < charsSequence.length(); i++) {
        final char c = charsSequence.charAt(i);
        endOffset = i;
        if (c == '\n') {
          break;
        }
        if (c != ' ' && c != '\t') {
          return;
        }
      }

      document.deleteString(startOffset, endOffset);
    }
예제 #10
0
  private static void autoImport(
      @NotNull final PsiFile file, int offset, @NotNull final Editor editor) {
    final CharSequence text = editor.getDocument().getCharsSequence();
    while (offset > 0 && Character.isJavaIdentifierPart(text.charAt(offset))) offset--;
    if (offset <= 0) return;

    while (offset > 0 && Character.isWhitespace(text.charAt(offset))) offset--;
    if (offset <= 0 || text.charAt(offset) != '.') return;

    offset--;

    while (offset > 0 && Character.isWhitespace(text.charAt(offset))) offset--;
    if (offset <= 0) return;

    autoImportReference(
        file,
        editor,
        extractReference(
            PsiTreeUtil.findElementOfClassAtOffset(file, offset, PsiExpression.class, false)));
  }
  @Override
  public PsiDocTag findTagByName(String name) {
    if (getFirstChildNode().getElementType() == JavaDocElementType.DOC_COMMENT) {
      if (getFirstChildNode().getText().indexOf(name) < 0) return null;
    }

    for (ASTNode child = getFirstChildNode(); child != null; child = child.getTreeNext()) {
      if (child.getElementType() == DOC_TAG) {
        PsiDocTag tag = (PsiDocTag) SourceTreeToPsiMap.treeElementToPsi(child);
        final CharSequence nameText = ((LeafElement) tag.getNameElement()).getChars();

        if (nameText.length() > 0
            && nameText.charAt(0) == '@'
            && CharArrayUtil.regionMatches(nameText, 1, name)) {
          return tag;
        }
      }
    }

    return null;
  }
    @Nullable
    private PsiComment createComment(final CharSequence buffer, final CodeInsightSettings settings)
        throws IncorrectOperationException {
      myDocument.insertString(myOffset, buffer);

      PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
      CodeStyleManager.getInstance(getProject())
          .adjustLineIndent(myFile, myOffset + buffer.length() - 2);

      PsiComment comment =
          PsiTreeUtil.getNonStrictParentOfType(myFile.findElementAt(myOffset), PsiComment.class);

      comment = createJavaDocStub(settings, comment, getProject());
      if (comment == null) {
        return null;
      }

      CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(getProject());
      CodeStyleSettings codeStyleSettings = CodeStyleSettingsManager.getSettings(getProject());
      boolean old = codeStyleSettings.ENABLE_JAVADOC_FORMATTING;
      codeStyleSettings.ENABLE_JAVADOC_FORMATTING = false;

      try {
        comment = (PsiComment) codeStyleManager.reformat(comment);
      } finally {
        codeStyleSettings.ENABLE_JAVADOC_FORMATTING = old;
      }
      PsiElement next = comment.getNextSibling();
      if (next == null && comment.getParent().getClass() == comment.getClass()) {
        next =
            comment
                .getParent()
                .getNextSibling(); // expanding chameleon comment produces comment under comment
      }
      if (next != null) {
        next =
            myFile.findElementAt(
                next.getTextRange().getStartOffset()); // maybe switch to another tree
      }
      if (next != null
          && (!FormatterUtil.containsWhiteSpacesOnly(next.getNode())
              || !next.getText().contains(LINE_SEPARATOR))) {
        int lineBreakOffset = comment.getTextRange().getEndOffset();
        myDocument.insertString(lineBreakOffset, LINE_SEPARATOR);
        PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
        codeStyleManager.adjustLineIndent(myFile, lineBreakOffset + 1);
        comment =
            PsiTreeUtil.getNonStrictParentOfType(myFile.findElementAt(myOffset), PsiComment.class);
      }
      return comment;
    }
예제 #13
0
  @Nullable
  private static String parseAttributeValue(final Lexer lexer, CharSequence data) {
    lexer.advance();
    IElementType tokenType = lexer.getTokenType();
    if (XmlElementType.XML_EQ == tokenType) {
      lexer.advance();
      tokenType = lexer.getTokenType();

      if (tokenType == XmlElementType.XML_ATTRIBUTE_VALUE_START_DELIMITER) {
        lexer.advance();
        tokenType = lexer.getTokenType();

        if (XmlElementType.XML_ATTRIBUTE_VALUE_TOKEN == tokenType) {
          return data.subSequence(lexer.getTokenStart(), lexer.getTokenEnd()).toString();
        }
      } else if (tokenType != XmlTokenType.XML_TAG_END
          && tokenType != XmlTokenType.XML_EMPTY_ELEMENT_END) {
        return data.subSequence(lexer.getTokenStart(), lexer.getTokenEnd()).toString();
      }
    }

    return null;
  }
  @Override
  @NotNull
  public DiffLog reparseRange(
      @NotNull final PsiFile file,
      @NotNull TextRange changedPsiRange,
      @NotNull final CharSequence newFileText,
      @NotNull final ProgressIndicator indicator) {
    final PsiFileImpl fileImpl = (PsiFileImpl) file;

    final Couple<ASTNode> reparseableRoots =
        findReparseableRoots(fileImpl, changedPsiRange, newFileText);
    return reparseableRoots != null
        ? mergeTrees(fileImpl, reparseableRoots.first, reparseableRoots.second, indicator)
        : makeFullParse(
            fileImpl.getTreeElement(), newFileText, newFileText.length(), fileImpl, indicator);
  }
 private TextRange expandRange(int delOffset1, int delOffset2) {
   CharSequence chars = myDocument.getCharsSequence();
   int offset1 = CharArrayUtil.shiftBackward(chars, delOffset1 - 1, " \t");
   if (offset1 < 0 || chars.charAt(offset1) == '\n' || chars.charAt(offset1) == '\r') {
     int offset2 = CharArrayUtil.shiftForward(chars, delOffset2, " \t");
     if (offset2 == myDocument.getTextLength()
         || chars.charAt(offset2) == '\r'
         || chars.charAt(offset2) == '\n') {
       delOffset1 = offset1 + 1;
       if (offset2 < myDocument.getTextLength()) {
         delOffset2 = offset2 + 1;
         if (chars.charAt(offset2) == '\r'
             && offset2 + 1 < myDocument.getTextLength()
             && chars.charAt(offset2 + 1) == '\n') {
           delOffset2++;
         }
       }
     }
   }
   return new TextRange(delOffset1, delOffset2);
 }
  /**
   * This method searches ast node that could be reparsed incrementally and returns pair of target
   * reparseable node and new replacement node. Returns null if there is no any chance to make
   * incremental parsing.
   */
  @Nullable
  public Couple<ASTNode> findReparseableRoots(
      @NotNull PsiFileImpl file,
      @NotNull TextRange changedPsiRange,
      @NotNull CharSequence newFileText) {
    Project project = file.getProject();
    final FileElement fileElement = file.getTreeElement();
    final CharTable charTable = fileElement.getCharTable();
    int lengthShift = newFileText.length() - fileElement.getTextLength();

    if (fileElement.getElementType() instanceof ITemplateDataElementType || isTooDeep(file)) {
      // unable to perform incremental reparse for template data in JSP, or in exceptionally deep
      // trees
      return null;
    }

    final ASTNode leafAtStart =
        fileElement.findLeafElementAt(Math.max(0, changedPsiRange.getStartOffset() - 1));
    final ASTNode leafAtEnd =
        fileElement.findLeafElementAt(
            Math.min(changedPsiRange.getEndOffset(), fileElement.getTextLength() - 1));
    ASTNode node =
        leafAtStart != null && leafAtEnd != null
            ? TreeUtil.findCommonParent(leafAtStart, leafAtEnd)
            : fileElement;
    Language baseLanguage = file.getViewProvider().getBaseLanguage();

    while (node != null && !(node instanceof FileElement)) {
      IElementType elementType = node.getElementType();
      if (elementType instanceof IReparseableElementType) {
        final TextRange textRange = node.getTextRange();
        final IReparseableElementType reparseable = (IReparseableElementType) elementType;

        if (baseLanguage.isKindOf(reparseable.getLanguage())
            && textRange.getLength() + lengthShift > 0) {
          final int start = textRange.getStartOffset();
          final int end = start + textRange.getLength() + lengthShift;
          if (end > newFileText.length()) {
            reportInconsistentLength(file, newFileText, node, start, end);
            break;
          }

          CharSequence newTextStr = newFileText.subSequence(start, end);

          if (reparseable.isParsable(node.getTreeParent(), newTextStr, baseLanguage, project)) {
            ASTNode chameleon = reparseable.createNode(newTextStr);
            if (chameleon != null) {
              DummyHolder holder =
                  DummyHolderFactory.createHolder(
                      file.getManager(), null, node.getPsi(), charTable);
              holder.getTreeElement().rawAddChildren((TreeElement) chameleon);

              if (holder.getTextLength() != newTextStr.length()) {
                String details =
                    ApplicationManager.getApplication().isInternal()
                        ? "text=" + newTextStr + "; treeText=" + holder.getText() + ";"
                        : "";
                LOG.error("Inconsistent reparse: " + details + " type=" + elementType);
              }

              return Couple.of(node, chameleon);
            }
          }
        }
      }
      node = node.getTreeParent();
    }
    return null;
  }
  public static boolean checkConsistency(PsiFile psiFile, Document document) {
    // todo hack
    if (psiFile.getVirtualFile() == null) return true;

    CharSequence editorText = document.getCharsSequence();
    int documentLength = document.getTextLength();
    if (psiFile.textMatches(editorText)) {
      LOG.assertTrue(psiFile.getTextLength() == documentLength);
      return true;
    }

    char[] fileText = psiFile.textToCharArray();
    @SuppressWarnings({"NonConstantStringShouldBeStringBuffer"})
    @NonNls
    String error =
        "File '"
            + psiFile.getName()
            + "' text mismatch after reparse. "
            + "File length="
            + fileText.length
            + "; Doc length="
            + documentLength
            + "\n";
    int i = 0;
    for (; i < documentLength; i++) {
      if (i >= fileText.length) {
        error += "editorText.length > psiText.length i=" + i + "\n";
        break;
      }
      if (i >= editorText.length()) {
        error += "editorText.length > psiText.length i=" + i + "\n";
        break;
      }
      if (editorText.charAt(i) != fileText[i]) {
        error += "first unequal char i=" + i + "\n";
        break;
      }
    }
    // error += "*********************************************" + "\n";
    // if (i <= 500){
    //  error += "Equal part:" + editorText.subSequence(0, i) + "\n";
    // }
    // else{
    //  error += "Equal part start:\n" + editorText.subSequence(0, 200) + "\n";
    //  error += "................................................" + "\n";
    //  error += "................................................" + "\n";
    //  error += "................................................" + "\n";
    //  error += "Equal part end:\n" + editorText.subSequence(i - 200, i) + "\n";
    // }
    error += "*********************************************" + "\n";
    error +=
        "Editor Text tail:("
            + (documentLength - i)
            + ")\n"; // + editorText.subSequence(i, Math.min(i + 300, documentLength)) + "\n";
    error += "*********************************************" + "\n";
    error += "Psi Text tail:(" + (fileText.length - i) + ")\n";
    error += "*********************************************" + "\n";

    if (document instanceof DocumentWindow) {
      error += "doc: '" + document.getText() + "'\n";
      error += "psi: '" + psiFile.getText() + "'\n";
      error += "ast: '" + psiFile.getNode().getText() + "'\n";
      error += psiFile.getLanguage() + "\n";
      PsiElement context =
          InjectedLanguageManager.getInstance(psiFile.getProject()).getInjectionHost(psiFile);
      if (context != null) {
        error += "context: " + context + "; text: '" + context.getText() + "'\n";
        error += "context file: " + context.getContainingFile() + "\n";
      }
      error +=
          "document window ranges: "
              + Arrays.asList(((DocumentWindow) document).getHostRanges())
              + "\n";
    }
    LOG.error(error);
    // document.replaceString(0, documentLength, psiFile.getText());
    return false;
  }
    @Override
    public void run() {
      CaretModel caretModel = myEditor.getCaretModel();
      try {
        final CharSequence chars = myDocument.getCharsSequence();
        int i = CharArrayUtil.shiftBackwardUntil(chars, myOffset - 1, LINE_SEPARATOR) - 1;
        i = CharArrayUtil.shiftBackwardUntil(chars, i, LINE_SEPARATOR) + 1;
        if (i < 0) i = 0;
        int lineStart = CharArrayUtil.shiftForward(chars, i, " \t");
        CodeDocumentationUtil.CommentContext commentContext =
            CodeDocumentationUtil.tryParseCommentContext(myFile, chars, myOffset, lineStart);

        PsiDocumentManager psiDocumentManager = PsiDocumentManager.getInstance(getProject());
        if (commentContext.docStart) {
          PsiElement element = myFile.findElementAt(commentContext.lineStart);
          final String text = element.getText();
          final PsiElement parent = element.getParent();

          if (text.equals(commentContext.commenter.getDocumentationCommentPrefix())
                  && isDocComment(parent, commentContext.commenter)
              || text.startsWith(commentContext.commenter.getDocumentationCommentPrefix())
                  && element instanceof PsiComment) {
            PsiComment comment =
                isDocComment(parent, commentContext.commenter)
                    ? (PsiComment) parent
                    : (PsiComment) element;
            int commentEnd = comment.getTextRange().getEndOffset();

            if (myOffset >= commentEnd) {
              commentContext.docStart = false;
            } else {
              if (isCommentComplete(comment, commentContext.commenter, myEditor)) {
                if (myOffset >= commentEnd) {
                  commentContext.docAsterisk = false;
                  commentContext.docStart = false;
                } else {
                  commentContext.docAsterisk = true;
                  commentContext.docStart = false;
                }
              } else {
                generateJavadoc(commentContext.commenter);
              }
            }
          } else {
            commentContext.docStart = false;
          }
        } else if (commentContext.cStyleStart) {
          PsiElement element = myFile.findElementAt(commentContext.lineStart);
          if (element instanceof PsiComment
              && commentContext.commenter.getBlockCommentTokenType()
                  == ((PsiComment) element).getTokenType()) {
            final PsiComment comment = (PsiComment) element;
            int commentEnd = comment.getTextRange().getEndOffset();
            if (myOffset >= commentEnd && myOffset < myFile.getTextRange().getEndOffset()) {
              commentContext.docStart = false;
            } else {
              if (isCommentComplete(comment, commentContext.commenter, myEditor)) {
                if (myOffset >= commentEnd) {
                  commentContext.docAsterisk = false;
                  commentContext.docStart = false;
                } else {
                  commentContext.docAsterisk = true;
                  commentContext.docStart = false;
                }
              } else {
                final int currentEndOfLine = CharArrayUtil.shiftForwardUntil(chars, myOffset, "\n");
                myDocument.insertString(
                    currentEndOfLine, " " + commentContext.commenter.getBlockCommentSuffix());
                int lstart = CharArrayUtil.shiftBackwardUntil(chars, myOffset, "\n");
                myDocument.insertString(currentEndOfLine, chars.subSequence(lstart, myOffset));
                psiDocumentManager.commitDocument(myDocument);
              }
            }
          } else {
            commentContext.docStart = false;
          }
        }

        String indentInsideJavadoc = null;
        if (myOffset < myDocument.getTextLength()) {
          final int line = myDocument.getLineNumber(myOffset);
          if (line > 0 && (commentContext.docAsterisk || commentContext.docStart)) {
            indentInsideJavadoc =
                CodeDocumentationUtil.getIndentInsideJavadoc(
                    myDocument, myDocument.getLineStartOffset(line - 1));
          }
        }

        if (commentContext.docAsterisk) {
          commentContext.docAsterisk =
              insertDocAsterisk(
                  commentContext.lineStart,
                  commentContext.docAsterisk,
                  !StringUtil.isEmpty(indentInsideJavadoc),
                  commentContext.commenter);
        }

        boolean docIndentApplied = false;
        CodeInsightSettings codeInsightSettings = CodeInsightSettings.getInstance();
        if (codeInsightSettings.SMART_INDENT_ON_ENTER
            || myForceIndent
            || commentContext.docStart
            || commentContext.docAsterisk
            || commentContext.slashSlash) {
          final CodeStyleManager codeStyleManager = CodeStyleManager.getInstance(getProject());
          myOffset = codeStyleManager.adjustLineIndent(myFile, myOffset);
          psiDocumentManager.commitAllDocuments();

          if (!StringUtil.isEmpty(indentInsideJavadoc) && myOffset < myDocument.getTextLength()) {
            myDocument.insertString(myOffset + 1, indentInsideJavadoc);
            myOffset += indentInsideJavadoc.length();
            docIndentApplied = true;
          }

          if (myForceIndent && indentInsideJavadoc != null) {
            int indentSize =
                CodeStyleSettingsManager.getSettings(myProject).getIndentSize(myFile.getFileType());
            myDocument.insertString(myOffset + 1, StringUtil.repeatSymbol(' ', indentSize));
            myCaretAdvance += indentSize;
          }
        }

        if ((commentContext.docAsterisk || commentContext.docStart || commentContext.slashSlash)
            && !docIndentApplied) {
          if (myInsertSpace) {
            if (myOffset == myDocument.getTextLength()) {
              myDocument.insertString(myOffset, " ");
            }
            myDocument.insertString(myOffset + 1, " ");
          }

          final char c = myDocument.getCharsSequence().charAt(myOffset);
          if (c != '\n') {
            myOffset += 1;
          }
        }

        if ((commentContext.docAsterisk || commentContext.slashSlash) && !commentContext.docStart) {
          myCaretAdvance +=
              commentContext.slashSlash
                  ? commentContext.commenter.getLineCommentPrefix().length()
                  : 1;
        }
      } catch (IncorrectOperationException e) {
        LOG.error(e);
      }

      myOffset = Math.min(myOffset, myDocument.getTextLength());
      caretModel.moveToOffset(myOffset);
      myEditor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE);
      myEditor.getSelectionModel().removeSelection();
      if (myCaretAdvance != 0) {
        LogicalPosition caretPosition = caretModel.getLogicalPosition();
        LogicalPosition pos =
            new LogicalPosition(caretPosition.line, caretPosition.column + myCaretAdvance);
        caretModel.moveToLogicalPosition(pos);
      }
    }
  private void executeWriteActionInner(Editor editor, DataContext dataContext, Project project) {
    CodeInsightSettings settings = CodeInsightSettings.getInstance();
    if (project == null) {
      myOriginalHandler.execute(editor, dataContext);
      return;
    }
    final Document document = editor.getDocument();
    final PsiFile file = PsiUtilBase.getPsiFileInEditor(editor, project);

    if (file == null) {
      myOriginalHandler.execute(editor, dataContext);
      return;
    }

    CommandProcessor.getInstance()
        .setCurrentCommandName(CodeInsightBundle.message("command.name.typing"));

    EditorModificationUtil.deleteSelectedText(editor);

    int caretOffset = editor.getCaretModel().getOffset();
    CharSequence text = document.getCharsSequence();
    int length = document.getTextLength();
    if (caretOffset < length && text.charAt(caretOffset) != '\n') {
      int offset1 = CharArrayUtil.shiftBackward(text, caretOffset, " \t");
      if (offset1 < 0 || text.charAt(offset1) == '\n') {
        int offset2 = CharArrayUtil.shiftForward(text, offset1 + 1, " \t");
        boolean isEmptyLine = offset2 >= length || text.charAt(offset2) == '\n';
        if (!isEmptyLine) { // we are in leading spaces of a non-empty line
          myOriginalHandler.execute(editor, dataContext);
          return;
        }
      }
    }

    final PsiDocumentManager documentManager = PsiDocumentManager.getInstance(project);
    documentManager.commitDocument(document);

    boolean forceIndent = false;
    boolean forceSkipIndent = false;
    Ref<Integer> caretOffsetRef = new Ref<Integer>(caretOffset);
    Ref<Integer> caretAdvanceRef = new Ref<Integer>(0);

    final EnterHandlerDelegate[] delegates = Extensions.getExtensions(EnterHandlerDelegate.EP_NAME);
    for (EnterHandlerDelegate delegate : delegates) {
      EnterHandlerDelegate.Result result =
          delegate.preprocessEnter(
              file, editor, caretOffsetRef, caretAdvanceRef, dataContext, myOriginalHandler);
      if (caretOffsetRef.get() > document.getTextLength()) {
        throw new AssertionError("Wrong caret offset change by " + delegate);
      }

      if (result == EnterHandlerDelegate.Result.Stop) {
        return;
      }
      if (result != EnterHandlerDelegate.Result.Continue) {
        if (result == EnterHandlerDelegate.Result.DefaultForceIndent) {
          forceIndent = true;
        } else if (result == EnterHandlerDelegate.Result.DefaultSkipIndent) {
          forceSkipIndent = true;
        }
        break;
      }
    }

    text = document.getCharsSequence(); // update after changes done in preprocessEnter()
    caretOffset = caretOffsetRef.get().intValue();
    boolean isFirstColumn = caretOffset == 0 || text.charAt(caretOffset - 1) == '\n';
    final boolean insertSpace =
        !isFirstColumn
            && !(caretOffset >= text.length()
                || text.charAt(caretOffset) == ' '
                || text.charAt(caretOffset) == '\t');
    editor.getCaretModel().moveToOffset(caretOffset);
    myOriginalHandler.execute(editor, dataContext);
    if (!editor.isInsertMode() || forceSkipIndent) {
      return;
    }

    if (settings.SMART_INDENT_ON_ENTER || forceIndent) {
      caretOffset += 1;
      caretOffset =
          CharArrayUtil.shiftForward(editor.getDocument().getCharsSequence(), caretOffset, " \t");
    } else {
      caretOffset = editor.getCaretModel().getOffset();
    }

    documentManager.commitAllDocuments();
    final DoEnterAction action =
        new DoEnterAction(
            file,
            editor,
            document,
            dataContext,
            caretOffset,
            !insertSpace,
            caretAdvanceRef.get(),
            project);
    action.setForceIndent(forceIndent);
    action.run();
    documentManager.commitDocument(document);
    for (EnterHandlerDelegate delegate : delegates) {
      if (delegate.postProcessEnter(file, editor, dataContext)
          == EnterHandlerDelegate.Result.Stop) {
        break;
      }
    }
    documentManager.commitDocument(document);
  }
  /**
   * There is a possible case that target code block already starts with the empty line:
   *
   * <pre>
   *   void test(int i) {
   *     if (i > 1[caret]) {
   *
   *     }
   *   }
   * </pre>
   *
   * We want just move caret to correct position at that empty line without creating additional
   * empty line then.
   *
   * @param editor target editor
   * @param codeBlock target code block to which new empty line is going to be inserted
   * @param element target element under caret
   * @return <code>true</code> if it was found out that the given code block starts with the empty
   *     line and caret is pointed to correct position there, i.e. no additional processing is
   *     required; <code>false</code> otherwise
   */
  private static boolean processExistingBlankLine(
      @NotNull Editor editor, @Nullable PsiCodeBlock codeBlock, @Nullable PsiElement element) {
    PsiWhiteSpace whiteSpace = null;
    if (codeBlock == null) {
      if (element != null) {
        final PsiElement next = PsiTreeUtil.nextLeaf(element);
        if (next instanceof PsiWhiteSpace) {
          whiteSpace = (PsiWhiteSpace) next;
        }
      }
    } else {
      whiteSpace = PsiTreeUtil.findChildOfType(codeBlock, PsiWhiteSpace.class);
      if (whiteSpace == null) {
        return false;
      }

      PsiElement lbraceCandidate = whiteSpace.getPrevSibling();
      if (lbraceCandidate == null) {
        return false;
      }

      ASTNode node = lbraceCandidate.getNode();
      if (node == null || node.getElementType() != JavaTokenType.LBRACE) {
        return false;
      }
    }

    if (whiteSpace == null) {
      return false;
    }

    final TextRange textRange = whiteSpace.getTextRange();
    final Document document = editor.getDocument();
    final CharSequence whiteSpaceText =
        document
            .getCharsSequence()
            .subSequence(textRange.getStartOffset(), textRange.getEndOffset());
    if (StringUtil.countNewLines(whiteSpaceText) < 2) {
      return false;
    }

    int i = CharArrayUtil.shiftForward(whiteSpaceText, 0, " \t");
    if (i >= whiteSpaceText.length() - 1) {
      assert false
          : String.format(
              "code block: %s, white space: %s",
              codeBlock == null ? "undefined" : codeBlock.getTextRange(),
              whiteSpace.getTextRange());
      return false;
    }

    editor.getCaretModel().moveToOffset(i + 1 + textRange.getStartOffset());
    EditorActionManager actionManager = EditorActionManager.getInstance();
    EditorActionHandler actionHandler =
        actionManager.getActionHandler(IdeActions.ACTION_EDITOR_MOVE_LINE_END);
    final DataContext dataContext = DataManager.getInstance().getDataContext(editor.getComponent());
    if (dataContext == null) {
      i = CharArrayUtil.shiftForwardUntil(whiteSpaceText, i, "\n");
      if (i >= whiteSpaceText.length()) {
        i = whiteSpaceText.length();
      }
      editor.getCaretModel().moveToOffset(i + textRange.getStartOffset());
    } else {
      actionHandler.execute(editor, dataContext);
    }
    return true;
  }
예제 #21
0
  private static void mapHtml(FileContent inputData, Language language, List<LinkInfo> result) {
    final Lexer original =
        HTMLLanguage.INSTANCE == language
            ? new HtmlHighlightingLexer()
            : new XHtmlHighlightingLexer();
    final Lexer lexer =
        new FilterLexer(
            original,
            new FilterLexer.Filter() {
              public boolean reject(final IElementType type) {
                return XmlElementType.XML_WHITE_SPACE == type;
              }
            });

    final CharSequence data = inputData.getContentAsText();
    lexer.start(data);

    IElementType tokenType = lexer.getTokenType();
    boolean linkTag = false;
    while (tokenType != null) {
      if (XmlElementType.XML_TAG_NAME == tokenType) {
        final String tagName =
            data.subSequence(lexer.getTokenStart(), lexer.getTokenEnd()).toString();
        linkTag = LINK.equalsIgnoreCase(tagName);
        // if (BODY_TAG.equalsIgnoreCase(tagName)) {
        //  break; // there are no LINK tags under the body
        // }
      }

      if (linkTag && XmlElementType.XML_NAME == tokenType) {
        int linkTagOffset = lexer.getTokenStart();
        String href = null;
        String type = null;
        String media = null;
        String rel = null;
        String title = null;

        while (true) {
          if (tokenType == null
              || tokenType == XmlTokenType.XML_END_TAG_START
              || tokenType == XmlTokenType.XML_EMPTY_ELEMENT_END
              || tokenType == XmlTokenType.XML_START_TAG_START) {
            break;
          }

          if (XmlElementType.XML_NAME == tokenType) {
            final String attrName =
                data.subSequence(lexer.getTokenStart(), lexer.getTokenEnd()).toString();
            if (HREF_ATTR.equalsIgnoreCase(attrName)) {
              href = parseAttributeValue(lexer, data);
            } else if (MEDIA_ATTR.equalsIgnoreCase(attrName)) {
              media = parseAttributeValue(lexer, data);
            } else if (TYPE_ATTR.equalsIgnoreCase(attrName)) {
              type = parseAttributeValue(lexer, data);
            } else if (REL_ATTR.equalsIgnoreCase(attrName)) {
              rel = parseAttributeValue(lexer, data);
            } else if (TITLE_ATTR.equalsIgnoreCase(attrName)) {
              title = parseAttributeValue(lexer, data);
            }
          }

          lexer.advance();
          tokenType = lexer.getTokenType();
        }

        addResult(result, linkTagOffset, href, media, type, rel, title, false);
      }

      lexer.advance();
      tokenType = lexer.getTokenType();
    }
  }
    public void replace(int initialStart, int length, String replace) {
      // calculating fragment
      // minimize replace
      int start = 0;
      int end = start + length;

      final int replaceLength = replace.length();
      final String chars = getText(start + initialStart, end + initialStart);
      if (chars.equals(replace)) return;

      int newStartInReplace = 0;
      int newEndInReplace = replaceLength;
      while (newStartInReplace < replaceLength
          && start < end
          && replace.charAt(newStartInReplace) == chars.charAt(start)) {
        start++;
        newStartInReplace++;
      }

      while (start < end
          && newStartInReplace < newEndInReplace
          && replace.charAt(newEndInReplace - 1) == chars.charAt(end - 1)) {
        newEndInReplace--;
        end--;
      }

      // optimization: when delete fragment from the middle of the text, prefer split at the line
      // boundaries
      if (newStartInReplace == newEndInReplace
          && start > 0
          && start < end
          && StringUtil.indexOf(chars, '\n', start, end) != -1) {
        // try to align to the line boundaries
        while (start > 0
            && newStartInReplace > 0
            && chars.charAt(start - 1) == chars.charAt(end - 1)
            && chars.charAt(end - 1) != '\n') {
          start--;
          end--;
          newStartInReplace--;
          newEndInReplace--;
        }
      }

      // [mike] dirty hack for xml:
      // make sure that deletion of <t> in: <tag><t/><tag> doesn't remove t/><
      // which is perfectly valid but invalidates range markers
      start += initialStart;
      end += initialStart;
      final CharSequence charsSequence = myDocument.getCharsSequence();
      while (start < charsSequence.length()
          && end < charsSequence.length()
          && start > 0
          && charsSequence.subSequence(start, end).toString().endsWith("><")
          && charsSequence.charAt(start - 1) == '<') {
        start--;
        newStartInReplace--;
        end--;
        newEndInReplace--;
      }

      replace = replace.substring(newStartInReplace, newEndInReplace);
      length = end - start;

      final Pair<MutableTextRange, StringBuffer> fragment = getFragmentByRange(start, length);
      final StringBuffer fragmentReplaceText = fragment.getSecond();
      final int startInFragment = start - fragment.getFirst().getStartOffset();

      // text range adjustment
      final int lengthDiff = replace.length() - length;
      final Iterator<Pair<MutableTextRange, StringBuffer>> iterator =
          myAffectedFragments.iterator();
      boolean adjust = false;
      while (iterator.hasNext()) {
        final Pair<MutableTextRange, StringBuffer> pair = iterator.next();
        if (adjust) pair.getFirst().shift(lengthDiff);
        if (pair == fragment) adjust = true;
      }

      fragmentReplaceText.replace(startInFragment, startInFragment + length, replace);
    }
  @Nullable
  private TextRange findCommentedRange(final Commenter commenter) {
    final CharSequence text = myDocument.getCharsSequence();
    final FileType fileType = myFile.getFileType();
    if (fileType instanceof CustomSyntaxTableFileType) {
      Lexer lexer =
          new CustomFileTypeLexer(((CustomSyntaxTableFileType) fileType).getSyntaxTable());
      final int caretOffset = myCaret.getOffset();
      int commentStart =
          CharArrayUtil.lastIndexOf(text, commenter.getBlockCommentPrefix(), caretOffset);
      if (commentStart == -1) return null;

      lexer.start(text, commentStart, text.length());
      if (lexer.getTokenType() == CustomHighlighterTokenType.MULTI_LINE_COMMENT
          && lexer.getTokenEnd() >= caretOffset) {
        return new TextRange(commentStart, lexer.getTokenEnd());
      }
      return null;
    }

    final String prefix;
    final String suffix;
    // Custom uncommenter is able to find commented block inside of selected text
    final String selectedText = myCaret.getSelectedText();
    if ((commenter instanceof CustomUncommenter) && selectedText != null) {
      final TextRange commentedRange =
          ((CustomUncommenter) commenter).findMaximumCommentedRange(selectedText);
      if (commentedRange == null) {
        return null;
      }
      // Uncommenter returns range relative to text start, so we need to shift it to make abosolute.
      return commentedRange.shiftRight(myCaret.getSelectionStart());
    }

    if (commenter instanceof SelfManagingCommenter) {
      SelfManagingCommenter selfManagingCommenter = (SelfManagingCommenter) commenter;

      prefix =
          selfManagingCommenter.getBlockCommentPrefix(
              myCaret.getSelectionStart(), myDocument, mySelfManagedCommenterData);
      suffix =
          selfManagingCommenter.getBlockCommentSuffix(
              myCaret.getSelectionEnd(), myDocument, mySelfManagedCommenterData);
    } else {
      prefix = trim(commenter.getBlockCommentPrefix());
      suffix = trim(commenter.getBlockCommentSuffix());
    }
    if (prefix == null || suffix == null) return null;

    TextRange commentedRange;

    if (commenter instanceof SelfManagingCommenter) {
      commentedRange =
          ((SelfManagingCommenter) commenter)
              .getBlockCommentRange(
                  myCaret.getSelectionStart(),
                  myCaret.getSelectionEnd(),
                  myDocument,
                  mySelfManagedCommenterData);
    } else {
      if (!testSelectionForNonComments()) {
        return null;
      }

      commentedRange = getSelectedComments(text, prefix, suffix);
    }
    if (commentedRange == null) {
      PsiElement comment = findCommentAtCaret();
      if (comment != null) {

        String commentText = comment.getText();
        if (commentText.startsWith(prefix) && commentText.endsWith(suffix)) {
          commentedRange = comment.getTextRange();
        }
      }
    }
    return commentedRange;
  }
    public void replace(
        int psiStart, int length, @NotNull String replace, @Nullable PsiElement replacement) {
      // calculating fragment
      // minimize replace
      int start = 0;
      int end = start + length;

      final CharSequence chars = myPsiText.subSequence(psiStart, psiStart + length);
      if (StringUtil.equals(chars, replace)) return;

      int newStartInReplace = 0;
      final int replaceLength = replace.length();
      while (newStartInReplace < replaceLength
          && start < end
          && replace.charAt(newStartInReplace) == chars.charAt(start)) {
        start++;
        newStartInReplace++;
      }

      int newEndInReplace = replaceLength;
      while (start < end
          && newStartInReplace < newEndInReplace
          && replace.charAt(newEndInReplace - 1) == chars.charAt(end - 1)) {
        newEndInReplace--;
        end--;
      }

      // increase the changed range to start and end on PSI token boundaries
      // this will help to survive smart pointers with the same boundaries
      if (replacement != null && (newStartInReplace > 0 || newEndInReplace < replaceLength)) {
        PsiElement startLeaf = replacement.findElementAt(newStartInReplace);
        PsiElement endLeaf = replacement.findElementAt(newEndInReplace - 1);
        if (startLeaf != null && endLeaf != null) {
          int leafStart =
              startLeaf.getTextRange().getStartOffset()
                  - replacement.getTextRange().getStartOffset();
          int leafEnd =
              endLeaf.getTextRange().getEndOffset() - replacement.getTextRange().getStartOffset();
          start += leafStart - newStartInReplace;
          end += leafEnd - newEndInReplace;
          newStartInReplace = leafStart;
          newEndInReplace = leafEnd;
        }
      }

      // optimization: when delete fragment from the middle of the text, prefer split at the line
      // boundaries
      if (newStartInReplace == newEndInReplace
          && start > 0
          && start < end
          && StringUtil.indexOf(chars, '\n', start, end) != -1) {
        // try to align to the line boundaries
        while (start > 0
            && newStartInReplace > 0
            && chars.charAt(start - 1) == chars.charAt(end - 1)
            && chars.charAt(end - 1) != '\n') {
          start--;
          end--;
          newStartInReplace--;
          newEndInReplace--;
        }
      }

      start += psiStart;
      end += psiStart;

      // [mike] dirty hack for xml:
      // make sure that deletion of <t> in: <tag><t/><tag> doesn't remove t/><
      // which is perfectly valid but invalidates range markers
      final CharSequence charsSequence = myPsiText;
      while (start < charsSequence.length()
          && end < charsSequence.length()
          && start > 0
          && charsSequence.subSequence(start, end).toString().endsWith("><")
          && charsSequence.charAt(start - 1) == '<') {
        start--;
        newStartInReplace--;
        end--;
        newEndInReplace--;
      }

      updateFragments(start, end, replace.substring(newStartInReplace, newEndInReplace));
    }
 @NotNull
 public GrMethod createMethodFromText(@NotNull CharSequence methodText) {
   return createMethodFromText(methodText.toString(), null);
 }
 @NotNull
 public GrExpression createExpressionFromText(@NotNull CharSequence exprText) {
   return createExpressionFromText(exprText.toString(), null);
 }