Create from Usage: Java -> Kotlin interoperability
diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index cd9fdec..8dff411 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml
@@ -628,6 +628,10 @@ order="first" implementationClass="org.jetbrains.kotlin.idea.refactoring.pullUp.JavaToKotlinPullUpHelperFactory"/> + <psi.jvmElementMutatorFactory + language="kotlin" + implementationClass="org.jetbrains.kotlin.idea.KotlinJVMElementMutatorFactory"/> + <problemFileHighlightFilter implementation="org.jetbrains.kotlin.idea.projectView.KotlinProblemFileHighlightFilter"/> <codeInsight.typeInfo language="kotlin" implementationClass="org.jetbrains.kotlin.idea.codeInsight.KotlinExpressionTypeProvider"/>
diff --git a/idea/src/com/intellij/codeInsight/daemon/impl/quickfix/JVMElementMutatorWithEditor.java b/idea/src/com/intellij/codeInsight/daemon/impl/quickfix/JVMElementMutatorWithEditor.java new file mode 100644 index 0000000..a8048aa --- /dev/null +++ b/idea/src/com/intellij/codeInsight/daemon/impl/quickfix/JVMElementMutatorWithEditor.java
@@ -0,0 +1,24 @@ +/* + * Copyright 2010-2016 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 com.intellij.codeInsight.daemon.impl.quickfix; + +import com.intellij.openapi.editor.Editor; +import com.intellij.psi.JVMElementMutator; +import com.intellij.psi.PsiElement; + +public interface JVMElementMutatorWithEditor extends JVMElementMutator { + Editor openEditor(PsiElement element); +} \ No newline at end of file
diff --git a/idea/src/com/intellij/psi/JVMElementMutator.java b/idea/src/com/intellij/psi/JVMElementMutator.java new file mode 100644 index 0000000..42d6654 --- /dev/null +++ b/idea/src/com/intellij/psi/JVMElementMutator.java
@@ -0,0 +1,29 @@ +/* + * Copyright 2010-2016 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 com.intellij.psi; + +import com.intellij.openapi.Disposable; +import org.jetbrains.annotations.NotNull; + +public interface JVMElementMutator extends Disposable { + @NotNull + JVMElementFactory getFactory(); + + @NotNull + PsiElement getRootView(); + + void synchronize(); +} \ No newline at end of file
diff --git a/idea/src/com/intellij/psi/JVMElementMutatorFactory.java b/idea/src/com/intellij/psi/JVMElementMutatorFactory.java new file mode 100644 index 0000000..5a5a128 --- /dev/null +++ b/idea/src/com/intellij/psi/JVMElementMutatorFactory.java
@@ -0,0 +1,27 @@ +/* + * Copyright 2010-2016 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 com.intellij.psi; + +import com.intellij.lang.LanguageExtension; +import com.intellij.openapi.editor.Editor; +import org.jetbrains.annotations.NotNull; + +public interface JVMElementMutatorFactory { + LanguageExtension<JVMElementMutatorFactory> EXTENSION_POINT = + new LanguageExtension<JVMElementMutatorFactory>("com.intellij.psi.jvmElementMutatorFactory"); + + JVMElementMutator createMutator(@NotNull PsiElement rootElement, @NotNull Editor editor); +} \ No newline at end of file
diff --git a/idea/src/org/jetbrains/kotlin/idea/KotlinJVMElementMutator.kt b/idea/src/org/jetbrains/kotlin/idea/KotlinJVMElementMutator.kt new file mode 100644 index 0000000..7ce60e41 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/KotlinJVMElementMutator.kt
@@ -0,0 +1,169 @@ +/* + * Copyright 2010-2016 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 + +import com.intellij.codeInsight.daemon.impl.quickfix.JVMElementMutatorWithEditor +import com.intellij.ide.highlighter.JavaFileType +import com.intellij.openapi.editor.Editor +import com.intellij.pom.java.LanguageLevel +import com.intellij.psi.* +import com.intellij.psi.search.GlobalSearchScope +import com.intellij.testFramework.LightVirtualFile +import org.jetbrains.kotlin.asJava.KtLightClassForExplicitDeclaration +import org.jetbrains.kotlin.idea.quickfix.createFromUsage.callableBuilder.DialogWithTemplateEditor +import org.jetbrains.kotlin.idea.refactoring.createJavaClass +import org.jetbrains.kotlin.idea.util.DialogWithEditor + +abstract class KotlinJVMElementFactoryBase : JVMElementFactory { + override fun createDocCommentFromText(name: String) = throw UnsupportedOperationException() + + override fun createInterface(name: String) = throw UnsupportedOperationException() + + override fun createPrimitiveType(name: String) = throw UnsupportedOperationException() + + override fun createExpressionFromText(text: String, context: PsiElement?) = throw UnsupportedOperationException() + + override fun isValidMethodName(name: String) = throw UnsupportedOperationException() + + override fun createMethodFromText(text: String?, context: PsiElement?) = throw UnsupportedOperationException() + + override fun createType(klass: PsiClass) = throw UnsupportedOperationException() + + override fun createType(klass: PsiClass, substitutor: PsiSubstitutor) = throw UnsupportedOperationException() + + override fun createType(klass: PsiClass, substitutor: PsiSubstitutor, level: LanguageLevel) = throw UnsupportedOperationException() + + override fun createType(klass: PsiClass, substitutor: PsiSubstitutor, level: LanguageLevel, annotations: Array<out PsiAnnotation>) = + throw UnsupportedOperationException() + + override fun createType(klass: PsiClass, parameter: PsiType?) = throw UnsupportedOperationException() + + override fun createType(klass: PsiClass, vararg parameters: PsiType?) = throw UnsupportedOperationException() + + override fun createField(name: String, type: PsiType) = throw UnsupportedOperationException() + + override fun createParameterList(names: Array<out String>, types: Array<out PsiType>) = throw UnsupportedOperationException() + + override fun createClass(name: String) = throw UnsupportedOperationException() + + override fun createEnum(name: String) = throw UnsupportedOperationException() + + override fun createClassInitializer() = throw UnsupportedOperationException() + + override fun createConstructor() = throw UnsupportedOperationException() + + override fun createConstructor(name: String) = throw UnsupportedOperationException() + + override fun createConstructor(name: String, context: PsiElement?) = throw UnsupportedOperationException() + + override fun createRawSubstitutor(owner: PsiTypeParameterListOwner) = throw UnsupportedOperationException() + + override fun isValidLocalVariableName(name: String) = throw UnsupportedOperationException() + + override fun createAnnotationFromText(text: String, context: PsiElement?) = throw UnsupportedOperationException() + + override fun createReferenceElementByType(type: PsiClassType?) = throw UnsupportedOperationException() + + override fun isValidFieldName(name: String) = throw UnsupportedOperationException() + + override fun createTypeParameterList() = throw UnsupportedOperationException() + + override fun createTypeByFQClassName(fqName: String) = throw UnsupportedOperationException() + + override fun createTypeByFQClassName(fqName: String, searchScope: GlobalSearchScope) = throw UnsupportedOperationException() + + override fun createParameter(name: String, type: PsiType?) = throw UnsupportedOperationException() + + override fun createParameter(name: String, type: PsiType?, context: PsiElement?) = throw UnsupportedOperationException() + + override fun createTypeParameter(name: String?, superTypes: Array<out PsiClassType>?) = throw UnsupportedOperationException() + + override fun createSubstitutor(substitutionMap: MutableMap<PsiTypeParameter, PsiType>) = throw UnsupportedOperationException() + + override fun createMethod(name: String, returnType: PsiType?) = throw UnsupportedOperationException() + + override fun createMethod(name: String, returnType: PsiType?, context: PsiElement?) = throw UnsupportedOperationException() + + override fun isValidClassName(name: String) = throw UnsupportedOperationException() + + override fun isValidParameterName(name: String) = throw UnsupportedOperationException() + + override fun createAnnotationType(name: String) = throw UnsupportedOperationException() +} + +class KotlinJVMElementMutator private constructor ( + private val rootElement: PsiElement, + private val rootElementView: PsiElement, + private val mainEditor: Editor +) : JVMElementMutatorWithEditor { + private var isDisposed = false + + private val elementFactory: JVMElementFactory by lazy { + checkNotDisposed() + object : KotlinJVMElementFactoryBase() { + + } + } + + private val dialogWithEditor: DialogWithEditor + + init { + dialogWithEditor = DialogWithTemplateEditor(rootElement.project, mainEditor, "Create from usage") { + LightVirtualFile("dummy.java", JavaFileType.INSTANCE, "") + }.apply { + psiFile!!.add(rootElementView) + } + } + + private fun checkNotDisposed() { + if (isDisposed) throw IllegalStateException("Mutator is already disposed") + } + + override fun getFactory() = elementFactory + + override fun getRootView(): PsiElement = rootElementView + + override fun openEditor(element: PsiElement?): Editor? { + checkNotDisposed() + if (!dialogWithEditor.isVisible) { + dialogWithEditor.show() + } + return dialogWithEditor.editor + } + + override fun synchronize() { + checkNotDisposed() + } + + override fun dispose() { + isDisposed = true + } + + companion object { + fun create(rootElement: PsiElement, editor: Editor): KotlinJVMElementMutator? { + val elementView = when (rootElement) { + is KtLightClassForExplicitDeclaration -> createJavaClass(rootElement.getOrigin(), null) + else -> return null + } + return KotlinJVMElementMutator(rootElement, elementView, editor) + } + } +} + +class KotlinJVMElementMutatorFactory : JVMElementMutatorFactory { + override fun createMutator(rootElement: PsiElement, editor: Editor) = KotlinJVMElementMutator.create(rootElement, editor) +}
diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt index c9cb3d7..26e4cdd 100644 --- a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/CallableBuilder.kt
@@ -20,7 +20,6 @@ import com.intellij.codeInsight.navigation.NavigationUtil import com.intellij.codeInsight.template.* import com.intellij.codeInsight.template.impl.TemplateImpl -import com.intellij.codeInsight.template.impl.TemplateManagerImpl import com.intellij.ide.fileTemplates.FileTemplate import com.intellij.ide.fileTemplates.FileTemplateManager import com.intellij.openapi.application.ApplicationManager @@ -33,6 +32,7 @@ import com.intellij.psi.* import com.intellij.psi.codeStyle.JavaCodeStyleManager import com.intellij.psi.util.PsiTreeUtil +import com.intellij.testFramework.LightVirtualFile import com.intellij.util.IncorrectOperationException import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode @@ -42,6 +42,7 @@ import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl +import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult import org.jetbrains.kotlin.idea.caches.resolve.getJavaClassDescriptor import org.jetbrains.kotlin.idea.codeInsight.CodeInsightUtils @@ -53,7 +54,6 @@ import org.jetbrains.kotlin.idea.util.DialogWithEditor import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers import org.jetbrains.kotlin.idea.util.ShortenReferences -import org.jetbrains.kotlin.idea.util.application.executeWriteCommand import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.lexer.KtTokens @@ -273,20 +273,11 @@ } dialogWithEditor = null } else { - val dialog = object: DialogWithEditor(project, "Create from usage", "") { - override fun doOKAction() { - project.executeWriteCommand("Premature end of template") { - TemplateManagerImpl.getTemplateState(editor)?.gotoEnd(false) - } - super.doOKAction() - } + val dialog = DialogWithTemplateEditor(project, config.currentEditor!!, "Create from usage") { + LightVirtualFile("dummy.kt", KotlinFileType.INSTANCE, "") } containingFileEditor = dialog.editor - with(containingFileEditor.settings) { - additionalColumnsCount = config.currentEditor!!.settings.getRightMargin(project) - additionalLinesCount = 5 - } - jetFileToEdit = PsiDocumentManager.getInstance(project).getPsiFile(containingFileEditor.document) as KtFile + jetFileToEdit = dialog.psiFile as KtFile jetFileToEdit.analysisContext = config.currentFile dialogWithEditor = dialog }
diff --git a/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/DialogWithTemplateEditor.kt b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/DialogWithTemplateEditor.kt new file mode 100644 index 0000000..1004dbc --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/quickfix/createFromUsage/callableBuilder/DialogWithTemplateEditor.kt
@@ -0,0 +1,45 @@ +/* + * Copyright 2010-2016 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.quickfix.createFromUsage.callableBuilder + +import com.intellij.codeInsight.template.impl.TemplateManagerImpl +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile +import org.jetbrains.kotlin.idea.util.DialogWithEditor +import org.jetbrains.kotlin.idea.util.application.executeWriteCommand + +class DialogWithTemplateEditor( + project: Project, + mainEditor: Editor, + title: String, + createFile: () -> VirtualFile +): DialogWithEditor(project, title, createFile) { + init { + with(editor.settings) { + additionalColumnsCount = mainEditor.settings.getRightMargin(project) + additionalLinesCount = 5 + } + } + + override fun doOKAction() { + project.executeWriteCommand("Premature end of template") { + TemplateManagerImpl.getTemplateState(editor)?.gotoEnd(false) + } + super.doOKAction() + } +}
diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/jetRefactoringUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/jetRefactoringUtil.kt index e67b997..e510e9a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/jetRefactoringUtil.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/jetRefactoringUtil.kt
@@ -537,13 +537,13 @@ return field } -fun createJavaClass(klass: KtClass, targetClass: PsiClass?, forcePlainClass: Boolean = false): PsiClass { +fun createJavaClass(klass: KtClassOrObject, targetClass: PsiClass?, forcePlainClass: Boolean = false): PsiClass { val kind = if (forcePlainClass) ClassKind.CLASS else (klass.resolveToDescriptor() as ClassDescriptor).kind val factory = PsiElementFactory.SERVICE.getInstance(klass.project) val className = klass.name!! val javaClassToAdd = when (kind) { - ClassKind.CLASS -> factory.createClass(className) + ClassKind.CLASS, ClassKind.OBJECT -> factory.createClass(className) ClassKind.INTERFACE -> factory.createInterface(className) ClassKind.ANNOTATION_CLASS -> factory.createAnnotationType(className) ClassKind.ENUM_CLASS -> factory.createEnum(className)
diff --git a/idea/src/org/jetbrains/kotlin/idea/util/DialogWithEditor.kt b/idea/src/org/jetbrains/kotlin/idea/util/DialogWithEditor.kt index 3a09fc9..891c28e 100644 --- a/idea/src/org/jetbrains/kotlin/idea/util/DialogWithEditor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/util/DialogWithEditor.kt
@@ -27,13 +27,14 @@ import com.intellij.openapi.fileEditor.* import com.intellij.openapi.editor.ex.* import com.intellij.openapi.editor.colors.* +import com.intellij.openapi.vfs.VirtualFile import java.awt.event.* import com.intellij.psi.* open class DialogWithEditor( val project: Project, title: String, - val initialText: String + private val createFile: () -> VirtualFile ) : DialogWrapper(project, true) { val editor: Editor = createEditor() @@ -44,7 +45,7 @@ private fun createEditor(): Editor { val editorFactory = EditorFactory.getInstance()!! - val virtualFile = LightVirtualFile("dummy.kt", KotlinFileType.INSTANCE, initialText) + val virtualFile = createFile() val document = FileDocumentManager.getInstance().getDocument(virtualFile)!! val editor = editorFactory.createEditor(document, project, KotlinFileType.INSTANCE, false) @@ -79,4 +80,7 @@ super.dispose() EditorFactory.getInstance()!!.releaseEditor(editor) } + + val psiFile: PsiFile? + get() = PsiDocumentManager.getInstance(project).getPsiFile(editor.document) }