11package de .espend .idea .php .phpunit .intention ;
22
33import com .intellij .codeInsight .hint .HintManager ;
4+ import com .intellij .codeInsight .intention .HighPriorityAction ;
45import com .intellij .codeInsight .intention .PsiElementBaseIntentionAction ;
6+ import com .intellij .codeInsight .intention .preview .IntentionPreviewUtils ;
57import com .intellij .openapi .command .WriteCommandAction ;
68import com .intellij .openapi .editor .Editor ;
79import com .intellij .openapi .editor .ScrollType ;
810import com .intellij .openapi .project .Project ;
911import com .intellij .openapi .ui .popup .JBPopupFactory ;
12+ import com .intellij .openapi .util .Iconable ;
1013import com .intellij .psi .PsiElement ;
1114import com .intellij .psi .util .PsiTreeUtil ;
12- import com .intellij .ui .components .JBList ;
1315import com .intellij .util .IncorrectOperationException ;
1416import com .jetbrains .php .PhpIndex ;
1517import com .jetbrains .php .lang .psi .PhpPsiElementFactory ;
1618import com .jetbrains .php .lang .psi .elements .*;
19+ import de .espend .idea .php .phpunit .PhpUnitIcons ;
1720import de .espend .idea .php .phpunit .utils .processor .MethodReferenceNameProcessor ;
1821import org .jetbrains .annotations .Nls ;
1922import org .jetbrains .annotations .NotNull ;
2023import org .jetbrains .annotations .Nullable ;
2124
22- import java . util . Set ;
23- import java .util .TreeSet ;
25+ import javax . swing .* ;
26+ import java .util .* ;
2427import java .util .stream .Collectors ;
2528
2629/**
2932 *
3033 * @author Daniel Espendiller <daniel@espendiller.net>
3134 */
32- public class AddMockMethodIntention extends PsiElementBaseIntentionAction {
35+ public class AddMockMethodIntention extends PsiElementBaseIntentionAction implements Iconable , HighPriorityAction {
3336 @ Override
3437 public void invoke (@ NotNull Project project , Editor editor , @ NotNull PsiElement psiElement ) throws IncorrectOperationException {
3538 String parameter = getMockInstanceFromMethodReferenceScope (psiElement );
3639
3740 if (parameter == null ) {
38- HintManager .getInstance ().showErrorHint (editor , "No mock context found" );
41+ if (!IntentionPreviewUtils .isPreviewElement (psiElement )) {
42+ HintManager .getInstance ().showErrorHint (editor , "No mock context found" );
43+ }
44+
3945 return ;
4046 }
4147
4248 Set <String > methods = new TreeSet <>();
4349 for (PhpClass phpClass : PhpIndex .getInstance (psiElement .getProject ()).getAnyByFQN (parameter )) {
4450 methods .addAll (phpClass .getMethods ().stream ()
45- .filter (method -> method .getAccess ().isPublic () && !method .getName ().startsWith ("__" ))
51+ .filter (method -> method .getAccess ().isPublic () && !method .getName ().startsWith ("__" ) && ! method . isStatic () && ! method . isFinal () )
4652 .map (PhpNamedElement ::getName ).collect (Collectors .toSet ())
4753 );
4854 }
4955
5056 if (methods .size () == 0 ) {
51- HintManager .getInstance ().showErrorHint (editor , "No public method found" );
57+ if (!IntentionPreviewUtils .isPreviewElement (psiElement )) {
58+ HintManager .getInstance ().showErrorHint (editor , "No public method found" );
59+ }
60+
61+ return ;
62+ }
63+
64+ if (IntentionPreviewUtils .isPreviewElement (psiElement )) {
65+ new MyMockWriteCommand (editor , methods , psiElement , false ).run ();
66+
5267 return ;
5368 }
5469
5570 // Single item direct execution without selection
5671 if (methods .size () == 1 ) {
57- new MyMockWriteCommand (editor , methods .iterator ().next (), psiElement ).execute ();
72+ WriteCommandAction .runWriteCommandAction (
73+ psiElement .getProject (),
74+ getText (),
75+ "" ,
76+ new MyMockWriteCommand (editor , new ArrayList <>(methods ), psiElement , true ),
77+ psiElement .getContainingFile ()
78+ );
79+
5880 return ;
5981 }
6082
61- final JBList <String > list = new JBList <>(methods );
83+ final List <String > list = new ArrayList <>(methods );
6284
63- JBPopupFactory .getInstance ().createListPopupBuilder (list )
85+ JBPopupFactory .getInstance ().createPopupChooserBuilder (list )
6486 .setTitle ("PHPUnit: Mock Method" )
65- .setItemChoosenCallback (() -> new MyMockWriteCommand (editor , list .getSelectedValue (), psiElement ).execute ())
87+ .setItemsChosenCallback (strings -> WriteCommandAction .runWriteCommandAction (
88+ psiElement .getProject (),
89+ getText (),
90+ "" ,
91+ new MyMockWriteCommand (editor , new ArrayList <>(strings ), psiElement , true ),
92+ psiElement .getContainingFile ()
93+ ))
6694 .createPopup ()
6795 .showInBestPositionFor (editor );
6896
@@ -115,25 +143,25 @@ public boolean startInWriteAction() {
115143 return true ;
116144 }
117145
118- private static class MyMockWriteCommand extends WriteCommandAction . Simple {
146+ private static class MyMockWriteCommand implements Runnable {
119147 @ NotNull
120148 private final Editor editor ;
121149
122150 @ NotNull
123- private final String selectedValue ;
151+ private final Collection < String > selectedValues ;
124152
125153 @ NotNull
126154 private final PsiElement psiElement ;
155+ private final boolean jumpToLastElement ;
127156
128- private MyMockWriteCommand (@ NotNull Editor editor , @ NotNull String selectedValue , @ NotNull PsiElement psiElement ) {
129- super (editor .getProject (), "Add method mock" );
157+ private MyMockWriteCommand (@ NotNull Editor editor , @ NotNull Collection <String > selectedValues , @ NotNull PsiElement psiElement , boolean jumpToLastElement ) {
130158 this .editor = editor ;
131- this .selectedValue = selectedValue ;
159+ this .selectedValues = selectedValues ;
132160 this .psiElement = psiElement ;
161+ this .jumpToLastElement = jumpToLastElement ;
133162 }
134163
135- @ Override
136- protected void run () {
164+ public void run () {
137165 Statement statement = PsiTreeUtil .getParentOfType (psiElement , Statement .class );
138166 if (statement == null ) {
139167 HintManager .getInstance ().showErrorHint (editor , "No mock context found" );
@@ -149,25 +177,41 @@ protected void run() {
149177 // $foobar
150178 String prefix = childOfAnyType .getText ();
151179
152- Statement methodReference = PhpPsiElementFactory .createStatement (
153- psiElement .getProject (),
154- String .format ("%s->method('%s')->willReturn();" , prefix , selectedValue )
155- );
180+ PsiElement elementJumpTo = null ;
156181
157- PsiElement add = statement .add (methodReference );
182+ for (String selectedValue : selectedValues ) {
183+ Project project = psiElement .getProject ();
158184
159- for (MethodReference reference : PsiTreeUtil .getChildrenOfTypeAsList (add , MethodReference .class )) {
160- if (!"willReturn" .equals (reference .getName ())) {
161- continue ;
162- }
185+ Statement methodReference = PhpPsiElementFactory .createStatement (
186+ project ,
187+ String .format ("%s->method('%s')->willReturn();" , prefix , selectedValue )
188+ );
189+
190+ elementJumpTo = statement .add (methodReference );
191+ statement .add (PhpPsiElementFactory .createNewLine (project ));
192+ }
193+
194+ if (this .jumpToLastElement && elementJumpTo != null ) {
195+ for (MethodReference reference : PsiTreeUtil .getChildrenOfTypeAsList (elementJumpTo , MethodReference .class )) {
196+ if (!"willReturn" .equals (reference .getName ())) {
197+ continue ;
198+ }
163199
164- PsiElement lastChild = reference .getLastChild ();
165- if (lastChild != null ) {
166- editor .getCaretModel ().moveToOffset (lastChild .getTextRange ().getStartOffset ());
167- editor .getScrollingModel ().scrollToCaret (ScrollType .RELATIVE );
200+ PsiElement lastChild = reference .getLastChild ();
201+ if (lastChild != null ) {
202+ editor .getCaretModel ().moveToOffset (lastChild .getTextRange ().getStartOffset ());
203+ editor .getScrollingModel ().scrollToCaret (ScrollType .RELATIVE );
204+ }
205+
206+ break ;
168207 }
169- return ;
170208 }
209+
171210 }
172211 }
212+
213+ @ Override
214+ public Icon getIcon (int flags ) {
215+ return PhpUnitIcons .PHPUNIT ;
216+ }
173217}
0 commit comments