@Nullable
  public static Commenter getCommenter(
      final PsiFile file,
      final Editor editor,
      final Language lineStartLanguage,
      final Language lineEndLanguage) {

    final FileViewProvider viewProvider = file.getViewProvider();

    for (MultipleLangCommentProvider provider :
        MultipleLangCommentProvider.EP_NAME.getExtensions()) {
      if (provider.canProcess(file, viewProvider)) {
        return provider.getLineCommenter(file, editor, lineStartLanguage, lineEndLanguage);
      }
    }

    final Language fileLanguage = file.getLanguage();
    Language lang =
        lineStartLanguage == null
                || LanguageCommenters.INSTANCE.forLanguage(lineStartLanguage) == null
                || fileLanguage.getBaseLanguage()
                    == lineStartLanguage // file language is a more specific dialect of the line
            // language
            ? fileLanguage
            : lineStartLanguage;

    if (viewProvider instanceof TemplateLanguageFileViewProvider
        && lang == ((TemplateLanguageFileViewProvider) viewProvider).getTemplateDataLanguage()) {
      lang = viewProvider.getBaseLanguage();
    }

    return LanguageCommenters.INSTANCE.forLanguage(lang);
  }
Exemple #2
0
  /**
   * We want to treat comments specially in a way to skip comment prefix on line indent calculation.
   *
   * <p>Example:
   *
   * <pre>
   *   if (true) {
   *     int i1;
   * //    int i2;
   *     int i3;
   *   }
   * </pre>
   *
   * We want to use 'int i2;' start offset as the third line indent (though it has non-white space
   * comment prefix (//) at the first column.
   *
   * <p>This method tries to parse comment prefix for the language implied by the given comment
   * type. It uses {@link #NO_COMMENT_INFO_MARKER} as an indicator that that information is
   * unavailable
   *
   * @param commentType target comment type
   * @return prefix of the comment denoted by the given type if any; {@link #NO_COMMENT_INFO_MARKER}
   *     otherwise
   */
  @NotNull
  private static String getCommentPrefix(@NotNull IElementType commentType) {
    Commenter c = LanguageCommenters.INSTANCE.forLanguage(commentType.getLanguage());
    if (!(c instanceof CodeDocumentationAwareCommenter)) {
      COMMENT_PREFIXES.put(commentType, NO_COMMENT_INFO_MARKER);
      return NO_COMMENT_INFO_MARKER;
    }
    CodeDocumentationAwareCommenter commenter = (CodeDocumentationAwareCommenter) c;

    IElementType lineCommentType = commenter.getLineCommentTokenType();
    String lineCommentPrefix = commenter.getLineCommentPrefix();
    if (lineCommentType != null) {
      COMMENT_PREFIXES.put(
          lineCommentType, lineCommentPrefix == null ? NO_COMMENT_INFO_MARKER : lineCommentPrefix);
    }

    IElementType blockCommentType = commenter.getBlockCommentTokenType();
    String blockCommentPrefix = commenter.getBlockCommentPrefix();
    if (blockCommentType != null) {
      COMMENT_PREFIXES.put(
          blockCommentType,
          blockCommentPrefix == null ? NO_COMMENT_INFO_MARKER : blockCommentPrefix);
    }

    IElementType docCommentType = commenter.getDocumentationCommentTokenType();
    String docCommentPrefix = commenter.getDocumentationCommentPrefix();
    if (docCommentType != null) {
      COMMENT_PREFIXES.put(
          docCommentType, docCommentPrefix == null ? NO_COMMENT_INFO_MARKER : docCommentPrefix);
    }

    COMMENT_PREFIXES.putIfAbsent(commentType, NO_COMMENT_INFO_MARKER);
    return COMMENT_PREFIXES.get(commentType);
  }
  public static boolean isCommentTextElement(final PsiElement element) {
    final Commenter commenter = LanguageCommenters.INSTANCE.forLanguage(element.getLanguage());
    if (commenter instanceof CodeDocumentationAwareCommenterEx) {
      final CodeDocumentationAwareCommenterEx commenterEx =
          (CodeDocumentationAwareCommenterEx) commenter;
      if (commenterEx.isDocumentationCommentText(element)) return true;
      if (element instanceof PsiComment && commenterEx.isDocumentationComment((PsiComment) element))
        return false;
    }

    return isComment(element);
  }
  @Override
  public String generateDocumentationContentStub(PsiComment contextComment) {
    if (!(contextComment instanceof GrDocComment)) {
      return null;
    }

    final GrDocCommentOwner owner = GrDocCommentUtil.findDocOwner((GrDocComment) contextComment);
    if (owner == null) return null;

    Project project = contextComment.getProject();
    final CodeDocumentationAwareCommenter commenter =
        (CodeDocumentationAwareCommenter)
            LanguageCommenters.INSTANCE.forLanguage(owner.getLanguage());

    StringBuilder builder = StringBuilderSpinAllocator.alloc();
    try {
      if (owner instanceof GrMethod) {
        final GrMethod method = (GrMethod) owner;
        JavaDocumentationProvider.generateParametersTakingDocFromSuperMethods(
            project, builder, commenter, method);

        final PsiType returnType = method.getInferredReturnType();
        if ((returnType != null || method.getModifierList().hasModifierProperty(GrModifier.DEF))
            && !PsiType.VOID.equals(returnType)) {
          builder.append(
              CodeDocumentationUtil.createDocCommentLine(RETURN_TAG, project, commenter));
          builder.append(LINE_SEPARATOR);
        }

        final PsiClassType[] references = method.getThrowsList().getReferencedTypes();
        for (PsiClassType reference : references) {
          builder.append(
              CodeDocumentationUtil.createDocCommentLine(THROWS_TAG, project, commenter));
          builder.append(reference.getClassName());
          builder.append(LINE_SEPARATOR);
        }
      } else if (owner instanceof GrTypeDefinition) {
        final PsiTypeParameterList typeParameterList = ((PsiClass) owner).getTypeParameterList();
        if (typeParameterList != null) {
          JavaDocumentationProvider.createTypeParamsListComment(
              builder, project, commenter, typeParameterList);
        }
      }
      return builder.length() > 0 ? builder.toString() : null;
    } finally {
      StringBuilderSpinAllocator.dispose(builder);
    }
  }
  @Override
  public Result preprocessEnter(
      @NotNull final PsiFile file,
      @NotNull final Editor editor,
      @NotNull final Ref<Integer> caretOffsetRef,
      @NotNull final Ref<Integer> caretAdvance,
      @NotNull final DataContext dataContext,
      final EditorActionHandler originalHandler) {
    int caretOffset = caretOffsetRef.get().intValue();
    PsiElement psiAtOffset = file.findElementAt(caretOffset);
    if (psiAtOffset != null && psiAtOffset.getTextOffset() < caretOffset) {
      ASTNode token = psiAtOffset.getNode();
      Document document = editor.getDocument();
      CharSequence text = document.getText();
      final Language language = psiAtOffset.getLanguage();
      final Commenter languageCommenter = LanguageCommenters.INSTANCE.forLanguage(language);
      final CodeDocumentationAwareCommenter commenter =
          languageCommenter instanceof CodeDocumentationAwareCommenter
              ? (CodeDocumentationAwareCommenter) languageCommenter
              : null;
      if (commenter != null && token.getElementType() == commenter.getLineCommentTokenType()) {
        final int offset = CharArrayUtil.shiftForward(text, caretOffset, " \t");

        if (offset < document.getTextLength() && text.charAt(offset) != '\n') {
          String prefix = commenter.getLineCommentPrefix();
          assert prefix != null : "Line Comment type is set but Line Comment Prefix is null!";
          if (!StringUtil.startsWith(text, offset, prefix)) {
            if (text.charAt(caretOffset) != ' ' && !prefix.endsWith(" ")) {
              prefix += " ";
            }
            document.insertString(caretOffset, prefix);
            return Result.Default;
          } else {
            int afterPrefix = offset + prefix.length();
            if (afterPrefix < document.getTextLength() && text.charAt(afterPrefix) != ' ') {
              document.insertString(afterPrefix, " ");
              // caretAdvance.set(0);
            }
            caretOffsetRef.set(offset);
          }
          return Result.Default;
        }
      }
    }
    return Result.Continue;
  }
  private static void process(
      @NotNull final PsiFile file,
      @NotNull final Editor editor,
      @NotNull final Project project,
      int offset) {
    PsiElement elementAtOffset = file.findElementAt(offset);
    if (elementAtOffset == null) {
      return;
    }

    Language language = PsiUtilCore.getLanguageAtOffset(file, offset);
    final CodeDocumentationProvider docProvider;
    final DocumentationProvider langDocumentationProvider =
        LanguageDocumentation.INSTANCE.forLanguage(language);
    if (langDocumentationProvider instanceof CompositeDocumentationProvider) {
      docProvider =
          ((CompositeDocumentationProvider) langDocumentationProvider)
              .getFirstCodeDocumentationProvider();
    } else if (langDocumentationProvider instanceof CodeDocumentationProvider) {
      docProvider = (CodeDocumentationProvider) langDocumentationProvider;
    } else {
      docProvider = null;
    }
    if (docProvider == null) {
      return;
    }

    final Pair<PsiElement, PsiComment> pair = docProvider.parseContext(elementAtOffset);
    if (pair == null) {
      return;
    }

    Commenter c = LanguageCommenters.INSTANCE.forLanguage(language);
    if (!(c instanceof CodeDocumentationAwareCommenter)) {
      return;
    }
    final CodeDocumentationAwareCommenter commenter = (CodeDocumentationAwareCommenter) c;
    final Runnable task;
    if (pair.second == null || pair.second.getTextRange().isEmpty()) {
      task =
          new Runnable() {
            @Override
            public void run() {
              generateComment(pair.first, editor, docProvider, commenter, project);
            }
          };
    } else {
      final DocCommentFixer fixer = DocCommentFixer.EXTENSION.forLanguage(language);
      if (fixer == null) {
        return;
      } else {
        task =
            new Runnable() {
              @Override
              public void run() {
                fixer.fixComment(project, editor, pair.second);
              }
            };
      }
    }
    final Runnable command =
        new Runnable() {
          @Override
          public void run() {
            ApplicationManager.getApplication().runWriteAction(task);
          }
        };
    CommandProcessor.getInstance().executeCommand(project, command, "Fix documentation", null);
  }
  private void doComment() {
    myStartLine = myDocument.getLineNumber(myStartOffset);
    myEndLine = myDocument.getLineNumber(myEndOffset);

    if (myEndLine > myStartLine && myDocument.getLineStartOffset(myEndLine) == myEndOffset) {
      myEndLine--;
    }

    myStartOffsets = new int[myEndLine - myStartLine + 1];
    myEndOffsets = new int[myEndLine - myStartLine + 1];
    myCommenters = new Commenter[myEndLine - myStartLine + 1];
    myCommenterStateMap = new THashMap<SelfManagingCommenter, CommenterDataHolder>();
    CharSequence chars = myDocument.getCharsSequence();

    boolean singleline = myStartLine == myEndLine;
    int offset = myDocument.getLineStartOffset(myStartLine);
    offset = CharArrayUtil.shiftForward(myDocument.getCharsSequence(), offset, " \t");

    final Language languageSuitableForCompleteFragment =
        PsiUtilBase.reallyEvaluateLanguageInRange(
            offset,
            CharArrayUtil.shiftBackward(
                myDocument.getCharsSequence(), myDocument.getLineEndOffset(myEndLine), " \t\n"),
            myFile);

    Commenter blockSuitableCommenter =
        languageSuitableForCompleteFragment == null
            ? LanguageCommenters.INSTANCE.forLanguage(myFile.getLanguage())
            : null;
    if (blockSuitableCommenter == null && myFile.getFileType() instanceof AbstractFileType) {
      blockSuitableCommenter =
          new Commenter() {
            final SyntaxTable mySyntaxTable =
                ((AbstractFileType) myFile.getFileType()).getSyntaxTable();

            @Nullable
            public String getLineCommentPrefix() {
              return mySyntaxTable.getLineComment();
            }

            @Nullable
            public String getBlockCommentPrefix() {
              return mySyntaxTable.getStartComment();
            }

            @Nullable
            public String getBlockCommentSuffix() {
              return mySyntaxTable.getEndComment();
            }

            public String getCommentedBlockCommentPrefix() {
              return null;
            }

            public String getCommentedBlockCommentSuffix() {
              return null;
            }
          };
    }

    boolean allLineCommented = true;
    boolean commentWithIndent =
        !CodeStyleSettingsManager.getSettings(myProject).LINE_COMMENT_AT_FIRST_COLUMN;

    for (int line = myStartLine; line <= myEndLine; line++) {
      Commenter commenter =
          blockSuitableCommenter != null ? blockSuitableCommenter : findCommenter(line);
      if (commenter == null) return;
      if (commenter.getLineCommentPrefix() == null
          && (commenter.getBlockCommentPrefix() == null
              || commenter.getBlockCommentSuffix() == null)) {
        return;
      }

      if (commenter instanceof SelfManagingCommenter
          && myCommenterStateMap.get(commenter) == null) {
        final SelfManagingCommenter selfManagingCommenter = (SelfManagingCommenter) commenter;
        CommenterDataHolder state =
            selfManagingCommenter.createLineCommentingState(
                myStartLine, myEndLine, myDocument, myFile);
        if (state == null) state = SelfManagingCommenter.EMPTY_STATE;
        myCommenterStateMap.put(selfManagingCommenter, state);
      }

      myCommenters[line - myStartLine] = commenter;
      if (!isLineCommented(line, chars, commenter) && (singleline || !isLineEmpty(line))) {
        allLineCommented = false;
        if (commenter instanceof IndentedCommenter) {
          final Boolean value = ((IndentedCommenter) commenter).forceIndentedLineComment();
          if (value != null) {
            commentWithIndent = value;
          }
        }
        break;
      }
    }

    if (!allLineCommented) {
      if (!commentWithIndent) {
        doDefaultCommenting(blockSuitableCommenter);
      } else {
        doIndentCommenting(blockSuitableCommenter);
      }
    } else {
      for (int line = myEndLine; line >= myStartLine; line--) {
        uncommentLine(line);
        // int offset1 = myStartOffsets[line - myStartLine];
        // int offset2 = myEndOffsets[line - myStartLine];
        // if (offset1 == offset2) continue;
        // Commenter commenter = myCommenters[line - myStartLine];
        // String prefix = commenter.getBlockCommentPrefix();
        // if (prefix == null || !myDocument.getText().substring(offset1,
        // myDocument.getTextLength()).startsWith(prefix)) {
        //  prefix = commenter.getLineCommentPrefix();
        // }
        //
        // String suffix = commenter.getBlockCommentSuffix();
        // if (suffix == null && prefix != null) suffix = "";
        //
        // if (prefix != null && suffix != null) {
        //  final int suffixLen = suffix.length();
        //  final int prefixLen = prefix.length();
        //  if (offset2 >= 0) {
        //    if (!CharArrayUtil.regionMatches(chars, offset1 + prefixLen, prefix)) {
        //      myDocument.deleteString(offset2 - suffixLen, offset2);
        //    }
        //  }
        //  if (offset1 >= 0) {
        //    for (int i = offset2 - suffixLen - 1; i > offset1 + prefixLen; --i) {
        //      if (CharArrayUtil.regionMatches(chars, i, suffix)) {
        //        myDocument.deleteString(i, i + suffixLen);
        //      }
        //      else if (CharArrayUtil.regionMatches(chars, i, prefix)) {
        //        myDocument.deleteString(i, i + prefixLen);
        //      }
        //    }
        //    myDocument.deleteString(offset1, offset1 + prefixLen);
        //  }
        // }
      }
    }
  }
  public String generateDocumentationContentStub(PsiComment contextComment) {
    if (!(contextComment instanceof GrDocComment)) {
      return null;
    }

    final GrDocCommentOwner owner = GrDocCommentUtil.findDocOwner((GrDocComment) contextComment);
    if (owner == null) return null;

    Project project = contextComment.getProject();
    final CodeDocumentationAwareCommenter commenter =
        (CodeDocumentationAwareCommenter)
            LanguageCommenters.INSTANCE.forLanguage(owner.getLanguage());

    StringBuilder builder = StringBuilderSpinAllocator.alloc();
    try {
      if (owner instanceof GrMethod) {
        final GrMethod method = (GrMethod) owner;
        final GrParameter[] parameters = method.getParameters();
        final Map<String, String> param2Description = new HashMap<String, String>();
        final PsiMethod[] superMethods = method.findSuperMethods();

        for (PsiMethod superMethod : superMethods) {
          final PsiDocComment comment = superMethod.getDocComment();
          if (comment != null) {
            final PsiDocTag[] params = comment.findTagsByName("param");
            for (PsiDocTag param : params) {
              final PsiElement[] dataElements = param.getDataElements();
              if (dataElements != null) {
                String paramName = null;
                for (PsiElement dataElement : dataElements) {
                  if (dataElement instanceof PsiDocParamRef) {
                    paramName = dataElement.getReference().getCanonicalText();
                    break;
                  }
                }
                if (paramName != null) {
                  param2Description.put(paramName, param.getText());
                }
              }
            }
          }
        }
        for (PsiParameter parameter : parameters) {
          String description = param2Description.get(parameter.getName());
          if (description != null) {
            builder.append(CodeDocumentationUtil.createDocCommentLine("", project, commenter));
            if (description.indexOf('\n') > -1)
              description = description.substring(0, description.lastIndexOf('\n'));
            builder.append(description);
          } else {
            builder.append(
                CodeDocumentationUtil.createDocCommentLine(PARAM_TAG, project, commenter));
            builder.append(parameter.getName());
          }
          builder.append(LINE_SEPARATOR);
        }

        final PsiType returnType = method.getInferredReturnType();
        if ((returnType != null || method.getModifierList().hasModifierProperty(GrModifier.DEF))
            && returnType != PsiType.VOID) {
          builder.append(
              CodeDocumentationUtil.createDocCommentLine(RETURN_TAG, project, commenter));
          builder.append(LINE_SEPARATOR);
        }

        final PsiClassType[] references = method.getThrowsList().getReferencedTypes();
        for (PsiClassType reference : references) {
          builder.append(
              CodeDocumentationUtil.createDocCommentLine(THROWS_TAG, project, commenter));
          builder.append(reference.getClassName());
          builder.append(LINE_SEPARATOR);
        }
      } else if (owner instanceof GrTypeDefinition) {
        final PsiTypeParameterList typeParameterList = ((PsiClass) owner).getTypeParameterList();
        if (typeParameterList != null) {
          createTypeParamsListComment(builder, project, commenter, typeParameterList);
        }
      }
      return builder.length() > 0 ? builder.toString() : null;
    } finally {
      StringBuilderSpinAllocator.dispose(builder);
    }
  }