[jvm-lang] port JavaFX event handler reference fixes

This commit is contained in:
Daniil Ovchinnikov
2017-09-07 16:49:03 +03:00
parent df649998fe
commit 51395418c1
20 changed files with 145 additions and 103 deletions

View File

@@ -34,6 +34,12 @@ private inline fun createActions(crossinline actions: (JvmElementActionsFactory)
}
}
fun createMethodActions(target: JvmClass, request: CreateMethodRequest): List<IntentionAction> {
return createActions {
it.createAddMethodActions(target, request)
}
}
fun createModifierActions(target: JvmModifiersOwner, request: MemberRequest.Modifier): List<IntentionAction> {
return createActions {
it.createChangeModifierActions(target, request)
@@ -53,12 +59,6 @@ fun createMethodAction(target: JvmClass, request: MemberRequest.Method): Intenti
return null
}
fun createMethodActions(target: JvmClass, request: MemberRequest.Method): List<IntentionAction> {
return createActions {
it.createAddMethodActions(target, request)
}
}
fun createPropertyActions(target: JvmClass, request: MemberRequest.Property): List<IntentionAction> {
return createActions {
it.createAddPropertyActions(target, request)

View File

@@ -23,7 +23,6 @@ import com.intellij.openapi.module.Module;
import com.intellij.openapi.roots.ContentEntry;
import com.intellij.openapi.roots.ModifiableRootModel;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.codeStyle.JavaCodeStyleSettings;
import com.intellij.psi.xml.XmlFile;
import com.intellij.testFramework.LightProjectDescriptor;
@@ -56,7 +55,7 @@ public class JavaFXQuickfixTest extends LightCodeInsightFixtureTestCase {
}
public void testCreateControllerMethod() {
doTest("Create method 'void bar(ActionEvent)'", ".java");
doTest("Create method 'bar'", ".java");
}
@Bombed(year = 2017, month = Calendar.SEPTEMBER, day = 1, user = "Daniil Ovchinnikov")
@@ -65,11 +64,11 @@ public class JavaFXQuickfixTest extends LightCodeInsightFixtureTestCase {
}
public void testCreateControllerMethodGeneric() {
doTest("Create method 'void onSort(SortEvent)'", ".java");
doTest("Create method 'onSort'", ".java");
}
public void testCreateControllerMethodHalfRaw() {
doTest("Create method 'void onSort(SortEvent)'", ".java");
doTest("Create method 'onSort'", ".java");
}
public void testCreateFieldPublicVisibility() {
@@ -93,23 +92,23 @@ public class JavaFXQuickfixTest extends LightCodeInsightFixtureTestCase {
}
public void testCreateMethodPublicVisibility() {
doTestWithDefaultVisibility("Create method 'void onAction(ActionEvent)'", "CreateMethod", PsiModifier.PUBLIC, ".java");
doTestWithDefaultVisibility("Create method 'onAction'", "CreateMethod", PsiModifier.PUBLIC, ".java");
}
public void testCreateMethodProtectedVisibility() {
doTestWithDefaultVisibility("Create method 'void onAction(ActionEvent)'", "CreateMethod", PsiModifier.PROTECTED, ".java");
doTestWithDefaultVisibility("Create method 'onAction'", "CreateMethod", PsiModifier.PROTECTED, ".java");
}
public void testCreateMethodPrivateVisibility() {
doTestWithDefaultVisibility("Create method 'void onAction(ActionEvent)'", "CreateMethod", PsiModifier.PRIVATE, ".java");
doTestWithDefaultVisibility("Create method 'onAction'", "CreateMethod", PsiModifier.PRIVATE, ".java");
}
public void testCreateMethodPackageLocalVisibility() {
doTestWithDefaultVisibility("Create method 'void onAction(ActionEvent)'", "CreateMethod", PsiModifier.PACKAGE_LOCAL, ".java");
doTestWithDefaultVisibility("Create method 'onAction'", "CreateMethod", PsiModifier.PACKAGE_LOCAL, ".java");
}
public void testCreateMethodEscalateVisibility() {
doTestWithDefaultVisibility("Create method 'void onAction(ActionEvent)'", "CreateMethod", VisibilityUtil.ESCALATE_VISIBILITY,
doTestWithDefaultVisibility("Create method 'onAction'", "CreateMethod", VisibilityUtil.ESCALATE_VISIBILITY,
".java");
}
@@ -144,7 +143,7 @@ public class JavaFXQuickfixTest extends LightCodeInsightFixtureTestCase {
final String inputName,
final String defaultVisibility,
final String extension) {
JavaCodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject()).getCustomSettings(JavaCodeStyleSettings.class);
JavaCodeStyleSettings settings = JavaCodeStyleSettings.getInstance(getProject());
String savedVisibility = settings.VISIBILITY;
try {
settings.VISIBILITY = defaultVisibility;
@@ -161,8 +160,8 @@ public class JavaFXQuickfixTest extends LightCodeInsightFixtureTestCase {
private void doTest(final String actionName, final String inputName, final String outputName, final String extension) {
String path = PlatformTestUtil.lowercaseFirstLetter(inputName, true) + ".fxml";
final IntentionAction intention =
myFixture.getAvailableIntention(actionName, path, inputName + extension);
myFixture.configureByFiles(path, inputName + extension);
final IntentionAction intention = myFixture.findSingleIntention(actionName);
assertNotNull(intention);
myFixture.launchAction(intention);
myFixture.checkResultByFile(inputName + extension, outputName + "_after" + extension, true);

View File

@@ -58,19 +58,19 @@ public class JavaFxEventHandlerInspectionTest extends AbstractJavaFXTestCase {
}
public void testQuickfixRaw() {
doQuickfixTest("Create method 'void onSort(SortEvent)'");
doQuickfixTest("Create method 'onSort'");
}
public void testQuickfixHalfRaw() {
doQuickfixTest("Create method 'void onSort(SortEvent)'");
doQuickfixTest("Create method 'onSort'");
}
public void testQuickfixSpecific() {
doQuickfixTest("Create method 'void onSort(SortEvent)'");
doQuickfixTest("Create method 'onSort'");
}
public void testQuickfixNoField() {
doQuickfixTest("Create method 'void onSort(SortEvent)'");
doQuickfixTest("Create method 'onSort'");
}
public void testQuickfixFieldType() {
@@ -82,7 +82,7 @@ public class JavaFxEventHandlerInspectionTest extends AbstractJavaFXTestCase {
final boolean oldImports = settings.INSERT_INNER_CLASS_IMPORTS;
try {
settings.INSERT_INNER_CLASS_IMPORTS = true;
doQuickfixTest("Create method 'void onColumnEditStart(CellEditEvent)'");
doQuickfixTest("Create method 'onColumnEditStart'");
}
finally {
settings.INSERT_INNER_CLASS_IMPORTS = oldImports;
@@ -90,7 +90,7 @@ public class JavaFxEventHandlerInspectionTest extends AbstractJavaFXTestCase {
}
public void testQuickfixSuper() {
doQuickfixTest("Create method 'void click(MouseEvent)'");
doQuickfixTest("Create method 'click'");
}
private void doHighlightingTest() {
@@ -100,7 +100,8 @@ public class JavaFxEventHandlerInspectionTest extends AbstractJavaFXTestCase {
private void doQuickfixTest(final String actionName) {
String path = getTestName(true) + ".fxml";
final IntentionAction intention = myFixture.getAvailableIntention(actionName, path, getTestName(false) + ".java");
myFixture.configureByFiles(path, getTestName(false) + ".java");
IntentionAction intention = myFixture.findSingleIntention(actionName);
assertNotNull(intention);
myFixture.launchAction(intention);
myFixture.checkResultByFile(getTestName(false) + ".java", getTestName(false) + "_after.java", true);

View File

@@ -9,7 +9,7 @@
<metaDataContributor implementation="org.jetbrains.plugins.javaFX.fxml.JavaFxNamespaceDataProvider"/>
<xml.schemaProvider implementation="org.jetbrains.plugins.javaFX.fxml.JavaFxSchemaProvider"/>
<psi.referenceContributor implementation="org.jetbrains.plugins.javaFX.fxml.refs.FxmlReferencesContributor"/>
<codeInsight.unresolvedReferenceQuickFixProvider implementation="org.jetbrains.plugins.javaFX.fxml.refs.JavaFxEventHandlerReference$JavaFxUnresolvedReferenceHandlerQuickfixProvider"/>
<codeInsight.unresolvedReferenceQuickFixProvider implementation="org.jetbrains.plugins.javaFX.fxml.refs.JavaFxEventHandlerReferenceQuickFixProvider"/>
<codeInsight.unresolvedReferenceQuickFixProvider implementation="org.jetbrains.plugins.javaFX.fxml.refs.JavaFxTagNameReference$JavaFxUnresolvedTagRefsProvider"/>
<multiHostInjector implementation="org.jetbrains.plugins.javaFX.fxml.ScriptLanguageInjector"/>
<annotator language="XML" implementationClass="org.jetbrains.plugins.javaFX.fxml.refs.JavaFxAnnotator"/>

View File

@@ -15,32 +15,23 @@
*/
package org.jetbrains.plugins.javaFX.fxml.refs;
import com.intellij.codeInsight.daemon.QuickFixActionRegistrar;
import com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixProvider;
import com.intellij.lang.jvm.actions.JvmElementActionFactories;
import com.intellij.lang.jvm.actions.MemberRequest;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.*;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.TypeConversionUtil;
import com.intellij.psi.xml.XmlAttribute;
import com.intellij.psi.xml.XmlAttributeValue;
import com.intellij.util.ArrayUtil;
import com.intellij.util.VisibilityUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.plugins.javaFX.fxml.JavaFxCommonNames;
import org.jetbrains.plugins.javaFX.fxml.JavaFxPsiUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class JavaFxEventHandlerReference extends PsiReferenceBase<XmlAttributeValue> {
private final PsiMethod myEventHandler;
private final PsiClass myController;
final PsiMethod myEventHandler;
final PsiClass myController;
public JavaFxEventHandlerReference(XmlAttributeValue element, final PsiMethod method, PsiClass controller) {
super(element);
@@ -93,70 +84,4 @@ public class JavaFxEventHandlerReference extends PsiReferenceBase<XmlAttributeVa
final TextRange range = super.getRangeInElement();
return new TextRange(range.getStartOffset() + 1, range.getEndOffset());
}
public static class JavaFxUnresolvedReferenceHandlerQuickfixProvider
extends UnresolvedReferenceQuickFixProvider<JavaFxEventHandlerReference> {
@Override
public void registerFixes(@NotNull final JavaFxEventHandlerReference ref, @NotNull final QuickFixActionRegistrar registrar) {
if (ref.myController != null && ref.myEventHandler == null) {
String javaSignature = getHandlerSignature(ref);
PsiMethod javaMethod = JavaPsiFacade.getElementFactory(ref.myController.getProject())
.createMethodFromText(javaSignature, ref.myController);
MemberRequest.Method method =
MemberRequest.simpleMethodRequest(javaMethod.getName(),
Arrays.asList(javaMethod.getAnnotations()),
Arrays.asList(javaMethod.getModifiers()),
javaMethod.getReturnType(),
Arrays.asList(javaMethod.getParameterList().getParameters()));
JvmElementActionFactories.createMethodActions(ref.myController, method).forEach(registrar::register);
}
}
private static String getHandlerSignature(JavaFxEventHandlerReference ref) {
final XmlAttributeValue element = ref.getElement();
PsiType eventType = getEventType(element);
final String modifiers = getModifiers(element.getProject());
return modifiers + " void " + element.getValue().substring(1) + "(" +
eventType.getCanonicalText() + " " + suggestParamName(element.getProject(), eventType) +
")";
}
private static String suggestParamName(Project project, PsiType eventType) {
SuggestedNameInfo suggestedInfo = JavaCodeStyleManager.getInstance(project)
.suggestVariableName(VariableKind.PARAMETER, null, null, eventType);
if (suggestedInfo.names.length < 1) {
return "e";
}
return suggestedInfo.names[0];
}
@NotNull
private static PsiType getEventType(XmlAttributeValue element) {
final PsiElement parent = element.getParent();
if (parent instanceof XmlAttribute) {
final PsiClassType eventType = JavaFxPsiUtil.getDeclaredEventType((XmlAttribute)parent);
if (eventType != null) {
return eventType;
}
}
return PsiType.getTypeByName(JavaFxCommonNames.JAVAFX_EVENT, element.getProject(), element.getResolveScope());
}
@NotNull
private static String getModifiers(@NotNull Project project) {
String visibility = CodeStyleSettingsManager.getSettings(project).getCustomSettings(JavaCodeStyleSettings.class).VISIBILITY;
if (VisibilityUtil.ESCALATE_VISIBILITY.equals(visibility)) visibility = PsiModifier.PRIVATE;
final boolean needAnnotation = !PsiModifier.PUBLIC.equals(visibility);
final String modifier = !PsiModifier.PACKAGE_LOCAL.equals(visibility) ? visibility : "";
return needAnnotation ? "@" + JavaFxCommonNames.JAVAFX_FXML_ANNOTATION + " " + modifier : modifier;
}
@NotNull
@Override
public Class<JavaFxEventHandlerReference> getReferenceClass() {
return JavaFxEventHandlerReference.class;
}
}
}

View File

@@ -0,0 +1,103 @@
package org.jetbrains.plugins.javaFX.fxml.refs
import com.intellij.codeInsight.ExpectedTypeInfo
import com.intellij.codeInsight.ExpectedTypesProvider.createInfo
import com.intellij.codeInsight.TailType
import com.intellij.codeInsight.daemon.QuickFixActionRegistrar
import com.intellij.codeInsight.quickfix.UnresolvedReferenceQuickFixProvider
import com.intellij.lang.jvm.JvmModifier
import com.intellij.lang.jvm.actions.*
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiModifier
import com.intellij.psi.PsiType
import com.intellij.psi.codeStyle.JavaCodeStyleManager
import com.intellij.psi.codeStyle.JavaCodeStyleSettings
import com.intellij.psi.codeStyle.SuggestedNameInfo
import com.intellij.psi.codeStyle.VariableKind
import com.intellij.psi.util.createSmartPointer
import com.intellij.psi.xml.XmlAttribute
import com.intellij.psi.xml.XmlAttributeValue
import com.intellij.util.VisibilityUtil
import org.jetbrains.plugins.javaFX.fxml.JavaFxCommonNames
import org.jetbrains.plugins.javaFX.fxml.JavaFxPsiUtil
class JavaFxEventHandlerReferenceQuickFixProvider : UnresolvedReferenceQuickFixProvider<JavaFxEventHandlerReference>() {
override fun getReferenceClass() = JavaFxEventHandlerReference::class.java
override fun registerFixes(ref: JavaFxEventHandlerReference, registrar: QuickFixActionRegistrar) {
val controller = ref.myController ?: return
if (ref.myEventHandler != null) return
val element = ref.element ?: return
val request = CreateEventHandlerRequest(element)
createMethodActions(controller, request).forEach(registrar::register)
}
}
class CreateEventHandlerRequest(element: XmlAttributeValue) : CreateMethodRequest {
private val myProject = element.project
private val myVisibility = getVisibility(myProject)
private val myPointer = element.createSmartPointer(myProject)
override val isValid: Boolean get() {
val element = myPointer.element
return element != null && element.value != null
}
private val myElement get() = myPointer.element!!
override val methodName: String get() = myElement.value!!.substring(1)
override val returnType: Any? get() {
val typeInfo = createInfo(PsiType.VOID, ExpectedTypeInfo.TYPE_STRICTLY, PsiType.VOID, TailType.NONE)
return arrayOf(typeInfo)
}
override val parameters: List<ExpectedParameter> get() {
val eventType = getEventType(myElement)
val typeInfo = createInfo(eventType, ExpectedTypeInfo.TYPE_STRICTLY, eventType, TailType.NONE)
val nameInfo = suggestParamName(myProject, eventType)
return listOf(ExpectedParameter(nameInfo, arrayOf(typeInfo)))
}
override val modifiers: Collection<JvmModifier> get() = setOf(myVisibility)
override val annotations: Collection<AnnotationRequest> get() {
return if (myVisibility != JvmModifier.PUBLIC) {
listOf(annotationRequest(JavaFxCommonNames.JAVAFX_FXML_ANNOTATION))
}
else {
emptyList()
}
}
}
private fun getVisibility(project: Project): JvmModifier {
val visibility = JavaCodeStyleSettings.getInstance(project).VISIBILITY
if (VisibilityUtil.ESCALATE_VISIBILITY == visibility) return JvmModifier.PRIVATE
if (visibility == PsiModifier.PACKAGE_LOCAL) return JvmModifier.PACKAGE_LOCAL
return JvmModifier.valueOf(visibility.toUpperCase())
}
private fun suggestParamName(project: Project, eventType: PsiType): SuggestedNameInfo {
val codeStyleManager = JavaCodeStyleManager.getInstance(project)!!
val suggestedNameInfo = codeStyleManager.suggestVariableName(VariableKind.PARAMETER, null, null, eventType)
return if (suggestedNameInfo.names.isEmpty()) {
object : SuggestedNameInfo(arrayOf("e")) {}
}
else {
suggestedNameInfo
}
}
private fun getEventType(element: XmlAttributeValue): PsiType {
val parent = element.parent
if (parent is XmlAttribute) {
val eventType = JavaFxPsiUtil.getDeclaredEventType(parent)
if (eventType != null) {
return eventType
}
}
return PsiType.getTypeByName(JavaFxCommonNames.JAVAFX_EVENT, element.project, element.resolveScope)
}

View File

@@ -7,5 +7,6 @@ public class QuickfixHalfRaw {
@FXML TableView<Pair> table;
public void onSort(SortEvent<TableView<Pair>> tableViewSortEvent) {
}
}

View File

@@ -2,5 +2,6 @@ import javafx.scene.control.TableColumn.CellEditEvent;
public class QuickfixNoFieldNested {
public void onColumnEditStart(CellEditEvent cellEditEvent) {
}
}

View File

@@ -3,5 +3,6 @@ import javafx.scene.control.TableView;
public class QuickfixNoField {
public void onSort(SortEvent<TableView> tableViewSortEvent) {
}
}

View File

@@ -6,5 +6,6 @@ public class QuickfixRaw {
@FXML TableView table;
public void onSort(SortEvent<TableView> tableViewSortEvent) {
}
}

View File

@@ -8,5 +8,6 @@ public class QuickfixSpecific {
TableView<Pair<Integer, String>> table;
public void onSort(SortEvent<TableView<Pair<Integer, String>>> tableViewSortEvent) {
}
}

View File

@@ -2,5 +2,6 @@ import javafx.scene.input.MouseEvent;
public class QuickfixSuper{
public void click(MouseEvent mouseEvent) {
}
}

View File

@@ -9,5 +9,6 @@ public class CreateControllerMethodGeneric {
TableView<Map<Integer, String>> table;
public void onSort(SortEvent<TableView<Map<Integer, String>>> tableViewSortEvent) {
}
}

View File

@@ -9,5 +9,6 @@ public class CreateControllerMethodHalfRaw {
TableView<Map> table;
public void onSort(SortEvent<TableView<Map>> tableViewSortEvent) {
}
}

View File

@@ -2,5 +2,6 @@ import javafx.event.ActionEvent;
public class CreateControllerMethod {
public void bar(ActionEvent actionEvent) {
}
}

View File

@@ -4,5 +4,6 @@ import javafx.fxml.FXML;
public class CreateMethod {
@FXML
private void onAction(ActionEvent actionEvent) {
}
}

View File

@@ -4,5 +4,6 @@ import javafx.fxml.FXML;
public class CreateMethod {
@FXML
void onAction(ActionEvent actionEvent) {
}
}

View File

@@ -4,5 +4,6 @@ import javafx.fxml.FXML;
public class CreateMethod {
@FXML
private void onAction(ActionEvent actionEvent) {
}
}

View File

@@ -4,5 +4,6 @@ import javafx.fxml.FXML;
public class CreateMethod {
@FXML
protected void onAction(ActionEvent actionEvent) {
}
}

View File

@@ -2,5 +2,6 @@ import javafx.event.ActionEvent;
public class CreateMethod {
public void onAction(ActionEvent actionEvent) {
}
}