From f60715dfb31cfa6ad58a05eb32453a7bbd216af6 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Thu, 21 Sep 2023 23:07:22 -0700 Subject: [PATCH] idea: proper scoping support, completion now works --- ast/src/main/ast/pork.yml | 13 ++- ast/src/main/graph/types.dot | 6 +- .../kotlin/gay/pizza/pork/ast/ArgumentSpec.kt | 22 ++++- .../main/kotlin/gay/pizza/pork/ast/ForIn.kt | 8 +- .../kotlin/gay/pizza/pork/ast/ForInItem.kt | 28 ++++++ .../gay/pizza/pork/ast/FunctionDefinition.kt | 2 +- .../gay/pizza/pork/ast/NodeCoalescer.kt | 6 ++ .../kotlin/gay/pizza/pork/ast/NodeParser.kt | 4 + .../pizza/pork/ast/NodeParserExtensions.kt | 2 + .../kotlin/gay/pizza/pork/ast/NodeType.kt | 2 + .../kotlin/gay/pizza/pork/ast/NodeVisitor.kt | 4 + .../pizza/pork/ast/NodeVisitorExtensions.kt | 2 + .../pizza/pork/evaluator/EvaluationVisitor.kt | 8 +- .../kotlin/gay/pizza/pork/parser/Parser.kt | 20 +++-- .../kotlin/gay/pizza/pork/parser/Printer.kt | 18 ++-- .../kotlin/gay/pizza/pork/parser/TokenType.kt | 4 +- .../idea/PorkRefactoringSupportProvider.kt | 11 +++ .../pizza/pork/idea/psi/PorkElementHelpers.kt | 5 +- .../pork/idea/psi/PorkFunctionReference.kt | 41 --------- .../pork/idea/psi/PorkIdentifierReference.kt | 90 +++++++++++++++++++ .../pork/idea/psi/gen/ArgumentSpecElement.kt | 17 ++++ .../pork/idea/psi/gen/ForInItemElement.kt | 17 ++++ .../pork/idea/psi/gen/PorkElementFactory.kt | 2 + .../pork/idea/psi/gen/SetAssignmentElement.kt | 13 +-- .../src/main/resources/META-INF/plugin.xml | 3 + 25 files changed, 265 insertions(+), 83 deletions(-) create mode 100644 ast/src/main/kotlin/gay/pizza/pork/ast/ForInItem.kt create mode 100644 support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/PorkRefactoringSupportProvider.kt delete mode 100644 support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/PorkFunctionReference.kt create mode 100644 support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/PorkIdentifierReference.kt create mode 100644 support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/ArgumentSpecElement.kt create mode 100644 support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/ForInItemElement.kt diff --git a/ast/src/main/ast/pork.yml b/ast/src/main/ast/pork.yml index 1e31aa9..199c562 100644 --- a/ast/src/main/ast/pork.yml +++ b/ast/src/main/ast/pork.yml @@ -52,7 +52,6 @@ types: type: Expression SetAssignment: parent: Expression - namedElementValue: symbol values: - name: symbol type: Symbol @@ -138,6 +137,8 @@ types: - name: arguments type: List ArgumentSpec: + parent: Node + namedElementValue: symbol values: - name: symbol type: Symbol @@ -270,11 +271,17 @@ types: type: Expression - name: block type: Block - ForIn: - parent: Expression + ForInItem: + parent: Node + namedElementValue: symbol values: - name: symbol type: Symbol + ForIn: + parent: Expression + values: + - name: item + type: ForInItem - name: expression type: Expression - name: block diff --git a/ast/src/main/graph/types.dot b/ast/src/main/graph/types.dot index ee65a08..b887e13 100644 --- a/ast/src/main/graph/types.dot +++ b/ast/src/main/graph/types.dot @@ -31,6 +31,7 @@ digraph A { type_StringLiteral [shape=box,label="StringLiteral"] type_SymbolReference [shape=box,label="SymbolReference"] type_While [shape=box,label="While"] + type_ForInItem [shape=box,label="ForInItem"] type_ForIn [shape=box,label="ForIn"] type_Break [shape=box,label="Break"] type_Continue [shape=box,label="Continue"] @@ -43,6 +44,8 @@ digraph A { type_Node -> type_Definition type_Node -> type_Block type_Node -> type_CompilationUnit + type_Node -> type_ArgumentSpec + type_Node -> type_ForInItem type_Node -> type_Native type_Expression -> type_LetAssignment type_Expression -> type_VarAssignment @@ -105,7 +108,8 @@ digraph A { type_SymbolReference -> type_Symbol [style=dotted] type_While -> type_Expression [style=dotted] type_While -> type_Block [style=dotted] - type_ForIn -> type_Symbol [style=dotted] + type_ForInItem -> type_Symbol [style=dotted] + type_ForIn -> type_ForInItem [style=dotted] type_ForIn -> type_Expression [style=dotted] type_ForIn -> type_Block [style=dotted] type_Native -> type_Symbol [style=dotted] diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/ArgumentSpec.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/ArgumentSpec.kt index 977565c..61dbc69 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/ArgumentSpec.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/ArgumentSpec.kt @@ -6,4 +6,24 @@ import kotlinx.serialization.Serializable @Serializable @SerialName("argumentSpec") -class ArgumentSpec(var symbol: Symbol, var multiple: Boolean = false) +class ArgumentSpec(val symbol: Symbol, val multiple: Boolean = false) : Node() { + override val type: NodeType = NodeType.ArgumentSpec + + override fun visitChildren(visitor: NodeVisitor): List = + visitor.visitNodes(symbol) + + override fun visit(visitor: NodeVisitor): T = + visitor.visitArgumentSpec(this) + + override fun equals(other: Any?): Boolean { + if (other !is ArgumentSpec) return false + return other.symbol == symbol && other.multiple == multiple + } + + override fun hashCode(): Int { + var result = symbol.hashCode() + result = 31 * result + multiple.hashCode() + result = 31 * result + type.hashCode() + return result + } +} diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/ForIn.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/ForIn.kt index b934fdd..3f71735 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/ForIn.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/ForIn.kt @@ -6,22 +6,22 @@ import kotlinx.serialization.Serializable @Serializable @SerialName("forIn") -class ForIn(val symbol: Symbol, val expression: Expression, val block: Block) : Expression() { +class ForIn(val item: ForInItem, val expression: Expression, val block: Block) : Expression() { override val type: NodeType = NodeType.ForIn override fun visitChildren(visitor: NodeVisitor): List = - visitor.visitNodes(symbol, expression, block) + visitor.visitNodes(item, expression, block) override fun visit(visitor: NodeVisitor): T = visitor.visitForIn(this) override fun equals(other: Any?): Boolean { if (other !is ForIn) return false - return other.symbol == symbol && other.expression == expression && other.block == block + return other.item == item && other.expression == expression && other.block == block } override fun hashCode(): Int { - var result = symbol.hashCode() + var result = item.hashCode() result = 31 * result + expression.hashCode() result = 31 * result + block.hashCode() result = 31 * result + type.hashCode() diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/ForInItem.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/ForInItem.kt new file mode 100644 index 0000000..1e6cfdc --- /dev/null +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/ForInItem.kt @@ -0,0 +1,28 @@ +// GENERATED CODE FROM PORK AST CODEGEN +package gay.pizza.pork.ast + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +@SerialName("forInItem") +class ForInItem(val symbol: Symbol) : Node() { + override val type: NodeType = NodeType.ForInItem + + override fun visitChildren(visitor: NodeVisitor): List = + visitor.visitNodes(symbol) + + override fun visit(visitor: NodeVisitor): T = + visitor.visitForInItem(this) + + override fun equals(other: Any?): Boolean { + if (other !is ForInItem) return false + return other.symbol == symbol + } + + override fun hashCode(): Int { + var result = symbol.hashCode() + result = 31 * result + type.hashCode() + return result + } +} diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/FunctionDefinition.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/FunctionDefinition.kt index 0862d80..b6f5759 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/FunctionDefinition.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/FunctionDefinition.kt @@ -10,7 +10,7 @@ class FunctionDefinition(override val modifiers: DefinitionModifiers, override v override val type: NodeType = NodeType.FunctionDefinition override fun visitChildren(visitor: NodeVisitor): List = - visitor.visitAll(listOf(symbol), listOf(block), listOf(native)) + visitor.visitAll(listOf(symbol), arguments, listOf(block), listOf(native)) override fun visit(visitor: NodeVisitor): T = visitor.visitFunctionDefinition(this) diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeCoalescer.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeCoalescer.kt index 925f032..159505e 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeCoalescer.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeCoalescer.kt @@ -2,6 +2,9 @@ package gay.pizza.pork.ast class NodeCoalescer(val handler: (Node) -> Unit) : NodeVisitor { + override fun visitArgumentSpec(node: ArgumentSpec): Unit = + handle(node) + override fun visitBlock(node: Block): Unit = handle(node) @@ -23,6 +26,9 @@ class NodeCoalescer(val handler: (Node) -> Unit) : NodeVisitor { override fun visitForIn(node: ForIn): Unit = handle(node) + override fun visitForInItem(node: ForInItem): Unit = + handle(node) + override fun visitFunctionCall(node: FunctionCall): Unit = handle(node) diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeParser.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeParser.kt index 555ba29..e68f5b0 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeParser.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeParser.kt @@ -2,6 +2,8 @@ package gay.pizza.pork.ast interface NodeParser { + fun parseArgumentSpec(): ArgumentSpec + fun parseBlock(): Block fun parseExpression(): Expression @@ -22,6 +24,8 @@ interface NodeParser { fun parseForIn(): ForIn + fun parseForInItem(): ForInItem + fun parseFunctionCall(): FunctionCall fun parseFunctionDefinition(): FunctionDefinition diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeParserExtensions.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeParserExtensions.kt index a159120..6bfbbe9 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeParserExtensions.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeParserExtensions.kt @@ -15,6 +15,7 @@ fun NodeParser.parse(type: NodeType): Node = NodeType.InfixOperation -> parseInfixOperation() NodeType.BooleanLiteral -> parseBooleanLiteral() NodeType.FunctionCall -> parseFunctionCall() + NodeType.ArgumentSpec -> parseArgumentSpec() NodeType.FunctionDefinition -> parseFunctionDefinition() NodeType.LetDefinition -> parseLetDefinition() NodeType.If -> parseIf() @@ -29,6 +30,7 @@ fun NodeParser.parse(type: NodeType): Node = NodeType.StringLiteral -> parseStringLiteral() NodeType.SymbolReference -> parseSymbolReference() NodeType.While -> parseWhile() + NodeType.ForInItem -> parseForInItem() NodeType.ForIn -> parseForIn() NodeType.Break -> parseBreak() NodeType.Continue -> parseContinue() diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeType.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeType.kt index 43442ea..4df9da1 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeType.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeType.kt @@ -3,6 +3,7 @@ package gay.pizza.pork.ast enum class NodeType(val parent: NodeType? = null) { Node, + ArgumentSpec(Node), Block(Node), Expression(Node), BooleanLiteral(Expression), @@ -13,6 +14,7 @@ enum class NodeType(val parent: NodeType? = null) { Definition(Node), DoubleLiteral(Expression), ForIn(Expression), + ForInItem(Node), FunctionCall(Expression), FunctionDefinition(Definition), If(Expression), diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitor.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitor.kt index 25a4cbb..3aec7e8 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitor.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitor.kt @@ -2,6 +2,8 @@ package gay.pizza.pork.ast interface NodeVisitor { + fun visitArgumentSpec(node: ArgumentSpec): T + fun visitBlock(node: Block): T fun visitBooleanLiteral(node: BooleanLiteral): T @@ -16,6 +18,8 @@ interface NodeVisitor { fun visitForIn(node: ForIn): T + fun visitForInItem(node: ForInItem): T + fun visitFunctionCall(node: FunctionCall): T fun visitFunctionDefinition(node: FunctionDefinition): T diff --git a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitorExtensions.kt b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitorExtensions.kt index 976ede6..f108247 100644 --- a/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitorExtensions.kt +++ b/ast/src/main/kotlin/gay/pizza/pork/ast/NodeVisitorExtensions.kt @@ -12,6 +12,7 @@ fun NodeVisitor.visit(node: Node): T = is InfixOperation -> visitInfixOperation(node) is BooleanLiteral -> visitBooleanLiteral(node) is FunctionCall -> visitFunctionCall(node) + is ArgumentSpec -> visitArgumentSpec(node) is FunctionDefinition -> visitFunctionDefinition(node) is LetDefinition -> visitLetDefinition(node) is If -> visitIf(node) @@ -26,6 +27,7 @@ fun NodeVisitor.visit(node: Node): T = is StringLiteral -> visitStringLiteral(node) is SymbolReference -> visitSymbolReference(node) is While -> visitWhile(node) + is ForInItem -> visitForInItem(node) is ForIn -> visitForIn(node) is Break -> visitBreak(node) is Continue -> visitContinue(node) diff --git a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt index 7ce0f1b..bb893e2 100644 --- a/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt +++ b/evaluator/src/main/kotlin/gay/pizza/pork/evaluator/EvaluationVisitor.kt @@ -26,7 +26,7 @@ class EvaluationVisitor(root: Scope, val stack: CallStack) : NodeVisitor { } scoped(reuseScope, node = node) { - currentScope.define(node.symbol.id, item ?: None) + currentScope.define(node.item.symbol.id, item ?: None) result = blockFunction.call() } } catch (_: BreakMarker) { @@ -39,6 +39,9 @@ class EvaluationVisitor(root: Scope, val stack: CallStack) : NodeVisitor { return result ?: None } + override fun visitForInItem(node: ForInItem): Any = + throw RuntimeException("Visiting ForInItem is not supported.") + override fun visitStringLiteral(node: StringLiteral): Any = node.text override fun visitBooleanLiteral(node: BooleanLiteral): Any = node.value @@ -363,6 +366,9 @@ class EvaluationVisitor(root: Scope, val stack: CallStack) : NodeVisitor { } } + override fun visitArgumentSpec(node: ArgumentSpec): Any = + throw RuntimeException("Visiting ArgumentSpec is not supported.") + override fun visitBlock(node: Block): BlockFunction { val visitor = this return object : BlockFunction() { diff --git a/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt b/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt index e93d869..90e1bf1 100644 --- a/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt +++ b/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt @@ -4,6 +4,11 @@ import gay.pizza.pork.ast.* class Parser(source: TokenSource, attribution: NodeAttribution) : ParserBase(source, attribution) { + override fun parseArgumentSpec(): ArgumentSpec = guarded(NodeType.ArgumentSpec) { + val symbol = parseSymbol() + ArgumentSpec(symbol, next(TokenType.DotDotDot)) + } + override fun parseBlock(): Block = guarded(NodeType.Block) { expect(TokenType.LeftCurly) val items = collect(TokenType.RightCurly) { @@ -177,11 +182,15 @@ class Parser(source: TokenSource, attribution: NodeAttribution) : override fun parseForIn(): ForIn = guarded(NodeType.ForIn) { expect(TokenType.For) - val symbol = parseSymbol() + val forInItem = parseForInItem() expect(TokenType.In) val value = parseExpression() val block = parseBlock() - ForIn(symbol, value, block) + ForIn(forInItem, value, block) + } + + override fun parseForInItem(): ForInItem = guarded(NodeType.ForInItem) { + ForInItem(parseSymbol()) } override fun parseFunctionCall(): FunctionCall = guarded(NodeType.FunctionCall) { @@ -200,12 +209,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) : val name = parseSymbol() expect(TokenType.LeftParentheses) val arguments = collect(TokenType.RightParentheses, TokenType.Comma) { - val symbol = parseSymbol() - var multiple = false - if (next(TokenType.DotDotDot)) { - multiple = true - } - ArgumentSpec(symbol, multiple) + parseArgumentSpec() } expect(TokenType.RightParentheses) diff --git a/parser/src/main/kotlin/gay/pizza/pork/parser/Printer.kt b/parser/src/main/kotlin/gay/pizza/pork/parser/Printer.kt index 5b78985..5d750e4 100644 --- a/parser/src/main/kotlin/gay/pizza/pork/parser/Printer.kt +++ b/parser/src/main/kotlin/gay/pizza/pork/parser/Printer.kt @@ -30,13 +30,17 @@ class Printer(buffer: StringBuilder) : NodeVisitor { override fun visitForIn(node: ForIn) { append("for ") - visit(node.symbol) + visit(node.item) append(" in ") visit(node.expression) append(" ") visit(node.block) } + override fun visitForInItem(node: ForInItem) { + node.symbol.visit(this) + } + override fun visitStringLiteral(node: StringLiteral) { append("\"") append(StringEscape.escape(node.text)) @@ -189,10 +193,7 @@ class Printer(buffer: StringBuilder) : NodeVisitor { visit(node.symbol) append("(") for ((index, argument) in node.arguments.withIndex()) { - visit(argument.symbol) - if (argument.multiple) { - append("...") - } + argument.visit(this) if (index + 1 != node.arguments.size) { append(", ") } @@ -207,6 +208,13 @@ class Printer(buffer: StringBuilder) : NodeVisitor { } } + override fun visitArgumentSpec(node: ArgumentSpec) { + node.symbol.visit(this) + if (node.multiple) { + append("...") + } + } + override fun visitBlock(node: Block) { append("{") if (node.expressions.isNotEmpty()) { diff --git a/parser/src/main/kotlin/gay/pizza/pork/parser/TokenType.kt b/parser/src/main/kotlin/gay/pizza/pork/parser/TokenType.kt index e21d0ea..c1fbea5 100644 --- a/parser/src/main/kotlin/gay/pizza/pork/parser/TokenType.kt +++ b/parser/src/main/kotlin/gay/pizza/pork/parser/TokenType.kt @@ -23,8 +23,8 @@ enum class TokenType(vararg properties: TokenTypeProperty) { Minus(SingleChar('-'), OperatorFamily, Promotion('-', MinusMinus)), Multiply(SingleChar('*'), OperatorFamily), Divide(SingleChar('/'), OperatorFamily), - And(ManyChars("and"), OperatorFamily), - Or(ManyChars("or"), OperatorFamily), + And(ManyChars("and"), KeywordFamily), + Or(ManyChars("or"), KeywordFamily), Tilde(SingleChar('~'), OperatorFamily), Ampersand(SingleChar('&'), OperatorFamily), Pipe(SingleChar('|'), OperatorFamily), diff --git a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/PorkRefactoringSupportProvider.kt b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/PorkRefactoringSupportProvider.kt new file mode 100644 index 0000000..9d7d4e7 --- /dev/null +++ b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/PorkRefactoringSupportProvider.kt @@ -0,0 +1,11 @@ +package gay.pizza.pork.idea + +import com.intellij.lang.refactoring.RefactoringSupportProvider +import com.intellij.psi.PsiElement +import gay.pizza.pork.idea.psi.gen.PorkNamedElement + +class PorkRefactoringSupportProvider : RefactoringSupportProvider() { + override fun isMemberInplaceRenameAvailable(element: PsiElement, context: PsiElement?): Boolean { + return element is PorkNamedElement + } +} diff --git a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/PorkElementHelpers.kt b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/PorkElementHelpers.kt index 94369f0..6aa41f6 100644 --- a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/PorkElementHelpers.kt +++ b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/PorkElementHelpers.kt @@ -26,9 +26,6 @@ object PorkElementHelpers { fun referenceOfElement(element: PorkElement, type: NodeType): PsiReference? { val textRangeOfSymbolInElement = element.childrenOfType().firstOrNull()?.textRangeInParent ?: return null - if (type == NodeType.FunctionDefinition) { - return PorkFunctionReference(element, textRangeOfSymbolInElement) - } - return null + return PorkIdentifierReference(element, textRangeOfSymbolInElement) } } diff --git a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/PorkFunctionReference.kt b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/PorkFunctionReference.kt deleted file mode 100644 index e367b5e..0000000 --- a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/PorkFunctionReference.kt +++ /dev/null @@ -1,41 +0,0 @@ -package gay.pizza.pork.idea.psi - -import com.intellij.openapi.util.TextRange -import com.intellij.psi.util.PsiTreeUtil -import gay.pizza.pork.idea.psi.gen.FunctionDefinitionElement -import gay.pizza.pork.idea.psi.gen.PorkElement - -class PorkFunctionReference(element: PorkElement, textRange: TextRange) : PorkReference(element, textRange) { - override fun resolve(): PorkElement? { - val functionName = canonicalText - val functionDefinitions = findFunctionDefinitions(functionName) - if (functionDefinitions.isNotEmpty()) { - return functionDefinitions.first() - } - return null - } - - override fun getVariants(): Array { - return findFunctionDefinitions().toTypedArray() - } - - fun findFunctionDefinitions(name: String? = null): List { - val foundFunctionDefinitions = mutableListOf() - for (file in getRelevantFiles()) { - val fileFunctionDefinitions = PsiTreeUtil.collectElementsOfType(file, FunctionDefinitionElement::class.java) - if (name != null) { - val fileFoundDefinition = fileFunctionDefinitions.firstOrNull { - it.name == name - } - - if (fileFoundDefinition != null) { - foundFunctionDefinitions.add(fileFoundDefinition) - return foundFunctionDefinitions - } - } else { - foundFunctionDefinitions.addAll(fileFunctionDefinitions) - } - } - return foundFunctionDefinitions - } -} diff --git a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/PorkIdentifierReference.kt b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/PorkIdentifierReference.kt new file mode 100644 index 0000000..6e1fa81 --- /dev/null +++ b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/PorkIdentifierReference.kt @@ -0,0 +1,90 @@ +package gay.pizza.pork.idea.psi + +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiElement +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.psi.util.childrenOfType +import gay.pizza.pork.idea.psi.gen.* + +class PorkIdentifierReference(element: PorkElement, textRange: TextRange) : PorkReference(element, textRange) { + override fun resolve(): PorkElement? { + val identifierName = canonicalText + val items = findAllCandidates(identifierName) + if (items.isNotEmpty()) { + return items.first() + } + return null + } + + override fun getVariants(): Array { + return findAllCandidates().toTypedArray() + } + + fun findAllCandidates(name: String? = null): List = + listOf(findAnyLocals(name), findAnyDefinitions(name)).flatten() + + fun findAnyLocals(name: String? = null): List { + val functionDefinitionElement = PsiTreeUtil.getParentOfType(element, FunctionDefinitionElement::class.java) + if (functionDefinitionElement == null) { + return emptyList() + } + val locals = mutableListOf() + + fun check(localCandidate: PsiElement, upward: Boolean) { + if (localCandidate is BlockElement && !upward) { + return + } + + if (localCandidate == element) { + return + } + + if (localCandidate is ArgumentSpecElement || + localCandidate is LetAssignmentElement || + localCandidate is VarAssignmentElement) { + locals.add(localCandidate as PorkElement) + } + + if (localCandidate is ForInElement) { + val forInItem = localCandidate.childrenOfType().first() + locals.add(forInItem) + } + + localCandidate.children.forEach { check(it, false) } + } + + PsiTreeUtil.treeWalkUp(element, functionDefinitionElement) { _, localCandidate -> + if (localCandidate != null) { + check(localCandidate, true) + } + true + } + + val argumentSpecElements = functionDefinitionElement.childrenOfType() + locals.addAll(argumentSpecElements) + return locals.filter { it.name == name } + } + + fun findAnyDefinitions(name: String? = null): List { + val foundDefinitions = mutableListOf() + for (file in getRelevantFiles()) { + val definitions = PsiTreeUtil.collectElements(file) { element -> + element is FunctionDefinitionElement || + element is LetDefinitionElement + }.filterIsInstance() + if (name != null) { + val fileFoundDefinition = definitions.firstOrNull { + it.name == name + } + + if (fileFoundDefinition != null) { + foundDefinitions.add(fileFoundDefinition) + return foundDefinitions + } + } else { + foundDefinitions.addAll(definitions) + } + } + return foundDefinitions + } +} diff --git a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/ArgumentSpecElement.kt b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/ArgumentSpecElement.kt new file mode 100644 index 0000000..905bfc9 --- /dev/null +++ b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/ArgumentSpecElement.kt @@ -0,0 +1,17 @@ +// GENERATED CODE FROM PORK AST CODEGEN +package gay.pizza.pork.idea.psi.gen + +import com.intellij.psi.PsiElement +import com.intellij.lang.ASTNode +import gay.pizza.pork.idea.psi.PorkElementHelpers + +class ArgumentSpecElement(node: ASTNode) : PorkNamedElement(node) { + override fun getName(): String? = + PorkElementHelpers.nameOfNamedElement(this) + + override fun setName(name: String): PsiElement = + PorkElementHelpers.setNameOfNamedElement(this, name) + + override fun getNameIdentifier(): PsiElement? = + PorkElementHelpers.nameIdentifierOfNamedElement(this) +} diff --git a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/ForInItemElement.kt b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/ForInItemElement.kt new file mode 100644 index 0000000..6d26830 --- /dev/null +++ b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/ForInItemElement.kt @@ -0,0 +1,17 @@ +// GENERATED CODE FROM PORK AST CODEGEN +package gay.pizza.pork.idea.psi.gen + +import com.intellij.psi.PsiElement +import com.intellij.lang.ASTNode +import gay.pizza.pork.idea.psi.PorkElementHelpers + +class ForInItemElement(node: ASTNode) : PorkNamedElement(node) { + override fun getName(): String? = + PorkElementHelpers.nameOfNamedElement(this) + + override fun setName(name: String): PsiElement = + PorkElementHelpers.setNameOfNamedElement(this, name) + + override fun getNameIdentifier(): PsiElement? = + PorkElementHelpers.nameIdentifierOfNamedElement(this) +} diff --git a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/PorkElementFactory.kt b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/PorkElementFactory.kt index ce088e8..89a09ba 100644 --- a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/PorkElementFactory.kt +++ b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/PorkElementFactory.kt @@ -19,6 +19,7 @@ object PorkElementFactory { NodeType.InfixOperation -> InfixOperationElement(node) NodeType.BooleanLiteral -> BooleanLiteralElement(node) NodeType.FunctionCall -> FunctionCallElement(node) + NodeType.ArgumentSpec -> ArgumentSpecElement(node) NodeType.FunctionDefinition -> FunctionDefinitionElement(node) NodeType.LetDefinition -> LetDefinitionElement(node) NodeType.If -> IfElement(node) @@ -33,6 +34,7 @@ object PorkElementFactory { NodeType.StringLiteral -> StringLiteralElement(node) NodeType.SymbolReference -> SymbolReferenceElement(node) NodeType.While -> WhileElement(node) + NodeType.ForInItem -> ForInItemElement(node) NodeType.ForIn -> ForInElement(node) NodeType.Break -> BreakElement(node) NodeType.Continue -> ContinueElement(node) diff --git a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/SetAssignmentElement.kt b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/SetAssignmentElement.kt index 2b875e5..12bd63b 100644 --- a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/SetAssignmentElement.kt +++ b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/psi/gen/SetAssignmentElement.kt @@ -1,17 +1,6 @@ // GENERATED CODE FROM PORK AST CODEGEN package gay.pizza.pork.idea.psi.gen -import com.intellij.psi.PsiElement import com.intellij.lang.ASTNode -import gay.pizza.pork.idea.psi.PorkElementHelpers -class SetAssignmentElement(node: ASTNode) : PorkNamedElement(node) { - override fun getName(): String? = - PorkElementHelpers.nameOfNamedElement(this) - - override fun setName(name: String): PsiElement = - PorkElementHelpers.setNameOfNamedElement(this, name) - - override fun getNameIdentifier(): PsiElement? = - PorkElementHelpers.nameIdentifierOfNamedElement(this) -} +class SetAssignmentElement(node: ASTNode) : PorkElement(node) diff --git a/support/pork-idea/src/main/resources/META-INF/plugin.xml b/support/pork-idea/src/main/resources/META-INF/plugin.xml index 6087c44..005f081 100644 --- a/support/pork-idea/src/main/resources/META-INF/plugin.xml +++ b/support/pork-idea/src/main/resources/META-INF/plugin.xml @@ -20,6 +20,9 @@ +