Skip to content

Commit 89c8f6d

Browse files
committed
Add support for game variables
1 parent b3f06bc commit 89c8f6d

18 files changed

+208
-13
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
### Added
55
- Support for basic spell checking and custom dictionary.
66
- Support for specific parameter types in triggers.
7+
- Support for game variable type checking/refactoring/find usages.
78

89
## [1.1.0] - 2023-08-11
910

src/main/gen/io/runescript/plugin/lang/psi/RsElementTypes.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public interface RsElementTypes {
4343
IElementType RELATIONAL_VALUE_EXPRESSION = new RsElementType("RELATIONAL_VALUE_EXPRESSION");
4444
IElementType RETURN_LIST = StubElementTypeFactory.create("RETURN_LIST");
4545
IElementType RETURN_STATEMENT = new RsElementType("RETURN_STATEMENT");
46-
IElementType SCOPED_VARIABLE_EXPRESSION = new RsElementType("SCOPED_VARIABLE_EXPRESSION");
46+
IElementType SCOPED_VARIABLE_EXPRESSION = StubElementTypeFactory.create("SCOPED_VARIABLE_EXPRESSION");
4747
IElementType SCRIPT = StubElementTypeFactory.create("SCRIPT");
4848
IElementType STATEMENT = new RsElementType("STATEMENT");
4949
IElementType STATEMENT_LIST = new RsElementType("STATEMENT_LIST");

src/main/gen/io/runescript/plugin/lang/psi/RsScopedVariableExpression.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
import java.util.List;
55
import org.jetbrains.annotations.*;
66
import com.intellij.psi.PsiElement;
7+
import com.intellij.psi.StubBasedPsiElement;
8+
import io.runescript.plugin.lang.stubs.RsScopedVariableExpressionStub;
79

8-
public interface RsScopedVariableExpression extends RsExpression {
10+
public interface RsScopedVariableExpression extends RsExpression, RsNamedElement, StubBasedPsiElement<RsScopedVariableExpressionStub> {
911

1012
@NotNull
1113
RsNameLiteral getNameLiteral();

src/main/gen/io/runescript/plugin/lang/psi/RsVisitor.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ public void visitReturnStatement(@NotNull RsReturnStatement o) {
155155

156156
public void visitScopedVariableExpression(@NotNull RsScopedVariableExpression o) {
157157
visitExpression(o);
158+
// visitNamedElement(o);
158159
}
159160

160161
public void visitScript(@NotNull RsScript o) {

src/main/gen/io/runescript/plugin/lang/psi/impl/RsScopedVariableExpressionImpl.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,26 @@
88
import com.intellij.psi.PsiElementVisitor;
99
import com.intellij.psi.util.PsiTreeUtil;
1010
import static io.runescript.plugin.lang.psi.RsElementTypes.*;
11+
import io.runescript.plugin.lang.psi.mixin.RsScopedVariableExpressionMixin;
1112
import io.runescript.plugin.lang.psi.*;
13+
import io.runescript.plugin.lang.stubs.RsScopedVariableExpressionStub;
14+
import com.intellij.psi.stubs.IStubElementType;
15+
import com.intellij.psi.tree.IElementType;
1216

13-
public class RsScopedVariableExpressionImpl extends RsExpressionImpl implements RsScopedVariableExpression {
17+
public class RsScopedVariableExpressionImpl extends RsScopedVariableExpressionMixin implements RsScopedVariableExpression {
1418

1519
public RsScopedVariableExpressionImpl(@NotNull ASTNode node) {
1620
super(node);
1721
}
1822

19-
@Override
23+
public RsScopedVariableExpressionImpl(@NotNull RsScopedVariableExpressionStub stub, @NotNull IStubElementType<?, ?> type) {
24+
super(stub, type);
25+
}
26+
27+
public RsScopedVariableExpressionImpl(@Nullable RsScopedVariableExpressionStub stub, @Nullable IElementType type, @Nullable ASTNode node) {
28+
super(stub, type, node);
29+
}
30+
2031
public void accept(@NotNull RsVisitor visitor) {
2132
visitor.visitScopedVariableExpression(this);
2233
}
@@ -30,7 +41,7 @@ public void accept(@NotNull PsiElementVisitor visitor) {
3041
@Override
3142
@NotNull
3243
public RsNameLiteral getNameLiteral() {
33-
return notNullChild(PsiTreeUtil.getChildOfType(this, RsNameLiteral.class));
44+
return notNullChild(PsiTreeUtil.getStubChildOfType(this, RsNameLiteral.class));
3445
}
3546

3647
@Override

src/main/grammars/RuneScript.bnf

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,13 @@ LocalVariableExpression ::= '$' NameLiteral {
203203
}
204204

205205
ArrayAccessExpression ::= LocalVariableExpression '(' Expression ')'
206-
ScopedVariableExpression ::= '%' NameLiteral
206+
ScopedVariableExpression ::= '%' NameLiteral {
207+
mixin="io.runescript.plugin.lang.psi.mixin.RsScopedVariableExpressionMixin"
208+
stubClass="io.runescript.plugin.lang.stubs.RsScopedVariableExpressionStub"
209+
elementTypeFactory = "io.runescript.plugin.lang.stubs.StubElementTypeFactory.create"
210+
implements="io.runescript.plugin.lang.psi.RsNamedElement"
211+
}
212+
207213
private LiteralExpression ::= IntegerLiteralExpression
208214
| CoordLiteralExpression
209215
| BooleanLiteralExpression
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package io.runescript.plugin.ide.inspections
2+
3+
import com.intellij.codeInspection.LocalInspectionTool
4+
import com.intellij.codeInspection.ProblemHighlightType
5+
import com.intellij.codeInspection.ProblemsHolder
6+
import com.intellij.psi.PsiElementVisitor
7+
import io.runescript.plugin.ide.RsBundle
8+
import io.runescript.plugin.lang.psi.RsScopedVariableExpression
9+
import io.runescript.plugin.lang.psi.RsVisitor
10+
11+
class RuneScriptUnresolvedScopedVariableInspection : LocalInspectionTool() {
12+
13+
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
14+
return object : RsVisitor() {
15+
override fun visitScopedVariableExpression(o: RsScopedVariableExpression) {
16+
val reference = o.reference ?: return
17+
if (reference.resolve() == null) {
18+
holder.registerProblem(o,
19+
RsBundle.message("inspection.error.unresolved.scoped.variable", o.nameLiteral.text),
20+
ProblemHighlightType.LIKE_UNKNOWN_SYMBOL
21+
)
22+
}
23+
}
24+
}
25+
}
26+
}

src/main/kotlin/io/runescript/plugin/ide/usages/RsReadWriteAccessDetector.kt

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,24 @@ package io.runescript.plugin.ide.usages
33
import com.intellij.codeInsight.highlighting.ReadWriteAccessDetector
44
import com.intellij.psi.PsiElement
55
import com.intellij.psi.PsiReference
6-
import io.runescript.plugin.lang.psi.RsAssignmentStatement
7-
import io.runescript.plugin.lang.psi.RsDynamicExpression
8-
import io.runescript.plugin.lang.psi.RsLocalVariableDeclarationStatement
9-
import io.runescript.plugin.lang.psi.RsLocalVariableExpression
6+
import io.runescript.plugin.lang.psi.*
7+
import io.runescript.plugin.symbollang.psi.RsSymSymbol
8+
import io.runescript.plugin.symbollang.psi.isVarFile
109

1110
class RsReadWriteAccessDetector : ReadWriteAccessDetector() {
1211

1312
// TODO(Walied): Add array variable support and do not assume declaration
1413
// without initializer are write accesses.
1514

1615
override fun isReadWriteAccessible(element: PsiElement): Boolean {
16+
if (element is RsSymSymbol) {
17+
return element.containingFile?.virtualFile?.isVarFile() ?: false
18+
}
1719
return element is RsLocalVariableExpression
1820
}
1921

2022
override fun isDeclarationWriteAccess(element: PsiElement): Boolean {
21-
require(element is RsLocalVariableExpression)
23+
require(element is RsLocalVariableExpression || element is RsSymSymbol)
2224
val parent = element.parent
2325
return element is RsAssignmentStatement || parent is RsLocalVariableDeclarationStatement
2426
}
@@ -28,7 +30,9 @@ class RsReadWriteAccessDetector : ReadWriteAccessDetector() {
2830
}
2931

3032
override fun getExpressionAccess(expression: PsiElement): Access {
31-
require(expression is RsLocalVariableExpression || expression is RsDynamicExpression)
33+
require(expression is RsLocalVariableExpression
34+
|| expression is RsScopedVariableExpression
35+
|| expression is RsDynamicExpression)
3236
val parent = expression.parent
3337
if (parent is RsAssignmentStatement || parent is RsLocalVariableDeclarationStatement) {
3438
return Access.Write
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package io.runescript.plugin.lang.psi.mixin
2+
3+
import com.intellij.extapi.psi.StubBasedPsiElementBase
4+
import com.intellij.lang.ASTNode
5+
import com.intellij.psi.PsiElement
6+
import com.intellij.psi.PsiReference
7+
import com.intellij.psi.search.GlobalSearchScope
8+
import com.intellij.psi.search.SearchScope
9+
import com.intellij.psi.stubs.IStubElementType
10+
import com.intellij.psi.tree.IElementType
11+
import com.intellij.refactoring.suggested.startOffset
12+
import io.runescript.plugin.lang.psi.RsPsiImplUtil
13+
import io.runescript.plugin.lang.psi.RsScopedVariableExpression
14+
import io.runescript.plugin.lang.psi.refs.RsScopedVariableReference
15+
import io.runescript.plugin.lang.stubs.RsScopedVariableExpressionStub
16+
17+
abstract class RsScopedVariableExpressionMixin : StubBasedPsiElementBase<RsScopedVariableExpressionStub>, RsScopedVariableExpression {
18+
19+
constructor(node: ASTNode) : super(node)
20+
constructor(stub: RsScopedVariableExpressionStub, type: IStubElementType<*, *>) : super(stub, type)
21+
constructor(stub: RsScopedVariableExpressionStub?, type: IElementType?, node: ASTNode?) : super(stub, type, node)
22+
23+
override fun getUseScope(): SearchScope {
24+
return GlobalSearchScope.projectScope(project)
25+
}
26+
27+
override fun getReference(): PsiReference? {
28+
return RsScopedVariableReference(this)
29+
}
30+
31+
override fun getTextOffset(): Int {
32+
return nameLiteral.startOffset
33+
}
34+
35+
override fun getNameIdentifier(): PsiElement {
36+
return nameLiteral
37+
}
38+
39+
override fun getName(): String {
40+
return RsPsiImplUtil.getName(nameLiteral)
41+
}
42+
43+
override fun setName(newName: String): PsiElement {
44+
return RsPsiImplUtil.setName(nameLiteral, newName)
45+
}
46+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package io.runescript.plugin.lang.psi.refs
2+
3+
import com.intellij.psi.*
4+
import io.runescript.plugin.lang.psi.RsScopedVariableExpression
5+
import io.runescript.plugin.lang.psi.type.RsPrimitiveType
6+
import io.runescript.plugin.symbollang.psi.index.RsSymbolIndex
7+
8+
class RsScopedVariableReference(element: RsScopedVariableExpression) :
9+
PsiReferenceBase<RsScopedVariableExpression>(element, element.nameLiteral.textRangeInParent), PsiPolyVariantReference {
10+
11+
override fun resolve(): PsiElement? {
12+
val result = multiResolve(false)
13+
return result.singleOrNull()?.element
14+
}
15+
16+
override fun multiResolve(incompleteCode: Boolean): Array<ResolveResult> {
17+
return scopedVarTypes
18+
.mapNotNull { RsSymbolIndex.lookup(element.project, it, element.name!!) }
19+
.map { PsiElementResolveResult(it) }
20+
.toTypedArray()
21+
}
22+
23+
override fun handleElementRename(newElementName: String): PsiElement {
24+
element.setName(newElementName)
25+
return element
26+
}
27+
28+
companion object {
29+
private val scopedVarTypes = arrayOf(
30+
RsPrimitiveType.VARP,
31+
RsPrimitiveType.VARC,
32+
RsPrimitiveType.VARBIT
33+
)
34+
}
35+
}

0 commit comments

Comments
 (0)