AT-1811 Add refactoring tests on Hadoop

IJ-CR-156099

GitOrigin-RevId: fae70cf3c5984cf87b453a5c23436c6d2fd84180
This commit is contained in:
Eugene Morozov
2025-02-21 17:47:58 +01:00
committed by intellij-monorepo-bot
parent e15674293c
commit 4da538f634
6 changed files with 268 additions and 1 deletions

View File

@@ -21,5 +21,7 @@
<orderEntry type="module" module-name="intellij.java.psi" />
<orderEntry type="module" module-name="intellij.java.impl" />
<orderEntry type="module" module-name="intellij.performanceTesting.vcs" />
<orderEntry type="module" module-name="intellij.java.impl.refactorings" />
<orderEntry type="module" module-name="intellij.java" />
</component>
</module>

View File

@@ -0,0 +1,79 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.java.performancePlugin
import com.intellij.openapi.application.EDT
import com.intellij.openapi.application.readAction
import com.intellij.openapi.application.writeIntentReadAction
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.playback.PlaybackContext
import com.intellij.openapi.ui.playback.commands.PlaybackCommandCoroutineAdapter
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiType
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.refactoring.changeSignature.ParameterInfoImpl
import com.intellij.refactoring.openapi.impl.JavaRefactoringFactoryImpl
import com.jetbrains.performancePlugin.PerformanceTestSpan
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
/**
* Change the signature of the Java method under the caret.
* Syntax: %changeJavaSignature ACTION name
* Example: %changeJavaSignature ADD_PARAMETER myParameter
*/
@Suppress("DuplicatedCode")
class ChangeJavaSignatureCommand(text: String, line: Int) : PlaybackCommandCoroutineAdapter(text, line) {
private enum class ChangeSignatureAction { ADD_PARAMETER }
companion object {
const val PREFIX: String = "${CMD_PREFIX}changeJavaSignature"
const val ADD_PARAM_SPAN_NAME: String = "changeJavaSignature: add parameter"
private suspend fun handleAddParameter(project: Project, psiMethod: PsiMethod, name: String) {
val refactoringFactory = JavaRefactoringFactoryImpl(project)
@Suppress("DEPRECATION") val parameterInfo = arrayOf(ParameterInfoImpl(-1, name, PsiType.BOOLEAN, "false", true))
val span = PerformanceTestSpan.TRACER.spanBuilder(ADD_PARAM_SPAN_NAME).startSpan()
val processor = readAction {
refactoringFactory.createChangeSignatureProcessor(
psiMethod, false, "public", name, psiMethod.returnType, parameterInfo, null, null, null, null)
}
withContext(Dispatchers.EDT) {
writeIntentReadAction {
processor.run()
span.end()
}
}
}
}
override suspend fun doExecute(context: PlaybackContext) {
val project = context.project
val commandArgs = extractCommandArgument(PREFIX)
val args = commandArgs.split(" ")
val action = args.getOrNull(0)?.uppercase()?.let { mode -> ChangeSignatureAction.entries.find { it.name == mode } }
?: throw IllegalArgumentException("Action is missing or wrong")
val name = args.getOrNull(1) ?: throw IllegalArgumentException("Name is missing")
if (args.size > 2) throw IllegalArgumentException("Too many arguments provided")
val editor = readAction { FileEditorManager.getInstance(context.project).selectedTextEditor }
?: throw IllegalArgumentException("There is no selected editor")
val elementUnderCaret = readAction {
PsiDocumentManager.getInstance(project).getPsiFile(editor.document)?.findElementAt(editor.caretModel.offset)
?: throw IllegalStateException("Unable to locate the element under the caret")
}
val psiMethod = PsiTreeUtil.getParentOfType(elementUnderCaret, PsiMethod::class.java)
?: throw IllegalStateException("Caret is not inside a method")
when (action) {
ChangeSignatureAction.ADD_PARAMETER -> handleAddParameter(project, psiMethod, name)
}
}
}

View File

@@ -0,0 +1,60 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.java.performancePlugin
import com.intellij.openapi.application.EDT
import com.intellij.openapi.application.readAction
import com.intellij.openapi.application.runReadAction
import com.intellij.openapi.application.writeIntentReadAction
import com.intellij.openapi.fileEditor.FileEditorManager
import com.intellij.openapi.ui.playback.PlaybackContext
import com.intellij.openapi.ui.playback.commands.PlaybackCommandCoroutineAdapter
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiMethod
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.refactoring.BaseRefactoringProcessor
import com.intellij.refactoring.inline.InlineMethodProcessor
import com.jetbrains.performancePlugin.PerformanceTestSpan
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
/**
* Inline the Java method under the caret.
* Syntax: %inlineJavaMethod
*/
@Suppress("DuplicatedCode")
class InlineJavaMethodCommand(text: String, line: Int) : PlaybackCommandCoroutineAdapter(text, line) {
companion object {
const val PREFIX: String = "${CMD_PREFIX}inlineJavaMethod"
const val SPAN_NAME: String = "inlineJavaMethod"
}
override suspend fun doExecute(context: PlaybackContext) {
val project = context.project
val editor = readAction { FileEditorManager.getInstance(context.project).selectedTextEditor }
?: throw IllegalArgumentException("There is no selected editor")
val elementUnderCaret = readAction {
PsiDocumentManager.getInstance(project).getPsiFile(editor.document)?.findElementAt(editor.caretModel.offset)
} ?: throw IllegalStateException("Unable to locate the element under the caret")
val psiMethod = PsiTreeUtil.getParentOfType(elementUnderCaret, PsiMethod::class.java)
?: throw IllegalStateException("Caret is not inside a method")
val span = PerformanceTestSpan.TRACER.spanBuilder(SPAN_NAME).startSpan()
val processor: BaseRefactoringProcessor = runReadAction {
InlineMethodProcessor(project, psiMethod, null, editor, false, false, false, true)
}
withContext(Dispatchers.EDT) {
writeIntentReadAction {
processor.run()
span.end()
}
}
}
}

View File

@@ -13,7 +13,10 @@ final class JavaCommandProvider implements CommandProvider {
return Map.of(
BuildCommand.PREFIX, BuildCommand::new,
SyncJpsLibrariesCommand.PREFIX, SyncJpsLibrariesCommand::new,
CreateJavaFileCommand.PREFIX, CreateJavaFileCommand::new
CreateJavaFileCommand.PREFIX, CreateJavaFileCommand::new,
RenameDirectoryAsPackageCommand.PREFIX, RenameDirectoryAsPackageCommand::new,
ChangeJavaSignatureCommand.PREFIX, ChangeJavaSignatureCommand::new,
InlineJavaMethodCommand.PREFIX, InlineJavaMethodCommand::new
);
}
}

View File

@@ -0,0 +1,103 @@
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.java.performancePlugin
import com.intellij.openapi.application.EDT
import com.intellij.openapi.application.readAction
import com.intellij.openapi.application.writeIntentReadAction
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.guessProjectDir
import com.intellij.openapi.ui.playback.PlaybackContext
import com.intellij.openapi.ui.playback.commands.PlaybackCommandCoroutineAdapter
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiManager
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.refactoring.BaseRefactoringProcessor
import com.intellij.refactoring.RefactoringBundle
import com.intellij.refactoring.move.moveClassesOrPackages.MoveDirectoryWithClassesProcessor
import com.intellij.refactoring.rename.DirectoryAsPackageRenameHandler
import com.jetbrains.performancePlugin.PerformanceTestSpan
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
/**
* Rename the directory as a Java package.
* Syntax: %renameDirectoryAsPackage relativePath newName whereToRename[DIRECTORY|MODULE|PROJECT]
* Example: %renameDirectoryAsPackage community/platform/core-api/src/com/intellij/psi newPsi PROJECT
*/
class RenameDirectoryAsPackageCommand(text: String, line: Int) : PlaybackCommandCoroutineAdapter(text, line) {
private enum class WhereToRename() { DIRECTORY, MODULE, PROJECT }
companion object {
const val PREFIX: String = "${CMD_PREFIX}renameDirectoryAsPackage"
const val SPAN_NAME: String = "renameDirectoryAsPackage"
}
override suspend fun doExecute(context: PlaybackContext) {
val project = context.project
val commandArgs = extractCommandArgument(PREFIX)
val args = commandArgs.split(" ")
val directoryPath = args.getOrNull(0) ?: throw IllegalArgumentException("Directory path is missing")
val newName = args.getOrNull(1) ?: throw IllegalArgumentException("New name is missing")
val whereToRename = args.getOrNull(2)?.uppercase()?.let { mode -> WhereToRename.entries.find { it.name == mode } } ?: WhereToRename.DIRECTORY
if (args.size > 3) throw IllegalArgumentException("Too many arguments provided")
val directory = readAction {
val projectDir = project.guessProjectDir() ?: throw IllegalArgumentException("Project directory not found")
val directoryVirtualFile = projectDir.findFileByRelativePath(directoryPath)
?: throw IllegalArgumentException("Directory not found: $directoryPath")
PsiManager.getInstance(project).findDirectory(directoryVirtualFile)
} ?: throw IllegalArgumentException("Directory not found: $directoryPath")
val span = PerformanceTestSpan.TRACER.spanBuilder(SPAN_NAME).startSpan()
val processor = readAction {
MyDirectoryAsPackageRenameHandler().createProcessor(newName, whereToRename, project, directory, false, false)
}
withContext(Dispatchers.EDT) {
writeIntentReadAction {
processor.run()
span.end()
}
}
}
private class MyDirectoryAsPackageRenameHandler: DirectoryAsPackageRenameHandler() {
fun createProcessor(
newName: String,
whereToRename: WhereToRename,
project: Project,
directory: PsiDirectory,
searchInComments: Boolean,
searchInNonJavaFiles: Boolean
): BaseRefactoringProcessor {
val module = ModuleUtilCore.findModuleForFile(directory.getVirtualFile(), project)!!
val aPackage = this.getPackage(directory) ?: throw IllegalArgumentException("Couldn't get a package for the directory: ${directory.getVirtualFile().getPath()}")
val newQName = StringUtil.getQualifiedName(StringUtil.getPackageName(getQualifiedName(aPackage)), newName)
val dirsToRename = when (whereToRename) {
WhereToRename.DIRECTORY -> arrayOf(directory)
WhereToRename.MODULE -> aPackage.getDirectories(GlobalSearchScope.moduleScope(module))
WhereToRename.PROJECT -> aPackage.getDirectories(GlobalSearchScope.projectScope(project))
}
return object : MoveDirectoryWithClassesProcessor(project, dirsToRename, null, searchInComments, searchInNonJavaFiles, false, null) {
override fun getTargetDirectory(dir: PsiDirectory): TargetDirectoryWrapper {
return TargetDirectoryWrapper(dir.getParentDirectory(), StringUtil.getShortName(newQName))
}
override fun getTargetName(): String { return newQName }
override fun getCommandName(): String {
return RefactoringBundle.message(if (dirsToRename.size == 1) "rename.directory.command.name" else "rename.directories.command.name")
}
}
}
}
}

View File

@@ -1264,3 +1264,23 @@ fun <T : CommandChain> T.waitForVfsRefreshSelectedEditor(): T = apply {
fun <T : CommandChain> T.closeLookup(): T = apply {
addCommand("${CMD_PREFIX}closeLookup")
}
/** @see com.intellij.java.performancePlugin.RenameDirectoryAsPackageCommand */
@Suppress("KDocUnresolvedReference", "unused")
enum class RenameDirectoryAsPackageTarget { DIRECTORY, MODULE, PROJECT }
fun <T : CommandChain> T.renameDirectoryAsPackage(directory: String, newName: String, whereToRename: RenameDirectoryAsPackageTarget): T = apply {
addCommand("${CMD_PREFIX}renameDirectoryAsPackage $directory $newName $whereToRename")
}
/** @see com.intellij.java.performancePlugin.ChangeJavaSignatureCommand */
@Suppress("KDocUnresolvedReference")
enum class ChangeJavaSignatureAction { ADD_PARAMETER }
fun <T : CommandChain> T.changeJavaSignature(action: ChangeJavaSignatureAction, name: String): T = apply {
addCommand("${CMD_PREFIX}changeJavaSignature $action $name")
}
/** @see com.intellij.java.performancePlugin.InlineJavaMethodCommand */
@Suppress("KDocUnresolvedReference")
fun <T : CommandChain> T.inlineJavaMethod(): T = apply {
addCommand("${CMD_PREFIX}inlineJavaMethod")
}