From 28b0eefe5d2aa5cc108d77755ceb714b2e48c62b Mon Sep 17 00:00:00 2001 From: Nikolay Krasko Date: Wed, 18 Jul 2012 14:32:27 +0400 Subject: [PATCH] - KT-2353 convert a selection of Java code to Kotlin by copy paste (or context menu) - Apply patch from Sergey Ignatov #KT-2353 fixed --- idea/src/META-INF/plugin.xml | 6 + .../plugin/conversion/copy/ConvertedCode.java | 60 ++++++++ .../copy/JavaCopyPastePostProcessor.java | 139 ++++++++++++++++++ .../copy/KotlinPasteFromJavaDialog.form | 34 +++++ .../copy/KotlinPasteFromJavaDialog.java | 54 +++++++ .../jet/plugin/editor/JetEditorOptions.java | 80 ++++++++++ .../editor/JetSettingEditorConfigurable.java | 31 ++++ j2k/src/org/jetbrains/jet/j2k/Converter.java | 10 +- 8 files changed, 410 insertions(+), 4 deletions(-) create mode 100644 idea/src/org/jetbrains/jet/plugin/conversion/copy/ConvertedCode.java create mode 100644 idea/src/org/jetbrains/jet/plugin/conversion/copy/JavaCopyPastePostProcessor.java create mode 100644 idea/src/org/jetbrains/jet/plugin/conversion/copy/KotlinPasteFromJavaDialog.form create mode 100644 idea/src/org/jetbrains/jet/plugin/conversion/copy/KotlinPasteFromJavaDialog.java create mode 100644 idea/src/org/jetbrains/jet/plugin/editor/JetEditorOptions.java create mode 100644 idea/src/org/jetbrains/jet/plugin/editor/JetSettingEditorConfigurable.java diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 97f4a191f09..606443aceae 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -161,6 +161,7 @@ + @@ -209,6 +210,11 @@ icon="/org/jetbrains/jet/plugin/icons/kotlin_13.png" /> + + + + org.jetbrains.jet.plugin.intentions.SpecifyTypeExplicitlyAction Kotlin diff --git a/idea/src/org/jetbrains/jet/plugin/conversion/copy/ConvertedCode.java b/idea/src/org/jetbrains/jet/plugin/conversion/copy/ConvertedCode.java new file mode 100644 index 00000000000..8fc6c8f454c --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/conversion/copy/ConvertedCode.java @@ -0,0 +1,60 @@ +/* + * Copyright 2010-2012 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.jet.plugin.conversion.copy; + +import com.intellij.codeInsight.editorActions.TextBlockTransferableData; +import org.jetbrains.annotations.NotNull; + +import java.awt.datatransfer.DataFlavor; + +/** + * @author ignatov + */ +class ConvertedCode implements TextBlockTransferableData { + @NotNull + public static final DataFlavor DATA_FLAVOR = new DataFlavor(JavaCopyPastePostProcessor.class, "class: JavaCopyPastePostProcessor"); + private final String data; + + ConvertedCode(String data) { + this.data = data; + } + + @NotNull + @Override + public DataFlavor getFlavor() { + return DATA_FLAVOR; + } + + @Override + public int getOffsetCount() { + return 0; + } + + @Override + public int getOffsets(int[] offsets, int index) { + return 0; + } + + @Override + public int setOffsets(int[] offsets, int index) { + return 0; + } + + public String getData() { + return data; + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/conversion/copy/JavaCopyPastePostProcessor.java b/idea/src/org/jetbrains/jet/plugin/conversion/copy/JavaCopyPastePostProcessor.java new file mode 100644 index 00000000000..989ef761ca9 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/conversion/copy/JavaCopyPastePostProcessor.java @@ -0,0 +1,139 @@ +/* + * Copyright 2010-2012 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.jet.plugin.conversion.copy; + +import com.intellij.codeInsight.editorActions.CopyPastePostProcessor; +import com.intellij.codeInsight.editorActions.TextBlockTransferableData; +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.text.StringUtil; +import com.intellij.psi.PsiDocumentManager; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiJavaFile; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.j2k.Converter; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.plugin.editor.JetEditorOptions; + +import java.awt.datatransfer.Transferable; +import java.util.ArrayList; +import java.util.List; + +/** + * @author ignatov + */ +public class JavaCopyPastePostProcessor implements CopyPastePostProcessor { + private static final Logger LOG = Logger.getInstance("#org.jetbrains.jet.plugin.conversion.copy.JavaCopyPastePostProcessor"); + + @Override + public TextBlockTransferableData collectTransferableData(@NotNull PsiFile file, Editor editor, @NotNull int[] startOffsets, @NotNull int[] endOffsets) { + try { + if (!(file instanceof PsiJavaFile)) { + return null; + } + final String eol = System.getProperty("line.separator"); + List buffer = getSelectedElements(file, startOffsets, endOffsets); + StringBuilder result = new StringBuilder(); + + for (PsiElement e : buffer) { + String converted = new Converter().elementToKotlin(e); + if (!converted.isEmpty()) { + result.append(converted).append(eol); + } + } + return new ConvertedCode(StringUtil.convertLineSeparators(result.toString())); + } catch (Throwable t) { + LOG.error(t); + } + return null; + } + + @NotNull + private static List getSelectedElements(@NotNull PsiFile file, @NotNull int[] startOffsets, @NotNull int[] endOffsets) { + ArrayList buffer = new ArrayList(); + + assert startOffsets.length == endOffsets.length : "Must have the same length"; + + for (int i = 0; i < startOffsets.length; i++) { + int startOffset = startOffsets[i]; + int endOffset = endOffsets[i]; + PsiElement elem = file.findElementAt(startOffset); + while (elem != null && elem.getParent() != null && !(elem.getParent() instanceof PsiFile) && + elem.getParent().getTextRange().getEndOffset() <= endOffset) { + elem = elem.getParent(); + } + buffer.add(elem); + + while (elem != null && elem.getTextRange().getEndOffset() < endOffset) { + elem = elem.getNextSibling(); + buffer.add(elem); + } + } + return buffer; + } + + @Override + public TextBlockTransferableData extractTransferableData(@NotNull Transferable content) { + try { + if (content.isDataFlavorSupported(ConvertedCode.DATA_FLAVOR)) { + return (TextBlockTransferableData) content.getTransferData(ConvertedCode.DATA_FLAVOR); + } + } catch (Throwable e) { + LOG.error(e); + } + return null; + } + + @Override + public void processTransferableData(Project project, @NotNull final Editor editor, @NotNull final RangeMarker bounds, int caretColumn, Ref indented, @NotNull final TextBlockTransferableData value) { + try { + final PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()); + if (!(file instanceof JetFile)) { + return; + } + JetEditorOptions jetEditorOptions = JetEditorOptions.getInstance(); + boolean needConvert = jetEditorOptions.isEnableJavaToKotlinConversion() && (jetEditorOptions.isDonTShowConversionDialog() || okFromDialog(project)); + if (needConvert) { + if (value instanceof ConvertedCode) { + final String text = ((ConvertedCode) value).getData(); + ApplicationManager.getApplication().runWriteAction(new Runnable() { + @Override + public void run() { + editor.getDocument().replaceString(bounds.getStartOffset(), bounds.getEndOffset(), text); + editor.getCaretModel().moveToOffset(bounds.getStartOffset() + text.length()); + PsiDocumentManager.getInstance(file.getProject()).commitDocument(editor.getDocument()); + } + }); + } + } + } catch (Throwable t) { + LOG.error(t); + } + } + + private static boolean okFromDialog(@NotNull Project project) { + KotlinPasteFromJavaDialog dialog = new KotlinPasteFromJavaDialog(project); + dialog.show(); + return dialog.isOK(); + } +} + diff --git a/idea/src/org/jetbrains/jet/plugin/conversion/copy/KotlinPasteFromJavaDialog.form b/idea/src/org/jetbrains/jet/plugin/conversion/copy/KotlinPasteFromJavaDialog.form new file mode 100644 index 00000000000..1ec65cefabe --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/conversion/copy/KotlinPasteFromJavaDialog.form @@ -0,0 +1,34 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/idea/src/org/jetbrains/jet/plugin/conversion/copy/KotlinPasteFromJavaDialog.java b/idea/src/org/jetbrains/jet/plugin/conversion/copy/KotlinPasteFromJavaDialog.java new file mode 100644 index 00000000000..6664a6117ac --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/conversion/copy/KotlinPasteFromJavaDialog.java @@ -0,0 +1,54 @@ +package org.jetbrains.jet.plugin.conversion.copy; + +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.DialogWrapper; +import org.jetbrains.jet.plugin.editor.JetEditorOptions; + +import javax.swing.*; +import java.awt.*; + +/** + * @ignatov + */ +@SuppressWarnings("UnusedDeclaration") +public class KotlinPasteFromJavaDialog extends DialogWrapper { + private JPanel myPanel; + private JCheckBox donTShowThisCheckBox; + private JButton buttonOK; + + public KotlinPasteFromJavaDialog(Project project) { + super(project, true); + setModal(true); + getRootPane().setDefaultButton(buttonOK); + setTitle("Convert Code From Java"); + init(); + } + + @Override + protected JComponent createCenterPanel() { + return myPanel; + } + + @Override + public Container getContentPane() { + return myPanel; + } + + @Override + protected Action[] createActions() { + return new Action[] {getOKAction(), getCancelAction()}; + } + + @Override + protected void doOKAction() { + if (donTShowThisCheckBox.isSelected()) { + JetEditorOptions.getInstance().setDonTShowConversionDialog(true); + } + super.doOKAction(); + } + + @Override + public void dispose() { + super.dispose(); + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/editor/JetEditorOptions.java b/idea/src/org/jetbrains/jet/plugin/editor/JetEditorOptions.java new file mode 100644 index 00000000000..1129fc4d74c --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/editor/JetEditorOptions.java @@ -0,0 +1,80 @@ +/* + * Copyright 2010-2012 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.jet.plugin.editor; + +import com.intellij.openapi.components.PersistentStateComponent; +import com.intellij.openapi.components.ServiceManager; +import com.intellij.openapi.components.State; +import com.intellij.openapi.components.Storage; +import com.intellij.util.xmlb.XmlSerializerUtil; +import org.jetbrains.annotations.Nullable; + +/** + * @author ignatov + */ +@State( + name = "JetEditorOptions", + storages = { + @Storage( + file = "$APP_CONFIG$/editor.xml" + )} +) +public class JetEditorOptions implements PersistentStateComponent { + private boolean donTShowConversionDialog = false; + private boolean enableJavaToKotlinConversion = true; + + public boolean isDonTShowConversionDialog() { + return donTShowConversionDialog; + } + + public void setDonTShowConversionDialog(boolean donTShowConversionDialog) { + this.donTShowConversionDialog = donTShowConversionDialog; + } + + public boolean isEnableJavaToKotlinConversion() { + return enableJavaToKotlinConversion; + } + + @SuppressWarnings("UnusedDeclaration") + public void setEnableJavaToKotlinConversion(boolean enableJavaToKotlinConversion) { + this.enableJavaToKotlinConversion = enableJavaToKotlinConversion; + } + + @Override + public JetEditorOptions getState() { + return this; + } + + @Override + public void loadState(JetEditorOptions state) { + XmlSerializerUtil.copyBean(state, this); + } + + public static JetEditorOptions getInstance() { + return ServiceManager.getService(JetEditorOptions.class); + } + + @Override + @Nullable + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + return null; + } + } +} diff --git a/idea/src/org/jetbrains/jet/plugin/editor/JetSettingEditorConfigurable.java b/idea/src/org/jetbrains/jet/plugin/editor/JetSettingEditorConfigurable.java new file mode 100644 index 00000000000..a71f9fceb81 --- /dev/null +++ b/idea/src/org/jetbrains/jet/plugin/editor/JetSettingEditorConfigurable.java @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2012 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.jet.plugin.editor; + +import com.intellij.openapi.options.BeanConfigurable; +import com.intellij.openapi.options.UnnamedConfigurable; + +/** + * @author ignatov + */ +public class JetSettingEditorConfigurable extends BeanConfigurable implements UnnamedConfigurable { + public JetSettingEditorConfigurable() { + super(JetEditorOptions.getInstance()); + checkBox("enableJavaToKotlinConversion", "Enable Java To Kotlin Conversion"); + checkBox("donTShowConversionDialog", "Don't show Java to Kotlin conversion dialog"); + } +} diff --git a/j2k/src/org/jetbrains/jet/j2k/Converter.java b/j2k/src/org/jetbrains/jet/j2k/Converter.java index 16780397791..98ae433ccef 100644 --- a/j2k/src/org/jetbrains/jet/j2k/Converter.java +++ b/j2k/src/org/jetbrains/jet/j2k/Converter.java @@ -175,7 +175,9 @@ public class Converter { else if (e instanceof PsiClassInitializer) { members.add(initializerToInitializer((PsiClassInitializer) e)); } - else if (e instanceof PsiMember) System.out.println(e.getClass() + " " + e.getText()); + else if (e instanceof PsiMember) { + // System.out.println(e.getClass() + " " + e.getText()); + } } return members; } @@ -219,7 +221,7 @@ public class Converter { } @NotNull - private Class classToClass(@NotNull PsiClass psiClass) { + public Class classToClass(@NotNull PsiClass psiClass) { final Set modifiers = modifiersListToModifiersSet(psiClass.getModifierList()); final List fields = fieldsToFieldList(psiClass.getFields(), psiClass); final List typeParameters = elementsToElementList(psiClass.getTypeParameters()); @@ -233,7 +235,7 @@ public class Converter { // we try to find super() call and generate class declaration like that: class A(name: String, i : Int) : Base(name) final SuperVisitor visitor = new SuperVisitor(); psiClass.accept(visitor); - final HashSet resolvedSuperCallParameters = visitor.getResolvedSuperCallParameters(); + final Collection resolvedSuperCallParameters = visitor.getResolvedSuperCallParameters(); if (resolvedSuperCallParameters.size() == 1) { baseClassParams.addAll( expressionsToExpressionList( @@ -677,7 +679,7 @@ public class Converter { @NotNull public static Set modifiersListToModifiersSet(@Nullable PsiModifierList modifierList) { - HashSet modifiersSet = new HashSet(); + Set modifiersSet = new HashSet(); if (modifierList != null) { if (modifierList.hasExplicitModifier(PsiModifier.ABSTRACT)) modifiersSet.add(Modifier.ABSTRACT); if (modifierList.hasModifierProperty(PsiModifier.FINAL)) modifiersSet.add(Modifier.FINAL);