Skip to content

Commit

Permalink
#8: Add support for generating CallMatcher initializer when invoked o…
Browse files Browse the repository at this point in the history
…n method calls
  • Loading branch information
picimako committed May 2, 2023
1 parent 6619c0d commit e1573de
Show file tree
Hide file tree
Showing 8 changed files with 307 additions and 23 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- Added missing `@Override` annotation to `getState()` and `loadState()` methods when converting a class to `PersistentStateComponent`.
- Relaxed the conditions in what the service `getInstance()` generation action is available.
It is now available in interfaces, abstract classes and non-light-service classes as well, and users can also choose the service level.
- [#8](https://github.com/picimako/just-kitting/issues/8): `CallMatcher` initializers can now be generated when invoked on method calls as well.

## [0.1.0]
### Added
Expand Down
6 changes: 4 additions & 2 deletions docs/call_matcher.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,15 @@ In case the selected expression has multiple parent classes, it lets the user ch

![](https://img.shields.io/badge/intention-orange) ![](https://img.shields.io/badge/since-0.1.0-blue) [![](https://img.shields.io/badge/implementation-CallMatchersConversionToAnyOfIntention-blue)](../src/main/java/com/picimako/justkitting/intention/callmatcher/GenerateCallMatcherFromSignatureIntention.java)

This intention generates a `com.siyeh.ig.callMatcher.CallMatcher` initializer call based on the Java class method it is invoked on.
(More specifically, the intention is available on the method's identifier.)
This intention generates a `com.siyeh.ig.callMatcher.CallMatcher` initializer call based on the Java class method, or the Java method call, it is invoked on.
(More specifically, the intention is available on the method's or method call's identifier.)

The generated code is copied to the clipboard, so after pasting it, `CallMatcher` has to be imported manually.

Kotlin or other JVM language methods are not supported yet.

If the called method cannot be resolved, thus the `CallMatcher` cannot be generated, an error hint is shown to inform you.

### Generation logic

If the target method is static, you'll get a code like this:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
/**
* Base type for generating {@link com.siyeh.ig.callMatcher.CallMatcher} initializer calls.
*/
public interface CallMatcherGenerator<T> {
public interface CallMatcherGenerator<METHOD, METHOD_CALL> {

/**
* Generates the CallMatcher initializer for the argument method's signature.
Expand All @@ -17,5 +17,15 @@ public interface CallMatcherGenerator<T> {
* This makes it possible to execute logic after for example a user's
* choice of some sort on the UI.
*/
void generateCallMatcher(T method, Consumer<String> postActions);
void generateCallMatcherForMethod(METHOD method, Consumer<String> postActions);

/**
* Generates the CallMatcher initializer for the argument method call's signature.
*
* @param methodCall the method call to generate a CallMatcher for
* @param postActions any action to execute after the CallMatcher initializer is deleted.
* This makes it possible to execute logic after for example a user's
* choice of some sort on the UI.
*/
void generateCallMatcherForMethodCall(METHOD_CALL methodCall, Consumer<String> postActions);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,17 @@
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiIdentifier;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiMethodCallExpression;
import com.intellij.psi.PsiReferenceExpression;
import com.intellij.util.IncorrectOperationException;
import com.picimako.justkitting.resources.JustKittingBundle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.awt.datatransfer.DataFlavor;

/**
* This intention generates a {@link com.siyeh.ig.callMatcher.CallMatcher} initializer call
* based on the Java class method it is invoked on.
* based on the Java class method, or the Java method call, it is invoked on.
* <p>
* The generated code is copied to the clipboard, so after pasting it, {@code CallMatcher} has
* to be imported manually.
Expand All @@ -48,25 +49,37 @@ public class GenerateCallMatcherFromSignatureIntention implements IntentionActio
public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) {
if (!file.getFileType().equals(JavaFileType.INSTANCE)) return false;

return getMethodSignatureAtCaretOrEmpty(file, editor) != null;
return isPsiMethodOrMethodCall(file, editor);
}

@Nullable
public static PsiMethod getMethodSignatureAtCaretOrEmpty(PsiFile file, Editor editor) {
public static boolean isPsiMethodOrMethodCall(PsiFile file, Editor editor) {
var elementAtCaret = file.findElementAt(editor.getCaretModel().getOffset());
return elementAtCaret instanceof PsiIdentifier && elementAtCaret.getParent() instanceof PsiMethod
? (PsiMethod) elementAtCaret.getParent()
: null;

if (elementAtCaret instanceof PsiIdentifier) {
var parent = elementAtCaret.getParent();
return parent instanceof PsiMethod
|| (parent instanceof PsiReferenceExpression && parent.getParent() instanceof PsiMethodCallExpression);
}

return false;
}

//---- Invocation ----

@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile file) throws IncorrectOperationException {
var targetMethod = (PsiMethod) file.findElementAt(editor.getCaretModel().getOffset()).getParent();
new JavaCallMatcherGenerator(project, editor)
.generateCallMatcher(targetMethod,
callMatcher -> CopyPasteManager.getInstance().setContents(new SimpleTransferable(callMatcher, DataFlavor.stringFlavor)));
var elementAtCaret = file.findElementAt(editor.getCaretModel().getOffset());
if (elementAtCaret.getParent() instanceof PsiMethod) {
var targetMethod = (PsiMethod) elementAtCaret.getParent();
new JavaCallMatcherGenerator(project, editor)
.generateCallMatcherForMethod(targetMethod,
callMatcher -> CopyPasteManager.getInstance().setContents(new SimpleTransferable(callMatcher, DataFlavor.stringFlavor)));
} else {
var targetMethodCall = (PsiMethodCallExpression) elementAtCaret.getParent().getParent();
new JavaCallMatcherGenerator(project, editor)
.generateCallMatcherForMethodCall(targetMethodCall,
callMatcher -> CopyPasteManager.getInstance().setContents(new SimpleTransferable(callMatcher, DataFlavor.stringFlavor)));
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@
import com.intellij.openapi.ui.popup.PopupStep;
import com.intellij.openapi.ui.popup.util.BaseListPopupStep;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiMethodCallExpression;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.PsiParameterList;
import com.intellij.refactoring.util.CommonRefactoringUtil;
import com.intellij.ui.SimpleListCellRenderer;
import com.picimako.justkitting.resources.JustKittingBundle;
import lombok.RequiredArgsConstructor;
Expand All @@ -33,12 +35,20 @@
* @since 0.1.0
*/
@RequiredArgsConstructor
public class JavaCallMatcherGenerator implements CallMatcherGenerator<PsiMethod> {
public class JavaCallMatcherGenerator implements CallMatcherGenerator<PsiMethod, PsiMethodCallExpression> {
private final Project project;
private final Editor editor;

@Override
public void generateCallMatcher(PsiMethod method, Consumer<String> postActions) {
public void generateCallMatcherForMethod(@Nullable PsiMethod method, Consumer<String> postActions) {
if (method == null) {
CommonRefactoringUtil.showErrorHint(project, editor,
JustKittingBundle.message("intention.call.matcher.could.not.resolve.method.message"),
JustKittingBundle.message("intention.call.matcher.could.not.resolve.method.title"),
"");
return;
}

//In case of a static method it generates {@code CallMatcher.staticCall()}.
if (method.getModifierList().hasModifierProperty(PsiModifier.STATIC)) {
postActions.accept(generateCallMatcher(method, STATIC_CALL));
Expand All @@ -63,6 +73,11 @@ public void generateCallMatcher(PsiMethod method, Consumer<String> postActions)
}
}

@Override
public void generateCallMatcherForMethodCall(PsiMethodCallExpression methodCall, Consumer<String> postActions) {
generateCallMatcherForMethod(methodCall.resolveMethod(), postActions);
}

private String generateCallMatcher(PsiMethod method, String callType) {
//Initializer: CallMatcher.instanceCall(" or CallMatcher.staticCall("
var callMatcher = new StringBuilder("CallMatcher.").append(callType).append("(\"")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<body>
<!-- tooltip end -->
This intention generates a <code>com.siyeh.ig.callMatcher.CallMatcher</code> initializer call
based on the Java class method it is invoked on.
based on the Java class method, or the Java method call, it is invoked on.
<p>
The intention is available on the method's identifier.
The intention is available on the method's or method call's identifier.
<p>
The generated code is copied to the clipboard, so after pasting it, <code>CallMatcher</code> has
to be imported manually. It will be something like this:
Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/messages/JustKittingBundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ intention.call.matcher.family.name=Combine CallMatchers to CallMatcher.anyOf()
intention.call.matcher.combine.to.any.of.select.class=Select Target Class
intention.call.matcher.generate.from.signature=Generate CallMatcher
intention.call.matcher.select.instance.call.type=Select Instance Call Type
intention.call.matcher.could.not.resolve.method.title=Generate CallMatcher
intention.call.matcher.could.not.resolve.method.message=Can't generate CallMatcher. The referenced method could not be resolved.

# UI
intention.ui.generate.multi.checkbox.panel=MultipleCheckboxOptionsPanel...
Expand Down
Loading

0 comments on commit e1573de

Please sign in to comment.