mirror of
https://github.com/GayPizzaSpecifications/pork.git
synced 2025-08-03 05:10:55 +00:00
pork: it's got it all, ffi, state machine tokenizer, and better IDE support
This commit is contained in:
@ -8,13 +8,14 @@ import com.intellij.platform.backend.navigation.NavigationRequest
|
||||
import com.intellij.platform.backend.navigation.NavigationTarget
|
||||
import com.intellij.platform.backend.presentation.TargetPresentation
|
||||
import gay.pizza.pork.idea.psi.gen.PorkElement
|
||||
import gay.pizza.pork.idea.resolution.PorkReferenceResolution
|
||||
|
||||
@Suppress("UnstableApiUsage")
|
||||
data class PorkDeclarationSymbol(val module: String, val name: String) : Symbol, NavigatableSymbol {
|
||||
override fun createPointer(): Pointer<out Symbol> = Pointer { this }
|
||||
override fun getNavigationTargets(project: Project): MutableCollection<out NavigationTarget> {
|
||||
return PorkReferenceResolution.getAllProjectPorkFiles(project)
|
||||
.flatMap { PorkReferenceResolution.findAnyDefinitions(it) }
|
||||
.flatMap { PorkReferenceResolution.findAnyDefinitions(it.file) }
|
||||
.map { PorkNavigationTarget(it) }
|
||||
.toMutableList()
|
||||
}
|
||||
|
@ -35,6 +35,10 @@ object PorkElementTypes {
|
||||
elementTypeFor(TokenType.StringLiteral)
|
||||
)
|
||||
|
||||
val QuoteSet = TokenSet.create(
|
||||
elementTypeFor(TokenType.Quote)
|
||||
)
|
||||
|
||||
fun tokenTypeFor(elementType: IElementType): TokenType? =
|
||||
elementTypeToTokenType[elementType]
|
||||
|
||||
|
@ -10,4 +10,8 @@ class PorkFile(viewProvider: FileViewProvider) : PsiFileBase(viewProvider, PorkL
|
||||
}
|
||||
|
||||
override fun toString(): String = "Pork"
|
||||
|
||||
override fun isPhysical(): Boolean {
|
||||
return super.isPhysical()
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,34 @@
|
||||
package gay.pizza.pork.idea
|
||||
|
||||
import com.intellij.codeInsight.hints.InlayInfo
|
||||
import com.intellij.codeInsight.hints.InlayParameterHintsProvider
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.childrenOfType
|
||||
import gay.pizza.pork.idea.psi.gen.ArgumentSpecElement
|
||||
import gay.pizza.pork.idea.psi.gen.FunctionDefinitionElement
|
||||
|
||||
@Suppress("UnstableApiUsage")
|
||||
class PorkInlayParameterHintsProvider : InlayParameterHintsProvider {
|
||||
override fun getParameterHints(element: PsiElement): MutableList<InlayInfo> {
|
||||
val inlays = mutableListOf<InlayInfo>()
|
||||
val resolved = element.reference?.resolve()
|
||||
if (resolved !is FunctionDefinitionElement) {
|
||||
return inlays
|
||||
}
|
||||
val argumentSpecs = resolved.childrenOfType<ArgumentSpecElement>()
|
||||
val arguments = if (element.children.isNotEmpty()) {
|
||||
element.children.drop(1)
|
||||
} else emptyList()
|
||||
|
||||
for ((argument, spec) in arguments.zip(argumentSpecs)) {
|
||||
val name = spec.name
|
||||
if (name != null) {
|
||||
inlays.add(InlayInfo(name, argument.textOffset))
|
||||
}
|
||||
}
|
||||
return inlays
|
||||
}
|
||||
|
||||
override fun getDefaultBlackList(): MutableSet<String> =
|
||||
mutableSetOf()
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
package gay.pizza.pork.idea
|
||||
|
||||
import com.intellij.lang.parameterInfo.CreateParameterInfoContext
|
||||
import com.intellij.lang.parameterInfo.ParameterInfoHandler
|
||||
import com.intellij.lang.parameterInfo.ParameterInfoUIContext
|
||||
import com.intellij.lang.parameterInfo.ParameterInfoUtils
|
||||
import com.intellij.lang.parameterInfo.UpdateParameterInfoContext
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.util.childrenOfType
|
||||
import com.intellij.psi.util.elementsAtOffsetUp
|
||||
import gay.pizza.pork.idea.psi.gen.ArgumentSpecElement
|
||||
import gay.pizza.pork.idea.psi.gen.FunctionCallElement
|
||||
import gay.pizza.pork.idea.psi.gen.FunctionDefinitionElement
|
||||
import gay.pizza.pork.parser.TokenType
|
||||
|
||||
@Suppress("UnstableApiUsage")
|
||||
class PorkParameterInfoHandler : ParameterInfoHandler<FunctionCallElement, FunctionDefinitionElement> {
|
||||
override fun findElementForParameterInfo(context: CreateParameterInfoContext): FunctionCallElement? {
|
||||
return context.file.elementsAtOffsetUp(context.offset).asSequence()
|
||||
.map { it.first }
|
||||
.filterIsInstance<FunctionCallElement>()
|
||||
.firstOrNull()
|
||||
}
|
||||
|
||||
override fun findElementForUpdatingParameterInfo(context: UpdateParameterInfoContext): FunctionCallElement? {
|
||||
return context.file.elementsAtOffsetUp(context.offset).asSequence()
|
||||
.map { it.first }
|
||||
.filterIsInstance<FunctionCallElement>()
|
||||
.firstOrNull()
|
||||
}
|
||||
|
||||
override fun updateUI(p: FunctionDefinitionElement, context: ParameterInfoUIContext) {
|
||||
val argumentSpecs = p.childrenOfType<ArgumentSpecElement>()
|
||||
val signature = argumentSpecs.mapNotNull { it.name }.joinToString(", ")
|
||||
if (argumentSpecs.isEmpty()) {
|
||||
context.setupUIComponentPresentation(
|
||||
"<no parameters>",
|
||||
-1,
|
||||
-1,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
context.defaultParameterColor
|
||||
)
|
||||
return
|
||||
}
|
||||
if (context.currentParameterIndex >= argumentSpecs.size) {
|
||||
context.setupUIComponentPresentation(
|
||||
signature,
|
||||
-1,
|
||||
-1,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
context.defaultParameterColor
|
||||
)
|
||||
} else {
|
||||
var range: TextRange? = null
|
||||
var start = 0
|
||||
for ((index, item) in signature.split(", ").withIndex()) {
|
||||
if (index == context.currentParameterIndex) {
|
||||
range = TextRange(index, index + item.length)
|
||||
}
|
||||
start += item.length + 2
|
||||
}
|
||||
context.setupUIComponentPresentation(
|
||||
signature,
|
||||
range?.startOffset ?: 0,
|
||||
range?.endOffset ?: (signature.length - 1),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
context.defaultParameterColor
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateParameterInfo(parameterOwner: FunctionCallElement, context: UpdateParameterInfoContext) {
|
||||
val offset = ParameterInfoUtils.getCurrentParameterIndex(
|
||||
parameterOwner.node,
|
||||
context.offset,
|
||||
PorkElementTypes.elementTypeFor(TokenType.Comma)
|
||||
)
|
||||
context.setCurrentParameter(offset)
|
||||
}
|
||||
|
||||
override fun showParameterInfo(element: FunctionCallElement, context: CreateParameterInfoContext) {
|
||||
context.showHint(element, element.textOffset, this)
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package gay.pizza.pork.idea
|
||||
|
||||
import com.intellij.codeInsight.editorActions.SimpleTokenSetQuoteHandler
|
||||
|
||||
class PorkQuoteHandler : SimpleTokenSetQuoteHandler(PorkElementTypes.QuoteSet)
|
@ -1,109 +0,0 @@
|
||||
package gay.pizza.pork.idea
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.search.FilenameIndex
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.psi.util.childrenOfType
|
||||
import gay.pizza.pork.idea.psi.gen.*
|
||||
|
||||
object PorkReferenceResolution {
|
||||
fun getRelevantFiles(containingFile: PsiFile): List<PsiFile> {
|
||||
if (containingFile.virtualFile == null) {
|
||||
return getAllProjectPorkFiles(containingFile.project)
|
||||
}
|
||||
val importDeclarationElements = PsiTreeUtil.collectElementsOfType(containingFile, ImportDeclarationElement::class.java)
|
||||
val files = mutableListOf(containingFile)
|
||||
for (importDeclaration in importDeclarationElements) {
|
||||
val symbolElements = importDeclaration.childrenOfType<SymbolElement>()
|
||||
val importType = importDeclaration.childrenOfType<SymbolElement>().first().text
|
||||
if (importType != "local") {
|
||||
continue
|
||||
}
|
||||
|
||||
val basicImportPath = symbolElements.drop(1).joinToString("/") { it.text.trim() }
|
||||
val actualImportPath = "../${basicImportPath}.pork"
|
||||
val virtualFile = containingFile.virtualFile?.findFileByRelativePath(actualImportPath) ?: continue
|
||||
val psiFile = PsiManager.getInstance(containingFile.project).findFile(virtualFile) ?: continue
|
||||
files.add(psiFile)
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
fun getAllProjectPorkFiles(project: Project): List<PsiFile> {
|
||||
val porkVirtualFiles = FilenameIndex.getAllFilesByExt(project, "pork")
|
||||
return porkVirtualFiles.mapNotNull { virtualFile ->
|
||||
PsiManager.getInstance(project).findFile(virtualFile)
|
||||
}
|
||||
}
|
||||
|
||||
fun findAllCandidates(internalPorkElement: PorkElement, name: String? = null): List<PorkElement> =
|
||||
listOf(findAnyLocals(internalPorkElement, name), findAnyDefinitions(internalPorkElement.containingFile, name)).flatten()
|
||||
|
||||
fun findAnyLocals(internalPorkElement: PorkElement, name: String? = null): List<PorkElement> {
|
||||
val functionDefinitionElement = PsiTreeUtil.getParentOfType(internalPorkElement, FunctionDefinitionElement::class.java)
|
||||
?: return emptyList()
|
||||
val locals = mutableListOf<PorkElement>()
|
||||
|
||||
fun check(localCandidate: PsiElement, upward: Boolean) {
|
||||
if (localCandidate is BlockElement && !upward) {
|
||||
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>().firstOrNull()
|
||||
if (forInItem != null) {
|
||||
locals.add(forInItem)
|
||||
}
|
||||
}
|
||||
|
||||
localCandidate.children.forEach { check(it, false) }
|
||||
}
|
||||
|
||||
PsiTreeUtil.treeWalkUp(internalPorkElement, functionDefinitionElement) { _, localCandidate ->
|
||||
if (localCandidate != null) {
|
||||
if (internalPorkElement == functionDefinitionElement) {
|
||||
return@treeWalkUp true
|
||||
}
|
||||
check(localCandidate, true)
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
val argumentSpecElements = functionDefinitionElement.childrenOfType<ArgumentSpecElement>()
|
||||
locals.addAll(argumentSpecElements)
|
||||
val finalLocals = locals.distinctBy { it.textRange }
|
||||
return finalLocals.filter { if (name != null) it.name == name else true }
|
||||
}
|
||||
|
||||
fun findAnyDefinitions(containingFile: PsiFile, name: String? = null): List<PorkElement> {
|
||||
val foundDefinitions = mutableListOf<PorkNamedElement>()
|
||||
for (file in getRelevantFiles(containingFile)) {
|
||||
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
|
||||
}
|
||||
}
|
@ -49,7 +49,13 @@ object PorkElementHelpers {
|
||||
|
||||
fun referenceOfElement(element: PorkElement, type: NodeType): PsiReference? {
|
||||
unused(type)
|
||||
val textRangeOfSymbolInElement = element.childrenOfType<SymbolElement>().firstOrNull()?.textRangeInParent ?: return null
|
||||
|
||||
if (element is ImportPathElement) {
|
||||
return PorkFileReference(element, element.textRange)
|
||||
}
|
||||
|
||||
val symbols = element.childrenOfType<SymbolElement>()
|
||||
val textRangeOfSymbolInElement = symbols.firstOrNull()?.textRangeInParent ?: return null
|
||||
return PorkIdentifierReference(element, textRangeOfSymbolInElement)
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,22 @@
|
||||
package gay.pizza.pork.idea.psi
|
||||
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.parentOfType
|
||||
import gay.pizza.pork.idea.psi.gen.ImportDeclarationElement
|
||||
import gay.pizza.pork.idea.psi.gen.PorkElement
|
||||
import gay.pizza.pork.idea.resolution.PorkReferenceResolution
|
||||
|
||||
class PorkFileReference(element: PorkElement, textRange: TextRange) : PorkReference(element, textRange) {
|
||||
override fun resolve(): PsiElement? {
|
||||
val importDeclarationElement = element.parentOfType<ImportDeclarationElement>() ?: return null
|
||||
val resolved = PorkReferenceResolution.resolveImportFile(
|
||||
element.containingFile,
|
||||
PorkReferenceResolution.findPorkStdDirectory(element.project),
|
||||
importDeclarationElement
|
||||
)
|
||||
return resolved?.file
|
||||
}
|
||||
|
||||
override fun getVariants(): Array<Any> = arrayOf()
|
||||
}
|
@ -1,15 +1,17 @@
|
||||
package gay.pizza.pork.idea.psi
|
||||
|
||||
import com.intellij.psi.PsiFile
|
||||
import gay.pizza.pork.idea.PorkReferenceResolution
|
||||
import gay.pizza.pork.idea.resolution.PorkReferenceResolution
|
||||
import gay.pizza.pork.idea.psi.gen.PorkElement
|
||||
import gay.pizza.pork.idea.resolution.PorkReferenceRelevantFile
|
||||
|
||||
interface PorkReferencable {
|
||||
val internalPorkElement: PorkElement
|
||||
|
||||
fun getRelevantFiles(): List<PsiFile> = PorkReferenceResolution.getRelevantFiles(internalPorkElement.containingFile)
|
||||
fun getRelevantFiles(): List<PorkReferenceRelevantFile> =
|
||||
PorkReferenceResolution.getRelevantFiles(internalPorkElement.containingFile)
|
||||
|
||||
fun findAllCandidates(name: String? = null): List<PorkElement> =
|
||||
listOf(findAnyLocals(name), findAnyDefinitions(name)).flatten()
|
||||
PorkReferenceResolution.findAllCandidates(internalPorkElement, name)
|
||||
|
||||
fun findAnyLocals(name: String? = null): List<PorkElement> =
|
||||
PorkReferenceResolution.findAnyLocals(internalPorkElement, name)
|
||||
|
@ -0,0 +1,20 @@
|
||||
// GENERATED CODE FROM PORK AST CODEGEN
|
||||
package gay.pizza.pork.idea.psi.gen
|
||||
|
||||
import com.intellij.lang.ASTNode
|
||||
import com.intellij.navigation.ItemPresentation
|
||||
import com.intellij.psi.PsiReference
|
||||
import gay.pizza.pork.ast.gen.NodeType
|
||||
import gay.pizza.pork.idea.psi.PorkElementHelpers
|
||||
import javax.swing.Icon
|
||||
|
||||
class ImportPathElement(node: ASTNode) : PorkElement(node) {
|
||||
override fun getReference(): PsiReference? =
|
||||
PorkElementHelpers.referenceOfElement(this, NodeType.CompilationUnit)
|
||||
|
||||
override fun getIcon(flags: Int): Icon? =
|
||||
PorkElementHelpers.iconOf(this)
|
||||
|
||||
override fun getPresentation(): ItemPresentation? =
|
||||
PorkElementHelpers.presentationOf(this)
|
||||
}
|
@ -23,6 +23,7 @@ object PorkElementFactory {
|
||||
NodeType.FunctionDefinition -> FunctionDefinitionElement(node)
|
||||
NodeType.LetDefinition -> LetDefinitionElement(node)
|
||||
NodeType.If -> IfElement(node)
|
||||
NodeType.ImportPath -> ImportPathElement(node)
|
||||
NodeType.ImportDeclaration -> ImportDeclarationElement(node)
|
||||
NodeType.IntegerLiteral -> IntegerLiteralElement(node)
|
||||
NodeType.LongLiteral -> LongLiteralElement(node)
|
||||
|
@ -0,0 +1,5 @@
|
||||
package gay.pizza.pork.idea.resolution
|
||||
|
||||
import com.intellij.psi.PsiFile
|
||||
|
||||
class PorkReferenceRelevantFile(val file: PsiFile, val type: PorkRelevantFileType)
|
@ -0,0 +1,197 @@
|
||||
package gay.pizza.pork.idea.resolution
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.guessProjectDir
|
||||
import com.intellij.openapi.vfs.*
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.search.FilenameIndex
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import com.intellij.psi.util.childrenOfType
|
||||
import gay.pizza.pork.idea.psi.gen.*
|
||||
|
||||
object PorkReferenceResolution {
|
||||
fun getRelevantFiles(containingFile: PsiFile): List<PorkReferenceRelevantFile> {
|
||||
if (containingFile.virtualFile == null) {
|
||||
return listOf(
|
||||
getAllProjectPorkFiles(containingFile.project),
|
||||
getAllPorkStdFiles(containingFile.project)
|
||||
).flatten()
|
||||
}
|
||||
val importDeclarationElements = PsiTreeUtil.collectElementsOfType(
|
||||
containingFile,
|
||||
ImportDeclarationElement::class.java
|
||||
)
|
||||
val files = mutableListOf(PorkReferenceRelevantFile(containingFile, PorkRelevantFileType.Self))
|
||||
val stdDirectory = findPorkStdDirectory(containingFile.project)
|
||||
val prelude = resolveStdImport(containingFile, stdDirectory, "lang/prelude")
|
||||
if (prelude != null) {
|
||||
files.add(prelude)
|
||||
}
|
||||
for (importDeclaration in importDeclarationElements) {
|
||||
val resolved = resolveImportFile(containingFile, stdDirectory, importDeclaration)
|
||||
if (resolved != null) {
|
||||
files.add(resolved)
|
||||
}
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
fun resolveImportFile(
|
||||
containingFile: PsiFile,
|
||||
stdDirectory: VirtualFile?,
|
||||
importDeclarationElement: ImportDeclarationElement
|
||||
): PorkReferenceRelevantFile? {
|
||||
val importType = importDeclarationElement.childrenOfType<SymbolElement>().firstOrNull()?.text ?: return null
|
||||
val importPathElement = importDeclarationElement.childrenOfType<ImportPathElement>().firstOrNull() ?: return null
|
||||
val basicImportPath = importPathElement.children.joinToString("/") { it.text.trim() }
|
||||
return when (importType) {
|
||||
"local" -> {
|
||||
val actualImportPath = "../${basicImportPath}.pork"
|
||||
val actualVirtualFile = containingFile.virtualFile?.findFileByRelativePath(actualImportPath) ?: return null
|
||||
referenceRelevantFile(containingFile.project, actualVirtualFile, PorkRelevantFileType.Local)
|
||||
}
|
||||
"std" -> {
|
||||
resolveStdImport(containingFile, stdDirectory, basicImportPath)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveStdImport(containingFile: PsiFile, stdDirectory: VirtualFile?, basicImportPath: String): PorkReferenceRelevantFile? {
|
||||
if (stdDirectory == null) {
|
||||
return null
|
||||
}
|
||||
val actualVirtualFile = stdDirectory.findFile("${basicImportPath}.pork") ?: return null
|
||||
return referenceRelevantFile(containingFile.project, actualVirtualFile, PorkRelevantFileType.Std)
|
||||
}
|
||||
|
||||
private fun referenceRelevantFile(
|
||||
project: Project,
|
||||
virtualFile: VirtualFile,
|
||||
type: PorkRelevantFileType
|
||||
): PorkReferenceRelevantFile? {
|
||||
val psiFile = PsiManager.getInstance(project).findFile(virtualFile) ?: return null
|
||||
return PorkReferenceRelevantFile(psiFile, type)
|
||||
}
|
||||
|
||||
fun getAllProjectPorkFiles(project: Project): List<PorkReferenceRelevantFile> {
|
||||
val psiManager = PsiManager.getInstance(project)
|
||||
val porkVirtualFiles = FilenameIndex.getAllFilesByExt(project, "pork")
|
||||
return porkVirtualFiles.mapNotNull { virtualFile ->
|
||||
psiManager.findFile(virtualFile)
|
||||
}.map { PorkReferenceRelevantFile(it, PorkRelevantFileType.Local) }
|
||||
}
|
||||
|
||||
fun findPorkStdDirectory(project: Project): VirtualFile? = if (isPorkItself(project)) {
|
||||
project.guessProjectDir()?.findDirectory("stdlib/src/main/pork")
|
||||
} else {
|
||||
project.guessProjectDir()?.fileSystem?.findFileByPath(
|
||||
"/opt/pork/std"
|
||||
)
|
||||
}
|
||||
|
||||
fun getAllPorkStdFiles(project: Project): List<PorkReferenceRelevantFile> {
|
||||
val stdDirectoryPath = findPorkStdDirectory(project) ?: return emptyList()
|
||||
|
||||
val psiManager = PsiManager.getInstance(project)
|
||||
val stdPorkFiles = mutableListOf<PorkReferenceRelevantFile>()
|
||||
VfsUtilCore.iterateChildrenRecursively(stdDirectoryPath, VirtualFileFilter.ALL) { file ->
|
||||
if (file.extension == "pork") {
|
||||
val psiFile = psiManager.findFile(file)
|
||||
if (psiFile != null) {
|
||||
stdPorkFiles.add(PorkReferenceRelevantFile(psiFile, PorkRelevantFileType.Std))
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
return stdPorkFiles
|
||||
}
|
||||
|
||||
private fun isPorkItself(project: Project): Boolean {
|
||||
if (project.name != "pork") {
|
||||
return false
|
||||
}
|
||||
|
||||
val projectDirectory = project.guessProjectDir() ?: return false
|
||||
|
||||
val prelude = projectDirectory.findFileOrDirectory(
|
||||
"stdlib/src/main/pork/lang/prelude.pork"
|
||||
)
|
||||
return prelude != null && prelude.isFile
|
||||
}
|
||||
|
||||
fun findAllCandidates(internalPorkElement: PorkElement, name: String? = null): List<PorkElement> =
|
||||
listOf(
|
||||
findAnyLocals(internalPorkElement, name),
|
||||
findAnyDefinitions(internalPorkElement.containingFile, name)
|
||||
).flatten()
|
||||
|
||||
fun findAnyLocals(internalPorkElement: PorkElement, name: String? = null): List<PorkElement> {
|
||||
val functionDefinitionElement = PsiTreeUtil.getParentOfType(
|
||||
internalPorkElement,
|
||||
FunctionDefinitionElement::class.java
|
||||
) ?: return emptyList()
|
||||
val locals = mutableListOf<PorkElement>()
|
||||
|
||||
fun check(localCandidate: PsiElement, upward: Boolean) {
|
||||
if (localCandidate is BlockElement && !upward) {
|
||||
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>().firstOrNull()
|
||||
if (forInItem != null) {
|
||||
locals.add(forInItem)
|
||||
}
|
||||
}
|
||||
|
||||
localCandidate.children.forEach { check(it, false) }
|
||||
}
|
||||
|
||||
PsiTreeUtil.treeWalkUp(internalPorkElement, functionDefinitionElement) { _, localCandidate ->
|
||||
if (localCandidate != null) {
|
||||
if (internalPorkElement == functionDefinitionElement) {
|
||||
return@treeWalkUp true
|
||||
}
|
||||
check(localCandidate, true)
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
val argumentSpecElements = functionDefinitionElement.childrenOfType<ArgumentSpecElement>()
|
||||
locals.addAll(argumentSpecElements)
|
||||
val finalLocals = locals.distinctBy { it.textRange }
|
||||
return finalLocals.filter { if (name != null) it.name == name else true }
|
||||
}
|
||||
|
||||
fun findAnyDefinitions(containingFile: PsiFile, name: String? = null): List<PorkElement> {
|
||||
val foundDefinitions = mutableListOf<PorkNamedElement>()
|
||||
for (file in getRelevantFiles(containingFile)) {
|
||||
val definitions = PsiTreeUtil.collectElements(file.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
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package gay.pizza.pork.idea.resolution
|
||||
|
||||
enum class PorkRelevantFileType {
|
||||
Self,
|
||||
Local,
|
||||
Std
|
||||
}
|
@ -26,6 +26,15 @@
|
||||
<lang.elementManipulator
|
||||
implementationClass="gay.pizza.pork.idea.PorkElementManipulator"
|
||||
forClass="gay.pizza.pork.idea.psi.gen.PorkElement"/>
|
||||
<codeInsight.parameterNameHints
|
||||
language="Pork"
|
||||
implementationClass="gay.pizza.pork.idea.PorkInlayParameterHintsProvider"/>
|
||||
<lang.quoteHandler
|
||||
language="Pork"
|
||||
implementationClass="gay.pizza.pork.idea.PorkQuoteHandler"/>
|
||||
<!-- <codeInsight.parameterInfo
|
||||
language="Pork"
|
||||
implementationClass="gay.pizza.pork.idea.PorkParameterInfoHandler"/>-->
|
||||
<psi.declarationProvider implementation="gay.pizza.pork.idea.PorkSymbolDeclarationProvider"/>
|
||||
</extensions>
|
||||
|
||||
|
Reference in New Issue
Block a user