idea: proper scoping support, completion now works

This commit is contained in:
2023-09-21 23:07:22 -07:00
parent c92a111af7
commit f60715dfb3
25 changed files with 265 additions and 83 deletions

View File

@ -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
}
}

View File

@ -26,9 +26,6 @@ object PorkElementHelpers {
fun referenceOfElement(element: PorkElement, type: NodeType): PsiReference? {
val textRangeOfSymbolInElement = element.childrenOfType<SymbolElement>().firstOrNull()?.textRangeInParent ?: return null
if (type == NodeType.FunctionDefinition) {
return PorkFunctionReference(element, textRangeOfSymbolInElement)
}
return null
return PorkIdentifierReference(element, textRangeOfSymbolInElement)
}
}

View File

@ -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<Any> {
return findFunctionDefinitions().toTypedArray()
}
fun findFunctionDefinitions(name: String? = null): List<FunctionDefinitionElement> {
val foundFunctionDefinitions = mutableListOf<FunctionDefinitionElement>()
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
}
}

View File

@ -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<Any> {
return findAllCandidates().toTypedArray()
}
fun findAllCandidates(name: String? = null): List<PorkElement> =
listOf(findAnyLocals(name), findAnyDefinitions(name)).flatten()
fun findAnyLocals(name: String? = null): List<PorkElement> {
val functionDefinitionElement = PsiTreeUtil.getParentOfType(element, FunctionDefinitionElement::class.java)
if (functionDefinitionElement == null) {
return emptyList()
}
val locals = mutableListOf<PorkElement>()
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<ForInItemElement>().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<ArgumentSpecElement>()
locals.addAll(argumentSpecElements)
return locals.filter { it.name == name }
}
fun findAnyDefinitions(name: String? = null): List<PorkElement> {
val foundDefinitions = mutableListOf<PorkNamedElement>()
for (file in getRelevantFiles()) {
val definitions = PsiTreeUtil.collectElements(file) { element ->
element is FunctionDefinitionElement ||
element is LetDefinitionElement
}.filterIsInstance<PorkNamedElement>()
if (name != null) {
val fileFoundDefinition = definitions.firstOrNull {
it.name == name
}
if (fileFoundDefinition != null) {
foundDefinitions.add(fileFoundDefinition)
return foundDefinitions
}
} else {
foundDefinitions.addAll(definitions)
}
}
return foundDefinitions
}
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)

View File

@ -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)

View File

@ -20,6 +20,9 @@
<lang.braceMatcher
language="Pork"
implementationClass="gay.pizza.pork.idea.PorkBraceMatcher"/>
<lang.refactoringSupport
language="Pork"
implementationClass="gay.pizza.pork.idea.PorkRefactoringSupportProvider"/>
<psi.declarationProvider implementation="gay.pizza.pork.idea.PorkSymbolDeclarationProvider"/>
</extensions>