Skip to content

Commit cc42903

Browse files
committed
(REF): initial support for rename refactoring
closes #575
1 parent bc2e513 commit cc42903

File tree

14 files changed

+115
-12
lines changed

14 files changed

+115
-12
lines changed

src/main/kotlin/org/rust/lang/core/grammar/rust.bnf

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,8 @@ alias ::= AS IDENTIFIER
515515

516516
extern_crate_item ::= attrs_and_vis EXTERN CRATE IDENTIFIER alias? ';' {
517517
hooks = [ leftBinder = "DOC_COMMENT_BINDER" ]
518-
implements = [ "org.rust.lang.core.psi.RustNamedElement"
518+
implements = [ "org.rust.lang.core.psi.RustReferenceElement"
519+
"org.rust.lang.core.psi.RustNamedElement"
519520
"org.rust.lang.core.psi.RustItemElement" ]
520521
}
521522

@@ -525,7 +526,8 @@ extern_crate_item ::= attrs_and_vis EXTERN CRATE IDENTIFIER alias? ';' {
525526

526527
mod_decl_item ::= attrs_and_vis MOD IDENTIFIER ';' {
527528
hooks = [ leftBinder = "DOC_COMMENT_BINDER" ]
528-
implements = [ "org.rust.lang.core.psi.RustNamedElement"
529+
implements = [ "org.rust.lang.core.psi.RustReferenceElement"
530+
"org.rust.lang.core.psi.RustNamedElement"
529531
"org.rust.lang.core.psi.RustItemElement" ]
530532
stubClass = "org.rust.lang.core.stubs.elements.RustModDeclElementItemStub"
531533
elementTypeFactory = "org.rust.lang.core.stubs.ElementFactoryKt.factory"

src/main/kotlin/org/rust/lang/core/psi/RustElementFactory.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.rust.lang.core.psi
22

33
import com.intellij.openapi.project.Project
4+
import com.intellij.psi.PsiElement
45
import com.intellij.psi.PsiFileFactory
56
import org.rust.lang.RustLanguage
67
import org.rust.lang.core.psi.util.childOfType
@@ -35,6 +36,9 @@ object RustElementFactory {
3536
return createFromText(project, "impl T for S { $methods }")
3637
}
3738

39+
fun createIdentifier(project: Project, name: String): PsiElement =
40+
createFromText<RustModDeclItemElement>(project, "mod $name;")!!.identifier
41+
3842
private inline fun <reified T : RustCompositeElement> createFromText(project: Project, code: String): T? =
3943
PsiFileFactory.getInstance(project)
4044
.createFileFromText("DUMMY.rs", RustLanguage, code)

src/main/kotlin/org/rust/lang/core/psi/impl/RustNamedElementImpl.kt

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package org.rust.lang.core.psi.impl
22

33
import com.intellij.lang.ASTNode
44
import com.intellij.psi.PsiElement
5+
import org.rust.lang.core.psi.RustElementFactory
56
import org.rust.lang.core.psi.RustNamedElement
67
import org.rust.lang.core.psi.RustTokenElementTypes
78

@@ -13,11 +14,9 @@ abstract class RustNamedElementImpl(node: ASTNode) : RustCompositeElementImpl(
1314

1415
override fun getName(): String? = nameElement?.text
1516

16-
/**
17-
* NOTE: This is orphaned purposefully
18-
*/
1917
override fun setName(name: String): PsiElement? {
20-
throw UnsupportedOperationException()
18+
nameElement?.replace(RustElementFactory.createIdentifier(project, name))
19+
return this
2120
}
2221

2322
override fun getNavigationElement(): PsiElement = nameElement ?: this

src/main/kotlin/org/rust/lang/core/psi/impl/RustStubbedNamedElementImpl.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import com.intellij.lang.ASTNode
55
import com.intellij.navigation.ItemPresentation
66
import com.intellij.psi.PsiElement
77
import com.intellij.psi.stubs.IStubElementType
8+
import org.rust.lang.core.psi.RustElementFactory
89
import org.rust.lang.core.psi.RustNamedElement
910
import org.rust.lang.core.psi.RustTokenElementTypes
1011
import org.rust.lang.core.stubs.RustNamedElementStub
@@ -26,8 +27,8 @@ abstract class RustStubbedNamedElementImpl<StubT> : RustStubbedElementImpl<StubT
2627
}
2728

2829
override fun setName(name: String): PsiElement? {
29-
// sic!
30-
throw UnsupportedOperationException()
30+
nameElement?.replace(RustElementFactory.createIdentifier(project, name))
31+
return this
3132
}
3233

3334
override fun getNavigationElement(): PsiElement = nameElement ?: this

src/main/kotlin/org/rust/lang/core/psi/impl/mixin/RustExternCrateItemImplMixin.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.rust.lang.core.psi.impl.mixin
22

33
import com.intellij.lang.ASTNode
4+
import com.intellij.psi.PsiElement
45
import org.rust.lang.core.psi.RustExternCrateItemElement
56
import org.rust.lang.core.psi.impl.RustNamedElementImpl
67
import org.rust.lang.core.psi.impl.RustPsiImplUtil
@@ -12,5 +13,7 @@ abstract class RustExternCrateItemImplMixin(node: ASTNode) : RustNamedElementImp
1213

1314
override fun getReference(): RustReference = RustExternCrateReferenceImpl(this)
1415

16+
override val referenceNameElement: PsiElement get() = identifier
17+
1518
override val isPublic: Boolean get() = RustPsiImplUtil.isPublicNonStubbed(this)
1619
}

src/main/kotlin/org/rust/lang/core/psi/impl/mixin/RustModDeclItemImplMixin.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package org.rust.lang.core.psi.impl.mixin
22

33

44
import com.intellij.lang.ASTNode
5+
import com.intellij.psi.PsiElement
56
import com.intellij.psi.PsiFile
67
import com.intellij.psi.stubs.IStubElementType
78
import org.rust.ide.icons.RustIcons
@@ -24,14 +25,16 @@ abstract class RustModDeclItemImplMixin : RustStubbedNamedElementImpl<RustModDec
2425

2526
override fun getReference(): RustReference = RustModReferenceImpl(this)
2627

28+
override val referenceNameElement: PsiElement get() = identifier
29+
2730
override fun getIcon(flags: Int): Icon? = iconWithVisibility(flags, RustIcons.MODULE)
2831

2932
override val isPublic: Boolean get() = RustPsiImplUtil.isPublic(this)
3033

3134
}
3235

3336
fun RustModDeclItemElement.getOrCreateModuleFile(): PsiFile? {
34-
return reference!!.resolve()?.let { it.containingFile } ?:
37+
return reference.resolve()?.containingFile ?:
3538
containingMod?.ownedDirectory?.createFile(suggestChildFileName ?: return null)
3639
}
3740

src/main/kotlin/org/rust/lang/core/resolve/ref/RustFieldExprReferenceImpl.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,9 @@ class RustFieldExprReferenceImpl(
1616

1717
override fun resolveVerbose(): RustResolveEngine.ResolveResult =
1818
RustResolveEngine.resolveFieldExpr(element)
19+
20+
override fun handleElementRename(newName: String): PsiElement {
21+
element.fieldId.identifier?.let { doRename(it, newName) }
22+
return element
23+
}
1924
}

src/main/kotlin/org/rust/lang/core/resolve/ref/RustReferenceBase.kt

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ import com.intellij.openapi.util.TextRange
44
import com.intellij.psi.PsiElement
55
import com.intellij.psi.PsiReferenceBase
66
import com.intellij.psi.impl.source.resolve.ResolveCache
7-
import org.rust.lang.core.psi.RustCompositeElement
7+
import org.rust.lang.core.psi.RustElementFactory
88
import org.rust.lang.core.psi.RustNamedElement
9+
import org.rust.lang.core.psi.RustReferenceElement
10+
import org.rust.lang.core.psi.RustTokenElementTypes
11+
import org.rust.lang.core.psi.util.elementType
912
import org.rust.lang.core.psi.util.parentRelativeRange
1013
import org.rust.lang.core.resolve.RustResolveEngine
1114

12-
abstract class RustReferenceBase<T : RustCompositeElement>(
15+
abstract class RustReferenceBase<T : RustReferenceElement>(
1316
element: T
1417
) : PsiReferenceBase<T>(element)
1518
, RustReference {
@@ -40,6 +43,11 @@ abstract class RustReferenceBase<T : RustCompositeElement>(
4043
return element.referenceAnchor.parentRelativeRange
4144
}
4245

46+
override fun handleElementRename(newName: String): PsiElement {
47+
doRename(element.referenceNameElement, newName)
48+
return element
49+
}
50+
4351
val cache = ResolveCache.getInstance(element.project)
4452

4553
private fun cache(block: (RustReferenceBase<T>, Boolean) -> RustResolveEngine.ResolveResult): RustResolveEngine.ResolveResult =
@@ -49,4 +57,11 @@ abstract class RustReferenceBase<T : RustCompositeElement>(
4957
false /* needToPreventRecursion = */ ,
5058
false /* incompleteCode = */
5159
) ?: RustResolveEngine.ResolveResult.Unresolved
60+
61+
companion object {
62+
@JvmStatic protected fun doRename(identifier: PsiElement, newName: String) {
63+
check(identifier.elementType == RustTokenElementTypes.IDENTIFIER)
64+
identifier.replace(RustElementFactory.createIdentifier(identifier.project, newName))
65+
}
66+
}
5267
}

src/main/kotlin/org/rust/lang/core/stubs/index/RustModulesIndex.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ class RustModulesIndex : StringStubIndexExtension<RustModDeclItemElement>() {
2828
StubIndex.getInstance().processElements(
2929
KEY, key, project, GlobalSearchScope.allScope(project), RustModDeclItemElement::class.java
3030
) { modDecl ->
31-
if (modDecl.reference?.resolve() == mod) {
31+
if (modDecl.reference.resolve() == mod) {
3232
result = modDecl.containingMod
3333
false
3434
} else {
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.rust.ide.refactoring
2+
3+
import org.rust.lang.RustTestCaseBase
4+
5+
class RenameTest: RustTestCaseBase() {
6+
override val dataPath = "org/rust/ide/refactoring/fixtures/rename"
7+
8+
fun testFunction() = doTest()
9+
fun testField() = doTest()
10+
11+
private fun doTest(name: String = "spam") {
12+
myFixture.configureByFile(fileName)
13+
myFixture.renameElementAtCaret(name)
14+
myFixture.checkResultByFile(fileName, fileName.replace(".rs", "_after.rs"), false);
15+
}
16+
}
17+

0 commit comments

Comments
 (0)