- KT-2353 convert a selection of Java code to Kotlin by copy paste (or context menu) - Apply patch from Sergey Ignatov

#KT-2353 fixed
This commit is contained in:
Nikolay Krasko
2012-07-18 14:32:27 +04:00
parent 00b0f26339
commit 28b0eefe5d
8 changed files with 410 additions and 4 deletions
+6
View File
@@ -161,6 +161,7 @@
<extendWordSelectionHandler implementation="org.jetbrains.jet.plugin.editor.wordSelection.JetListSelectioner"/>
<typedHandler implementation="org.jetbrains.jet.plugin.editor.KotlinTypedHandler" />
<copyPastePostProcessor implementation="org.jetbrains.jet.plugin.conversion.copy.JavaCopyPastePostProcessor"/>
<lang.documentationProvider language="JAVA" implementationClass="org.jetbrains.jet.plugin.JetQuickDocumentationProvider" order="first"/>
<documentationProvider implementation="org.jetbrains.jet.plugin.JetQuickDocumentationProvider"/>
<configurationType implementation="org.jetbrains.jet.plugin.run.JetRunConfigurationType"/>
@@ -209,6 +210,11 @@
icon="/org/jetbrains/jet/plugin/icons/kotlin_13.png"
/>
<applicationService serviceInterface="org.jetbrains.jet.plugin.editor.JetEditorOptions"
serviceImplementation="org.jetbrains.jet.plugin.editor.JetEditorOptions"/>
<exportable serviceInterface="org.jetbrains.jet.plugin.editor.JetEditorOptions"/>
<editorAppearanceConfigurable instance="org.jetbrains.jet.plugin.editor.JetSettingEditorConfigurable"/>
<intentionAction>
<className>org.jetbrains.jet.plugin.intentions.SpecifyTypeExplicitlyAction</className>
<category>Kotlin</category>
@@ -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;
}
}
@@ -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<TextBlockTransferableData> {
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<PsiElement> 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<PsiElement> getSelectedElements(@NotNull PsiFile file, @NotNull int[] startOffsets, @NotNull int[] endOffsets) {
ArrayList<PsiElement> buffer = new ArrayList<PsiElement>();
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<Boolean> 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();
}
}
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<form xmlns="http://www.intellij.com/uidesigner/form/" version="1" bind-to-class="org.jetbrains.jet.plugin.conversion.copy.KotlinPasteFromJavaDialog">
<grid id="27dc6" binding="myPanel" layout-manager="GridLayoutManager" row-count="3" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
<margin top="0" left="0" bottom="0" right="0"/>
<constraints>
<xy x="20" y="20" width="500" height="400"/>
</constraints>
<properties/>
<border type="none"/>
<children>
<component id="746fc" class="javax.swing.JLabel">
<constraints>
<grid row="0" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="0" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="Clipboard content copied from Java file. Do you want to convert it to Kotlin code?"/>
</properties>
</component>
<vspacer id="b8512">
<constraints>
<grid row="2" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<component id="98483" class="javax.swing.JCheckBox" binding="donTShowThisCheckBox" default-binding="true">
<constraints>
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
</constraints>
<properties>
<text value="&amp;Don't show this dialog next time"/>
</properties>
</component>
</children>
</grid>
</form>
@@ -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();
}
}
@@ -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<JetEditorOptions> {
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;
}
}
}
@@ -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<JetEditorOptions> implements UnnamedConfigurable {
public JetSettingEditorConfigurable() {
super(JetEditorOptions.getInstance());
checkBox("enableJavaToKotlinConversion", "Enable Java To Kotlin Conversion");
checkBox("donTShowConversionDialog", "Don't show Java to Kotlin conversion dialog");
}
}
+6 -4
View File
@@ -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<String> modifiers = modifiersListToModifiersSet(psiClass.getModifierList());
final List<Field> fields = fieldsToFieldList(psiClass.getFields(), psiClass);
final List<Element> 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<PsiExpressionList> resolvedSuperCallParameters = visitor.getResolvedSuperCallParameters();
final Collection<PsiExpressionList> resolvedSuperCallParameters = visitor.getResolvedSuperCallParameters();
if (resolvedSuperCallParameters.size() == 1) {
baseClassParams.addAll(
expressionsToExpressionList(
@@ -677,7 +679,7 @@ public class Converter {
@NotNull
public static Set<String> modifiersListToModifiersSet(@Nullable PsiModifierList modifierList) {
HashSet<String> modifiersSet = new HashSet<String>();
Set<String> modifiersSet = new HashSet<String>();
if (modifierList != null) {
if (modifierList.hasExplicitModifier(PsiModifier.ABSTRACT)) modifiersSet.add(Modifier.ABSTRACT);
if (modifierList.hasModifierProperty(PsiModifier.FINAL)) modifiersSet.add(Modifier.FINAL);