From 40bbf82a414b3ebbda7d48f2c56402243f88c73f Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Sun, 19 Mar 2017 16:51:25 +0300 Subject: [PATCH] Hint action to update usages on cut/paste of top-level declarations --- .../kotlin/renderer/DescriptorRenderer.kt | 2 +- .../kotlin/renderer/DescriptorRendererImpl.kt | 4 +- .../renderer/DescriptorRendererOptionsImpl.kt | 5 +- .../kotlin/generators/tests/GenerateTests.kt | 4 + .../kotlin/idea/completion/Statisticians.kt | 2 +- .../OverrideMemberChooserObject.kt | 2 +- idea/src/META-INF/plugin.xml | 5 + .../ChangeMemberFunctionSignatureFix.kt | 4 +- .../ReplaceProtectedToPublishedApiCallFix.kt | 2 +- .../MoveDeclarationsCopyPasteProcessor.kt | 108 ++++++++++++++ .../cutPaste/MoveDeclarationsEditorCookie.kt | 30 ++++ .../MoveDeclarationsIntentionAction.kt | 64 +++++++++ .../cutPaste/MoveDeclarationsPassFactory.kt | 79 +++++++++++ .../cutPaste/MoveDeclarationsProcessor.kt | 134 ++++++++++++++++++ .../MoveDeclarationsTransferableData.kt | 43 ++++++ .../ChangePackage.dependency.expected.kt | 11 ++ .../ChangePackage.dependency.kt | 9 ++ .../ChangePackage.expected.kt | 7 + .../moveDeclarations/ChangePackage.kt | 20 +++ .../ChangePackage.to.expected.kt | 19 +++ .../moveDeclarations/ChangePackage.to.kt | 5 + .../copyPaste/moveDeclarations/Copy.kt | 8 ++ ...mizeImportsAfterCut.dependency.expected.kt | 7 + .../OptimizeImportsAfterCut.dependency.kt | 9 ++ .../OptimizeImportsAfterCut.expected.kt | 6 + .../OptimizeImportsAfterCut.kt | 12 ++ .../OptimizeImportsAfterCut.to.expected.kt | 7 + .../copyPaste/moveDeclarations/SamePackage.kt | 7 + .../moveDeclarations/SamePackage.to.kt | 3 + .../codeInsight/AbstractMoveOnCutPasteTest.kt | 89 ++++++++++++ .../MoveOnCutPasteTestGenerated.java | 62 ++++++++ 31 files changed, 759 insertions(+), 10 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/refactoring/cutPaste/MoveDeclarationsCopyPasteProcessor.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/refactoring/cutPaste/MoveDeclarationsEditorCookie.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/refactoring/cutPaste/MoveDeclarationsIntentionAction.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/refactoring/cutPaste/MoveDeclarationsPassFactory.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/refactoring/cutPaste/MoveDeclarationsProcessor.kt create mode 100644 idea/src/org/jetbrains/kotlin/idea/refactoring/cutPaste/MoveDeclarationsTransferableData.kt create mode 100644 idea/testData/copyPaste/moveDeclarations/ChangePackage.dependency.expected.kt create mode 100644 idea/testData/copyPaste/moveDeclarations/ChangePackage.dependency.kt create mode 100644 idea/testData/copyPaste/moveDeclarations/ChangePackage.expected.kt create mode 100644 idea/testData/copyPaste/moveDeclarations/ChangePackage.kt create mode 100644 idea/testData/copyPaste/moveDeclarations/ChangePackage.to.expected.kt create mode 100644 idea/testData/copyPaste/moveDeclarations/ChangePackage.to.kt create mode 100644 idea/testData/copyPaste/moveDeclarations/Copy.kt create mode 100644 idea/testData/copyPaste/moveDeclarations/OptimizeImportsAfterCut.dependency.expected.kt create mode 100644 idea/testData/copyPaste/moveDeclarations/OptimizeImportsAfterCut.dependency.kt create mode 100644 idea/testData/copyPaste/moveDeclarations/OptimizeImportsAfterCut.expected.kt create mode 100644 idea/testData/copyPaste/moveDeclarations/OptimizeImportsAfterCut.kt create mode 100644 idea/testData/copyPaste/moveDeclarations/OptimizeImportsAfterCut.to.expected.kt create mode 100644 idea/testData/copyPaste/moveDeclarations/SamePackage.kt create mode 100644 idea/testData/copyPaste/moveDeclarations/SamePackage.to.kt create mode 100644 idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractMoveOnCutPasteTest.kt create mode 100644 idea/tests/org/jetbrains/kotlin/idea/codeInsight/MoveOnCutPasteTestGenerated.java diff --git a/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRenderer.kt b/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRenderer.kt index fd5828183ce..4b90d4f6c5b 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRenderer.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRenderer.kt @@ -206,7 +206,7 @@ interface DescriptorRendererOptions { var renderCompanionObjectName: Boolean var withoutSuperTypes: Boolean var typeNormalizer: (KotlinType) -> KotlinType - var renderDefaultValues: Boolean + var defaultParameterValueRenderer: ((ValueParameterDescriptor) -> String)? var secondaryConstructorsAsPrimary: Boolean var renderAccessors: Boolean var renderDefaultAnnotationArguments: Boolean diff --git a/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererImpl.kt b/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererImpl.kt index ee710a3351a..f5eb5264fa3 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererImpl.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererImpl.kt @@ -807,9 +807,9 @@ internal class DescriptorRendererImpl( renderVariable(valueParameter, includeName, builder, topLevel) - val withDefaultValue = renderDefaultValues && (if (debugMode) valueParameter.declaresDefaultValue() else valueParameter.hasDefaultValue()) + val withDefaultValue = defaultParameterValueRenderer != null && (if (debugMode) valueParameter.declaresDefaultValue() else valueParameter.hasDefaultValue()) if (withDefaultValue) { - builder.append(" = ...") + builder.append(" = ${defaultParameterValueRenderer!!(valueParameter)}") } } diff --git a/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererOptionsImpl.kt b/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererOptionsImpl.kt index 9cace3597b4..1091b8dfc67 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererOptionsImpl.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/renderer/DescriptorRendererOptionsImpl.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.renderer +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.types.KotlinType import java.lang.IllegalStateException @@ -47,7 +48,7 @@ internal class DescriptorRendererOptionsImpl : DescriptorRendererOptions { this, PropertyReference1Impl(DescriptorRendererOptionsImpl::class, field.name, "get" + field.name.capitalize()) ) - field.set(copy, copy.property(value as Any)) + field.set(copy, copy.property(value)) } return copy @@ -81,7 +82,7 @@ internal class DescriptorRendererOptionsImpl : DescriptorRendererOptions { override var withoutTypeParameters by property(false) override var withoutSuperTypes by property(false) override var typeNormalizer by property<(KotlinType) -> KotlinType>({ it }) - override var renderDefaultValues by property(true) + override var defaultParameterValueRenderer by property<((ValueParameterDescriptor) -> String)?>({ "..." }) override var secondaryConstructorsAsPrimary by property(true) override var overrideRenderingPolicy by property(OverrideRenderingPolicy.RENDER_OPEN) override var valueParametersHandler: DescriptorRenderer.ValueParametersHandler by property(DescriptorRenderer.ValueParametersHandler.DEFAULT) diff --git a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index 482dded093b..698039087c5 100755 --- a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -834,6 +834,10 @@ fun main(args: Array) { model("copyPaste/imports", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doTestCut", testClassName = "Cut", recursive = false) } + testClass { + model("copyPaste/moveDeclarations", pattern = KT_WITHOUT_DOTS_IN_NAME, testMethod = "doTest") + } + testClass { model("exitPoints") } diff --git a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/Statisticians.kt b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/Statisticians.kt index e80bf488642..f1f3a93a490 100644 --- a/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/Statisticians.kt +++ b/idea/idea-completion/src/org/jetbrains/kotlin/idea/completion/Statisticians.kt @@ -66,7 +66,7 @@ object KotlinStatisticsInfo { startFromName = true receiverAfterName = true modifiers = emptySet() - renderDefaultValues = false + defaultParameterValueRenderer = null parameterNameRenderingPolicy = ParameterNameRenderingPolicy.NONE } diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMemberChooserObject.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMemberChooserObject.kt index 2d1a60e617f..9168009b991 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMemberChooserObject.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/overrideImplement/OverrideMemberChooserObject.kt @@ -125,7 +125,7 @@ fun OverrideMemberChooserObject.generateMember(project: Project, copyDoc: Boolea } private val OVERRIDE_RENDERER = DescriptorRenderer.withOptions { - renderDefaultValues = false + defaultParameterValueRenderer = null modifiers = setOf(DescriptorRendererModifier.OVERRIDE) withDefinedIn = false classifierNamePolicy = ClassifierNamePolicy.SOURCE_CODE_QUALIFIED diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 379246cd29c..35465587a74 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -34,6 +34,10 @@ org.jetbrains.kotlin.idea.highlighter.KotlinBeforeResolveHighlightingPass$Factory + + org.jetbrains.kotlin.idea.refactoring.cutPaste.MoveDeclarationsPassFactory + + org.jetbrains.kotlin.idea.completion.LookupCancelWatcher @@ -582,6 +586,7 @@ + diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeMemberFunctionSignatureFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeMemberFunctionSignatureFix.kt index c0f4db1c82a..4a7cc63cfe0 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeMemberFunctionSignatureFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/ChangeMemberFunctionSignatureFix.kt @@ -66,7 +66,7 @@ class ChangeMemberFunctionSignatureFix private constructor( companion object { private val SIGNATURE_SOURCE_RENDERER = IdeDescriptorRenderers.SOURCE_CODE.withOptions { - renderDefaultValues = false + defaultParameterValueRenderer = null } private val SIGNATURE_PREVIEW_RENDERER = DescriptorRenderer.withOptions { @@ -75,7 +75,7 @@ class ChangeMemberFunctionSignatureFix private constructor( modifiers = emptySet() classifierNamePolicy = ClassifierNamePolicy.SHORT unitReturnType = false - renderDefaultValues = false + defaultParameterValueRenderer = null } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ReplaceProtectedToPublishedApiCallFix.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ReplaceProtectedToPublishedApiCallFix.kt index 476a0ca1aa6..510ace68eba 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ReplaceProtectedToPublishedApiCallFix.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/replaceWith/ReplaceProtectedToPublishedApiCallFix.kt @@ -84,7 +84,7 @@ class ReplaceProtectedToPublishedApiCallFix( companion object : KotlinSingleIntentionActionFactory() { val signatureRenderer = IdeDescriptorRenderers.SOURCE_CODE.withOptions { - renderDefaultValues = false + defaultParameterValueRenderer = null startFromDeclarationKeyword = true withoutReturnType = true } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/cutPaste/MoveDeclarationsCopyPasteProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/cutPaste/MoveDeclarationsCopyPasteProcessor.kt new file mode 100644 index 00000000000..0b103aa1e4f --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/cutPaste/MoveDeclarationsCopyPasteProcessor.kt @@ -0,0 +1,108 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.refactoring.cutPaste + +import com.intellij.codeInsight.editorActions.CopyPastePostProcessor +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.diagnostic.Logger +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.editor.RangeMarker +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Ref +import com.intellij.openapi.util.TextRange +import com.intellij.psi.PsiComment +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiWhiteSpace +import com.intellij.psi.util.PsiModificationTracker +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtNamedDeclaration +import org.jetbrains.kotlin.psi.psiUtil.elementsInRange +import java.awt.datatransfer.Transferable + +class MoveDeclarationsCopyPasteProcessor : CopyPastePostProcessor() { + companion object { + private val LOG = Logger.getInstance(MoveDeclarationsCopyPasteProcessor::class.java) + + fun rangeToDeclarations(file: KtFile, startOffset: Int, endOffset: Int): List { + val elementsInRange = file.elementsInRange(TextRange(startOffset, endOffset)) + val meaningfulElements = elementsInRange.filterNot { it is PsiWhiteSpace || it is PsiComment } + if (meaningfulElements.isEmpty()) return emptyList() + if (!meaningfulElements.all { it is KtNamedDeclaration }) return emptyList() + @Suppress("UNCHECKED_CAST") + return meaningfulElements as List + } + } + + override fun collectTransferableData( + file: PsiFile, + editor: Editor, + startOffsets: IntArray, + endOffsets: IntArray + ): List { + if (file !is KtFile) return emptyList() + if (startOffsets.size != 1) return emptyList() + + val declarations = rangeToDeclarations(file, startOffsets[0], endOffsets[0]) + if (declarations.isEmpty() || declarations.any { it.parent !is KtFile }) return emptyList() + + if (declarations.any { it.name == null }) return emptyList() + val declarationNames = declarations.map { it.name!! }.toSet() + + val stubTexts = declarations.map { MoveDeclarationsTransferableData.STUB_RENDERER.render(it.resolveToDescriptor()) } + return listOf(MoveDeclarationsTransferableData(file.virtualFile.url, stubTexts, declarationNames)) + } + + override fun extractTransferableData(content: Transferable): List { + try { + if (content.isDataFlavorSupported(MoveDeclarationsTransferableData.DATA_FLAVOR)) { + return listOf(content.getTransferData(MoveDeclarationsTransferableData.DATA_FLAVOR) as MoveDeclarationsTransferableData) + } + } + catch (e: Throwable) { + LOG.error(e) + } + return emptyList() + } + + override fun processTransferableData( + project: Project, + editor: Editor, + bounds: RangeMarker, + caretOffset: Int, + indented: Ref, + values: List + ) { + val data = values.single() + + fun putCookie() { + if (bounds.isValid) { + val cookie = MoveDeclarationsEditorCookie(data, bounds, PsiModificationTracker.SERVICE.getInstance(project).modificationCount) + editor.putUserData(MoveDeclarationsEditorCookie.KEY, cookie) + } + } + + if (ApplicationManager.getApplication().isUnitTestMode) { + putCookie() + } + else { + // in real application we put cookie later to allow all other paste handlers do their work (because modificationCount will change) + ApplicationManager.getApplication().invokeLater(::putCookie) + } + } +} + diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/cutPaste/MoveDeclarationsEditorCookie.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/cutPaste/MoveDeclarationsEditorCookie.kt new file mode 100644 index 00000000000..db96b838416 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/cutPaste/MoveDeclarationsEditorCookie.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.refactoring.cutPaste + +import com.intellij.openapi.editor.RangeMarker +import com.intellij.openapi.util.Key + +class MoveDeclarationsEditorCookie( + val data: MoveDeclarationsTransferableData, + val bounds: RangeMarker, + val modificationCount: Long +) { + companion object { + val KEY = Key("MoveDeclarationsEditorCookie") + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/cutPaste/MoveDeclarationsIntentionAction.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/cutPaste/MoveDeclarationsIntentionAction.kt new file mode 100644 index 00000000000..8d7abf5474a --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/cutPaste/MoveDeclarationsIntentionAction.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.refactoring.cutPaste + +import com.intellij.codeInsight.hint.HintManager +import com.intellij.codeInspection.HintAction +import com.intellij.openapi.actionSystem.ActionManager +import com.intellij.openapi.actionSystem.IdeActions +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.editor.RangeMarker +import com.intellij.openapi.keymap.KeymapUtil +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import com.intellij.psi.util.PsiModificationTracker +import com.intellij.refactoring.BaseRefactoringIntentionAction +import org.jetbrains.kotlin.idea.conversion.copy.range + +class MoveDeclarationsIntentionAction( + private val processor: MoveDeclarationsProcessor, + private val bounds: RangeMarker, + private val modificationCount: Long +) : BaseRefactoringIntentionAction(), HintAction { + override fun startInWriteAction() = false + + override fun getText() = "Update usages to reflect package name change" + override fun getFamilyName() = "Update usages on declarations cut/paste" + + override fun isAvailable(project: Project, editor: Editor, element: PsiElement): Boolean { + return PsiModificationTracker.SERVICE.getInstance(processor.project).modificationCount == modificationCount + } + + override fun invoke(project: Project, editor: Editor, element: PsiElement) { + processor.performRefactoring() + } + + override fun showHint(editor: Editor): Boolean { + val range = bounds.range ?: return false + if (editor.caretModel.offset != range.endOffset) return false + + if (PsiModificationTracker.SERVICE.getInstance(processor.project).modificationCount != modificationCount) return false + + val hintText = "$text? ${KeymapUtil.getFirstKeyboardShortcutText(ActionManager.getInstance().getAction(IdeActions.ACTION_SHOW_INTENTION_ACTIONS))}" + HintManager.getInstance().showQuestionHint(editor, hintText, range.endOffset, range.endOffset) { + processor.performRefactoring() + true + } + + return true + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/cutPaste/MoveDeclarationsPassFactory.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/cutPaste/MoveDeclarationsPassFactory.kt new file mode 100644 index 00000000000..60e1e5f46b5 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/cutPaste/MoveDeclarationsPassFactory.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.refactoring.cutPaste + +import com.intellij.codeHighlighting.Pass +import com.intellij.codeHighlighting.TextEditorHighlightingPass +import com.intellij.codeHighlighting.TextEditorHighlightingPassFactory +import com.intellij.codeHighlighting.TextEditorHighlightingPassRegistrar +import com.intellij.codeInsight.daemon.impl.HighlightInfo +import com.intellij.codeInsight.daemon.impl.HighlightInfoType +import com.intellij.codeInsight.daemon.impl.UpdateHighlightersUtil +import com.intellij.codeInsight.daemon.impl.quickfix.QuickFixAction +import com.intellij.openapi.components.AbstractProjectComponent +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.progress.ProgressIndicator +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiFile +import com.intellij.psi.util.PsiModificationTracker +import org.jetbrains.kotlin.idea.conversion.copy.range + +class MoveDeclarationsPassFactory(project: Project, highlightingPassRegistrar: TextEditorHighlightingPassRegistrar) + : AbstractProjectComponent(project), TextEditorHighlightingPassFactory { + + init { + highlightingPassRegistrar.registerTextEditorHighlightingPass(this, TextEditorHighlightingPassRegistrar.Anchor.BEFORE, Pass.POPUP_HINTS, true, true) + } + + override fun createHighlightingPass(file: PsiFile, editor: Editor): TextEditorHighlightingPass? { + return MyPass(file.project, file, editor) + } + + private class MyPass( + private val project: Project, + private val file: PsiFile, + private val editor: Editor + ) : TextEditorHighlightingPass(project, editor.document, true) { + + override fun doCollectInformation(progress: ProgressIndicator) {} + + override fun doApplyInformationToEditor() { + val info = buildHighlightingInfo() + UpdateHighlightersUtil.setHighlightersToEditor(project, myDocument!!, 0, file.textLength, listOfNotNull(info), colorsScheme, id) + } + + private fun buildHighlightingInfo(): HighlightInfo? { + val cookie = editor.getUserData(MoveDeclarationsEditorCookie.KEY) ?: return null + + if (cookie.modificationCount != PsiModificationTracker.SERVICE.getInstance(project).modificationCount) return null + + val processor = MoveDeclarationsProcessor.build(editor, cookie) + + if (processor == null) { + editor.putUserData(MoveDeclarationsEditorCookie.KEY, null) + return null + } + + val info = HighlightInfo.newHighlightInfo(HighlightInfoType.INFORMATION) + .range(cookie.bounds.range!!) + .createUnconditionally() + QuickFixAction.registerQuickFixAction(info, MoveDeclarationsIntentionAction(processor, cookie.bounds, cookie.modificationCount)) + + return info + } + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/cutPaste/MoveDeclarationsProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/cutPaste/MoveDeclarationsProcessor.kt new file mode 100644 index 00000000000..ffa6203e114 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/cutPaste/MoveDeclarationsProcessor.kt @@ -0,0 +1,134 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.refactoring.cutPaste + +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.editor.RangeMarker +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.TextRange +import com.intellij.openapi.vfs.VirtualFileManager +import com.intellij.psi.PsiDocumentManager +import com.intellij.psi.PsiManager +import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor +import org.jetbrains.kotlin.idea.codeInsight.shorten.runRefactoringAndKeepDelayedRequests +import org.jetbrains.kotlin.idea.conversion.copy.range +import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.* +import org.jetbrains.kotlin.idea.util.application.executeWriteCommand +import org.jetbrains.kotlin.idea.util.getSourceRoot +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtNamedDeclaration +import org.jetbrains.kotlin.psi.psiUtil.startOffset + +class MoveDeclarationsProcessor( + val project: Project, + private val sourcePsiFile: KtFile, + private val targetPsiFile: KtFile, + private val pastedDeclarations: List, + private val stubTexts: List +) { + companion object { + fun build(editor: Editor, cookie: MoveDeclarationsEditorCookie): MoveDeclarationsProcessor? { + val data = cookie.data + val project = editor.project ?: return null + val range = cookie.bounds.range ?: return null + + val sourceFileUrl = data.sourceFileUrl + val sourceFile = VirtualFileManager.getInstance().findFileByUrl(sourceFileUrl) ?: return null + if (sourceFile.getSourceRoot(project) == null) return null + + val psiDocumentManager = PsiDocumentManager.getInstance(project) + psiDocumentManager.commitAllDocuments() + + val targetPsiFile = psiDocumentManager.getPsiFile(editor.document) as? KtFile ?: return null + if (targetPsiFile.virtualFile.getSourceRoot(project) == null) return null + val sourcePsiFile = PsiManager.getInstance(project).findFile(sourceFile) as? KtFile ?: return null + if (targetPsiFile == sourcePsiFile) return null + + val declarations = MoveDeclarationsCopyPasteProcessor.rangeToDeclarations(targetPsiFile, range.startOffset, range.endOffset) + if (declarations.isEmpty() || declarations.any { it.parent !is KtFile }) return null + + if (sourcePsiFile.packageFqName == targetPsiFile.packageFqName) return null + + // check that declarations were cut (not copied) + val filteredDeclarations = sourcePsiFile.declarations.filter { it.name in data.declarationNames } + val stubs = data.stubTexts.toSet() + if (filteredDeclarations.any { MoveDeclarationsTransferableData.STUB_RENDERER.render(it.resolveToDescriptor()) in stubs }) return null + + return MoveDeclarationsProcessor(project, sourcePsiFile, targetPsiFile, declarations, data.stubTexts) + } + } + + private val psiDocumentManager = PsiDocumentManager.getInstance(project) + private val sourceDocument = psiDocumentManager.getDocument(sourcePsiFile)!! + + fun performRefactoring() { + psiDocumentManager.commitAllDocuments() + + val commandName = "Usage update" + val commandGroupId = Any() // we need to group both commands for undo + + val insertedRange = project.executeWriteCommand(commandName, commandGroupId) { + //TODO: can stub declarations interfere with pasted declarations? I could not find such cases + insertStubDeclarations() + } + psiDocumentManager.commitDocument(sourceDocument) + + val stubDeclarations = MoveDeclarationsCopyPasteProcessor.rangeToDeclarations(sourcePsiFile, insertedRange.startOffset, insertedRange.endOffset) + assert(stubDeclarations.size == pastedDeclarations.size) //TODO: can they ever differ? + + val mover = object: Mover { + override fun invoke(declaration: KtNamedDeclaration, targetContainer: KtElement): KtNamedDeclaration { + val index = stubDeclarations.indexOf(declaration) + assert(index >= 0) + declaration.delete() + return pastedDeclarations[index] + } + } + + val declarationProcessor = MoveKotlinDeclarationsProcessor( + MoveDeclarationsDescriptor( + elementsToMove = stubDeclarations, + moveTarget = KotlinMoveTargetForExistingElement(targetPsiFile), + delegate = MoveDeclarationsDelegate.TopLevel, + project = project + ), + mover + ) + + val declarationUsages = declarationProcessor.findUsages().toList() +// val changeInfo = ContainerChangeInfo(ContainerInfo.Package(sourcePackageName), ContainerInfo.Package(targetPackageName)) +// val internalUsages = file.getInternalReferencesToUpdateOnPackageNameChange(changeInfo) + + project.executeWriteCommand(commandName, commandGroupId) { + + // postProcessMoveUsages(internalUsages) //TODO? + project.runRefactoringAndKeepDelayedRequests { declarationProcessor.execute(declarationUsages) } + + psiDocumentManager.doPostponedOperationsAndUnblockDocument(sourceDocument) + assert(insertedRange.isValid) + sourceDocument.deleteString(insertedRange.startOffset, insertedRange.endOffset) + } + } + + private fun insertStubDeclarations(): RangeMarker { + val insertionOffset = sourcePsiFile.declarations.firstOrNull()?.startOffset ?: sourcePsiFile.textLength + val textToInsert = "\n//start\n\n" + stubTexts.joinToString(separator = "\n") + "\n//end\n" + sourceDocument.insertString(insertionOffset, textToInsert) + return sourceDocument.createRangeMarker(TextRange(insertionOffset, insertionOffset + textToInsert.length)) + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/cutPaste/MoveDeclarationsTransferableData.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/cutPaste/MoveDeclarationsTransferableData.kt new file mode 100644 index 00000000000..bfb7ec50863 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/cutPaste/MoveDeclarationsTransferableData.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.refactoring.cutPaste + +import com.intellij.codeInsight.editorActions.TextBlockTransferableData +import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers +import java.awt.datatransfer.DataFlavor + +//TODO: how to make it work inside same project only? +class MoveDeclarationsTransferableData( + val sourceFileUrl: String, + val stubTexts: List, + val declarationNames: Set +) : TextBlockTransferableData { + + override fun getFlavor() = DATA_FLAVOR + override fun getOffsetCount() = 0 + + override fun getOffsets(offsets: IntArray?, index: Int) = index + override fun setOffsets(offsets: IntArray?, index: Int) = index + + companion object { + val DATA_FLAVOR = DataFlavor(MoveDeclarationsCopyPasteProcessor::class.java, "class: MoveDeclarationsCopyPasteProcessor") + + val STUB_RENDERER = IdeDescriptorRenderers.SOURCE_CODE.withOptions { + defaultParameterValueRenderer = { "xxx" } // we need default value to be parsed as expression + } + } +} \ No newline at end of file diff --git a/idea/testData/copyPaste/moveDeclarations/ChangePackage.dependency.expected.kt b/idea/testData/copyPaste/moveDeclarations/ChangePackage.dependency.expected.kt new file mode 100644 index 00000000000..abc95287d5b --- /dev/null +++ b/idea/testData/copyPaste/moveDeclarations/ChangePackage.dependency.expected.kt @@ -0,0 +1,11 @@ +import source.* +import target.bar +import target.foo + +fun f() { + foo() + g(bar) + sourcePackFun() +} + +fun g(p: Int){} \ No newline at end of file diff --git a/idea/testData/copyPaste/moveDeclarations/ChangePackage.dependency.kt b/idea/testData/copyPaste/moveDeclarations/ChangePackage.dependency.kt new file mode 100644 index 00000000000..2ebd12f48f3 --- /dev/null +++ b/idea/testData/copyPaste/moveDeclarations/ChangePackage.dependency.kt @@ -0,0 +1,9 @@ +import source.* + +fun f() { + foo() + g(bar) + sourcePackFun() +} + +fun g(p: Int){} \ No newline at end of file diff --git a/idea/testData/copyPaste/moveDeclarations/ChangePackage.expected.kt b/idea/testData/copyPaste/moveDeclarations/ChangePackage.expected.kt new file mode 100644 index 00000000000..641ad62311b --- /dev/null +++ b/idea/testData/copyPaste/moveDeclarations/ChangePackage.expected.kt @@ -0,0 +1,7 @@ +package source + +import target.targetPackFun + +fun sourcePackFun(){} + + diff --git a/idea/testData/copyPaste/moveDeclarations/ChangePackage.kt b/idea/testData/copyPaste/moveDeclarations/ChangePackage.kt new file mode 100644 index 00000000000..4d6fb45bee5 --- /dev/null +++ b/idea/testData/copyPaste/moveDeclarations/ChangePackage.kt @@ -0,0 +1,20 @@ +package source + +import target.targetPackFun + +fun sourcePackFun(){} + + +/* comment 1 */ + +fun foo() { + sourcePackFun() + targetPackFun() +} + +/* comment 2 */ + +val bar = 10 + +/* comment 3 */ + diff --git a/idea/testData/copyPaste/moveDeclarations/ChangePackage.to.expected.kt b/idea/testData/copyPaste/moveDeclarations/ChangePackage.to.expected.kt new file mode 100644 index 00000000000..f05c5056ae6 --- /dev/null +++ b/idea/testData/copyPaste/moveDeclarations/ChangePackage.to.expected.kt @@ -0,0 +1,19 @@ +package target + +import source.sourcePackFun + +fun targetPackFun(){} + + +/* comment 1 */ + +fun foo() { + sourcePackFun() + targetPackFun() +} + +/* comment 2 */ + +val bar = 10 + +/* comment 3 */ diff --git a/idea/testData/copyPaste/moveDeclarations/ChangePackage.to.kt b/idea/testData/copyPaste/moveDeclarations/ChangePackage.to.kt new file mode 100644 index 00000000000..562684d97db --- /dev/null +++ b/idea/testData/copyPaste/moveDeclarations/ChangePackage.to.kt @@ -0,0 +1,5 @@ +package target + +fun targetPackFun(){} + + \ No newline at end of file diff --git a/idea/testData/copyPaste/moveDeclarations/Copy.kt b/idea/testData/copyPaste/moveDeclarations/Copy.kt new file mode 100644 index 00000000000..5ea6d1d3d57 --- /dev/null +++ b/idea/testData/copyPaste/moveDeclarations/Copy.kt @@ -0,0 +1,8 @@ +// COPY +// IS_AVAILABLE: false +package source + + +fun foo() { +} + diff --git a/idea/testData/copyPaste/moveDeclarations/OptimizeImportsAfterCut.dependency.expected.kt b/idea/testData/copyPaste/moveDeclarations/OptimizeImportsAfterCut.dependency.expected.kt new file mode 100644 index 00000000000..2b8b953b88e --- /dev/null +++ b/idea/testData/copyPaste/moveDeclarations/OptimizeImportsAfterCut.dependency.expected.kt @@ -0,0 +1,7 @@ +package dependency + +class Dependency + +fun f() { + to.foo(Dependency()) +} diff --git a/idea/testData/copyPaste/moveDeclarations/OptimizeImportsAfterCut.dependency.kt b/idea/testData/copyPaste/moveDeclarations/OptimizeImportsAfterCut.dependency.kt new file mode 100644 index 00000000000..66e81506ed4 --- /dev/null +++ b/idea/testData/copyPaste/moveDeclarations/OptimizeImportsAfterCut.dependency.kt @@ -0,0 +1,9 @@ +package dependency + +import source.* + +class Dependency + +fun f() { + foo(Dependency()) +} diff --git a/idea/testData/copyPaste/moveDeclarations/OptimizeImportsAfterCut.expected.kt b/idea/testData/copyPaste/moveDeclarations/OptimizeImportsAfterCut.expected.kt new file mode 100644 index 00000000000..3ab527021e3 --- /dev/null +++ b/idea/testData/copyPaste/moveDeclarations/OptimizeImportsAfterCut.expected.kt @@ -0,0 +1,6 @@ +// OPTIMIZE_IMPORTS_AFTER_CUT +package source + + +fun foo(o: Any) { +} diff --git a/idea/testData/copyPaste/moveDeclarations/OptimizeImportsAfterCut.kt b/idea/testData/copyPaste/moveDeclarations/OptimizeImportsAfterCut.kt new file mode 100644 index 00000000000..9de8d9f66f6 --- /dev/null +++ b/idea/testData/copyPaste/moveDeclarations/OptimizeImportsAfterCut.kt @@ -0,0 +1,12 @@ +// OPTIMIZE_IMPORTS_AFTER_CUT +package source + +import dependency.Dependency + + +fun foo(dependency: Dependency, o: Any? = null) { +} + + +fun foo(o: Any) { +} diff --git a/idea/testData/copyPaste/moveDeclarations/OptimizeImportsAfterCut.to.expected.kt b/idea/testData/copyPaste/moveDeclarations/OptimizeImportsAfterCut.to.expected.kt new file mode 100644 index 00000000000..814759b3db7 --- /dev/null +++ b/idea/testData/copyPaste/moveDeclarations/OptimizeImportsAfterCut.to.expected.kt @@ -0,0 +1,7 @@ +package to + +import dependency.Dependency + + +fun foo(dependency: Dependency, o: Any? = null) { +} diff --git a/idea/testData/copyPaste/moveDeclarations/SamePackage.kt b/idea/testData/copyPaste/moveDeclarations/SamePackage.kt new file mode 100644 index 00000000000..cb7839915aa --- /dev/null +++ b/idea/testData/copyPaste/moveDeclarations/SamePackage.kt @@ -0,0 +1,7 @@ +// IS_AVAILABLE: false +package source + + +fun foo() { +} + diff --git a/idea/testData/copyPaste/moveDeclarations/SamePackage.to.kt b/idea/testData/copyPaste/moveDeclarations/SamePackage.to.kt new file mode 100644 index 00000000000..bb6db7772aa --- /dev/null +++ b/idea/testData/copyPaste/moveDeclarations/SamePackage.to.kt @@ -0,0 +1,3 @@ +package source + + \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractMoveOnCutPasteTest.kt b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractMoveOnCutPasteTest.kt new file mode 100644 index 00000000000..7dc2fabfc9b --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractMoveOnCutPasteTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.codeInsight + +import com.intellij.codeInsight.actions.OptimizeImportsProcessor +import com.intellij.openapi.actionSystem.IdeActions +import com.intellij.openapi.util.io.FileUtil +import com.intellij.psi.PsiDocumentManager +import junit.framework.TestCase +import org.jetbrains.kotlin.idea.AbstractCopyPasteTest +import org.jetbrains.kotlin.idea.refactoring.cutPaste.MoveDeclarationsEditorCookie +import org.jetbrains.kotlin.idea.refactoring.cutPaste.MoveDeclarationsIntentionAction +import org.jetbrains.kotlin.idea.refactoring.cutPaste.MoveDeclarationsProcessor +import org.jetbrains.kotlin.idea.test.PluginTestCaseBase +import org.jetbrains.kotlin.idea.test.dumpTextWithErrors +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.test.InTextDirectivesUtils +import org.jetbrains.kotlin.test.KotlinTestUtils +import java.io.File + +abstract class AbstractMoveOnCutPasteTest : AbstractCopyPasteTest() { + private val BASE_PATH = PluginTestCaseBase.getTestDataPathBase() + "/copyPaste/moveDeclarations" + + private val OPTIMIZE_IMPORTS_AFTER_CUT_DIRECTIVE = "// OPTIMIZE_IMPORTS_AFTER_CUT" + private val IS_AVAILABLE_DIRECTIVE = "// IS_AVAILABLE:" + private val COPY_DIRECTIVE = "// COPY" + + override fun getTestDataPath() = BASE_PATH + + protected fun doTest(sourceFilePath: String) { + myFixture.testDataPath = BASE_PATH + val testFile = File(sourceFilePath) + val sourceFileName = testFile.name + val testFileText = FileUtil.loadFile(testFile, true) + + val dependencyFileName = sourceFileName.replace(".kt", ".dependency.kt") + val dependencyPsiFile = configureByDependencyIfExists(dependencyFileName) as KtFile? + val sourcePsiFile = myFixture.configureByFile(sourceFileName) as KtFile + val useCopy = InTextDirectivesUtils.isDirectiveDefined(testFileText, COPY_DIRECTIVE) + myFixture.performEditorAction(if (useCopy) IdeActions.ACTION_COPY else IdeActions.ACTION_CUT) + PsiDocumentManager.getInstance(project).commitAllDocuments() + + if (InTextDirectivesUtils.isDirectiveDefined(testFileText, OPTIMIZE_IMPORTS_AFTER_CUT_DIRECTIVE)) { + OptimizeImportsProcessor(project, sourcePsiFile).run() + } + + editor.putUserData(MoveDeclarationsEditorCookie.KEY, null) // because editor is reused + + val targetFileName = sourceFileName.replace(".kt", ".to.kt") + val targetPsiFile = configureTargetFile(targetFileName) + performNotWriteEditorAction(IdeActions.ACTION_PASTE) + + val shouldBeAvailable = InTextDirectivesUtils.getPrefixedBoolean(testFileText, IS_AVAILABLE_DIRECTIVE) ?: true + val cookie = editor.getUserData(MoveDeclarationsEditorCookie.KEY) + val processor = cookie?.let { MoveDeclarationsProcessor.build(editor, cookie) } + + TestCase.assertEquals(shouldBeAvailable, processor != null) + + if (processor != null) { + processor.performRefactoring() + + PsiDocumentManager.getInstance(project).commitAllDocuments() + + if (dependencyPsiFile != null) { + KotlinTestUtils.assertEqualsToFile(File(BASE_PATH, dependencyFileName.replace(".kt", ".expected.kt")), + dependencyPsiFile.dumpTextWithErrors()) + } + + KotlinTestUtils.assertEqualsToFile(File(BASE_PATH, sourceFileName.replace(".kt", ".expected.kt")), + sourcePsiFile.dumpTextWithErrors()) + KotlinTestUtils.assertEqualsToFile(File(BASE_PATH, targetFileName.replace(".kt", ".expected.kt")), + targetPsiFile.dumpTextWithErrors()) + } + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/MoveOnCutPasteTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/MoveOnCutPasteTestGenerated.java new file mode 100644 index 00000000000..57d2009a2cb --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/MoveOnCutPasteTestGenerated.java @@ -0,0 +1,62 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.codeInsight; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("idea/testData/copyPaste/moveDeclarations") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class MoveOnCutPasteTestGenerated extends AbstractMoveOnCutPasteTest { + public void testAllFilesPresentInMoveDeclarations() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/copyPaste/moveDeclarations"), Pattern.compile("^([^.]+)\\.kt$"), TargetBackend.ANY, true); + } + + @TestMetadata("ChangePackage.kt") + public void testChangePackage() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/copyPaste/moveDeclarations/ChangePackage.kt"); + doTest(fileName); + } + + @TestMetadata("Copy.kt") + public void testCopy() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/copyPaste/moveDeclarations/Copy.kt"); + doTest(fileName); + } + + @TestMetadata("OptimizeImportsAfterCut.kt") + public void testOptimizeImportsAfterCut() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/copyPaste/moveDeclarations/OptimizeImportsAfterCut.kt"); + doTest(fileName); + } + + @TestMetadata("SamePackage.kt") + public void testSamePackage() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/copyPaste/moveDeclarations/SamePackage.kt"); + doTest(fileName); + } +}