Skip to content

Commit 1119db7

Browse files
committed
#20 Polish Kotlin Support
Make sure that plugin works with a disabled Kotlin plugin
1 parent 41dd190 commit 1119db7

12 files changed

+297
-98
lines changed

build.gradle

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@ repositories {
3232
}
3333

3434
intellij {
35-
version System.getenv().getOrDefault('IDEA_VERSION', '2020.1.4')
35+
version System.getenv().getOrDefault('IDEA_VERSION', ideaVersion)
3636
type ideaType
3737
downloadSources Boolean.valueOf(sources)
3838
sameSinceUntilBuild Boolean.valueOf(isEAP)
3939
alternativeIdePath idePath
4040
updateSinceUntilBuild false
4141
pluginName 'MapStruct-Intellij-Plugin'
42+
// The properties plugin is needed because Kotlin uses it
43+
// and for some reason plugins does not transitively pull itx
4244
if ( !(version.startsWith('2018') || version.startsWith('2019.1'))) {
4345
plugins = ['java', 'Kotlin', 'properties']
4446
} else {

change-notes.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<html>
22
<h2>1.2.3</h2>
33
<ul>
4+
<li>Kotlin: Code completion for <code>target</code> and <code>source</code> in <code>@Mapping</code> and <code>@ValueMapping</code></li>
45
<li>Bug fix: False positive unmapped target property for multi source methods</li>
56
</ul>
67
<h2>1.2.2</h2>

src/main/java/org/mapstruct/intellij/codeinsight/references/BaseReference.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77

88
import com.intellij.openapi.util.TextRange;
99
import com.intellij.psi.PsiElement;
10+
import com.intellij.psi.PsiLiteral;
1011
import com.intellij.psi.PsiMethod;
1112
import com.intellij.psi.PsiReferenceBase;
13+
import com.intellij.psi.util.PsiTreeUtil;
1214
import org.jetbrains.annotations.NotNull;
1315
import org.jetbrains.annotations.Nullable;
1416
import org.mapstruct.intellij.util.MapstructUtilKt;
@@ -40,6 +42,15 @@ abstract class BaseReference extends PsiReferenceBase<PsiElement> {
4042
*/
4143
@Nullable
4244
PsiMethod getMappingMethod() {
43-
return MapstructUtilKt.getPsiMethod( getElement() );
45+
PsiElement element = getElement();
46+
if ( element instanceof PsiLiteral ) {
47+
return PsiTreeUtil.getParentOfType( element, PsiMethod.class );
48+
}
49+
else if ( "KtStringTemplateExpression".equals( element.getClass().getSimpleName() ) ) {
50+
// We cannot do an instanceOf check here because the kotlin class is optional
51+
return MapstructUtilKt.getPsiMethod( element );
52+
}
53+
54+
return null;
4455
}
4556
}

src/main/java/org/mapstruct/intellij/codeinsight/references/KtMapstructReferenceContributor.kt

Lines changed: 6 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,8 @@ package org.mapstruct.intellij.codeinsight.references
77

88
import com.intellij.psi.PsiReferenceContributor
99
import com.intellij.psi.PsiReferenceRegistrar
10-
import org.mapstruct.intellij.util.toMappingElementPattern
11-
import org.mapstruct.intellij.util.toMappingsElementPattern
12-
import org.mapstruct.intellij.util.toValueMappingPattern
13-
import org.mapstruct.intellij.util.toValueMappingsPattern
14-
10+
import org.mapstruct.intellij.util.MapstructKotlinElementUtils.mappingElementPattern
11+
import org.mapstruct.intellij.util.MapstructKotlinElementUtils.valueMappingElementPattern
1512

1613
/**
1714
* @author Frank Wang
@@ -20,36 +17,19 @@ class KtMapstructReferenceContributor : PsiReferenceContributor() {
2017

2118
override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {
2219
registrar.registerReferenceProvider(
23-
"target".toMappingElementPattern(),
24-
MappingTargetReferenceProvider(MapstructTargetReference::create)
25-
)
26-
registrar.registerReferenceProvider(
27-
"source".toMappingElementPattern(),
28-
MappingTargetReferenceProvider(MapstructSourceReference::create)
29-
)
30-
registrar.registerReferenceProvider(
31-
"target".toMappingsElementPattern(),
20+
mappingElementPattern("target"),
3221
MappingTargetReferenceProvider(MapstructTargetReference::create)
3322
)
3423
registrar.registerReferenceProvider(
35-
"source".toMappingsElementPattern(),
24+
mappingElementPattern("source"),
3625
MappingTargetReferenceProvider(MapstructSourceReference::create)
3726
)
38-
39-
registrar.registerReferenceProvider(
40-
"target".toValueMappingPattern(),
41-
MappingTargetReferenceProvider(ValueMappingSourceReference::create)
42-
)
43-
registrar.registerReferenceProvider(
44-
"source".toValueMappingPattern(),
45-
MappingTargetReferenceProvider(ValueMappingTargetReference::create)
46-
)
4727
registrar.registerReferenceProvider(
48-
"target".toValueMappingsPattern(),
28+
valueMappingElementPattern("target"),
4929
MappingTargetReferenceProvider(ValueMappingSourceReference::create)
5030
)
5131
registrar.registerReferenceProvider(
52-
"source".toValueMappingsPattern(),
32+
valueMappingElementPattern("source"),
5333
MappingTargetReferenceProvider(ValueMappingTargetReference::create)
5434
)
5535
}

src/main/java/org/mapstruct/intellij/util/KtMapstructElementUtil.kt

Lines changed: 0 additions & 62 deletions
This file was deleted.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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.util;
7+
8+
import com.intellij.patterns.StandardPatterns;
9+
import com.intellij.psi.PsiElement;
10+
import org.mapstruct.intellij.util.patterns.KotlinElementPattern;
11+
12+
import static org.mapstruct.intellij.util.patterns.MapStructKotlinPatterns.psiElement;
13+
14+
/**
15+
* Utils for working with MapStruct kotlin elements.
16+
*
17+
* @author Filip Hrisafov
18+
*/
19+
public final class MapstructKotlinElementUtils {
20+
21+
/**
22+
* Hide default constructor.
23+
*/
24+
private MapstructKotlinElementUtils() {
25+
}
26+
27+
/**
28+
* @param parameterName the name of the parameter in the {@code @ValueMapping} annotation
29+
*
30+
* @return an element pattern for a parameter in the {@code @ValueMapping} annotation
31+
*/
32+
public static KotlinElementPattern.Capture<? extends PsiElement> valueMappingElementPattern(String parameterName) {
33+
return elementPattern(
34+
parameterName,
35+
MapstructUtil.VALUE_MAPPING_ANNOTATION_FQN,
36+
MapstructUtil.VALUE_MAPPINGS_ANNOTATION_FQN
37+
);
38+
}
39+
40+
/**
41+
* @param parameterName the name of the parameter in the {@code @Mapping} annotation
42+
*
43+
* @return an element pattern for a parameter in the {@code @Mapping} annotation
44+
*/
45+
public static KotlinElementPattern.Capture<? extends PsiElement> mappingElementPattern(String parameterName) {
46+
return elementPattern(
47+
parameterName,
48+
MapstructUtil.MAPPING_ANNOTATION_FQN,
49+
MapstructUtil.MAPPINGS_ANNOTATION_FQN
50+
);
51+
}
52+
53+
private static KotlinElementPattern.Capture<? extends PsiElement> elementPattern(String parameterName,
54+
String annotationFQN,
55+
String annotationHolderFQN
56+
) {
57+
return psiElement()
58+
.insideRepeatableAnnotationParam(
59+
StandardPatterns.string().equalTo( annotationFQN ),
60+
StandardPatterns.string().equalTo( annotationHolderFQN ),
61+
parameterName
62+
);
63+
}
64+
}

src/main/java/org/mapstruct/intellij/util/MapstructUtil.kt

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,10 @@ import com.intellij.psi.PsiElement
99
import com.intellij.psi.PsiMethod
1010
import org.jetbrains.kotlin.asJava.classes.createGeneratedMethodFromDescriptor
1111
import org.jetbrains.kotlin.idea.caches.resolve.IDELightClassGenerationSupport
12-
import org.jetbrains.kotlin.idea.caches.resolve.analyze
1312
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
14-
import org.jetbrains.kotlin.name.FqName
15-
import org.jetbrains.kotlin.psi.KtAnnotationEntry
1613
import org.jetbrains.kotlin.psi.KtClass
1714
import org.jetbrains.kotlin.psi.KtNamedFunction
1815
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
19-
import org.jetbrains.kotlin.resolve.BindingContext
2016
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
2117

2218

@@ -27,10 +23,6 @@ fun PsiElement.getPsiMethod(): PsiMethod? {
2723
return this.getNonStrictParentOfType() ?: this.getNonStrictParentOfType<KtNamedFunction>()?.toPsiMethod()
2824
}
2925

30-
fun KtAnnotationEntry.getFqName(): FqName? {
31-
return this.analyze(BodyResolveMode.PARTIAL_FOR_COMPLETION).get(BindingContext.ANNOTATION, this)?.fqName
32-
}
33-
3426
private fun KtNamedFunction.toPsiMethod(): PsiMethod? {
3527
// dealing with kotlin class
3628
val ktClass = this.getNonStrictParentOfType(KtClass::class.java) ?: return null
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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.util.patterns;
7+
8+
import com.intellij.patterns.ElementPattern;
9+
import com.intellij.patterns.PsiElementPattern;
10+
import com.intellij.psi.PsiElement;
11+
import org.jetbrains.kotlin.psi.stubs.elements.KtStubElementTypes;
12+
13+
import static org.mapstruct.intellij.util.patterns.MapStructKotlinPatterns.ktAnnotation;
14+
import static org.mapstruct.intellij.util.patterns.MapStructKotlinPatterns.ktValueArgument;
15+
16+
/**
17+
* @author Filip Hrisafov
18+
*/
19+
public class KotlinElementPattern<T extends PsiElement, Self extends KotlinElementPattern<T, Self>>
20+
extends PsiElementPattern<T, Self> {
21+
22+
public KotlinElementPattern(final Class<T> aClass) {
23+
super( aClass );
24+
}
25+
26+
public Self insideRepeatableAnnotationParam(
27+
ElementPattern<String> annotationQualifiedName,
28+
ElementPattern<String> annotationHolderQualifiedName,
29+
String parameterName) {
30+
// A repeatable annotation in kotlin has 2 possible ways of PSI structure:
31+
// 1. Part of the repeatable holder
32+
// @Mappings(
33+
// Mapping(target = "name")
34+
// )
35+
// 2. Just the annotation
36+
// @Mapping(target = "name")
37+
38+
KtValueArgumentPattern ktValueArgumentPattern = ktValueArgument().withName( parameterName );
39+
return withElementType( KtStubElementTypes.STRING_TEMPLATE ).andOr(
40+
withParent(
41+
ktValueArgumentPattern
42+
.withAncestor( 5, ktAnnotation().qName( annotationHolderQualifiedName ) )
43+
),
44+
45+
withParent(
46+
ktValueArgumentPattern
47+
.withSuperParent( 2, ktAnnotation().qName( annotationQualifiedName ) )
48+
)
49+
);
50+
}
51+
52+
public static class Capture<T extends PsiElement> extends KotlinElementPattern<T, KotlinElementPattern.Capture<T>> {
53+
public Capture(Class<T> aClass) {
54+
super( aClass );
55+
}
56+
57+
}
58+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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.util.patterns;
7+
8+
import com.intellij.patterns.ElementPattern;
9+
import com.intellij.patterns.PatternCondition;
10+
import com.intellij.patterns.PsiElementPattern;
11+
import com.intellij.util.ProcessingContext;
12+
import org.jetbrains.annotations.NotNull;
13+
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor;
14+
import org.jetbrains.kotlin.idea.caches.resolve.ResolutionUtils;
15+
import org.jetbrains.kotlin.name.FqName;
16+
import org.jetbrains.kotlin.psi.KtAnnotationEntry;
17+
import org.jetbrains.kotlin.resolve.BindingContext;
18+
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode;
19+
20+
/**
21+
* @author Filip Hrisafov
22+
*/
23+
public class KtAnnotationEntryPattern extends PsiElementPattern<KtAnnotationEntry, KtAnnotationEntryPattern> {
24+
25+
static final KtAnnotationEntryPattern KT_ANNOTATION_ENTRY_PATTERN = new KtAnnotationEntryPattern();
26+
27+
private KtAnnotationEntryPattern() {
28+
super( KtAnnotationEntry.class );
29+
}
30+
31+
public KtAnnotationEntryPattern qName(ElementPattern<String> pattern) {
32+
return with( new PatternCondition<KtAnnotationEntry>( "qName" ) {
33+
@Override
34+
public boolean accepts(@NotNull KtAnnotationEntry ktAnnotation, ProcessingContext context) {
35+
AnnotationDescriptor descriptor = ResolutionUtils.analyze(
36+
ktAnnotation,
37+
BodyResolveMode.PARTIAL_FOR_COMPLETION
38+
).get( BindingContext.ANNOTATION, ktAnnotation );
39+
40+
if ( descriptor == null ) {
41+
return false;
42+
}
43+
44+
FqName fqName = descriptor.getFqName();
45+
if ( fqName == null ) {
46+
return false;
47+
}
48+
return pattern.accepts( fqName.asString(), context );
49+
}
50+
} );
51+
}
52+
53+
}

0 commit comments

Comments
 (0)