@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); }
/** * 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); } }