KT-43941 [Sealed interfaces]: preliminary, J2K

This commit is contained in:
Andrei Klunnyi
2020-12-21 19:06:48 +01:00
parent 86792131cd
commit 1cac8b0d61
3 changed files with 246 additions and 283 deletions
@@ -126,7 +126,7 @@ class CreateKotlinSubClassIntention : SelfTargetingRangeIntention<KtClass>(
val dlg = chooseSubclassToCreate(baseClass, baseName) ?: return
val targetName = dlg.className
val (file, klass) = runWriteAction {
val file = getOrCreateKotlinFile("$targetName.kt", dlg.targetDirectory)!!
val file = getOrCreateKotlinFile("$targetName.kt", dlg.targetDirectory!!)!!
val builder = buildClassHeader(targetName, baseClass, baseClass.fqName!!.asString())
file.add(factory.createClass(builder.asString()))
val klass = file.getChildOfType<KtClass>()!!
@@ -164,7 +164,7 @@ class CreateKotlinSubClassIntention : SelfTargetingRangeIntention<KtClass>(
CreateClassKind.CLASS, true,
ModuleUtilCore.findModuleForPsiElement(baseClass)
) {
override fun getBaseDir(packageName: String) = sourceDir
override fun getBaseDir(packageName: String?) = sourceDir
override fun reportBaseInTestSelectionInSource() = true
}
@@ -1,281 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.refactoring.ui;
import com.intellij.CommonBundle;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.codeInsight.daemon.impl.quickfix.ClassKind;
import com.intellij.ide.util.PackageUtil;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CustomShortcutSet;
import com.intellij.openapi.application.WriteAction;
import com.intellij.openapi.command.CommandProcessor;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.DialogWrapper;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Pass;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiDirectory;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiNameHelper;
import com.intellij.refactoring.MoveDestination;
import com.intellij.refactoring.PackageWrapper;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.move.moveClassesOrPackages.MultipleRootsMoveDestination;
import com.intellij.refactoring.ui.PackageNameReferenceEditorCombo;
import com.intellij.refactoring.util.RefactoringMessageUtil;
import com.intellij.ui.DocumentAdapter;
import com.intellij.ui.RecentsManager;
import com.intellij.ui.ReferenceEditorComboWithBrowseButton;
import com.intellij.ui.components.JBLabel;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.ui.JBUI;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.idea.KotlinBundle;
import static org.jetbrains.kotlin.idea.roots.ProjectRootUtilsKt.getSuitableDestinationSourceRoots;
import javax.swing.*;
import javax.swing.event.DocumentEvent;
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
// Based on com.intellij.codeInsight.intention.impl.CreateClassDialog
public class CreateKotlinClassDialog extends DialogWrapper {
private final JLabel myInformationLabel = new JLabel("#");
private final JLabel myPackageLabel = new JLabel(CodeInsightBundle.message("dialog.create.class.destination.package.label"));
private final ReferenceEditorComboWithBrowseButton myPackageComponent;
private final JTextField myTfClassName = new MyTextField();
private final Project myProject;
private PsiDirectory myTargetDirectory;
private final String myClassName;
private final boolean myClassNameEditable;
private final Module myModule;
private final KotlinDestinationFolderComboBox myDestinationCB = new KotlinDestinationFolderComboBox() {
@Override
public String getTargetPackage() {
return myPackageComponent.getText().trim();
}
@Override
protected boolean reportBaseInTestSelectionInSource() {
return CreateKotlinClassDialog.this.reportBaseInTestSelectionInSource();
}
@Override
protected boolean reportBaseInSourceSelectionInTest() {
return CreateKotlinClassDialog.this.reportBaseInSourceSelectionInTest();
}
};
@NonNls private static final String RECENTS_KEY = "CreateKotlinClassDialog.RecentsKey";
public CreateKotlinClassDialog(@NotNull Project project,
@NotNull String title,
@NotNull String targetClassName,
@NotNull String targetPackageName,
@NotNull ClassKind kind,
boolean classNameEditable,
@Nullable Module defaultModule) {
super(project, true);
myClassNameEditable = classNameEditable;
myModule = defaultModule;
myClassName = targetClassName;
myProject = project;
myPackageComponent = new PackageNameReferenceEditorCombo(targetPackageName, myProject, RECENTS_KEY, CodeInsightBundle.message("dialog.create.class.package.chooser.title"));
myPackageComponent.setTextFieldPreferredWidth(40);
init();
if (!myClassNameEditable) {
setTitle(CodeInsightBundle.message("dialog.create.class.name", StringUtil.capitalize(kind.getDescription()), targetClassName));
}
else {
myInformationLabel.setText(CodeInsightBundle.message("dialog.create.class.label", kind.getDescription()));
setTitle(title);
}
myTfClassName.setText(myClassName);
myDestinationCB.setData(myProject, getBaseDir(targetPackageName), new Pass<String>() {
@Override
public void pass(String s) {
setErrorText(s, myDestinationCB);
}
}, myPackageComponent.getChildComponent());
}
protected boolean reportBaseInTestSelectionInSource() {
return false;
}
protected boolean reportBaseInSourceSelectionInTest() {
return false;
}
@NotNull
@Override
protected Action[] createActions() {
return new Action[]{getOKAction(), getCancelAction()};
}
@Override
public JComponent getPreferredFocusedComponent() {
return myClassNameEditable ? myTfClassName : myPackageComponent.getChildComponent();
}
@Override
protected JComponent createCenterPanel() {
return new JPanel(new BorderLayout());
}
@Override
protected JComponent createNorthPanel() {
JPanel panel = new JPanel(new GridBagLayout());
GridBagConstraints gbConstraints = new GridBagConstraints();
gbConstraints.insets = JBUI.insets(4, 8);
gbConstraints.fill = GridBagConstraints.HORIZONTAL;
gbConstraints.anchor = GridBagConstraints.WEST;
if (myClassNameEditable) {
gbConstraints.weightx = 0;
gbConstraints.gridwidth = 1;
panel.add(myInformationLabel, gbConstraints);
gbConstraints.insets = JBUI.insets(4, 8);
gbConstraints.gridx = 1;
gbConstraints.weightx = 1;
gbConstraints.gridwidth = 1;
gbConstraints.fill = GridBagConstraints.HORIZONTAL;
gbConstraints.anchor = GridBagConstraints.WEST;
panel.add(myTfClassName, gbConstraints);
myTfClassName.getDocument().addDocumentListener(new DocumentAdapter() {
@Override
protected void textChanged(DocumentEvent e) {
getOKAction().setEnabled(PsiNameHelper.getInstance(myProject).isIdentifier(myTfClassName.getText()));
}
});
getOKAction().setEnabled(StringUtil.isNotEmpty(myClassName));
}
gbConstraints.gridx = 0;
gbConstraints.gridy = 2;
gbConstraints.weightx = 0;
gbConstraints.gridwidth = 1;
panel.add(myPackageLabel, gbConstraints);
gbConstraints.gridx = 1;
gbConstraints.weightx = 1;
new AnAction() {
@Override
public void actionPerformed(AnActionEvent e) {
myPackageComponent.getButton().doClick();
}
}.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.SHIFT_DOWN_MASK)), myPackageComponent.getChildComponent());
JPanel _panel = new JPanel(new BorderLayout());
_panel.add(myPackageComponent, BorderLayout.CENTER);
panel.add(_panel, gbConstraints);
gbConstraints.gridy = 3;
gbConstraints.gridx = 0;
gbConstraints.gridwidth = 2;
gbConstraints.insets.top = 12;
gbConstraints.anchor = GridBagConstraints.WEST;
gbConstraints.fill = GridBagConstraints.NONE;
JBLabel label = new JBLabel(RefactoringBundle.message("target.destination.folder"));
panel.add(label, gbConstraints);
gbConstraints.gridy = 4;
gbConstraints.gridx = 0;
gbConstraints.fill = GridBagConstraints.HORIZONTAL;
gbConstraints.insets.top = 4;
panel.add(myDestinationCB, gbConstraints);
boolean isMultipleSourceRoots = getSuitableDestinationSourceRoots(myProject).size() > 1;
myDestinationCB.setVisible(isMultipleSourceRoots);
label.setVisible(isMultipleSourceRoots);
label.setLabelFor(myDestinationCB);
return panel;
}
public PsiDirectory getTargetDirectory() {
return myTargetDirectory;
}
private String getPackageName() {
String name = myPackageComponent.getText();
return name != null ? name.trim() : "";
}
private static class MyTextField extends JTextField {
@Override
public Dimension getPreferredSize() {
Dimension size = super.getPreferredSize();
FontMetrics fontMetrics = getFontMetrics(getFont());
size.width = fontMetrics.charWidth('a') * 40;
return size;
}
}
@Override
protected void doOKAction() {
RecentsManager.getInstance(myProject).registerRecentEntry(RECENTS_KEY, myPackageComponent.getText());
String packageName = getPackageName();
String[] errorString = new String[1];
CommandProcessor.getInstance().executeCommand(myProject, () -> {
try {
PackageWrapper targetPackage = new PackageWrapper(PsiManager.getInstance(myProject), packageName);
MoveDestination destination = myDestinationCB.selectDirectory(targetPackage, false);
if (destination == null) return;
myTargetDirectory = WriteAction.compute(() -> {
PsiDirectory baseDir = getBaseDir(packageName);
if (baseDir == null && destination instanceof MultipleRootsMoveDestination) {
errorString[0] = KotlinBundle.message("destination.not.found.for.package.0", packageName);
return null;
}
return destination.getTargetDirectory(baseDir);
});
if (myTargetDirectory == null) {
return;
}
errorString[0] = RefactoringMessageUtil.checkCanCreateClass(myTargetDirectory, getClassName());
}
catch (IncorrectOperationException e) {
errorString[0] = e.getMessage();
}
}, CodeInsightBundle.message("create.directory.command"), null);
if (errorString[0] != null) {
if (errorString[0].length() > 0) {
Messages.showMessageDialog(myProject, errorString[0], CommonBundle.getErrorTitle(), Messages.getErrorIcon());
}
return;
}
super.doOKAction();
}
@Nullable
protected PsiDirectory getBaseDir(String packageName) {
return myModule == null? null : PackageUtil.findPossiblePackageDirectoryInModule(myModule, packageName);
}
@NotNull
public String getClassName() {
if (myClassNameEditable) {
return myTfClassName.getText();
}
else {
return myClassName;
}
}
}
@@ -0,0 +1,244 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.idea.refactoring.ui
import com.intellij.CommonBundle
import com.intellij.codeInsight.CodeInsightBundle
import com.intellij.codeInsight.daemon.impl.quickfix.ClassKind
import com.intellij.ide.util.PackageUtil
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.CustomShortcutSet
import com.intellij.openapi.application.WriteAction
import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.ui.Messages
import com.intellij.openapi.util.Pass
import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiManager
import com.intellij.psi.PsiNameHelper
import com.intellij.refactoring.PackageWrapper
import com.intellij.refactoring.RefactoringBundle
import com.intellij.refactoring.move.moveClassesOrPackages.MultipleRootsMoveDestination
import com.intellij.refactoring.ui.PackageNameReferenceEditorCombo
import com.intellij.refactoring.util.RefactoringMessageUtil
import com.intellij.ui.DocumentAdapter
import com.intellij.ui.RecentsManager
import com.intellij.ui.ReferenceEditorComboWithBrowseButton
import com.intellij.ui.components.JBLabel
import com.intellij.util.IncorrectOperationException
import com.intellij.util.ui.JBUI
import org.jetbrains.annotations.NonNls
import org.jetbrains.kotlin.idea.KotlinBundle.message
import org.jetbrains.kotlin.idea.roots.getSuitableDestinationSourceRoots
import java.awt.BorderLayout
import java.awt.Dimension
import java.awt.GridBagConstraints
import java.awt.GridBagLayout
import java.awt.event.InputEvent
import java.awt.event.KeyEvent
import javax.swing.*
import javax.swing.event.DocumentEvent
// Based on com.intellij.codeInsight.intention.impl.CreateClassDialog
open class CreateKotlinClassDialog(
private val myProject: Project,
title: String,
private val myClassName: String,
targetPackageName: String,
kind: ClassKind,
private val myClassNameEditable: Boolean,
private val myModule: Module?
) : DialogWrapper(myProject, true) {
private val myInformationLabel = JLabel("#")
private val myPackageLabel = JLabel(CodeInsightBundle.message("dialog.create.class.destination.package.label"))
private var myPackageComponent: ReferenceEditorComboWithBrowseButton = PackageNameReferenceEditorCombo(
targetPackageName,
myProject,
RECENTS_KEY,
CodeInsightBundle.message("dialog.create.class.package.chooser.title")
)
private val myTfClassName: JTextField = MyTextField()
var targetDirectory: PsiDirectory? = null
private set
private val myDestinationCB: KotlinDestinationFolderComboBox = object : KotlinDestinationFolderComboBox() {
override fun getTargetPackage(): String {
return myPackageComponent.text.trim()
}
override fun reportBaseInTestSelectionInSource(): Boolean {
return this@CreateKotlinClassDialog.reportBaseInTestSelectionInSource()
}
override fun reportBaseInSourceSelectionInTest(): Boolean {
return this@CreateKotlinClassDialog.reportBaseInSourceSelectionInTest()
}
}
protected open fun reportBaseInTestSelectionInSource(): Boolean {
return false
}
protected open fun reportBaseInSourceSelectionInTest(): Boolean {
return false
}
override fun createActions(): Array<Action> {
return arrayOf(okAction, cancelAction)
}
override fun getPreferredFocusedComponent(): JComponent? {
return if (myClassNameEditable) myTfClassName else myPackageComponent.childComponent
}
override fun createCenterPanel(): JComponent? {
return JPanel(BorderLayout())
}
override fun createNorthPanel(): JComponent? {
val panel = JPanel(GridBagLayout())
val gbConstraints = GridBagConstraints()
gbConstraints.insets = JBUI.insets(4, 8)
gbConstraints.fill = GridBagConstraints.HORIZONTAL
gbConstraints.anchor = GridBagConstraints.WEST
if (myClassNameEditable) {
gbConstraints.weightx = 0.0
gbConstraints.gridwidth = 1
panel.add(myInformationLabel, gbConstraints)
gbConstraints.insets = JBUI.insets(4, 8)
gbConstraints.gridx = 1
gbConstraints.weightx = 1.0
gbConstraints.gridwidth = 1
gbConstraints.fill = GridBagConstraints.HORIZONTAL
gbConstraints.anchor = GridBagConstraints.WEST
panel.add(myTfClassName, gbConstraints)
myTfClassName.document.addDocumentListener(object : DocumentAdapter() {
override fun textChanged(e: DocumentEvent) {
okAction.isEnabled = PsiNameHelper.getInstance(myProject).isIdentifier(myTfClassName.text)
}
})
okAction.isEnabled = StringUtil.isNotEmpty(myClassName)
}
gbConstraints.gridx = 0
gbConstraints.gridy = 2
gbConstraints.weightx = 0.0
gbConstraints.gridwidth = 1
panel.add(myPackageLabel, gbConstraints)
gbConstraints.gridx = 1
gbConstraints.weightx = 1.0
object : AnAction() {
override fun actionPerformed(e: AnActionEvent) {
myPackageComponent.button.doClick()
}
}.registerCustomShortcutSet(
CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, InputEvent.SHIFT_DOWN_MASK)),
myPackageComponent.childComponent
)
val _panel = JPanel(BorderLayout())
_panel.add(myPackageComponent, BorderLayout.CENTER)
panel.add(_panel, gbConstraints)
gbConstraints.gridy = 3
gbConstraints.gridx = 0
gbConstraints.gridwidth = 2
gbConstraints.insets.top = 12
gbConstraints.anchor = GridBagConstraints.WEST
gbConstraints.fill = GridBagConstraints.NONE
val label = JBLabel(RefactoringBundle.message("target.destination.folder"))
panel.add(label, gbConstraints)
gbConstraints.gridy = 4
gbConstraints.gridx = 0
gbConstraints.fill = GridBagConstraints.HORIZONTAL
gbConstraints.insets.top = 4
panel.add(myDestinationCB, gbConstraints)
val isMultipleSourceRoots = getSuitableDestinationSourceRoots(myProject).size > 1
myDestinationCB.isVisible = isMultipleSourceRoots
label.isVisible = isMultipleSourceRoots
label.labelFor = myDestinationCB
return panel
}
private val packageName: String
get() {
val name = myPackageComponent.text
return name?.trim() ?: ""
}
private class MyTextField : JTextField() {
override fun getPreferredSize(): Dimension {
val size = super.getPreferredSize()
val fontMetrics = getFontMetrics(font)
size.width = fontMetrics.charWidth('a') * 40
return size
}
}
override fun doOKAction() {
RecentsManager.getInstance(myProject).registerRecentEntry(RECENTS_KEY, myPackageComponent.text)
val packageName = packageName
var errorString: String? = null
CommandProcessor.getInstance().executeCommand(myProject, {
try {
val targetPackage = PackageWrapper(PsiManager.getInstance(myProject), packageName)
val destination = myDestinationCB.selectDirectory(targetPackage, false) ?: return@executeCommand
targetDirectory = WriteAction.compute<PsiDirectory, RuntimeException> {
val baseDir = getBaseDir(packageName)
if (baseDir == null && destination is MultipleRootsMoveDestination) {
errorString = message("destination.not.found.for.package.0", packageName)
return@compute null
}
destination.getTargetDirectory(baseDir)
}
if (targetDirectory == null) {
return@executeCommand
}
errorString = RefactoringMessageUtil.checkCanCreateClass(targetDirectory, className)
} catch (e: IncorrectOperationException) {
errorString = e.message
}
}, CodeInsightBundle.message("create.directory.command"), null)
errorString?.let {
if (it.isNotEmpty())
Messages.showMessageDialog(myProject, errorString, CommonBundle.getErrorTitle(), Messages.getErrorIcon())
} ?: super.doOKAction()
}
protected open fun getBaseDir(packageName: String?): PsiDirectory? {
return if (myModule == null) null else PackageUtil.findPossiblePackageDirectoryInModule(myModule, packageName)
}
val className: String
get() = if (myClassNameEditable) {
myTfClassName.text
} else {
myClassName
}
companion object {
@NonNls
private val RECENTS_KEY = "CreateKotlinClassDialog.RecentsKey"
}
init {
myPackageComponent.setTextFieldPreferredWidth(40)
init()
if (!myClassNameEditable) {
setTitle(CodeInsightBundle.message("dialog.create.class.name", StringUtil.capitalize(kind.description), myClassName))
} else {
myInformationLabel.text = CodeInsightBundle.message("dialog.create.class.label", kind.description)
setTitle(title)
}
myTfClassName.text = myClassName
myDestinationCB.setData(myProject, getBaseDir(targetPackageName), object : Pass<String?>() {
override fun pass(s: String?) {
setErrorText(s, myDestinationCB)
}
}, myPackageComponent.getChildComponent())
}
}