Skip to content

Commit 504f07d

Browse files
committed
init
0 parents  commit 504f07d

12 files changed

+323
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
*.jar
2+
out/

example/foo.php

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
/**
4+
* @param array<DateTimeImmutable> $dates
5+
* @return iterable<string, DateTimeImmutable>
6+
*/
7+
function addYearToDates(array $dates)
8+
{
9+
foreach ($dates as &$date) {
10+
$date = $date->modify('+1 year');
11+
}
12+
return $dates;
13+
}
14+
15+
foreach (addYearToDates([]) as $date) {
16+
$date->format('Y-m-d');
17+
}
18+

intellij-better-phpdoc.iml

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<module type="PLUGIN_MODULE" version="4">
3+
<component name="DevKit.ModuleBuildProperties" url="file://$MODULE_DIR$/resources/META-INF/plugin.xml" />
4+
<component name="NewModuleRootManager" inherit-compiler-output="true">
5+
<exclude-output />
6+
<content url="file://$MODULE_DIR$">
7+
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
8+
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
9+
</content>
10+
<orderEntry type="inheritedJdk" />
11+
<orderEntry type="sourceFolder" forTests="false" />
12+
<orderEntry type="library" scope="PROVIDED" name="php" level="application" />
13+
</component>
14+
</module>

resources/META-INF/plugin.xml

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<idea-plugin>
2+
<id>com.matej21.intellij.betterPhpDoc</id>
3+
<name>Better PhpDoc</name>
4+
<version>0.1</version>
5+
<vendor email="[email protected]">David Matejka</vendor>
6+
7+
<depends>com.intellij.modules.lang</depends>
8+
<depends>com.jetbrains.php</depends>
9+
10+
<description><![CDATA[
11+
]]></description>
12+
13+
<change-notes><![CDATA[
14+
]]>
15+
</change-notes>
16+
17+
<idea-version since-build="172.0"/>
18+
19+
20+
<extensions defaultExtensionNs="com.intellij">
21+
<lang.parserDefinition language="PHP" implementationClass="com.matej21.intellij.betterPhpDoc.ParserDefinition" order="first"/>
22+
</extensions>
23+
24+
<actions>
25+
<!-- Add your actions here -->
26+
</actions>
27+
28+
</idea-plugin>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package com.matej21.intellij.betterPhpDoc
2+
3+
import com.intellij.lang.ASTNode
4+
import com.intellij.openapi.util.NullableLazyValue
5+
import com.intellij.psi.PsiElement
6+
import com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocComment
7+
import com.jetbrains.php.lang.documentation.phpdoc.psi.impl.tags.PhpDocParamTagImpl
8+
import com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocParamTag
9+
import com.jetbrains.php.lang.documentation.phpdoc.psi.tags.PhpDocTag
10+
import com.jetbrains.php.lang.psi.elements.Method
11+
import com.jetbrains.php.lang.psi.elements.Parameter
12+
import com.jetbrains.php.lang.psi.elements.PhpPsiElement
13+
import com.jetbrains.php.lang.psi.resolve.types.PhpType
14+
15+
16+
class MyPhpDocParamTag(node: ASTNode) : PhpDocParamTagImpl(node) {
17+
18+
override fun getVarName(): String? {
19+
val varName = super.getVarName()
20+
if (varName != null && !varName.isEmpty()) {
21+
return varName
22+
}
23+
val parameter = getCorrespondingParameter()
24+
if (parameter != null) {
25+
return parameter.name
26+
}
27+
return ""
28+
}
29+
30+
override fun getType(): PhpType {
31+
val type = super.getType()
32+
val hasVarNameDefinition = !(super.getVarName() ?: "").isEmpty()
33+
if (hasVarNameDefinition) {
34+
return type
35+
}
36+
val parameter = getCorrespondingParameter() ?: return type
37+
type.add(parameter.declaredType)
38+
39+
return type
40+
}
41+
42+
private fun getCorrespondingParameter(): Parameter? {
43+
var prevDocTag = prevPsiSibling
44+
var nthParameter: Int = 0
45+
while (prevDocTag != null && prevDocTag is PhpDocTag) {
46+
if (prevDocTag is PhpDocParamTag) {
47+
nthParameter++
48+
}
49+
prevDocTag = prevDocTag.prevPsiSibling
50+
}
51+
val comment = parent as PhpDocComment
52+
val nextSibling = comment.nextPsiSibling
53+
val method = nextSibling as? Method ?: return null
54+
if (method.parameters.size > nthParameter) {
55+
return method.parameters[nthParameter]
56+
}
57+
return null
58+
}
59+
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.matej21.intellij.betterPhpDoc
2+
3+
import com.jetbrains.php.lang.documentation.phpdoc.parser.tags.PhpDocParamTagParser
4+
import com.jetbrains.php.lang.documentation.phpdoc.parser.tags.PhpDocTagParser
5+
import com.jetbrains.php.lang.parser.PhpPsiBuilder
6+
7+
8+
class MyPhpDocParamTagParser : PhpDocParamTagParser() {
9+
10+
override fun parseContents(builder: PhpPsiBuilder): Boolean {
11+
if (PhpDocTagParser.parseVar(builder)) {
12+
return true
13+
}
14+
var start = builder.mark()
15+
if (builder.parseTypes() && PhpDocTagParser.parseVar(builder)) {
16+
start.drop()
17+
return true
18+
}
19+
start.rollbackTo()
20+
start = builder.mark()
21+
if (builder.parseTypes()) {
22+
start.drop()
23+
return true
24+
}
25+
start.rollbackTo()
26+
return false
27+
}
28+
29+
30+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.matej21.intellij.betterPhpDoc
2+
3+
import com.jetbrains.php.lang.documentation.phpdoc.parser.tags.PhpDocPropertyTagParser
4+
import com.jetbrains.php.lang.documentation.phpdoc.parser.tags.PhpDocTagParser
5+
import com.jetbrains.php.lang.parser.PhpPsiBuilder
6+
7+
class MyPhpDocPropertyTagParser() : PhpDocPropertyTagParser() {
8+
override fun parseContents(builder: PhpPsiBuilder): Boolean {
9+
builder.parseTypes()
10+
PhpDocTagParser.parseProperty(builder)
11+
return true
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.matej21.intellij.betterPhpDoc
2+
3+
import com.jetbrains.php.lang.documentation.phpdoc.parser.tags.PhpDocReturnTagParser
4+
import com.jetbrains.php.lang.parser.PhpPsiBuilder
5+
6+
class MyPhpDocReturnTagParser() : PhpDocReturnTagParser() {
7+
override fun parseContents(builder: PhpPsiBuilder): Boolean {
8+
builder.parseTypes()
9+
return true
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.matej21.intellij.betterPhpDoc
2+
3+
import com.intellij.lang.ASTNode
4+
import com.jetbrains.php.lang.documentation.phpdoc.psi.impl.PhpDocTypeImpl
5+
import com.jetbrains.php.lang.documentation.phpdoc.parser.PhpDocElementTypes;
6+
import com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocType
7+
import com.jetbrains.php.lang.psi.resolve.types.PhpType
8+
9+
class MyPhpDocType(node: ASTNode) : PhpDocTypeImpl(node) {
10+
override fun getType(): PhpType {
11+
val type = super.getType()
12+
if (!this.text.contains("<")) {
13+
return type
14+
}
15+
val subTypes = this.findChildrenByType<PhpDocType>(PhpDocElementTypes.phpDocType)
16+
17+
when (this.name) {
18+
"array", "iterable" -> {
19+
when (subTypes.size) {
20+
1 -> subTypes[0].type.pluralise()
21+
2 -> subTypes[1].type.pluralise()
22+
else -> null
23+
}
24+
}
25+
else -> null
26+
}?.apply {
27+
type.add(this)
28+
}
29+
30+
return type
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.matej21.intellij.betterPhpDoc
2+
3+
import com.jetbrains.php.lang.documentation.phpdoc.parser.tags.PhpDocTagParser
4+
import com.jetbrains.php.lang.documentation.phpdoc.parser.tags.PhpDocVarTagParser
5+
import com.jetbrains.php.lang.parser.PhpPsiBuilder
6+
7+
class MyPhpDocVarTagParser() : PhpDocVarTagParser() {
8+
9+
override fun parseContents(builder: PhpPsiBuilder): Boolean {
10+
if (PhpDocTagParser.parseVar(builder)) {
11+
builder.parseTypes()
12+
return true
13+
} else {
14+
val start = builder.mark()
15+
if (builder.parseTypes() && PhpDocTagParser.parseVar(builder)) {
16+
start.drop()
17+
return true
18+
} else {
19+
start.rollbackTo()
20+
if (!builder.parseTypes()) {
21+
}
22+
23+
return false
24+
}
25+
}
26+
}
27+
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.matej21.intellij.betterPhpDoc
2+
3+
4+
import com.intellij.lang.ASTNode
5+
import com.intellij.psi.PsiElement
6+
import com.jetbrains.php.lang.documentation.phpdoc.parser.PhpDocParser
7+
import com.jetbrains.php.lang.documentation.phpdoc.parser.tags.PhpDocTagParserRegistry
8+
import com.jetbrains.php.lang.documentation.phpdoc.psi.PhpDocPsiCreator
9+
import com.jetbrains.php.lang.parser.PhpParserDefinition
10+
11+
12+
class ParserDefinition() : PhpParserDefinition() {
13+
14+
15+
override fun createElement(node: ASTNode): PsiElement {
16+
17+
val type = node.elementType
18+
if (type === PhpDocPsiCreator.phpDocParam) {
19+
return MyPhpDocParamTag(node)
20+
} else if (type === PhpDocPsiCreator.phpDocType) {
21+
return MyPhpDocType(node)
22+
}
23+
return super.createElement(node)
24+
}
25+
26+
init {
27+
PhpDocParser() //initialize phpdoc parser registry
28+
PhpDocTagParserRegistry.register("@param", MyPhpDocParamTagParser())
29+
PhpDocTagParserRegistry.register("@property", MyPhpDocPropertyTagParser())
30+
PhpDocTagParserRegistry.register("@return", MyPhpDocReturnTagParser())
31+
PhpDocTagParserRegistry.register("@var", MyPhpDocVarTagParser())
32+
}
33+
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.matej21.intellij.betterPhpDoc
2+
3+
import com.intellij.lang.PsiBuilder
4+
import com.jetbrains.php.lang.documentation.phpdoc.lexer.PhpDocTokenTypes
5+
import com.jetbrains.php.lang.documentation.phpdoc.parser.PhpDocElementTypes
6+
import com.jetbrains.php.lang.parser.PhpPsiBuilder
7+
import com.jetbrains.php.lang.parser.parsing.Namespace
8+
9+
fun PhpPsiBuilder.parseTypes(): Boolean {
10+
11+
var atLeastOneTypeParsed = false
12+
13+
while (this.compare(PhpDocTokenTypes.DOC_IDENTIFIER)
14+
|| this.compare(PhpDocTokenTypes.DOC_NAMESPACE)
15+
|| this.compare(PhpDocTokenTypes.DOC_VARIABLE) && "\$this" == this.tokenText
16+
|| this.compare(PhpDocTokenTypes.DOC_LPAREN)
17+
|| this.compare(PhpDocTokenTypes.DOC_HASH)
18+
) {
19+
this.compareAndEat(PhpDocTokenTypes.DOC_LPAREN)
20+
val type = this.mark()
21+
this.compareAndEat(PhpDocTokenTypes.DOC_HASH)
22+
Namespace.parseReference(this)
23+
if (!this.compareAndEat(PhpDocTokenTypes.DOC_IDENTIFIER) && "\$this" == this.tokenText) {
24+
this.advanceLexer()
25+
}
26+
27+
var array: PsiBuilder.Marker
28+
array = this.mark()
29+
while (this.compareAndEat(PhpDocTokenTypes.DOC_LBRACKET) && this.compareAndEat(PhpDocTokenTypes.DOC_RBRACKET)) {
30+
array.drop()
31+
array = this.mark()
32+
}
33+
34+
array.rollbackTo()
35+
if (this.compare(PhpDocTokenTypes.DOC_TEXT) && this.tokenText == "<") {
36+
this.advanceLexer()
37+
do {
38+
this.parseTypes()
39+
} while (this.compareAndEat(PhpDocTokenTypes.DOC_COMMA))
40+
if (this.compare(PhpDocTokenTypes.DOC_TEXT) && this.tokenText == ">") {
41+
this.advanceLexer()
42+
}
43+
}
44+
atLeastOneTypeParsed = true
45+
type.done(PhpDocElementTypes.phpDocType)
46+
this.compareAndEat(PhpDocTokenTypes.DOC_RPAREN)
47+
if (!this.compareAndEat(PhpDocTokenTypes.DOC_PIPE) && !this.compareAndEat(PhpDocTokenTypes.DOC_AMPERSAND)) {
48+
break
49+
}
50+
}
51+
52+
return atLeastOneTypeParsed
53+
}

0 commit comments

Comments
 (0)