Skip to content

Commit

Permalink
Qickfixes for add/remove typeTags for fields and methods
Browse files Browse the repository at this point in the history
  • Loading branch information
m0rkeulv committed Nov 5, 2023
1 parent 9a904a5 commit 2941a1a
Show file tree
Hide file tree
Showing 9 changed files with 356 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -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);
}
}



}
Original file line number Diff line number Diff line change
@@ -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<HaxeLocalVarDeclaration> 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);
}
}


}
Original file line number Diff line number Diff line change
@@ -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);
}
}

}
Original file line number Diff line number Diff line change
@@ -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<HaxeLocalVarDeclaration> 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);
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,21 @@ public static PsiElement getParent(@Nullable PsiElement element, @NotNull IEleme
return null;
}

@Nullable
public static <T extends PsiElement, K extends PsiElement> T findParentOfTypeButStopIfTypeIs(PsiElement element, @NotNull Class<T> toFind, @NotNull Class<K> 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<PsiElement> getPathToParentOfType(@Nullable PsiElement element,
@NotNull Class<? extends PsiElement> aClass) {
Expand Down
20 changes: 20 additions & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,26 @@
<className>com.intellij.plugins.haxe.ide.intention.ConvertQuotesIntention</className>
</intentionAction>

<intentionAction>
<language>Haxe</language>
<className>com.intellij.plugins.haxe.ide.intention.AddTypeTagToFieldIntention</className>
</intentionAction>

<intentionAction>
<language>Haxe</language>
<className>com.intellij.plugins.haxe.ide.intention.RemoveTypeTagFromFieldIntention</className>
</intentionAction>

<intentionAction>
<language>Haxe</language>
<className>com.intellij.plugins.haxe.ide.intention.AddReturnTypeTagIntention</className>
</intentionAction>

<intentionAction>
<language>Haxe</language>
<className>com.intellij.plugins.haxe.ide.intention.RemoveReturnTypeTagIntention</className>
</intentionAction>

<toolWindow id="Haxelib"
anchor="bottom"
canCloseContents="true"
Expand Down
5 changes: 5 additions & 0 deletions src/main/resources/messages/HaxeBundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,11 @@ haxe.quickfix.wrap.expression.with.parenthesis=Wrap expression with parenthesis.
haxe.quickfix.wrap.left.hand.side.with.parenthesis=Wrap left-hand side with parenthesis.
haxe.quickfix.missing.semi.colon=Add missing semicolon.

haxe.quickfix.add.type.tag=Add Type to definition
haxe.quickfix.remove.type.tag=Remove Type from definition
haxe.quickfix.add.return.type=Add return Type to definition
haxe.quickfix.remove.return.type=Remove return Type from definition

# HAXE parameter info helper
haxe.parameter.info.helper.no.parameters=No parameters
haxelib.dependency.list.prefix=Dependency of
Expand Down

0 comments on commit 2941a1a

Please sign in to comment.