diff --git a/src/main/java/com/intellij/plugins/haxe/ide/intention/AddReturnTypeTagIntention.java b/src/main/java/com/intellij/plugins/haxe/ide/intention/AddReturnTypeTagIntention.java new file mode 100644 index 000000000..35b777960 --- /dev/null +++ b/src/main/java/com/intellij/plugins/haxe/ide/intention/AddReturnTypeTagIntention.java @@ -0,0 +1,87 @@ +package com.intellij.plugins.haxe.ide.intention; + +import com.intellij.codeInsight.intention.impl.BaseIntentionAction; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.plugins.haxe.HaxeBundle; +import com.intellij.plugins.haxe.HaxeLanguage; +import com.intellij.plugins.haxe.lang.psi.*; +import com.intellij.plugins.haxe.model.type.HaxeGenericResolver; +import com.intellij.plugins.haxe.model.type.ResultHolder; +import com.intellij.plugins.haxe.model.type.SpecificTypeReference; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.PsiParameterList; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +import static com.intellij.plugins.haxe.util.HaxeElementGenerator.createTypeTag; +import static com.intellij.plugins.haxe.util.UsefulPsiTreeUtil.findParentOfTypeButStopIfTypeIs; + +public class AddReturnTypeTagIntention extends BaseIntentionAction { + + private HaxeMethod myMethod; + private ResultHolder returnType; + + public AddReturnTypeTagIntention() { + } + + @Nls + @NotNull + @Override + public String getFamilyName() { + return HaxeBundle.message("quick.fixes.family"); + } + + @NotNull + @Override + public String getText() { + return HaxeBundle.message("haxe.quickfix.add.return.type"); + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + if (file.getLanguage() != HaxeLanguage.INSTANCE) return false; + + attemptToFindMethod(editor, file); + + boolean isMissingTypeTag = myMethod != null + && !myMethod.isConstructor() + && myMethod instanceof HaxeMethodDeclaration declaration + && declaration.getTypeTag() == null; + if (isMissingTypeTag) { + HaxeGenericResolver resolver = myMethod.getModel().getGenericResolver(null); + resolver = resolver.withTypeParametersAsType(myMethod.getModel().getGenericParams()); + returnType = myMethod.getModel().getReturnType(resolver); + return !(returnType == null); + } + return false; + } + + + @Override + public void invoke(@NotNull final Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + + String typeText = returnType.isUnknown() ? SpecificTypeReference.VOID : returnType.getType().toPresentationString(); + HaxeTypeTag tag = createTypeTag(project, typeText); + PsiParameterList list = myMethod.getParameterList(); + PsiElement element = PsiTreeUtil.nextVisibleLeaf(list); + myMethod.addAfter(tag, element); + + } + + + private void attemptToFindMethod(Editor editor, PsiFile file) { + PsiElement place = file.findElementAt(editor.getCaretModel().getOffset()); + if (place instanceof HaxeMethod method) { + myMethod = method; + }else { + myMethod = findParentOfTypeButStopIfTypeIs(place, HaxeMethod.class, HaxeBlockStatement.class); + } + } + + + +} \ No newline at end of file diff --git a/src/main/java/com/intellij/plugins/haxe/ide/intention/AddTypeTagToFieldIntention.java b/src/main/java/com/intellij/plugins/haxe/ide/intention/AddTypeTagToFieldIntention.java new file mode 100644 index 000000000..bf7623f2d --- /dev/null +++ b/src/main/java/com/intellij/plugins/haxe/ide/intention/AddTypeTagToFieldIntention.java @@ -0,0 +1,85 @@ +package com.intellij.plugins.haxe.ide.intention; + +import com.intellij.codeInsight.intention.impl.BaseIntentionAction; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.plugins.haxe.HaxeBundle; +import com.intellij.plugins.haxe.HaxeLanguage; +import com.intellij.plugins.haxe.lang.psi.HaxeLocalVarDeclaration; +import com.intellij.plugins.haxe.lang.psi.HaxeLocalVarDeclarationList; +import com.intellij.plugins.haxe.lang.psi.HaxePsiField; +import com.intellij.plugins.haxe.lang.psi.HaxeTypeTag; +import com.intellij.plugins.haxe.model.type.HaxeExpressionEvaluator; +import com.intellij.plugins.haxe.model.type.HaxeExpressionEvaluatorContext; +import com.intellij.plugins.haxe.model.type.ResultHolder; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +import static com.intellij.plugins.haxe.util.HaxeElementGenerator.createTypeTag; + +public class AddTypeTagToFieldIntention extends BaseIntentionAction { + + private HaxePsiField myField; + private ResultHolder type; + + public AddTypeTagToFieldIntention() { + } + + @Nls + @NotNull + @Override + public String getFamilyName() { + return HaxeBundle.message("quick.fixes.family"); + } + + @NotNull + @Override + public String getText() { + return HaxeBundle.message("haxe.quickfix.add.type.tag"); + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + if (file.getLanguage() != HaxeLanguage.INSTANCE) return false; + + attemptToFindField(editor, file); + + boolean isMissingTypeTag = myField != null && myField.getTypeTag() == null; + if (isMissingTypeTag) { + type = HaxeExpressionEvaluator.evaluate(myField, new HaxeExpressionEvaluatorContext(myField), null).result; + return !(type == null || type.isUnknown()); + } + return false; + } + + + @Override + public void invoke(@NotNull final Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + + HaxeTypeTag tag = createTypeTag(project, type.getType().toString()); + myField.addAfter(tag, myField.getComponentName()); + + } + + + private void attemptToFindField(Editor editor, PsiFile file) { + PsiElement place = file.findElementAt(editor.getCaretModel().getOffset()); + HaxeLocalVarDeclarationList varDeclarationList = PsiTreeUtil.getParentOfType(place, HaxeLocalVarDeclarationList.class); + if (varDeclarationList != null) { + List list = varDeclarationList.getLocalVarDeclarationList(); + if (!list.isEmpty())myField = list.get(list.size() - 1); + } else if (place instanceof HaxePsiField psiField) { + myField = psiField; + }else { + myField = PsiTreeUtil.getParentOfType(place, HaxePsiField.class); + } + } + + +} \ No newline at end of file diff --git a/src/main/java/com/intellij/plugins/haxe/ide/intention/RemoveReturnTypeTagIntention.java b/src/main/java/com/intellij/plugins/haxe/ide/intention/RemoveReturnTypeTagIntention.java new file mode 100644 index 000000000..282f5afb2 --- /dev/null +++ b/src/main/java/com/intellij/plugins/haxe/ide/intention/RemoveReturnTypeTagIntention.java @@ -0,0 +1,67 @@ +package com.intellij.plugins.haxe.ide.intention; + +import com.intellij.codeInsight.intention.impl.BaseIntentionAction; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.plugins.haxe.HaxeBundle; +import com.intellij.plugins.haxe.HaxeLanguage; +import com.intellij.plugins.haxe.lang.psi.HaxeBlockStatement; +import com.intellij.plugins.haxe.lang.psi.HaxeMethod; +import com.intellij.plugins.haxe.lang.psi.HaxeMethodDeclaration; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +import static com.intellij.plugins.haxe.util.UsefulPsiTreeUtil.findParentOfTypeButStopIfTypeIs; + +public class RemoveReturnTypeTagIntention extends BaseIntentionAction { + + private HaxeMethod myMethod; + + public RemoveReturnTypeTagIntention() { + } + + @Nls + @NotNull + @Override + public String getFamilyName() { + return HaxeBundle.message("quick.fixes.family"); + } + @NotNull + @Override + public String getText() { + return HaxeBundle.message("haxe.quickfix.remove.return.type"); + } + + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + if (file.getLanguage() != HaxeLanguage.INSTANCE) return false; + + attemptToFindMethod(editor, file); + + return myMethod instanceof HaxeMethodDeclaration declaration && declaration.getTypeTag() != null; + } + + + @Override + public void invoke(@NotNull final Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + + HaxeMethodDeclaration declaration = (HaxeMethodDeclaration)myMethod; + declaration.getTypeTag().delete(); + } + + + private void attemptToFindMethod(Editor editor, PsiFile file) { + PsiElement place = file.findElementAt(editor.getCaretModel().getOffset()); + if (place instanceof HaxeMethod method) { + myMethod = method; + } + else { + myMethod = findParentOfTypeButStopIfTypeIs(place, HaxeMethod.class, HaxeBlockStatement.class); + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/intellij/plugins/haxe/ide/intention/RemoveTypeTagFromFieldIntention.java b/src/main/java/com/intellij/plugins/haxe/ide/intention/RemoveTypeTagFromFieldIntention.java new file mode 100644 index 000000000..eb4785829 --- /dev/null +++ b/src/main/java/com/intellij/plugins/haxe/ide/intention/RemoveTypeTagFromFieldIntention.java @@ -0,0 +1,67 @@ +package com.intellij.plugins.haxe.ide.intention; + +import com.intellij.codeInsight.intention.impl.BaseIntentionAction; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.plugins.haxe.HaxeBundle; +import com.intellij.plugins.haxe.HaxeLanguage; +import com.intellij.plugins.haxe.lang.psi.HaxeLocalVarDeclaration; +import com.intellij.plugins.haxe.lang.psi.HaxeLocalVarDeclarationList; +import com.intellij.plugins.haxe.lang.psi.HaxePsiField; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class RemoveTypeTagFromFieldIntention extends BaseIntentionAction { + + private HaxePsiField myField; + + + @Nls + @NotNull + @Override + public String getFamilyName() { + return HaxeBundle.message("quick.fixes.family"); + } + @NotNull + @Override + public String getText() { + return HaxeBundle.message("haxe.quickfix.remove.type.tag"); + } + + @Override + public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { + if (file.getLanguage() != HaxeLanguage.INSTANCE) return false; + + attemptToFindField(editor, file); + + return myField != null && myField.getTypeTag() != null; + } + + + @Override + public void invoke(@NotNull final Project project, Editor editor, PsiFile file) throws IncorrectOperationException { + myField.getTypeTag().delete(); + } + + + private void attemptToFindField(Editor editor, PsiFile file) { + PsiElement place = file.findElementAt(editor.getCaretModel().getOffset()); + HaxeLocalVarDeclarationList varDeclarationList = PsiTreeUtil.getParentOfType(place, HaxeLocalVarDeclarationList.class); + if (varDeclarationList != null) { + List list = varDeclarationList.getLocalVarDeclarationList(); + if (!list.isEmpty())myField = list.get(list.size() - 1); + } else if (place instanceof HaxePsiField psiField) { + myField = psiField; + }else { + myField = PsiTreeUtil.getParentOfType(place, HaxePsiField.class); + } + } + + +} \ No newline at end of file diff --git a/src/main/java/com/intellij/plugins/haxe/ide/intention/ReplaceImportStatementWithWildcardWithSingleClassImports.java b/src/main/java/com/intellij/plugins/haxe/ide/intention/ReplaceImportStatementWithWildcardWithSingleClassImports.java index e7e3c6236..29dd49c6c 100644 --- a/src/main/java/com/intellij/plugins/haxe/ide/intention/ReplaceImportStatementWithWildcardWithSingleClassImports.java +++ b/src/main/java/com/intellij/plugins/haxe/ide/intention/ReplaceImportStatementWithWildcardWithSingleClassImports.java @@ -74,7 +74,7 @@ public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws .map(element -> HaxeImportUtil.exposeReference(importStatement, element)) .filter(Objects::nonNull) .distinct() - .collect(Collectors.toList()); + .toList(); newImports.forEach(elementToImport -> { if (elementToImport instanceof HaxeModelTarget) { diff --git a/src/main/java/com/intellij/plugins/haxe/util/HaxeElementGenerator.java b/src/main/java/com/intellij/plugins/haxe/util/HaxeElementGenerator.java index e43c1d2e5..47368dd08 100644 --- a/src/main/java/com/intellij/plugins/haxe/util/HaxeElementGenerator.java +++ b/src/main/java/com/intellij/plugins/haxe/util/HaxeElementGenerator.java @@ -70,6 +70,15 @@ public static HaxeFieldDeclaration createVarDeclaration(Project myProject, Strin assert haxeClass != null; return haxeClass.getFieldSelf(null).iterator().next(); } + public static HaxeTypeTag createTypeTag(Project myProject, String text) { + var statment = "var someVar:" +text; + final HaxeFile dummyFile = createDummyFile(myProject, HaxeCodeGenerateUtil.wrapFunction(statment).getFirst()); + final HaxeModule haxeModule = PsiTreeUtil.getChildOfType(dummyFile, HaxeModule.class); + final HaxeClass haxeClass = PsiTreeUtil.getChildOfType(haxeModule, HaxeClass.class); + assert haxeClass != null; + HaxeFieldDeclaration next = haxeClass.getFieldSelf(null).iterator().next(); + return next.getTypeTag(); + } // XXX: Eventually, this ordering should come from the class order in // preferences... once we have one. diff --git a/src/main/java/com/intellij/plugins/haxe/util/UsefulPsiTreeUtil.java b/src/main/java/com/intellij/plugins/haxe/util/UsefulPsiTreeUtil.java index 457d41401..a87e3e317 100644 --- a/src/main/java/com/intellij/plugins/haxe/util/UsefulPsiTreeUtil.java +++ b/src/main/java/com/intellij/plugins/haxe/util/UsefulPsiTreeUtil.java @@ -213,6 +213,21 @@ public static PsiElement getParent(@Nullable PsiElement element, @NotNull IEleme return null; } + @Nullable + public static T findParentOfTypeButStopIfTypeIs(PsiElement element, @NotNull Class toFind, @NotNull Class toStop) { + PsiElement parent = element.getParent(); + while (parent != null) { + if (toFind.isInstance(parent)) { + return (T)parent; + }else if(toStop.isInstance(parent)) { + return null; + } + parent = parent.getParent(); + } + return null; + } + + @Nullable public static List getPathToParentOfType(@Nullable PsiElement element, @NotNull Class aClass) { diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index ad1c445fc..85bfa1c53 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -391,6 +391,26 @@ com.intellij.plugins.haxe.ide.intention.ConvertQuotesIntention + + Haxe + com.intellij.plugins.haxe.ide.intention.AddTypeTagToFieldIntention + + + + Haxe + com.intellij.plugins.haxe.ide.intention.RemoveTypeTagFromFieldIntention + + + + Haxe + com.intellij.plugins.haxe.ide.intention.AddReturnTypeTagIntention + + + + Haxe + com.intellij.plugins.haxe.ide.intention.RemoveReturnTypeTagIntention + +