change KDoc parsing so that space after parameter/section name is not included in tag content; support @property tag in KDocFinder

This commit is contained in:
Dmitry Jemerov
2015-03-05 20:06:04 +01:00
parent 69744c0550
commit 28ed30bcb3
12 changed files with 98 additions and 53 deletions
@@ -88,14 +88,14 @@ CODE_LINK=\[{QUALIFIED_NAME}\]
/* Example: @return[x] The return value of function x /* Example: @return[x] The return value of function x
^^^ ^^^
*/ */
{CODE_LINK} { yybegin(CONTENTS); {CODE_LINK} { yybegin(TAG_TEXT_BEGINNING);
return KDocTokens.MARKDOWN_LINK; } return KDocTokens.MARKDOWN_LINK; }
/* Example: @param aaa The value of aaa /* Example: @param aaa The value of aaa
^^^ ^^^
*/ */
{QUALIFIED_NAME} { {QUALIFIED_NAME} {
yybegin(CONTENTS); yybegin(TAG_TEXT_BEGINNING);
return KDocTokens.MARKDOWN_LINK; return KDocTokens.MARKDOWN_LINK;
} }
@@ -1,4 +1,4 @@
/* The following code was generated by JFlex 1.4.3 on 2/6/15 12:23 PM */ /* The following code was generated by JFlex 1.4.3 on 3/5/15 7:51 PM */
package org.jetbrains.kotlin.kdoc.lexer; package org.jetbrains.kotlin.kdoc.lexer;
@@ -14,7 +14,7 @@ import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag;
/** /**
* This class is a scanner generated by * This class is a scanner generated by
* <a href="http://www.jflex.de/">JFlex</a> 1.4.3 * <a href="http://www.jflex.de/">JFlex</a> 1.4.3
* on 2/6/15 12:23 PM from the specification file * on 3/5/15 7:51 PM from the specification file
* <tt>/Users/yole/jetbrains/kotlin/compiler/frontend/src/org/jetbrains/kotlin/kdoc/lexer/KDoc.flex</tt> * <tt>/Users/yole/jetbrains/kotlin/compiler/frontend/src/org/jetbrains/kotlin/kdoc/lexer/KDoc.flex</tt>
*/ */
class _KDocLexer implements FlexLexer { class _KDocLexer implements FlexLexer {
@@ -117,11 +117,11 @@ class _KDocLexer implements FlexLexer {
private static final String ZZ_ACTION_PACKED_0 = private static final String ZZ_ACTION_PACKED_0 =
"\6\0\3\1\1\2\1\3\1\2\1\4\3\2\1\5"+ "\6\0\3\1\1\2\1\3\1\2\1\4\3\2\1\5"+
"\1\6\1\7\2\5\1\0\1\10\2\0\1\11\1\12"+ "\1\6\1\7\3\5\1\0\1\10\2\0\1\11\1\12"+
"\1\0\1\13\1\0\1\14\1\15"; "\2\0\1\13\1\0\1\14\1\15\1\16";
private static int [] zzUnpackAction() { private static int [] zzUnpackAction() {
int [] result = new int[32]; int [] result = new int[35];
int offset = 0; int offset = 0;
offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result); offset = zzUnpackAction(ZZ_ACTION_PACKED_0, offset, result);
return result; return result;
@@ -148,11 +148,12 @@ class _KDocLexer implements FlexLexer {
private static final String ZZ_ROWMAP_PACKED_0 = private static final String ZZ_ROWMAP_PACKED_0 =
"\0\0\0\15\0\32\0\47\0\64\0\101\0\116\0\133"+ "\0\0\0\15\0\32\0\47\0\64\0\101\0\116\0\133"+
"\0\150\0\116\0\165\0\202\0\217\0\234\0\150\0\251"+ "\0\150\0\116\0\165\0\202\0\217\0\234\0\150\0\251"+
"\0\116\0\266\0\303\0\320\0\150\0\335\0\116\0\150"+ "\0\116\0\266\0\303\0\320\0\150\0\335\0\352\0\116"+
"\0\352\0\116\0\367\0\u0104\0\116\0\u0111\0\116\0\116"; "\0\150\0\367\0\116\0\u0104\0\u0111\0\u011e\0\116\0\u012b"+
"\0\116\0\116\0\116";
private static int [] zzUnpackRowMap() { private static int [] zzUnpackRowMap() {
int [] result = new int[32]; int [] result = new int[35];
int offset = 0; int offset = 0;
offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result); offset = zzUnpackRowMap(ZZ_ROWMAP_PACKED_0, offset, result);
return result; return result;
@@ -179,18 +180,18 @@ class _KDocLexer implements FlexLexer {
"\3\12\1\14\2\12\1\15\1\12\1\13\1\16\2\12"+ "\3\12\1\14\2\12\1\15\1\12\1\13\1\16\2\12"+
"\1\13\3\12\1\14\2\12\1\17\1\20\1\13\1\16"+ "\1\13\3\12\1\14\2\12\1\17\1\20\1\13\1\16"+
"\1\12\1\21\1\22\1\21\1\23\1\21\1\24\2\21"+ "\1\12\1\21\1\22\1\21\1\23\1\21\1\24\2\21"+
"\1\25\1\21\1\22\3\21\1\22\3\21\1\24\2\21"+ "\1\25\1\21\1\22\3\21\1\22\3\21\1\26\2\21"+
"\1\25\1\21\1\22\2\21\1\12\1\13\3\12\1\14"+ "\1\25\1\21\1\22\2\21\1\12\1\13\3\12\1\14"+
"\2\12\1\17\1\12\1\13\1\16\1\12\25\0\1\26"+ "\2\12\1\17\1\12\1\13\1\16\1\12\25\0\1\27"+
"\13\0\1\27\1\30\5\0\1\13\10\0\1\13\5\0"+ "\13\0\1\30\1\31\5\0\1\13\10\0\1\13\5\0"+
"\1\31\20\0\1\27\1\15\11\0\2\32\11\0\1\33"+ "\1\32\20\0\1\30\1\15\11\0\2\33\11\0\1\34"+
"\12\0\1\22\10\0\1\22\4\0\3\23\13\0\1\34"+ "\12\0\1\22\10\0\1\22\4\0\3\23\13\0\1\35"+
"\21\0\1\35\6\0\3\31\1\0\1\36\10\0\2\33"+ "\14\0\1\36\21\0\1\37\6\0\3\32\1\0\1\40"+
"\13\0\3\34\1\0\1\37\6\0\5\40\1\0\6\40"+ "\10\0\2\34\13\0\3\35\1\0\1\41\10\0\3\36"+
"\1\0"; "\1\0\1\42\6\0\5\43\1\0\6\43\1\0";
private static int [] zzUnpackTrans() { private static int [] zzUnpackTrans() {
int [] result = new int[286]; int [] result = new int[312];
int offset = 0; int offset = 0;
offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result); offset = zzUnpackTrans(ZZ_TRANS_PACKED_0, offset, result);
return result; return result;
@@ -231,11 +232,11 @@ class _KDocLexer implements FlexLexer {
private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute(); private static final int [] ZZ_ATTRIBUTE = zzUnpackAttribute();
private static final String ZZ_ATTRIBUTE_PACKED_0 = private static final String ZZ_ATTRIBUTE_PACKED_0 =
"\6\0\1\11\2\1\1\11\6\1\1\11\4\1\1\0"+ "\6\0\1\11\2\1\1\11\6\1\1\11\5\1\1\0"+
"\1\11\2\0\1\11\1\1\1\0\1\11\1\0\2\11"; "\1\11\2\0\1\11\1\1\2\0\1\11\1\0\3\11";
private static int [] zzUnpackAttribute() { private static int [] zzUnpackAttribute() {
int [] result = new int[32]; int [] result = new int[35];
int offset = 0; int offset = 0;
offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result); offset = zzUnpackAttribute(ZZ_ATTRIBUTE_PACKED_0, offset, result);
return result; return result;
@@ -567,7 +568,7 @@ class _KDocLexer implements FlexLexer {
yybegin(tag != null && tag.isReferenceRequired() ? TAG_BEGINNING : TAG_TEXT_BEGINNING); yybegin(tag != null && tag.isReferenceRequired() ? TAG_BEGINNING : TAG_TEXT_BEGINNING);
return KDocTokens.TAG_NAME; return KDocTokens.TAG_NAME;
} }
case 14: break; case 15: break;
case 3: case 3:
{ if (yytextContainLineBreaks()) { { if (yytextContainLineBreaks()) {
yybegin(LINE_BEGINNING); yybegin(LINE_BEGINNING);
@@ -577,65 +578,70 @@ class _KDocLexer implements FlexLexer {
return KDocTokens.TEXT; // internal white space return KDocTokens.TEXT; // internal white space
} }
} }
case 15: break; case 16: break;
case 11: case 11:
{ yybegin(CONTENTS_BEGINNING); { yybegin(CONTENTS_BEGINNING);
return KDocTokens.START; return KDocTokens.START;
} }
case 16: break; case 17: break;
case 8: case 8:
{ if (isLastToken()) return KDocTokens.END; { if (isLastToken()) return KDocTokens.END;
else return KDocTokens.TEXT; else return KDocTokens.TEXT;
} }
case 17: break; case 18: break;
case 5: case 5:
{ yybegin(CONTENTS); { yybegin(CONTENTS);
return KDocTokens.TEXT; return KDocTokens.TEXT;
} }
case 18: break; case 19: break;
case 12:
{ yybegin(TAG_TEXT_BEGINNING);
return KDocTokens.MARKDOWN_LINK;
}
case 20: break;
case 7:
{ yybegin(TAG_TEXT_BEGINNING);
return KDocTokens.MARKDOWN_LINK;
}
case 21: break;
case 6: case 6:
{ if (yytextContainLineBreaks()) { { if (yytextContainLineBreaks()) {
yybegin(LINE_BEGINNING); yybegin(LINE_BEGINNING);
} }
return TokenType.WHITE_SPACE; return TokenType.WHITE_SPACE;
} }
case 19: break; case 22: break;
case 13: case 14:
// lookahead expression with fixed lookahead length // lookahead expression with fixed lookahead length
yypushback(1); yypushback(1);
{ yybegin(CONTENTS); { yybegin(CONTENTS);
return KDocTokens.MARKDOWN_LINK; return KDocTokens.MARKDOWN_LINK;
} }
case 20: break; case 23: break;
case 12: case 13:
{ yybegin(CONTENTS); { yybegin(CONTENTS);
return KDocTokens.MARKDOWN_LINK; return KDocTokens.MARKDOWN_LINK;
} }
case 21: break; case 24: break;
case 1: case 1:
{ return TokenType.BAD_CHARACTER; { return TokenType.BAD_CHARACTER;
} }
case 22: break; case 25: break;
case 9: case 9:
{ yybegin(CONTENTS); { yybegin(CONTENTS);
return KDocTokens.MARKDOWN_ESCAPED_CHAR; return KDocTokens.MARKDOWN_ESCAPED_CHAR;
} }
case 23: break; case 26: break;
case 2: case 2:
{ yybegin(CONTENTS); { yybegin(CONTENTS);
return KDocTokens.TEXT; return KDocTokens.TEXT;
} }
case 24: break; case 27: break;
case 7:
{ yybegin(CONTENTS);
return KDocTokens.MARKDOWN_LINK;
}
case 25: break;
case 4: case 4:
{ yybegin(CONTENTS_BEGINNING); { yybegin(CONTENTS_BEGINNING);
return KDocTokens.LEADING_ASTERISK; return KDocTokens.LEADING_ASTERISK;
} }
case 26: break; case 28: break;
default: default:
if (zzInput == YYEOF && zzStartRead == zzCurrentPos) { if (zzInput == YYEOF && zzStartRead == zzCurrentPos) {
zzAtEOF = true; zzAtEOF = true;
@@ -27,4 +27,5 @@ public trait KDoc : PsiComment {
public fun getDefaultSection(): KDocSection public fun getDefaultSection(): KDocSection
public fun findSectionByName(name: String): KDocSection? public fun findSectionByName(name: String): KDocSection?
public fun findSectionByTag(tag: KDocKnownTag): KDocSection? public fun findSectionByTag(tag: KDocKnownTag): KDocSection?
public fun findSectionByTag(tag: KDocKnownTag, subjectName: String): KDocSection?
} }
@@ -46,4 +46,9 @@ public class KDocImpl(buffer: CharSequence?) : LazyParseablePsiElement(KDocToken
override fun findSectionByTag(tag: KDocKnownTag): KDocSection? = override fun findSectionByTag(tag: KDocKnownTag): KDocSection? =
findSectionByName(tag.name().toLowerCase()) findSectionByName(tag.name().toLowerCase())
override fun findSectionByTag(tag: KDocKnownTag, subjectName: String): KDocSection? =
getChildrenOfType<KDocSection>().firstOrNull {
it.getName() == tag.name().toLowerCase() && it.getSubjectName() == subjectName
}
} }
@@ -34,6 +34,9 @@ public class KDocSection(node: ASTNode) : KDocTag(node) {
override fun getName(): String? = override fun getName(): String? =
(getFirstChild() as? KDocTag)?.getName() (getFirstChild() as? KDocTag)?.getName()
override fun getSubjectName(): String? =
(getFirstChild() as? KDocTag)?.getSubjectName()
override fun getContent(): String = override fun getContent(): String =
(getFirstChild() as? KDocTag)?.getContent() ?: super.getContent() (getFirstChild() as? KDocTag)?.getContent() ?: super.getContent()
@@ -44,7 +44,7 @@ public open class KDocTag(node: ASTNode) : KDocElementImpl(node) {
* Returns the name of the entity documented by this tag (for example, the name of the parameter * Returns the name of the entity documented by this tag (for example, the name of the parameter
* for the @param tag), or null if this tag does not document any specific entity. * for the @param tag), or null if this tag does not document any specific entity.
*/ */
public fun getSubjectName(): String? = getSubjectLink()?.getLinkText() public open fun getSubjectName(): String? = getSubjectLink()?.getLinkText()
public fun getSubjectLink(): KDocLink? { public fun getSubjectLink(): KDocLink? {
val children = childrenAfterTagName() val children = childrenAfterTagName()
+2 -1
View File
@@ -13,6 +13,7 @@ JetFile: ParamTag.kt
KDOC_MARKDOWN_LINK KDOC_MARKDOWN_LINK
KDOC_NAME KDOC_NAME
PsiElement(IDENTIFIER)('a') PsiElement(IDENTIFIER)('a')
PsiElement(KDOC_TEXT)(' The description of a.') PsiWhiteSpace(' ')
PsiElement(KDOC_TEXT)('The description of a.')
PsiWhiteSpace('\n ') PsiWhiteSpace('\n ')
PsiElement(KDOC_END)('*/') PsiElement(KDOC_END)('*/')
@@ -11,5 +11,6 @@ JetFile: ParamTagAtStart.kt
KDOC_MARKDOWN_LINK KDOC_MARKDOWN_LINK
KDOC_NAME KDOC_NAME
PsiElement(IDENTIFIER)('a') PsiElement(IDENTIFIER)('a')
PsiElement(KDOC_TEXT)(' this is a ') PsiWhiteSpace(' ')
PsiElement(KDOC_TEXT)('this is a ')
PsiElement(KDOC_END)('*/') PsiElement(KDOC_END)('*/')
+4 -2
View File
@@ -16,7 +16,8 @@ JetFile: Sections.kt
KDOC_MARKDOWN_LINK KDOC_MARKDOWN_LINK
KDOC_NAME KDOC_NAME
PsiElement(IDENTIFIER)('T') PsiElement(IDENTIFIER)('T')
PsiElement(KDOC_TEXT)(' a type parameter.') PsiWhiteSpace(' ')
PsiElement(KDOC_TEXT)('a type parameter.')
PsiWhiteSpace('\n ') PsiWhiteSpace('\n ')
PsiElement(KDOC_LEADING_ASTERISK)('*') PsiElement(KDOC_LEADING_ASTERISK)('*')
PsiElement(KDOC_TEXT)(' ') PsiElement(KDOC_TEXT)(' ')
@@ -34,6 +35,7 @@ JetFile: Sections.kt
KDOC_MARKDOWN_LINK KDOC_MARKDOWN_LINK
KDOC_NAME KDOC_NAME
PsiElement(IDENTIFIER)('a') PsiElement(IDENTIFIER)('a')
PsiElement(KDOC_TEXT)(' a constructor parameter.') PsiWhiteSpace(' ')
PsiElement(KDOC_TEXT)('a constructor parameter.')
PsiWhiteSpace('\n ') PsiWhiteSpace('\n ')
PsiElement(KDOC_END)('*/') PsiElement(KDOC_END)('*/')
@@ -16,14 +16,13 @@
package org.jetbrains.kotlin.idea.kdoc package org.jetbrains.kotlin.idea.kdoc
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
import org.jetbrains.kotlin.psi.JetDeclaration
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag import org.jetbrains.kotlin.kdoc.parser.KDocKnownTag
import org.jetbrains.kotlin.kdoc.psi.api.KDoc
import org.jetbrains.kotlin.kdoc.psi.impl.KDocTag
import org.jetbrains.kotlin.psi.JetDeclaration
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
object KDocFinder { object KDocFinder {
fun findKDoc(declaration: DeclarationDescriptor): KDocTag? { fun findKDoc(declaration: DeclarationDescriptor): KDocTag? {
@@ -44,6 +43,20 @@ object KDocFinder {
} }
} }
if (declaration is PropertyDescriptor) {
val containingClassDescriptor = declaration.getContainingDeclaration() as? ClassDescriptor
if (containingClassDescriptor != null) {
val classKDoc = findKDoc(containingClassDescriptor)?.getParentOfType<KDoc>(false)
if (classKDoc != null) {
val propertySection = classKDoc.findSectionByTag(KDocKnownTag.PROPERTY,
declaration.getName().asString())
if (propertySection != null) {
return propertySection
}
}
}
}
if (declaration is CallableDescriptor) { if (declaration is CallableDescriptor) {
for (baseDescriptor in declaration.getOverriddenDescriptors()) { for (baseDescriptor in declaration.getOverriddenDescriptors()) {
val baseKDoc = findKDoc(baseDescriptor.getOriginal()) val baseKDoc = findKDoc(baseDescriptor.getOriginal())
+4
View File
@@ -0,0 +1,4 @@
/**
* @property xyzzy Doc for property xyzzy
*/
class Foo(val xyzzy: String)
@@ -55,4 +55,13 @@ public class KDocFinderTest() : LightPlatformCodeInsightFixtureTestCase() {
val doc = KDocFinder.findKDoc(overriddenFunctionDescriptor) val doc = KDocFinder.findKDoc(overriddenFunctionDescriptor)
Assert.assertEquals("Doc for method xyzzy", doc!!.getContent()) Assert.assertEquals("Doc for method xyzzy", doc!!.getContent())
} }
public fun testProperty() {
myFixture.configureByFile(getTestName(false) + ".kt")
val declaration = (myFixture.getFile() as JetFile).getDeclarations().single { it.getName() == "Foo" }
val descriptor = declaration.resolveToDescriptor() as ClassDescriptor
val propertyDescriptor = descriptor.getDefaultType().getMemberScope().getProperties(Name.identifier("xyzzy")).single()
val doc = KDocFinder.findKDoc(propertyDescriptor)
Assert.assertEquals("Doc for property xyzzy", doc!!.getContent())
}
} }