Add FUS support to move refactoring

This commit is contained in:
Anton Yalyshev
2020-02-03 09:37:02 +03:00
committed by Igor Yakovlev
parent f489eda0e7
commit 8877559c02
16 changed files with 279 additions and 59 deletions
@@ -0,0 +1,42 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.statistics
object MoveRefactoringFUSCollector {
/**
* @param isDefault is something changed in Move Refactoring Dialog check-boxes state
*/
fun log(
timeStarted: Long,
timeFinished: Long,
numberOfEntities: Int,
entity: MovedEntity,
destination: MoveRefactoringDestination,
isDefault: Boolean,
isSucceeded: Boolean
) {
val data = mapOf(
"lagging" to (timeFinished - timeStarted).toString(),
"entity" to entity.toString(),
"destination" to destination.toString(),
"number_of_entities" to numberOfEntities.toString(),
"are_settings_changed" to isDefault.toString(),
"succeeded" to isSucceeded.toString()
)
KotlinFUSLogger.log(FUSEventGroups.Refactoring, "Move", data)
}
enum class MoveRefactoringDestination {
PACKAGE, FILE, DECLARATION
}
enum class MovedEntity {
FUNCTIONS, CLASSES, MIXED, MPPCLASSES, MPPFUNCTIONS, MPPMIXED, PACKAGE, FILES
}
}
+1 -1
View File
@@ -95,7 +95,7 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<statistics.counterUsagesCollector groupId="kotlin.maven.target" version="3"/>
<statistics.counterUsagesCollector groupId="kotlin.jps.target" version="3"/>
<statistics.counterUsagesCollector groupId="kotlin.gradle.library" version="1"/>
<statistics.counterUsagesCollector groupId="kotlin.ide.action.refactoring" version="1"/>
<statistics.counterUsagesCollector groupId="kotlin.ide.refactoring" version="1"/>
<statistics.counterUsagesCollector groupId="kotlin.ide.newFileTempl" version="1"/>
<statistics.counterUsagesCollector groupId="kotlin.ide.npwizards" version="2"/>
<statistics.counterUsagesCollector groupId="kotlin.ide.debugger" version="2"/>
+1 -1
View File
@@ -108,7 +108,7 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<statistics.counterUsagesCollector groupId="kotlin.maven.target" version="3"/>
<statistics.counterUsagesCollector groupId="kotlin.jps.target" version="3"/>
<statistics.counterUsagesCollector groupId="kotlin.gradle.library" version="1"/>
<statistics.counterUsagesCollector groupId="kotlin.ide.action.refactoring" version="1"/>
<statistics.counterUsagesCollector groupId="kotlin.ide.refactoring" version="1"/>
<statistics.counterUsagesCollector groupId="kotlin.ide.newFileTempl" version="1"/>
<statistics.counterUsagesCollector groupId="kotlin.ide.npwizards" version="2"/>
<statistics.counterUsagesCollector groupId="kotlin.ide.debugger" version="2"/>
+1 -1
View File
@@ -108,7 +108,7 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<statistics.counterUsagesCollector groupId="kotlin.maven.target" version="3"/>
<statistics.counterUsagesCollector groupId="kotlin.jps.target" version="3"/>
<statistics.counterUsagesCollector groupId="kotlin.gradle.library" version="1"/>
<statistics.counterUsagesCollector groupId="kotlin.ide.action.refactoring" version="1"/>
<statistics.counterUsagesCollector groupId="kotlin.ide.refactoring" version="1"/>
<statistics.counterUsagesCollector groupId="kotlin.ide.newFileTempl" version="1"/>
<statistics.counterUsagesCollector groupId="kotlin.ide.npwizards" version="2"/>
<statistics.counterUsagesCollector groupId="kotlin.ide.debugger" version="2"/>
@@ -104,7 +104,7 @@ internal class MoveKotlinDeclarationsHandlerTestActions(private val caseDataKeep
caseDataKeeper.caseData = model.testDataString()
model.computeModelResult(throwOnConflicts = true).run()
model.computeModelResult(throwOnConflicts = true).processor.run()
}
private fun doWithMoveKotlinNestedClassesToUpperLevelModel(nestedClass: KtClassOrObject, targetContainer: PsiElement?) {
@@ -135,7 +135,7 @@ internal class MoveKotlinDeclarationsHandlerTestActions(private val caseDataKeep
caseDataKeeper.caseData = model.testDataString()
model.computeModelResult(throwOnConflicts = true).run()
model.computeModelResult(throwOnConflicts = true).processor.run()
}
override fun invokeMoveKotlinNestedClassesRefactoring(
@@ -188,7 +188,7 @@ internal class MoveKotlinDeclarationsHandlerTestActions(private val caseDataKeep
caseDataKeeper.caseData = model.testDataString()
model.computeModelResult(throwOnConflicts = true).run()
model.computeModelResult(throwOnConflicts = true).processor.run()
}
override fun invokeKotlinSelectNestedClassChooser(nestedClass: KtClassOrObject, targetContainer: PsiElement?) {
@@ -236,7 +236,7 @@ internal class MoveKotlinDeclarationsHandlerTestActions(private val caseDataKeep
caseDataKeeper.caseData = model.testDataString()
project.executeCommand(MoveHandler.REFACTORING_NAME) {
model.computeModelResult().run()
model.computeModelResult().processor.run()
}
}
}
@@ -6,11 +6,21 @@
package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui
import com.intellij.openapi.options.ConfigurationException
import com.intellij.refactoring.BaseRefactoringProcessor
import org.jetbrains.kotlin.idea.statistics.MoveRefactoringFUSCollector.MovedEntity
import org.jetbrains.kotlin.idea.statistics.MoveRefactoringFUSCollector.MoveRefactoringDestination
internal interface Model<out T> {
internal class ModelResultWithFUSData(
val processor: BaseRefactoringProcessor,
val elementsCount: Int,
val entityToMove: MovedEntity,
val destination: MoveRefactoringDestination
)
internal interface Model {
@Throws(ConfigurationException::class)
fun computeModelResult(throwOnConflicts: Boolean = false): T
fun computeModelResult(throwOnConflicts: Boolean = false): ModelResultWithFUSData
@Throws(ConfigurationException::class)
fun computeModelResult(): T
fun computeModelResult(): ModelResultWithFUSData
}
@@ -18,7 +18,10 @@ import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.ui.TextComponentAccessor
import com.intellij.openapi.util.Disposer
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.psi.*
import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiFileSystemItem
import com.intellij.psi.PsiManager
import com.intellij.refactoring.RefactoringBundle
import com.intellij.refactoring.copy.CopyFilesOrDirectoriesDialog
import com.intellij.refactoring.copy.CopyFilesOrDirectoriesHandler
@@ -34,9 +37,10 @@ import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.core.packageMatchesDirectoryOrImplicit
import org.jetbrains.kotlin.idea.core.util.onTextChange
import org.jetbrains.kotlin.idea.refactoring.isInKotlinAwareSourceRoot
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.KotlinAwareMoveFilesOrDirectoriesProcessor
import org.jetbrains.kotlin.idea.refactoring.move.logFusForMoveRefactoring
import org.jetbrains.kotlin.idea.util.application.executeCommand
import org.jetbrains.kotlin.psi.KtFile
import java.util.*
import javax.swing.JComponent
class KotlinAwareMoveFilesOrDirectoriesDialog(
@@ -69,6 +73,7 @@ class KotlinAwareMoveFilesOrDirectoriesDialog(
}
private val openInEditorCb = NonFocusableCheckBox(KotlinBundle.message("checkbox.text.open.moved.files.in.editor"))
private val updatePackageDirectiveCb = NonFocusableCheckBox()
private val initializedCheckBoxesState: BitSet
override fun getHelpId() = HELP_ID
@@ -76,6 +81,14 @@ class KotlinAwareMoveFilesOrDirectoriesDialog(
title = RefactoringBundle.message("move.title")
init()
initializeData()
initializedCheckBoxesState = getCheckboxesState(applyDefaults = true)
}
private fun getCheckboxesState(applyDefaults: Boolean) = BitSet(3).apply {
set(0, applyDefaults || searchReferencesCb.isSelected) //searchReferencesCb is true by default
set(1, !applyDefaults && openInEditorCb.isSelected) //openInEditorCb is false by default
set(2, updatePackageDirectiveCb.isSelected)
}
override fun createActions() = arrayOf(okAction, cancelAction, helpAction)
@@ -163,7 +176,7 @@ class KotlinAwareMoveFilesOrDirectoriesDialog(
}
}
private fun getModel(): Model<KotlinAwareMoveFilesOrDirectoriesProcessor> {
private fun getModel(): Model {
val directory = LocalFileSystem.getInstance().findFileByPath(selectedDirectoryName)?.let {
PsiManager.getInstance(project).findDirectory(it)
@@ -188,20 +201,28 @@ class KotlinAwareMoveFilesOrDirectoriesDialog(
override fun doOKAction() {
val processor: KotlinAwareMoveFilesOrDirectoriesProcessor
val modelResult: ModelResultWithFUSData
try {
processor = getModel().computeModelResult()
modelResult = getModel().computeModelResult()
} catch (e: ConfigurationException) {
setErrorText(e.message)
return
}
project.executeCommand(MoveHandler.REFACTORING_NAME) {
processor.run()
with(modelResult) {
logFusForMoveRefactoring(
elementsCount,
entityToMove,
destination,
getCheckboxesState(applyDefaults = false) == initializedCheckBoxesState,
Runnable { processor.run() }
)
}
}
setConfigurationValue(id = MOVE_FILES_OPEN_IN_EDITOR, value = openInEditorCb.isSelected, defaultValue = false)
setConfigurationValue(id = MOVE_FILES_SEARCH_REFERENCES, value = searchReferencesCb.isSelected, defaultValue = true)
setConfigurationValue(id = MOVE_FILES_OPEN_IN_EDITOR, value = openInEditorCb.isSelected, defaultValue = false)
RecentsManager.getInstance(project).registerRecentEntry(RECENT_KEYS, targetDirectoryField.childComponent.text)
@@ -20,19 +20,20 @@ import org.jetbrains.kotlin.idea.refactoring.isInKotlinAwareSourceRoot
import org.jetbrains.kotlin.idea.refactoring.move.getOrCreateDirectory
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.KotlinAwareMoveFilesOrDirectoriesProcessor
import org.jetbrains.kotlin.idea.refactoring.move.updatePackageDirective
import org.jetbrains.kotlin.idea.statistics.MoveRefactoringFUSCollector
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile
import java.nio.file.InvalidPathException
import java.nio.file.Paths
class KotlinAwareMoveFilesOrDirectoriesModel(
internal class KotlinAwareMoveFilesOrDirectoriesModel(
val project: Project,
val elementsToMove: List<PsiFileSystemItem>,
val targetDirectoryName: String,
val updatePackageDirective: Boolean,
val searchReferences: Boolean,
val moveCallback: MoveCallback?
) : Model<KotlinAwareMoveFilesOrDirectoriesProcessor> {
) : Model {
private fun checkedGetElementsToMove(selectedDirectory: PsiDirectory): List<PsiElement> {
@@ -94,20 +95,29 @@ class KotlinAwareMoveFilesOrDirectoriesModel(
}
@Throws(ConfigurationException::class)
override fun computeModelResult(throwOnConflicts: Boolean): KotlinAwareMoveFilesOrDirectoriesProcessor {
override fun computeModelResult(throwOnConflicts: Boolean): ModelResultWithFUSData {
checkModel()
val selectedDir = checkedGetTargetDirectory()
return KotlinAwareMoveFilesOrDirectoriesProcessor(
val elementsToMove = checkedGetElementsToMove(selectedDir)
val processor = KotlinAwareMoveFilesOrDirectoriesProcessor(
project,
checkedGetElementsToMove(selectedDir),
elementsToMove,
selectedDir,
searchReferences = searchReferences,
searchInComments = false,
searchInNonJavaFiles = false,
moveCallback = moveCallback
)
return ModelResultWithFUSData(
processor,
elementsToMove.size,
MoveRefactoringFUSCollector.MovedEntity.FILES,
MoveRefactoringFUSCollector.MoveRefactoringDestination.PACKAGE
)
}
}
@@ -34,7 +34,7 @@ import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringUtilKt;
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo;
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberSelectionPanel;
import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberSelectionTable;
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.*;
import org.jetbrains.kotlin.idea.refactoring.move.MoveUtilsKt;
import org.jetbrains.kotlin.idea.refactoring.ui.KotlinTypeReferenceEditorComboWithBrowseButton;
import org.jetbrains.kotlin.psi.*;
import javax.swing.*;
@@ -82,6 +82,13 @@ public class MoveKotlinNestedClassesDialog extends RefactoringDialog {
validateButtons();
myHelpAction.setEnabled(false);
initializedCheckBoxesState = getCheckboxesState();
}
private final int initializedCheckBoxesState;
private int getCheckboxesState() {
return openInEditorCheckBox.isSelected() ? 1 : 0;
}
private void initClassChooser(@NotNull KtClassOrObject initialTargetClass) {
@@ -217,7 +224,7 @@ public class MoveKotlinNestedClassesDialog extends RefactoringDialog {
return "#" + getClass().getName();
}
private Model<MoveKotlinDeclarationsProcessor> getModel() {
private Model getModel() {
return new MoveKotlinNestedClassesModel(
myProject,
openInEditorCheckBox.isSelected(),
@@ -230,16 +237,22 @@ public class MoveKotlinNestedClassesDialog extends RefactoringDialog {
@Override
protected void doAction() {
MoveKotlinDeclarationsProcessor processor;
ModelResultWithFUSData modelResult;
try {
processor = getModel().computeModelResult();
modelResult = getModel().computeModelResult();
}
catch (ConfigurationException e) {
setErrorText(e.getMessage());
return;
}
invokeRefactoring(processor);
MoveUtilsKt.logFusForMoveRefactoring(
modelResult.getElementsCount(),
modelResult.getEntityToMove(),
modelResult.getDestination(),
getCheckboxesState() == initializedCheckBoxesState,
() -> invokeRefactoring(modelResult.getProcessor())
);
}
@Override
@@ -13,6 +13,8 @@ import com.intellij.refactoring.RefactoringBundle
import com.intellij.refactoring.move.MoveCallback
import org.jetbrains.kotlin.idea.KotlinBundle
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.*
import org.jetbrains.kotlin.idea.statistics.MoveRefactoringFUSCollector.MoveRefactoringDestination
import org.jetbrains.kotlin.idea.statistics.MoveRefactoringFUSCollector.MovedEntity
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtElement
@@ -23,7 +25,7 @@ internal class MoveKotlinNestedClassesModel(
val originalClass: KtClassOrObject,
val targetClass: PsiElement?,
val moveCallback: MoveCallback?
) : Model<MoveKotlinDeclarationsProcessor> {
) : Model {
private fun getCheckedTargetClass(): KtElement {
val targetClass = this.targetClass ?: throw ConfigurationException(RefactoringBundle.message("no.destination.class.specified"))
@@ -51,7 +53,7 @@ internal class MoveKotlinNestedClassesModel(
override fun computeModelResult() = computeModelResult(throwOnConflicts = false)
@Throws(ConfigurationException::class)
override fun computeModelResult(throwOnConflicts: Boolean): MoveKotlinDeclarationsProcessor {
override fun computeModelResult(throwOnConflicts: Boolean): ModelResultWithFUSData {
val elementsToMove = selectedElementsToMove
val target = KotlinMoveTargetForExistingElement(getCheckedTargetClass())
val delegate = MoveDeclarationsDelegate.NestedClass()
@@ -67,6 +69,13 @@ internal class MoveKotlinNestedClassesModel(
openInEditor = openInEditorCheckBox
)
return MoveKotlinDeclarationsProcessor(descriptor, Mover.Default, throwOnConflicts)
val processor = MoveKotlinDeclarationsProcessor(descriptor, Mover.Default, throwOnConflicts)
return ModelResultWithFUSData(
processor,
elementsToMove.size,
MovedEntity.CLASSES,
MoveRefactoringDestination.DECLARATION
)
}
}
@@ -31,7 +31,6 @@ import org.jetbrains.kotlin.idea.core.KotlinNameSuggester;
import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator;
import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringSettings;
import org.jetbrains.kotlin.idea.refactoring.move.MoveUtilsKt;
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveKotlinDeclarationsProcessor;
import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.psi.KtClass;
import org.jetbrains.kotlin.psi.KtClassBody;
@@ -41,6 +40,7 @@ import org.jetbrains.kotlin.types.KotlinType;
import javax.swing.*;
import java.awt.*;
import java.util.BitSet;
import java.util.Collections;
import java.util.List;
@@ -126,6 +126,19 @@ public class MoveKotlinNestedClassesToUpperLevelDialog extends MoveDialogBase {
return ((ClassDescriptor) innerClassDescriptor.getContainingDeclaration()).getDefaultType();
}
private BitSet initializedCheckBoxesState;
private BitSet getCheckboxesState(boolean applyDefaults) {
BitSet state = new BitSet(3);
state.set(0, !applyDefaults && searchInCommentsCheckBox.isSelected()); //searchInCommentsCheckBox default is false
state.set(1, !applyDefaults && searchForTextOccurrencesCheckBox.isSelected()); //searchForTextOccurrencesCheckBox default is false
state.set(2, passOuterClassCheckBox.isSelected());
return state;
}
@Override
protected void init() {
classNameField.setText(innerClass.getName());
@@ -182,6 +195,8 @@ public class MoveKotlinNestedClassesToUpperLevelDialog extends MoveDialogBase {
searchInCommentsCheckBox.setSelected(settings.MOVE_TO_UPPER_LEVEL_SEARCH_IN_COMMENTS);
super.init();
initializedCheckBoxesState = getCheckboxesState(true);
}
@Override
@@ -233,7 +248,7 @@ public class MoveKotlinNestedClassesToUpperLevelDialog extends MoveDialogBase {
}
}
private Model<MoveKotlinDeclarationsProcessor> getModel() {
private Model getModel() {
return new MoveKotlinNestedClassesToUpperLevelModelWithUIChooser(
project,
innerClass,
@@ -251,9 +266,9 @@ public class MoveKotlinNestedClassesToUpperLevelDialog extends MoveDialogBase {
@Override
protected void doAction() {
MoveKotlinDeclarationsProcessor processor;
ModelResultWithFUSData modelResult;
try {
processor = getModel().computeModelResult();
modelResult = getModel().computeModelResult();
}
catch (ConfigurationException e) {
setErrorText(e.getMessage());
@@ -266,6 +281,12 @@ public class MoveKotlinNestedClassesToUpperLevelDialog extends MoveDialogBase {
saveOpenInEditorOption();
invokeRefactoring(processor);
MoveUtilsKt.logFusForMoveRefactoring(
modelResult.getElementsCount(),
modelResult.getEntityToMove(),
modelResult.getDestination(),
getCheckboxesState(false).equals(initializedCheckBoxesState),
() -> invokeRefactoring(modelResult.getProcessor())
);
}
}
@@ -28,6 +28,8 @@ import org.jetbrains.kotlin.idea.refactoring.createKotlinFile
import org.jetbrains.kotlin.idea.refactoring.move.getTargetPackageFqName
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.*
import org.jetbrains.kotlin.idea.roots.getSuitableDestinationSourceRoots
import org.jetbrains.kotlin.idea.statistics.MoveRefactoringFUSCollector.MovedEntity
import org.jetbrains.kotlin.idea.statistics.MoveRefactoringFUSCollector.MoveRefactoringDestination
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtClassOrObject
@@ -48,7 +50,7 @@ internal abstract class MoveKotlinNestedClassesToUpperLevelModel(
val isSearchInNonJavaFiles: Boolean,
val packageName: String,
val isOpenInEditor: Boolean
) : Model<MoveKotlinDeclarationsProcessor> {
) : Model {
protected abstract fun chooseSourceRoot(
newPackage: PackageWrapper,
@@ -157,9 +159,9 @@ internal abstract class MoveKotlinNestedClassesToUpperLevelModel(
}
@Throws(ConfigurationException::class)
private fun getMoveTarget(): KotlinMoveTarget {
private fun getMoveTarget(): Pair<KotlinMoveTarget, MoveRefactoringDestination> {
val target = getTargetContainerWithValidation()
if (target is PsiDirectory) {
return if (target is PsiDirectory) {
val targetPackageFqName = getTargetPackageFqName(target)
?: throw ConfigurationException(KotlinBundle.message("text.cannot.find.target.package.name"))
@@ -169,13 +171,15 @@ internal abstract class MoveKotlinNestedClassesToUpperLevelModel(
val targetFileName = suggestedName + "." + KotlinFileType.EXTENSION
return KotlinMoveTargetForDeferredFile(
val target = KotlinMoveTargetForDeferredFile(
targetPackageFqName,
target,
targetFile = null
) { createKotlinFile(targetFileName, target, targetPackageFqName.asString()) }
target to MoveRefactoringDestination.FILE
} else {
return KotlinMoveTargetForExistingElement(target as KtElement)
KotlinMoveTargetForExistingElement(target as KtElement) to MoveRefactoringDestination.DECLARATION
}
}
@@ -183,7 +187,7 @@ internal abstract class MoveKotlinNestedClassesToUpperLevelModel(
override fun computeModelResult() = computeModelResult(throwOnConflicts = false)
@Throws(ConfigurationException::class)
override fun computeModelResult(throwOnConflicts: Boolean): MoveKotlinDeclarationsProcessor {
override fun computeModelResult(throwOnConflicts: Boolean): ModelResultWithFUSData {
val moveTarget = getMoveTarget()
@@ -192,7 +196,7 @@ internal abstract class MoveKotlinNestedClassesToUpperLevelModel(
val moveDescriptor = MoveDeclarationsDescriptor(
project,
MoveSource(innerClass),
moveTarget,
moveTarget.first,
delegate,
searchInComments,
isSearchInNonJavaFiles,
@@ -201,6 +205,13 @@ internal abstract class MoveKotlinNestedClassesToUpperLevelModel(
openInEditor = isOpenInEditor
)
return MoveKotlinDeclarationsProcessor(moveDescriptor, Mover.Default, throwOnConflicts)
val processor = MoveKotlinDeclarationsProcessor(moveDescriptor, Mover.Default, throwOnConflicts)
return ModelResultWithFUSData(
processor = processor,
elementsCount = 1,
entityToMove = MovedEntity.CLASSES,
destination = moveTarget.second
)
}
}
@@ -12,7 +12,6 @@ import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.util.Pass;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiFile;
import com.intellij.refactoring.BaseRefactoringProcessor;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.classMembers.AbstractMemberInfoModel;
import com.intellij.refactoring.classMembers.MemberInfoBase;
@@ -42,7 +41,6 @@ import org.jetbrains.kotlin.idea.refactoring.move.MoveUtilsKt;
import org.jetbrains.kotlin.idea.refactoring.ui.KotlinDestinationFolderComboBox;
import org.jetbrains.kotlin.idea.refactoring.ui.KotlinFileChooserDialog;
import org.jetbrains.kotlin.idea.util.ExpectActualUtilKt;
import org.jetbrains.kotlin.psi.KtElement;
import org.jetbrains.kotlin.psi.KtFile;
import org.jetbrains.kotlin.psi.KtNamedDeclaration;
import org.jetbrains.kotlin.psi.KtPureElement;
@@ -51,10 +49,8 @@ import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.util.Collection;
import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.jetbrains.kotlin.idea.roots.ProjectRootUtilsKt.getSuitableDestinationSourceRoots;
@@ -148,6 +144,24 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
initMemberInfo(elementsToMove, sourceFiles);
updateControls();
initializedCheckBoxesState = getCheckboxesState(true);
}
private final BitSet initializedCheckBoxesState;
private BitSet getCheckboxesState(boolean applyDefaults) {
BitSet state = new BitSet(7);
state.set(0, applyDefaults || cbSearchInComments.isSelected()); //cbSearchInComments default is true
state.set(1, applyDefaults || cbSearchTextOccurrences.isSelected()); //cbSearchTextOccurrences default is true
state.set(2, applyDefaults || cbDeleteEmptySourceFiles.isSelected()); //cbDeleteEmptySourceFiles default is true
state.set(3, applyDefaults || cbApplyMPPDeclarationsMove.isSelected()); //cbApplyMPPDeclarationsMove default is true
state.set(4, cbSpecifyFileNameInPackage.isSelected());
state.set(5, cbUpdatePackageDirective.isSelected());
state.set(6, cbSearchReferences.isSelected());
return state;
}
private static List<KtFile> getSourceFiles(@NotNull Collection<KtNamedDeclaration> elementsToMove) {
@@ -427,16 +441,18 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
return elementsToMove;
}
private Model<BaseRefactoringProcessor> getModel() throws ConfigurationException {
private MoveKotlinTopLevelDeclarationsModel getModel() throws ConfigurationException {
DirectoryChooser.ItemWrapper selectedItem = (DirectoryChooser.ItemWrapper) destinationFolderCB.getComboBox().getSelectedItem();
PsiDirectory selectedPsiDirectory = selectedItem != null ? selectedItem.getDirectory() : initialTargetDirectory;
boolean mppDeclarationSelected = cbApplyMPPDeclarationsMove.isSelected() && isMppDeclarationSelected();
List<KtNamedDeclaration> selectedElements = getSelectedElementsToMoveChecked();
return new MoveKotlinTopLevelDeclarationsModel(
myProject,
getSelectedElementsToMoveChecked(),
selectedElements,
getTargetPackage(),
selectedPsiDirectory,
tfFileNameInPackage.getText(),
@@ -456,9 +472,9 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
@Override
protected void doAction() {
BaseRefactoringProcessor processor;
ModelResultWithFUSData modelResult;
try {
processor = getModel().computeModelResult();
modelResult = getModel().computeModelResult();
}
catch (ConfigurationException e) {
setErrorText(e.getMessage());
@@ -468,7 +484,13 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog {
saveRefactoringSettings();
try {
invokeRefactoring(processor);
MoveUtilsKt.logFusForMoveRefactoring(
modelResult.getElementsCount(),
modelResult.getEntityToMove(),
modelResult.getDestination(),
getCheckboxesState(false).equals(initializedCheckBoxesState),
() -> invokeRefactoring(modelResult.getProcessor())
);
} catch (IncorrectOperationException e) {
CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("error.title"), e.getMessage(), null, myProject);
}
@@ -27,11 +27,15 @@ import org.jetbrains.kotlin.idea.refactoring.getOrCreateKotlinFile
import org.jetbrains.kotlin.idea.refactoring.move.getOrCreateDirectory
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.*
import org.jetbrains.kotlin.idea.refactoring.move.updatePackageDirective
import org.jetbrains.kotlin.idea.statistics.MoveRefactoringFUSCollector.MovedEntity
import org.jetbrains.kotlin.idea.statistics.MoveRefactoringFUSCollector.MoveRefactoringDestination
import org.jetbrains.kotlin.idea.util.collectAllExpectAndActualDeclaration
import org.jetbrains.kotlin.idea.util.isEffectivelyActual
import org.jetbrains.kotlin.idea.util.isExpectDeclaration
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtClassOrObject
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtFunction
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import java.io.File
import java.nio.file.InvalidPathException
@@ -53,7 +57,7 @@ internal class MoveKotlinTopLevelDeclarationsModel(
val isFullFileMove: Boolean,
val applyMPPDeclarations: Boolean,
val moveCallback: MoveCallback?
) : Model<BaseRefactoringProcessor> {
) : Model {
private inline fun <T, K> Set<T>.mapToSingleOrNull(transform: (T) -> K?): K? =
mapTo(mutableSetOf(), transform).singleOrNull()
@@ -217,15 +221,29 @@ internal class MoveKotlinTopLevelDeclarationsModel(
override fun computeModelResult() = computeModelResult(throwOnConflicts = false)
@Throws(ConfigurationException::class)
override fun computeModelResult(throwOnConflicts: Boolean): BaseRefactoringProcessor {
override fun computeModelResult(throwOnConflicts: Boolean): ModelResultWithFUSData {
verifyBeforeRun()
val classType = if (elementsToMoveHasMPP) MovedEntity.MPPCLASSES else MovedEntity.CLASSES
val functionType = if (elementsToMoveHasMPP) MovedEntity.MPPFUNCTIONS else MovedEntity.FUNCTIONS
val mixedType = if (elementsToMoveHasMPP) MovedEntity.MPPMIXED else MovedEntity.MIXED
val allClasses = elementsToMove.any { element -> element is KtClassOrObject }
val allFunctions = elementsToMove.any { element -> element is KtFunction }
val entity = if (allClasses && allFunctions) mixedType else
if (allClasses) classType else functionType
val destination = if (isMoveToPackage) MoveRefactoringDestination.PACKAGE else MoveRefactoringDestination.FILE
if (isFullFileMove && isMoveToPackage) {
tryMoveFile(throwOnConflicts)?.let { return it }
tryMoveFile(throwOnConflicts)?.let {
return ModelResultWithFUSData(it, elementsToMove.size, entity, destination)
}
}
return moveDeclaration(throwOnConflicts)
val processor = moveDeclaration(throwOnConflicts)
return ModelResultWithFUSData(processor, elementsToMove.size, entity, destination)
}
private fun tryMoveFile(throwOnConflicts: Boolean): BaseRefactoringProcessor? {
@@ -16,6 +16,7 @@ import com.intellij.refactoring.move.MoveHandler
import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil
import org.jetbrains.kotlin.asJava.classes.KtLightClassForFacade
import org.jetbrains.kotlin.idea.refactoring.move.MoveFilesOrDirectoriesHandlerCompat
import org.jetbrains.kotlin.idea.refactoring.move.logFusForMoveRefactoring
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.KotlinAwareMoveFilesOrDirectoriesDialog
import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.KotlinAwareMoveFilesOrDirectoriesModel
import org.jetbrains.kotlin.idea.util.application.executeCommand
@@ -60,7 +61,8 @@ class KotlinMoveFilesOrDirectoriesHandler : MoveFilesOrDirectoriesHandlerCompat(
val initialTargetDirectory = MoveFilesOrDirectoriesUtil.getInitialTargetDirectory(targetDirectory, elements)
if (ApplicationManager.getApplication().isUnitTestMode && initialTargetDirectory !== null) {
val isTestUnitMode = ApplicationManager.getApplication().isUnitTestMode
if (isTestUnitMode && initialTargetDirectory !== null) {
KotlinAwareMoveFilesOrDirectoriesModel(
project,
adjustedElementsToMove,
@@ -70,7 +72,19 @@ class KotlinMoveFilesOrDirectoriesHandler : MoveFilesOrDirectoriesHandlerCompat(
moveCallback = callback
).run {
project.executeCommand(MoveHandler.REFACTORING_NAME) {
computeModelResult().run()
with(computeModelResult()) {
if (!isTestUnitMode) {
logFusForMoveRefactoring(
elementsCount,
entityToMove,
destination,
true,
Runnable { processor.run() }
)
} else {
processor.run()
}
}
}
}
return
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.idea.refactoring.fqName.isImported
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference.ShorteningMode
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.statistics.MoveRefactoringFUSCollector
import org.jetbrains.kotlin.idea.util.application.executeCommand
import org.jetbrains.kotlin.idea.util.application.runWriteAction
import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor
@@ -55,7 +56,9 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS
import org.jetbrains.kotlin.utils.addIfNotNull
import java.io.File
import java.lang.System.currentTimeMillis
import java.util.*
import javax.swing.JCheckBox
sealed class ContainerInfo {
abstract val fqName: FqName?
@@ -679,4 +682,30 @@ internal fun getTargetPackageFqName(targetContainer: PsiElement): FqName? {
return if (targetPackage != null) FqName(targetPackage.qualifiedName) else null
}
return if (targetContainer is KtFile) targetContainer.packageFqName else null
}
internal fun logFusForMoveRefactoring(
numberOfEntities: Int,
entity: MoveRefactoringFUSCollector.MovedEntity,
destination: MoveRefactoringFUSCollector.MoveRefactoringDestination,
isDefault: Boolean,
body: Runnable
) {
val timeStarted = currentTimeMillis()
var succeeded = false
try {
body.run()
succeeded = true
} finally {
MoveRefactoringFUSCollector.log(
timeStarted = timeStarted,
timeFinished = currentTimeMillis(),
numberOfEntities = numberOfEntities,
destination = destination,
isDefault = isDefault,
entity = entity,
isSucceeded = succeeded,
)
}
}