Skip to content

Commit 243f7b8

Browse files
authored
#28 Set caret position to empty source attribute after quick fix
1 parent 3ea9b63 commit 243f7b8

File tree

6 files changed

+159
-11
lines changed

6 files changed

+159
-11
lines changed

src/main/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesInspection.java

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -185,15 +185,18 @@ private static class UnmappedTargetPropertyFix extends LocalQuickFixOnPsiElement
185185
private final String myText;
186186
private final String myFamilyName;
187187
private final Supplier<Collection<PsiAnnotation>> myAnnotationSupplier;
188+
private final boolean myMoveCaretToEmptySourceAttribute;
188189

189190
private UnmappedTargetPropertyFix(@NotNull PsiMethod modifierListOwner,
190191
@NotNull String text,
191192
@NotNull String familyName,
192-
@NotNull Supplier<Collection<PsiAnnotation>> annotationSupplier) {
193+
@NotNull Supplier<Collection<PsiAnnotation>> annotationSupplier,
194+
boolean moveCaretToEmptySourceAttribute) {
193195
super( modifierListOwner );
194196
myText = text;
195197
myFamilyName = familyName;
196198
myAnnotationSupplier = annotationSupplier;
199+
myMoveCaretToEmptySourceAttribute = moveCaretToEmptySourceAttribute;
197200
}
198201

199202
@NotNull
@@ -232,7 +235,7 @@ public void invoke(@NotNull Project project,
232235
PsiMethod mappingMethod = (PsiMethod) startElement;
233236

234237
for ( PsiAnnotation annotation : myAnnotationSupplier.get() ) {
235-
addMappingAnnotation( project, mappingMethod, annotation );
238+
addMappingAnnotation( project, mappingMethod, annotation, myMoveCaretToEmptySourceAttribute );
236239
}
237240
}
238241

@@ -272,7 +275,8 @@ private static UnmappedTargetPropertyFix createAddUnmappedTargetPropertyFix(PsiM
272275
method,
273276
message,
274277
MapStructBundle.message( "intention.add.unmapped.target.property" ),
275-
new UnmappedTargetPropertyFixAnnotationSupplier( method, target )
278+
new UnmappedTargetPropertyFixAnnotationSupplier( method, target ),
279+
true
276280
);
277281
}
278282

@@ -296,7 +300,8 @@ private static UnmappedTargetPropertyFix createAddIgnoreUnmappedTargetPropertyFi
296300
method,
297301
message,
298302
MapStructBundle.message( "intention.add.ignore.unmapped.target.property" ),
299-
annotationSupplier
303+
annotationSupplier,
304+
false
300305
);
301306
}
302307

@@ -328,7 +333,8 @@ private static UnmappedTargetPropertyFix createAddIgnoreAllUnmappedTargetPropert
328333
method,
329334
message,
330335
MapStructBundle.message( "intention.add.ignore.all.unmapped.target.properties" ),
331-
annotationSupplier
336+
annotationSupplier,
337+
false
332338
);
333339
}
334340

src/main/java/org/mapstruct/intellij/util/MapstructAnnotationUtils.java

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,18 @@
1414
import com.intellij.codeInsight.MetaAnnotationUtil;
1515
import com.intellij.openapi.command.WriteCommandAction;
1616
import com.intellij.openapi.command.undo.UndoUtil;
17+
import com.intellij.openapi.editor.Editor;
18+
import com.intellij.openapi.editor.ScrollType;
19+
import com.intellij.openapi.fileEditor.FileEditor;
20+
import com.intellij.openapi.fileEditor.FileEditorManager;
21+
import com.intellij.openapi.fileEditor.TextEditor;
1722
import com.intellij.openapi.module.LanguageLevelUtil;
1823
import com.intellij.openapi.module.Module;
1924
import com.intellij.openapi.module.ModuleUtilCore;
2025
import com.intellij.openapi.project.Project;
2126
import com.intellij.openapi.util.Pair;
27+
import com.intellij.openapi.util.TextRange;
28+
import com.intellij.openapi.util.text.Strings;
2229
import com.intellij.pom.java.LanguageLevel;
2330
import com.intellij.psi.JavaPsiFacade;
2431
import com.intellij.psi.PsiAnnotation;
@@ -62,8 +69,9 @@ private MapstructAnnotationUtils() {
6269
* @param mappingAnnotation the {@link org.mapstruct.Mapping} annotation
6370
*/
6471
public static void addMappingAnnotation(@NotNull Project project,
65-
@NotNull PsiMethod mappingMethod,
66-
@NotNull PsiAnnotation mappingAnnotation) {
72+
@NotNull PsiMethod mappingMethod,
73+
@NotNull PsiAnnotation mappingAnnotation,
74+
boolean moveCaretToEmptySourceAttribute) {
6775
Pair<PsiAnnotation, Optional<PsiAnnotation>> mappingsPair = findOrCreateMappingsAnnotation(
6876
project,
6977
mappingMethod
@@ -81,7 +89,13 @@ public static void addMappingAnnotation(@NotNull Project project,
8189
if ( containerAnnotation != null && containerAnnotation.isPhysical() ) {
8290
runWriteCommandAction(
8391
project,
84-
() -> containerAnnotation.replace( newAnnotation ),
92+
() -> {
93+
PsiElement replaced = containerAnnotation.replace( newAnnotation );
94+
95+
if ( moveCaretToEmptySourceAttribute ) {
96+
moveCaretToEmptySourceAttribute( project, replaced );
97+
}
98+
},
8599
containingFile
86100
);
87101
}
@@ -108,13 +122,36 @@ public static void addMappingAnnotation(@NotNull Project project,
108122
mappingMethod.getModifierList()
109123
);
110124
JavaCodeStyleManager.getInstance( project ).shortenClassReferences( inserted );
125+
126+
if ( moveCaretToEmptySourceAttribute ) {
127+
moveCaretToEmptySourceAttribute( project, inserted );
128+
}
129+
111130
}, containingFile );
112131

113132
UndoUtil.markPsiFileForUndo( containingFile );
114133
}
115134
}
116135
}
117136

137+
private static void moveCaretToEmptySourceAttribute(@NotNull Project project, PsiElement element) {
138+
139+
FileEditor selectedEditor = FileEditorManager.getInstance( project ).getSelectedEditor();
140+
if ( selectedEditor instanceof TextEditor ) {
141+
Editor editor = ( (TextEditor) selectedEditor ).getEditor();
142+
143+
TextRange textRange = element.getTextRange();
144+
String textOfElement = String.valueOf( editor.getDocument()
145+
.getCharsSequence()
146+
.subSequence( textRange.getStartOffset(), textRange.getEndOffset() ) );
147+
int indexOfEmptySourceAttribute = Strings.indexOf( textOfElement, "\"\"" ) + 1;
148+
149+
editor.getCaretModel().moveToOffset( textRange.getStartOffset() + indexOfEmptySourceAttribute );
150+
editor.getScrollingModel().scrollToCaret( ScrollType.MAKE_VISIBLE );
151+
}
152+
153+
}
154+
118155
/**
119156
* This methods looks for the {@link org.mapstruct.Mappings} annotation on the given {@code mappingMethod},
120157
* if the annotation was not found and the {@link org.mapstruct.Mapping} repeatable annotation cannot be used

src/test/java/org/mapstruct/intellij/inspection/BaseInspectionTest.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@ protected void setUp() throws Exception {
2929

3030
protected void doTest() {
3131
String testName = getTestName( false );
32-
configureByFile( testName + ".java" );
32+
doTest( testName + ".java" );
33+
}
34+
35+
protected void doTest(String path) {
36+
configureByFile( path );
3337
myFixture.enableInspections( getInspection() );
3438
// Disable info checks as there is a difference between 2018.x and 2019.x
3539
// Links in 2019.x have an info highlighting
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.intellij.inspection;
7+
8+
import java.util.List;
9+
import java.util.stream.Collectors;
10+
11+
import com.intellij.codeInsight.intention.IntentionAction;
12+
import com.intellij.openapi.editor.Caret;
13+
import org.jetbrains.annotations.NotNull;
14+
15+
import static org.assertj.core.api.Assertions.assertThat;
16+
17+
/**
18+
* @author Oliver Erhart
19+
*/
20+
public class UnmappedTargetPropertiesInspectionCaretAfterQuickfixTest extends BaseInspectionTest {
21+
22+
@NotNull
23+
@Override
24+
protected Class<UnmappedTargetPropertiesInspection> getInspection() {
25+
return UnmappedTargetPropertiesInspection.class;
26+
}
27+
28+
@Override
29+
protected void setUp() throws Exception {
30+
super.setUp();
31+
myFixture.copyFileToProject(
32+
"UnmappedTargetPropertiesData.java",
33+
"org/example/data/UnmappedTargetPropertiesData.java"
34+
);
35+
}
36+
37+
public void testAddUnmappedTargetProperties() {
38+
doTest( "UnmappedTargetPropertiesJdk8.java" );
39+
40+
List<IntentionAction> addMissingTargetQuickfixes = myFixture.getAllQuickFixes()
41+
.stream()
42+
.filter( i -> i.getText().startsWith( "Add unmapped target property " ) )
43+
.collect( Collectors.toList() );
44+
45+
addMissingTargetQuickfixes.forEach( this::launchAndAssertCaretPositionInSource );
46+
}
47+
48+
public void testIgnoreUnmappedTargetProperties() {
49+
doTest( "UnmappedTargetPropertiesJdk8.java" );
50+
51+
List<IntentionAction> addMissingTargetQuickfixes = myFixture.getAllQuickFixes()
52+
.stream()
53+
.filter( i -> i.getText().startsWith( "Ignore unmapped target property" ) )
54+
.collect( Collectors.toList() );
55+
56+
addMissingTargetQuickfixes.forEach( this::launchAndAssertUnchangedCaretPosition );
57+
}
58+
59+
public void testIgnoreAllUnmappedTargetProperties() {
60+
doTest( "UnmappedTargetPropertiesJdk8.java" );
61+
62+
List<IntentionAction> addMissingTargetQuickfixes = myFixture.getAllQuickFixes()
63+
.stream()
64+
.filter( i -> i.getText().startsWith( "Ignore all unmapped target properties" ) )
65+
.collect( Collectors.toList() );
66+
67+
addMissingTargetQuickfixes.forEach( this::launchAndAssertUnchangedCaretPosition );
68+
}
69+
70+
private void launchAndAssertCaretPositionInSource(IntentionAction quickFix) {
71+
72+
myFixture.launchAction( quickFix );
73+
74+
assertThatCaretIsInsideOfSourceString();
75+
}
76+
77+
private void launchAndAssertUnchangedCaretPosition(IntentionAction quickFix) {
78+
79+
Caret caretBefore = myFixture.getEditor().getCaretModel().getCurrentCaret();
80+
81+
myFixture.launchAction( quickFix );
82+
83+
Caret caretAfter = myFixture.getEditor().getCaretModel().getCurrentCaret();
84+
85+
assertThat( caretAfter ).isEqualTo( caretBefore );
86+
}
87+
88+
private void assertThatCaretIsInsideOfSourceString() {
89+
assertThat( lineContentBeforeSelection() ).endsWith( "source = \"" );
90+
}
91+
92+
private CharSequence lineContentBeforeSelection() {
93+
94+
Caret currentCaret = myFixture.getEditor().getCaretModel().getCurrentCaret();
95+
96+
return myFixture.getEditor()
97+
.getDocument()
98+
.getCharsSequence()
99+
.subSequence( currentCaret.getVisualLineStart(), currentCaret.getSelectionEnd() );
100+
}
101+
}

testData/inspection/UnmappedTargetPropertiesJdk8.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ default Target map(Source source) {
7878
}
7979

8080
@Mapper
81-
abstract class AbstrctMapperWithoutAbstractMethod {
81+
abstract class AbstractMapperWithoutAbstractMethod {
8282

8383
protected Target map(Source source) {
8484
return null;

testData/inspection/UnmappedTargetPropertiesJdk8_after.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ default Target map(Source source) {
9292
}
9393

9494
@Mapper
95-
abstract class AbstrctMapperWithoutAbstractMethod {
95+
abstract class AbstractMapperWithoutAbstractMethod {
9696

9797
protected Target map(Source source) {
9898
return null;

0 commit comments

Comments
 (0)