diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/parsing/AbstractJetParsing.java b/compiler/frontend/src/org/jetbrains/jet/lang/parsing/AbstractJetParsing.java index 485591db5ba..b664c9edcc1 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/parsing/AbstractJetParsing.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/parsing/AbstractJetParsing.java @@ -378,9 +378,10 @@ import static org.jetbrains.jet.lexer.JetTokens.*; return myBuilder.newlineBeforeCurrentToken() || eof(); } - protected static void closeDeclarationWithCommentBinders(@NotNull PsiBuilder.Marker marker, @NotNull IElementType elementType) { + protected static void closeDeclarationWithCommentBinders(@NotNull PsiBuilder.Marker marker, @NotNull IElementType elementType, boolean precedingNonDocComments) { marker.done(elementType); - marker.setCustomEdgeTokenBinders(PrecedingWhitespacesAndCommentsBinder.INSTANCE$, TrailingWhitespacesAndCommentsBinder.INSTANCE$); + marker.setCustomEdgeTokenBinders(precedingNonDocComments ? PrecedingCommentsBinder.INSTANCE$ : PrecedingDocCommentsBinder.INSTANCE$, + TrailingCommentsBinder.INSTANCE$); } protected abstract JetParsing create(SemanticWhitespaceAwarePsiBuilder builder); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/parsing/CommentBinders.kt b/compiler/frontend/src/org/jetbrains/jet/lang/parsing/CommentBinders.kt index ddf1e9eb14f..7caa3e6862e 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/parsing/CommentBinders.kt +++ b/compiler/frontend/src/org/jetbrains/jet/lang/parsing/CommentBinders.kt @@ -21,7 +21,7 @@ import com.intellij.psi.tree.IElementType import org.jetbrains.jet.lexer.JetTokens import com.intellij.openapi.util.text.StringUtil -object PrecedingWhitespacesAndCommentsBinder : WhitespacesAndCommentsBinder { +object PrecedingCommentsBinder : WhitespacesAndCommentsBinder { override fun getEdgePosition(tokens: List, atStreamEdge: Boolean, getter: WhitespacesAndCommentsBinder.TokenTextGetter): Int { if (tokens.isEmpty()) return 0 @@ -52,8 +52,28 @@ object PrecedingWhitespacesAndCommentsBinder : WhitespacesAndCommentsBinder { } } +object PrecedingDocCommentsBinder : WhitespacesAndCommentsBinder { + + override fun getEdgePosition(tokens: List, atStreamEdge: Boolean, getter: WhitespacesAndCommentsBinder.TokenTextGetter): Int { + if (tokens.isEmpty()) return 0 + + for (idx in tokens.indices.reversed()) { + val tokenType = tokens[idx] + when (tokenType) { + JetTokens.DOC_COMMENT -> return idx + + JetTokens.WHITE_SPACE -> continue + + else -> break + } + } + + return tokens.size + } +} + // Binds comments on the same line -object TrailingWhitespacesAndCommentsBinder : WhitespacesAndCommentsBinder { +object TrailingCommentsBinder : WhitespacesAndCommentsBinder { override fun getEdgePosition(tokens: List, atStreamEdge: Boolean, getter: WhitespacesAndCommentsBinder.TokenTextGetter): Int { if (tokens.isEmpty()) return 0 diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java b/compiler/frontend/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java index 07a4dbf617b..404e9f9e483 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/parsing/JetExpressionParsing.java @@ -18,7 +18,6 @@ package org.jetbrains.jet.lang.parsing; import com.google.common.collect.ImmutableMap; import com.intellij.lang.PsiBuilder; -import com.intellij.lang.WhitespacesAndCommentsBinder; import com.intellij.psi.tree.IElementType; import com.intellij.psi.tree.TokenSet; import org.jetbrains.annotations.NotNull; @@ -960,13 +959,8 @@ public class JetExpressionParsing extends AbstractJetParsing { IElementType declType = parseLocalDeclarationRest(enumDetector.isDetected()); if (declType != null) { - decl.done(declType); - // we do not attach preceding comments to local variables because they are likely commenting a few statements below - WhitespacesAndCommentsBinder leftBinder = declType == JetNodeTypes.PROPERTY || declType == JetNodeTypes.MULTI_VARIABLE_DECLARATION - ? null - : PrecedingWhitespacesAndCommentsBinder.INSTANCE$; - decl.setCustomEdgeTokenBinders(leftBinder, - TrailingWhitespacesAndCommentsBinder.INSTANCE$); + // we do not attach preceding comments (non-doc) to local variables because they are likely commenting a few statements below + closeDeclarationWithCommentBinders(decl, declType, declType != JetNodeTypes.PROPERTY && declType != JetNodeTypes.MULTI_VARIABLE_DECLARATION); return true; } else { diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/parsing/JetParsing.java b/compiler/frontend/src/org/jetbrains/jet/lang/parsing/JetParsing.java index d8346bfaf9a..d156cf20215 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/parsing/JetParsing.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/parsing/JetParsing.java @@ -211,7 +211,7 @@ public class JetParsing extends AbstractJetParsing { packageDirective = mark(); packageDirective.done(PACKAGE_DIRECTIVE); // this is necessary to allow comments at the start of the file to be bound to the first declaration: - packageDirective.setCustomEdgeTokenBinders(PrecedingWhitespacesAndCommentsBinder.INSTANCE$, null); + packageDirective.setCustomEdgeTokenBinders(PrecedingCommentsBinder.INSTANCE$, null); } parseImportDirectives(); @@ -318,7 +318,7 @@ public class JetParsing extends AbstractJetParsing { } consumeIf(SEMICOLON); importDirective.done(IMPORT_DIRECTIVE); - importDirective.setCustomEdgeTokenBinders(null, TrailingWhitespacesAndCommentsBinder.INSTANCE$); + importDirective.setCustomEdgeTokenBinders(null, TrailingCommentsBinder.INSTANCE$); } private boolean closeImportWithErrorIfNewline(PsiBuilder.Marker importDirective, String errorMessage) { @@ -385,7 +385,7 @@ public class JetParsing extends AbstractJetParsing { decl.drop(); } else { - closeDeclarationWithCommentBinders(decl, declType); + closeDeclarationWithCommentBinders(decl, declType, true); } } @@ -685,7 +685,7 @@ public class JetParsing extends AbstractJetParsing { entryOrMember.drop(); } else { - closeDeclarationWithCommentBinders(entryOrMember, type); + closeDeclarationWithCommentBinders(entryOrMember, type, true); } } @@ -775,7 +775,7 @@ public class JetParsing extends AbstractJetParsing { decl.drop(); } else { - closeDeclarationWithCommentBinders(decl, declType); + closeDeclarationWithCommentBinders(decl, declType, true); } } @@ -873,7 +873,7 @@ public class JetParsing extends AbstractJetParsing { PsiBuilder.Marker objectDeclaration = mark(); parseObject(false, true); - closeDeclarationWithCommentBinders(objectDeclaration, OBJECT_DECLARATION); + closeDeclarationWithCommentBinders(objectDeclaration, OBJECT_DECLARATION, true); return CLASS_OBJECT; } @@ -1078,7 +1078,7 @@ public class JetParsing extends AbstractJetParsing { errorUntil("Accessor body expected", TokenSet.orSet(ACCESSOR_FIRST_OR_PROPERTY_END, TokenSet.create(LBRACE, LPAR, EQ))); } else { - getterOrSetter.done(PROPERTY_ACCESSOR); + closeDeclarationWithCommentBinders(getterOrSetter, PROPERTY_ACCESSOR, false); return true; } } @@ -1110,7 +1110,7 @@ public class JetParsing extends AbstractJetParsing { parseFunctionBody(); - getterOrSetter.done(PROPERTY_ACCESSOR); + closeDeclarationWithCommentBinders(getterOrSetter, PROPERTY_ACCESSOR, false); return true; } @@ -1891,8 +1891,7 @@ public class JetParsing extends AbstractJetParsing { PsiBuilder.Marker valueParameter = mark(); parseModifierList(MODIFIER_LIST, REGULAR_ANNOTATIONS_ONLY_WITH_BRACKETS); // lazy, out, ref parseTypeRef(); - valueParameter.done(VALUE_PARAMETER); - valueParameter.setCustomEdgeTokenBinders(null, TrailingWhitespacesAndCommentsBinder.INSTANCE$); + closeDeclarationWithCommentBinders(valueParameter, VALUE_PARAMETER, false); } } else { @@ -1942,8 +1941,7 @@ public class JetParsing extends AbstractJetParsing { return false; } - parameter.done(VALUE_PARAMETER); - parameter.setCustomEdgeTokenBinders(null, TrailingWhitespacesAndCommentsBinder.INSTANCE$); + closeDeclarationWithCommentBinders(parameter, VALUE_PARAMETER, false); return true; } diff --git a/compiler/testData/psi/CommentsBinding.txt b/compiler/testData/psi/CommentsBinding.txt index c1870851f80..5d09ab11264 100644 --- a/compiler/testData/psi/CommentsBinding.txt +++ b/compiler/testData/psi/CommentsBinding.txt @@ -400,8 +400,8 @@ JetFile: CommentsBinding.kt PsiWhiteSpace(' ') INTEGER_CONSTANT PsiElement(INTEGER_LITERAL)('1') - PsiWhiteSpace(' ') - PsiComment(EOL_COMMENT)('// this is getter') + PsiWhiteSpace(' ') + PsiComment(EOL_COMMENT)('// this is getter') PsiWhiteSpace('\n ') PROPERTY_ACCESSOR PsiElement(set)('set') @@ -414,8 +414,8 @@ JetFile: CommentsBinding.kt BLOCK PsiElement(LBRACE)('{') PsiElement(RBRACE)('}') - PsiWhiteSpace(' ') - PsiComment(EOL_COMMENT)('// this is setter') + PsiWhiteSpace(' ') + PsiComment(EOL_COMMENT)('// this is setter') PsiWhiteSpace('\n\n') PROPERTY PsiElement(val)('val') @@ -441,5 +441,5 @@ JetFile: CommentsBinding.kt PsiWhiteSpace(' ') INTEGER_CONSTANT PsiElement(INTEGER_LITERAL)('1') - PsiWhiteSpace(' ') - PsiComment(EOL_COMMENT)('// prop2') \ No newline at end of file + PsiWhiteSpace(' ') + PsiComment(EOL_COMMENT)('// prop2') \ No newline at end of file diff --git a/compiler/testData/psi/DocCommentsBinding.kt b/compiler/testData/psi/DocCommentsBinding.kt new file mode 100644 index 00000000000..c148f7366e2 --- /dev/null +++ b/compiler/testData/psi/DocCommentsBinding.kt @@ -0,0 +1,40 @@ +/** + * Doc comment for A + */ +// some comment +class A( + /** + * Doc comment for val-parameter + */ + val p: Int +) { + /** + * Doc comment for function + */ + fun foo() { + /** + * Doc comment for local function + */ + fun localFoo() { } + /** + * Doc comment for local class + */ + class LocalClass + } + + /** + * Doc comment for property + */ + var property: Int + /** Doc comment for getter */ + get() = 1 + /** Doc comment for setter */ + set(value) {} +} + +/** + * Doc comment for B + */ +class B { + +} \ No newline at end of file diff --git a/compiler/testData/psi/DocCommentsBinding.txt b/compiler/testData/psi/DocCommentsBinding.txt new file mode 100644 index 00000000000..19a1b2f7182 --- /dev/null +++ b/compiler/testData/psi/DocCommentsBinding.txt @@ -0,0 +1,169 @@ +JetFile: DocCommentsBinding.kt + PACKAGE_DIRECTIVE + + CLASS + KDoc + PsiElement(KDOC_START)('/**') + PsiWhiteSpace('\n ') + PsiElement(KDOC_LEADING_ASTERISK)('*') + PsiElement(KDOC_TEXT)(' Doc comment for A') + PsiWhiteSpace('\n ') + PsiElement(KDOC_END)('*/') + PsiWhiteSpace('\n') + PsiComment(EOL_COMMENT)('// some comment') + PsiWhiteSpace('\n') + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('A') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiWhiteSpace('\n ') + VALUE_PARAMETER + KDoc + PsiElement(KDOC_START)('/**') + PsiWhiteSpace('\n ') + PsiElement(KDOC_LEADING_ASTERISK)('*') + PsiElement(KDOC_TEXT)(' Doc comment for val-parameter') + PsiWhiteSpace('\n ') + PsiElement(KDOC_END)('*/') + PsiWhiteSpace('\n ') + PsiElement(val)('val') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('p') + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('Int') + PsiWhiteSpace('\n') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + CLASS_BODY + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + FUN + KDoc + PsiElement(KDOC_START)('/**') + PsiWhiteSpace('\n ') + PsiElement(KDOC_LEADING_ASTERISK)('*') + PsiElement(KDOC_TEXT)(' Doc comment for function') + PsiWhiteSpace('\n ') + PsiElement(KDOC_END)('*/') + PsiWhiteSpace('\n ') + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('foo') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n ') + FUN + KDoc + PsiElement(KDOC_START)('/**') + PsiWhiteSpace('\n ') + PsiElement(KDOC_LEADING_ASTERISK)('*') + PsiElement(KDOC_TEXT)(' Doc comment for local function') + PsiWhiteSpace('\n ') + PsiElement(KDOC_END)('*/') + PsiWhiteSpace('\n ') + PsiElement(fun)('fun') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('localFoo') + VALUE_PARAMETER_LIST + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiWhiteSpace(' ') + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n ') + CLASS + KDoc + PsiElement(KDOC_START)('/**') + PsiWhiteSpace('\n ') + PsiElement(KDOC_LEADING_ASTERISK)('*') + PsiElement(KDOC_TEXT)(' Doc comment for local class') + PsiWhiteSpace('\n ') + PsiElement(KDOC_END)('*/') + PsiWhiteSpace('\n ') + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('LocalClass') + PsiWhiteSpace('\n ') + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n\n ') + PROPERTY + KDoc + PsiElement(KDOC_START)('/**') + PsiWhiteSpace('\n ') + PsiElement(KDOC_LEADING_ASTERISK)('*') + PsiElement(KDOC_TEXT)(' Doc comment for property') + PsiWhiteSpace('\n ') + PsiElement(KDOC_END)('*/') + PsiWhiteSpace('\n ') + PsiElement(var)('var') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('property') + PsiElement(COLON)(':') + PsiWhiteSpace(' ') + TYPE_REFERENCE + USER_TYPE + REFERENCE_EXPRESSION + PsiElement(IDENTIFIER)('Int') + PsiWhiteSpace('\n ') + PROPERTY_ACCESSOR + KDoc + PsiElement(KDOC_START)('/**') + PsiElement(KDOC_TEXT)(' Doc comment for getter ') + PsiElement(KDOC_END)('*/') + PsiWhiteSpace('\n ') + PsiElement(get)('get') + PsiElement(LPAR)('(') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + PsiElement(EQ)('=') + PsiWhiteSpace(' ') + INTEGER_CONSTANT + PsiElement(INTEGER_LITERAL)('1') + PsiWhiteSpace('\n ') + PROPERTY_ACCESSOR + KDoc + PsiElement(KDOC_START)('/**') + PsiElement(KDOC_TEXT)(' Doc comment for setter ') + PsiElement(KDOC_END)('*/') + PsiWhiteSpace('\n ') + PsiElement(set)('set') + PsiElement(LPAR)('(') + VALUE_PARAMETER_LIST + VALUE_PARAMETER + PsiElement(IDENTIFIER)('value') + PsiElement(RPAR)(')') + PsiWhiteSpace(' ') + BLOCK + PsiElement(LBRACE)('{') + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n') + PsiElement(RBRACE)('}') + PsiWhiteSpace('\n\n') + CLASS + KDoc + PsiElement(KDOC_START)('/**') + PsiWhiteSpace('\n ') + PsiElement(KDOC_LEADING_ASTERISK)('*') + PsiElement(KDOC_TEXT)(' Doc comment for B') + PsiWhiteSpace('\n ') + PsiElement(KDOC_END)('*/') + PsiWhiteSpace('\n') + PsiElement(class)('class') + PsiWhiteSpace(' ') + PsiElement(IDENTIFIER)('B') + PsiWhiteSpace(' ') + CLASS_BODY + PsiElement(LBRACE)('{') + PsiWhiteSpace('\n\n') + PsiElement(RBRACE)('}') \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/jet/parsing/JetParsingTestGenerated.java b/compiler/tests/org/jetbrains/jet/parsing/JetParsingTestGenerated.java index c19b587ee0f..25e445498fc 100644 --- a/compiler/tests/org/jetbrains/jet/parsing/JetParsingTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/parsing/JetParsingTestGenerated.java @@ -154,6 +154,12 @@ public class JetParsingTestGenerated extends AbstractJetParsingTest { doParsingTest(fileName); } + @TestMetadata("DocCommentsBinding.kt") + public void testDocCommentsBinding() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/DocCommentsBinding.kt"); + doParsingTest(fileName); + } + @TestMetadata("DoubleColon.kt") public void testDoubleColon() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/psi/DoubleColon.kt"); diff --git a/idea/tests/org/jetbrains/jet/completion/handlers/SmartCompletionHandlerTestGenerated.java b/idea/tests/org/jetbrains/jet/completion/handlers/SmartCompletionHandlerTestGenerated.java index fc5e1dc84c9..f136bb1df0f 100644 --- a/idea/tests/org/jetbrains/jet/completion/handlers/SmartCompletionHandlerTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/completion/handlers/SmartCompletionHandlerTestGenerated.java @@ -40,7 +40,6 @@ public class SmartCompletionHandlerTestGenerated extends AbstractSmartCompletion @TestMetadata("AfterAs2.kt") public void testAfterAs2() throws Exception { - String fileName = JetTestUtils.navigationMetadata("idea/testData/completion/handlers/smart/AfterAs2.kt"); doTest(fileName); }