diff --git a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/lexer/KDocTokens.java b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/lexer/KDocTokens.java index 4eaf77f4b49..f8d918d4b45 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/lexer/KDocTokens.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/lexer/KDocTokens.java @@ -57,7 +57,7 @@ public interface KDocTokens { /** * First word following the tag name (@xxx). Depending on the tag name, this can be - * either a link (@param xxx) or just a plain text word (@author name). + * either a link (@param xxx) or just a plain text word (@since version). * We understand which one it is during parsing. */ KDocToken TEXT_OR_LINK = new KDocToken("KDOC_TEXT_OR_LINK"); diff --git a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/api/KDoc.java b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/api/KDoc.java index cf902a2b358..bd1a86f5384 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/api/KDoc.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/api/KDoc.java @@ -17,7 +17,9 @@ package org.jetbrains.kotlin.kdoc.psi.api; import com.intellij.psi.PsiComment; +import org.jetbrains.kotlin.kdoc.psi.impl.KDocSection; // Don't implement JetElement (or it will be treated as statement) public interface KDoc extends PsiComment { + KDocSection getDefaultSection(); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocImpl.java b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocImpl.java index b1b47a0eae9..4db5d18e0c3 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocImpl.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocImpl.java @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.kdoc.psi.impl; import com.intellij.lang.Language; import com.intellij.psi.impl.source.tree.LazyParseablePsiElement; import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.idea.JetLanguage; import org.jetbrains.kotlin.kdoc.lexer.KDocTokens; @@ -45,4 +46,9 @@ public class KDocImpl extends LazyParseablePsiElement implements KDoc { public IElementType getTokenType() { return JetTokens.DOC_COMMENT; } + + @Override + public KDocSection getDefaultSection() { + return PsiTreeUtil.getChildOfType(this, KDocSection.class); + } } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocSection.java b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocSection.java index 23d1e53fcc8..292d9524577 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocSection.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocSection.java @@ -25,7 +25,7 @@ import org.jetbrains.annotations.NotNull; * can have sections for the class itself, its primary constructor and each of the * properties defined in the primary constructor. */ -public class KDocSection extends KDocElementImpl { +public class KDocSection extends KDocTag { public KDocSection(@NotNull ASTNode node) { super(node); } diff --git a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocTag.java b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocTag.java index 59b2fc1403f..5e24ac6015d 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocTag.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/kdoc/psi/impl/KDocTag.java @@ -17,11 +17,68 @@ package org.jetbrains.kotlin.kdoc.psi.impl; import com.intellij.lang.ASTNode; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.TokenType; +import com.intellij.psi.tree.IElementType; +import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.kotlin.kdoc.lexer.KDocTokens; +import org.jetbrains.kotlin.kdoc.parser.KDocElementTypes; public class KDocTag extends KDocElementImpl { public KDocTag(@NotNull ASTNode node) { super(node); } + public String getContent() { + StringBuilder builder = new StringBuilder(); + + boolean contentStarted = false; + boolean afterAsterisk = false; + boolean startsWithTagName = false; + + ASTNode[] children = getNode().getChildren(null); + for (int i = 0; i < children.length; i++) { + ASTNode node = children[i]; + IElementType type = node.getElementType(); + if (i == 0 && type == KDocTokens.TAG_NAME) { + startsWithTagName = true; + } + if (KDocTokens.CONTENT_TOKENS.contains(type) || type == KDocElementTypes.KDOC_LINK) { + contentStarted = true; + builder.append(afterAsterisk ? StringUtil.trimLeading(node.getText()) : node.getText()); + afterAsterisk = false; + } + + if (type == KDocTokens.LEADING_ASTERISK || type == KDocTokens.START) { + afterAsterisk = true; + } + + if (type == TokenType.WHITE_SPACE) { + if (i == 1 && startsWithTagName) { + builder.append(node.getText()); + } + else if (contentStarted) { + builder.append(StringUtil.repeat("\n", StringUtil.countNewLines(node.getText()))); + } + } + + if (type == KDocElementTypes.KDOC_TAG) { + break; + } + } + + return builder.toString(); + } + + public String getContentWithTags() { + StringBuilder content = new StringBuilder(getContent()); + KDocTag[] subTags = PsiTreeUtil.getChildrenOfType(this, KDocTag.class); + if (subTags != null) { + for (KDocTag tag : subTags) { + content.append(tag.getContentWithTags()).append("\n"); + } + } + return content.toString(); + } } diff --git a/compiler/tests/org/jetbrains/kotlin/parsing/JetCodeConformanceTest.java b/compiler/tests/org/jetbrains/kotlin/parsing/JetCodeConformanceTest.java index 2f5de3cd832..10f0e887c81 100644 --- a/compiler/tests/org/jetbrains/kotlin/parsing/JetCodeConformanceTest.java +++ b/compiler/tests/org/jetbrains/kotlin/parsing/JetCodeConformanceTest.java @@ -46,6 +46,7 @@ public class JetCodeConformanceTest extends TestCase { new File("docs"), new File("ideaSDK"), new File("libraries/tools/kotlin-gradle-plugin-core/gradle_api_jar/build/tmp"), + new File("compiler/testData/psi/kdoc"), new File("compiler/tests/org/jetbrains/kotlin/parsing/JetCodeConformanceTest.java")); public static final Pattern AUTHOR_JAVADOC_PATTERN = Pattern.compile("/\\*.+@author.+\\*/", Pattern.DOTALL); diff --git a/idea/src/org/jetbrains/kotlin/idea/JetQuickDocumentationProvider.java b/idea/src/org/jetbrains/kotlin/idea/JetQuickDocumentationProvider.java index 00631b51c39..a8ad059cc91 100644 --- a/idea/src/org/jetbrains/kotlin/idea/JetQuickDocumentationProvider.java +++ b/idea/src/org/jetbrains/kotlin/idea/JetQuickDocumentationProvider.java @@ -22,13 +22,11 @@ import com.intellij.lang.java.JavaDocumentationProvider; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiWhiteSpace; -import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.asJava.KotlinLightMethod; import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.idea.caches.resolve.ResolvePackage; -import org.jetbrains.kotlin.kdoc.lexer.KDocTokens; import org.jetbrains.kotlin.kdoc.psi.api.KDoc; import org.jetbrains.kotlin.psi.JetDeclaration; import org.jetbrains.kotlin.psi.JetPackageDirective; @@ -117,36 +115,9 @@ public class JetQuickDocumentationProvider extends AbstractDocumentationProvider return null; } - private static String getKDocContent(@NotNull KDoc kDoc) { - StringBuilder builder = new StringBuilder(); - - boolean contentStarted = false; - boolean afterAsterisk = false; - - for (PsiElement element : kDoc.getChildren()) { - IElementType type = element.getNode().getElementType(); - - if (KDocTokens.CONTENT_TOKENS.contains(type)) { - contentStarted = true; - builder.append(afterAsterisk ? StringUtil.trimLeading(element.getText()) : element.getText()); - afterAsterisk = false; - } - - if (type == KDocTokens.LEADING_ASTERISK || type == KDocTokens.START) { - afterAsterisk = true; - } - - if (contentStarted && element instanceof PsiWhiteSpace) { - builder.append(StringUtil.repeat("\n", StringUtil.countNewLines(element.getText()))); - } - } - - return builder.toString(); - } - private static String kDocToHtml(@NotNull KDoc comment) { // TODO: Parse and show markdown comments as html - String content = getKDocContent(comment); + String content = comment.getDefaultSection().getContentWithTags(); String htmlContent = StringUtil.replace(content, "\n", "
") .replaceAll("(@param)\\s+(\\w+)", "@param - $2") .replaceAll("(@\\w+)", "$1"); diff --git a/idea/src/org/jetbrains/kotlin/idea/editor/wordSelection/KotlinWordSelectionFilter.kt b/idea/src/org/jetbrains/kotlin/idea/editor/wordSelection/KotlinWordSelectionFilter.kt index e342863544c..b037e6ce543 100644 --- a/idea/src/org/jetbrains/kotlin/idea/editor/wordSelection/KotlinWordSelectionFilter.kt +++ b/idea/src/org/jetbrains/kotlin/idea/editor/wordSelection/KotlinWordSelectionFilter.kt @@ -21,6 +21,7 @@ import com.intellij.psi.PsiElement import org.jetbrains.kotlin.JetNodeTypes.* import org.jetbrains.kotlin.psi.JetContainerNode import org.jetbrains.kotlin.idea.JetLanguage +import org.jetbrains.kotlin.kdoc.parser.KDocElementTypes public class KotlinWordSelectionFilter : Condition{ override fun value(e: PsiElement): Boolean { @@ -31,7 +32,7 @@ public class KotlinWordSelectionFilter : Condition{ if (e.getParent().getFirstChild().getNextSibling() == null && e.getParent() !is JetContainerNode) return false // skip nodes with the same range as their parent return when (e.getNode().getElementType()) { - BLOCK, LITERAL_STRING_TEMPLATE_ENTRY -> false + BLOCK, LITERAL_STRING_TEMPLATE_ENTRY, KDocElementTypes.KDOC_SECTION -> false else -> true } } diff --git a/idea/src/org/jetbrains/kotlin/idea/formatter/JetBlock.java b/idea/src/org/jetbrains/kotlin/idea/formatter/JetBlock.java index 6d9a544fd1c..8fc01c09af8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/formatter/JetBlock.java +++ b/idea/src/org/jetbrains/kotlin/idea/formatter/JetBlock.java @@ -31,6 +31,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.idea.JetLanguage; import org.jetbrains.kotlin.kdoc.lexer.KDocTokens; +import org.jetbrains.kotlin.kdoc.parser.KDocElementTypes; import org.jetbrains.kotlin.lexer.JetTokens; import org.jetbrains.kotlin.psi.JetDeclaration; @@ -65,6 +66,8 @@ public class JetBlock extends AbstractBlock { CLASS_BODY, FUNCTION_LITERAL); + private static final TokenSet KDOC_CONTENT = TokenSet.create(KDocTokens.KDOC, KDocElementTypes.KDOC_SECTION); + // private static final List public JetBlock( @@ -415,7 +418,7 @@ public class JetBlock extends AbstractBlock { .set(Indent.getContinuationWithoutFirstIndent(false)), strategy("KDoc comment indent") - .in(DOC_COMMENT) + .in(KDOC_CONTENT) .forType(KDocTokens.LEADING_ASTERISK, KDocTokens.END) .set(Indent.getSpaceIndent(KDOC_COMMENT_INDENT)), diff --git a/idea/testData/editor/quickDoc/MethodFromStdLib.kt b/idea/testData/editor/quickDoc/MethodFromStdLib.kt index fb877444ad4..eb2bed96877 100644 --- a/idea/testData/editor/quickDoc/MethodFromStdLib.kt +++ b/idea/testData/editor/quickDoc/MethodFromStdLib.kt @@ -2,4 +2,4 @@ fun test() { listOf(1, 2, 4).filter { it > 0 } } -// INFO: inline public fun <T> Iterable<T>.filter(predicate: (T) → Boolean): List<T>

Returns a list containing all elements matching the given *predicate*

+// INFO: inline public fun <T> Iterable<T>.filter(predicate: (T) → Boolean): List<T>

Returns a list containing all elements matching the given *predicate*

diff --git a/idea/testData/editor/quickDoc/OnClassDeclarationWithNoPackage.kt b/idea/testData/editor/quickDoc/OnClassDeclarationWithNoPackage.kt index f239fcee906..e85de36f4aa 100644 --- a/idea/testData/editor/quickDoc/OnClassDeclarationWithNoPackage.kt +++ b/idea/testData/editor/quickDoc/OnClassDeclarationWithNoPackage.kt @@ -3,4 +3,4 @@ */ class Some -// INFO: internal final class Some

Usefull comment

\ No newline at end of file +// INFO: internal final class Some

Usefull comment

\ No newline at end of file diff --git a/idea/testData/editor/quickDoc/TopLevelMethodFromJava.java b/idea/testData/editor/quickDoc/TopLevelMethodFromJava.java index d7ddf7007fd..fde04a1cd48 100644 --- a/idea/testData/editor/quickDoc/TopLevelMethodFromJava.java +++ b/idea/testData/editor/quickDoc/TopLevelMethodFromJava.java @@ -8,4 +8,4 @@ class Testing { } } -// INFO: internal fun foo(bar: Int): Unit

KDoc foo

\ No newline at end of file +// INFO: internal fun foo(bar: Int): Unit

KDoc foo

\ No newline at end of file