Phase 5: add rename and find usages
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
package dev.hgh.jai.findusages
|
||||
|
||||
import com.intellij.lang.cacheBuilder.DefaultWordsScanner
|
||||
import com.intellij.lang.cacheBuilder.WordsScanner
|
||||
import com.intellij.lang.findUsages.FindUsagesProvider
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiNameIdentifierOwner
|
||||
import com.intellij.psi.PsiNamedElement
|
||||
import com.intellij.psi.tree.TokenSet
|
||||
import dev.hgh.jai.lexer.JaiLexer
|
||||
import dev.hgh.jai.lexer.JaiTokenTypes
|
||||
import dev.hgh.jai.psi.JaiRefExpr
|
||||
|
||||
/** Supplies word indexing and descriptions for Jai declaration usages. */
|
||||
class JaiFindUsagesProvider : FindUsagesProvider {
|
||||
override fun getWordsScanner(): WordsScanner =
|
||||
DefaultWordsScanner(
|
||||
JaiLexer(),
|
||||
TokenSet.create(JaiTokenTypes.IDENT),
|
||||
JaiTokenTypes.COMMENTS,
|
||||
JaiTokenTypes.STRINGS,
|
||||
)
|
||||
|
||||
override fun canFindUsagesFor(psiElement: PsiElement): Boolean = psiElement is PsiNameIdentifierOwner || psiElement is JaiRefExpr
|
||||
|
||||
override fun getHelpId(psiElement: PsiElement): String? = null
|
||||
|
||||
override fun getType(element: PsiElement): String =
|
||||
if (element is PsiNameIdentifierOwner || element is JaiRefExpr) {
|
||||
"Jai declaration"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
|
||||
override fun getDescriptiveName(element: PsiElement): String = (element as? PsiNamedElement)?.name ?: element.text
|
||||
|
||||
override fun getNodeText(
|
||||
element: PsiElement,
|
||||
useFullName: Boolean,
|
||||
): String = element.text
|
||||
}
|
||||
23
src/main/kotlin/dev/hgh/jai/psi/JaiPsiElementFactory.kt
Normal file
23
src/main/kotlin/dev/hgh/jai/psi/JaiPsiElementFactory.kt
Normal file
@@ -0,0 +1,23 @@
|
||||
package dev.hgh.jai.psi
|
||||
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFileFactory
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import dev.hgh.jai.JaiLanguage
|
||||
import dev.hgh.jai.lexer.JaiTokenTypes
|
||||
|
||||
/** Creates small Jai PSI fragments for AST replacements used by rename refactoring. */
|
||||
object JaiPsiElementFactory {
|
||||
fun createIdentifier(
|
||||
project: Project,
|
||||
name: String,
|
||||
): PsiElement {
|
||||
val file = PsiFileFactory.getInstance(project).createFileFromText("rename.jai", JaiLanguage, "$name :: 0;")
|
||||
val declarationName =
|
||||
PsiTreeUtil.findChildOfType(file, JaiDeclName::class.java)
|
||||
?: error("Could not parse a Jai declaration name for '$name'")
|
||||
return declarationName.node.findChildByType(JaiTokenTypes.IDENT)?.psi
|
||||
?: error("Jai name '$name' is not an identifier")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package dev.hgh.jai.psi.mixin
|
||||
|
||||
import com.intellij.extapi.psi.ASTWrapperPsiElement
|
||||
import com.intellij.lang.ASTNode
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiNameIdentifierOwner
|
||||
import dev.hgh.jai.lexer.JaiTokenTypes
|
||||
import dev.hgh.jai.psi.JaiPsiElementFactory
|
||||
|
||||
/** Gives declaration names the standard PSI naming contract used by rename/find-usages. */
|
||||
abstract class JaiNamedElementMixin(
|
||||
node: ASTNode,
|
||||
) : ASTWrapperPsiElement(node),
|
||||
PsiNameIdentifierOwner {
|
||||
override fun getNameIdentifier(): PsiElement? = node.findChildByType(JaiTokenTypes.IDENT)?.psi
|
||||
|
||||
override fun getName(): String? = nameIdentifier?.text
|
||||
|
||||
override fun setName(name: String): PsiElement {
|
||||
val identifier = nameIdentifier ?: return this
|
||||
identifier.replace(JaiPsiElementFactory.createIdentifier(project, name))
|
||||
return this
|
||||
}
|
||||
|
||||
override fun getNavigationElement(): PsiElement = nameIdentifier ?: this
|
||||
|
||||
override fun getTextOffset(): Int = nameIdentifier?.textOffset ?: super.getTextOffset()
|
||||
}
|
||||
26
src/main/kotlin/dev/hgh/jai/refactoring/JaiNamesValidator.kt
Normal file
26
src/main/kotlin/dev/hgh/jai/refactoring/JaiNamesValidator.kt
Normal file
@@ -0,0 +1,26 @@
|
||||
package dev.hgh.jai.refactoring
|
||||
|
||||
import com.intellij.lang.refactoring.NamesValidator
|
||||
import com.intellij.openapi.project.Project
|
||||
import dev.hgh.jai.lexer.JaiTokenTypes
|
||||
|
||||
/** Applies Jai's ASCII identifier and keyword rules to rename input. */
|
||||
class JaiNamesValidator : NamesValidator {
|
||||
override fun isKeyword(
|
||||
name: String,
|
||||
project: Project?,
|
||||
): Boolean = name in JaiTokenTypes.KEYWORD_MAP
|
||||
|
||||
override fun isIdentifier(
|
||||
name: String,
|
||||
project: Project?,
|
||||
): Boolean {
|
||||
if (name.isEmpty() || isKeyword(name, project)) return false
|
||||
if (!isIdentifierStart(name.first())) return false
|
||||
return name.drop(1).all(::isIdentifierPart)
|
||||
}
|
||||
|
||||
private fun isIdentifierStart(character: Char): Boolean = character in 'a'..'z' || character in 'A'..'Z' || character == '_'
|
||||
|
||||
private fun isIdentifierPart(character: Char): Boolean = isIdentifierStart(character) || character in '0'..'9'
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package dev.hgh.jai.refactoring
|
||||
|
||||
import com.intellij.lang.refactoring.RefactoringSupportProvider
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiNameIdentifierOwner
|
||||
import dev.hgh.jai.JaiLanguage
|
||||
|
||||
/** Enables standard and in-place rename for Jai declaration names. */
|
||||
class JaiRefactoringSupportProvider : RefactoringSupportProvider() {
|
||||
override fun isMemberInplaceRenameAvailable(
|
||||
elementToRename: PsiElement,
|
||||
context: PsiElement?,
|
||||
): Boolean = elementToRename is PsiNameIdentifierOwner && elementToRename.language == JaiLanguage
|
||||
}
|
||||
@@ -22,6 +22,7 @@ import dev.hgh.jai.psi.JaiDeclName
|
||||
import dev.hgh.jai.psi.JaiDeclaration
|
||||
import dev.hgh.jai.psi.JaiDirectiveExpr
|
||||
import dev.hgh.jai.psi.JaiLiteralExpr
|
||||
import dev.hgh.jai.psi.JaiPsiElementFactory
|
||||
import dev.hgh.jai.psi.JaiRefExpr
|
||||
|
||||
/** Adds file references for directives and symbol references for Jai identifiers. */
|
||||
@@ -106,6 +107,12 @@ private class JaiSymbolReference(
|
||||
) : PsiReferenceBase<JaiRefExpr>(sourceElement, TextRange(0, sourceElement.textLength), true) {
|
||||
override fun resolve(): PsiElement? = JaiSymbolResolver.resolve(sourceElement)
|
||||
|
||||
override fun handleElementRename(newElementName: String): PsiElement {
|
||||
val identifier = sourceElement.node.findChildByType(JaiTokenTypes.IDENT)?.psi ?: return sourceElement
|
||||
identifier.replace(JaiPsiElementFactory.createIdentifier(sourceElement.project, newElementName))
|
||||
return sourceElement
|
||||
}
|
||||
|
||||
override fun getVariants(): Array<Any> = emptyArray()
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user