Skip to content

Commit f0808fc

Browse files
committed
Add support for generics in iterable types
1 parent 1c54f2c commit f0808fc

File tree

7 files changed

+79
-21
lines changed

7 files changed

+79
-21
lines changed

.github/workflows/build.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
uses: ChrisCarini/intellij-platform-plugin-verifier-action@v2.0.1
3434
with:
3535
ide-versions: |
36-
ideaIU:2022.1
36+
ideaIU:2022.2
3737
ideaIU:2023.1
3838
ideaIU:2024.1
3939
# ideaIU:LATEST-EAP-SNAPSHOT

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## [1.4.0] - 2025-04-07
4+
5+
### Added
6+
7+
- Support for typehints in iterables using generics
8+
39
## [1.3.2] - 2025-01-22
410

511
### Fixed

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ pluginGroup = org.nette.latte
44
pluginName = Latte Support
55
pluginRepositoryUrl = https://github.com/Rixafy/LatteSupport
66
# SemVer format -> https://semver.org
7-
pluginVersion = 1.3.2
7+
pluginVersion = 1.4.0
88

99
# Supported build number ranges and IntelliJ Platform versions -> https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html
1010
pluginSinceBuild = 222

src/main/java/org/nette/latte/completion/providers/LatteVariableCompletionProvider.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,16 @@ private void attachTemplateTypeCompletions(@NotNull CompletionResultSet result,
6161
if (!field.isConstant() && field.getModifier().isPublic()) {
6262
LookupElementBuilder builder = LookupElementBuilder.create(field, "$" + field.getName());
6363
builder = builder.withInsertHandler(PhpVariableInsertHandler.getInstance());
64-
builder = builder.withTypeText(NettePhpType.create(field.getType()).toString());
64+
65+
String foundType = field.getType().toString();
66+
for (String text : field.getType().getTypesWithParametrisedParts()) {
67+
if (text.contains("<")) {
68+
foundType = text;
69+
}
70+
}
71+
72+
builder = builder.withTypeText(NettePhpType.create(foundType).toString());
73+
6574
builder = builder.withIcon(PhpIcons.VARIABLE);
6675
if (field.isDeprecated() || field.isInternal()) {
6776
builder = builder.withStrikeoutness(true);

src/main/java/org/nette/latte/php/LattePhpTypeDetector.java

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,8 @@
1616
import com.jetbrains.php.lang.psi.elements.Field;
1717
import com.jetbrains.php.lang.psi.elements.Method;
1818
import com.jetbrains.php.lang.psi.elements.PhpClass;
19-
import com.jetbrains.php.lang.psi.resolve.types.PhpType;
2019
import org.jetbrains.annotations.NotNull;
2120
import org.jetbrains.annotations.Nullable;
22-
import org.nette.latte.psi.*;
23-
import org.nette.latte.psi.elements.*;
2421

2522
import java.util.ArrayList;
2623
import java.util.Collection;
@@ -159,11 +156,17 @@ private static class Detector {
159156
return customFunction == null ? NettePhpType.MIXED : NettePhpType.create(customFunction.getFunctionReturnType());
160157
}
161158

162-
List<PhpType> types = new ArrayList<>();
159+
List<String> types = new ArrayList<>();
163160
for (PhpClass phpClass : phpClasses) {
164161
for (Method phpMethod : phpClass.getMethods()) {
165162
if (phpMethod.getName().equals(name)) {
166-
types.add(phpMethod.getType());
163+
String foundType = phpMethod.getType().toString();
164+
for (String text : phpMethod.getType().getTypesWithParametrisedParts()) {
165+
if (text.contains("<")) {
166+
foundType = text;
167+
}
168+
}
169+
types.add(foundType);
167170
}
168171
}
169172
}
@@ -175,11 +178,17 @@ private static class Detector {
175178
Collection<PhpClass> phpClasses = type.getPhpClasses(project);
176179
String name = property.getPropertyName();
177180

178-
List<PhpType> types = new ArrayList<>();
181+
List<String> types = new ArrayList<>();
179182
for (PhpClass phpClass : phpClasses) {
180183
for (Field field : phpClass.getFields()) {
181184
if (!field.isConstant() && !field.getModifier().isStatic() && field.getModifier().isPublic() && field.getName().equals(name)) {
182-
types.add(field.getType());
185+
String foundType = field.getType().toString();
186+
for (String text : field.getType().getTypesWithParametrisedParts()) {
187+
if (text.contains("<")) {
188+
foundType = text;
189+
}
190+
}
191+
types.add(foundType);
183192
}
184193
}
185194
}
@@ -191,11 +200,17 @@ private static class Detector {
191200
Collection<PhpClass> phpClasses = type.getPhpClasses(project);
192201
String name = property.getVariableName();
193202

194-
List<PhpType> types = new ArrayList<>();
203+
List<String> types = new ArrayList<>();
195204
for (PhpClass phpClass : phpClasses) {
196205
for (Field field : phpClass.getFields()) {
197206
if (!field.isConstant() && field.getModifier().isStatic() && field.getModifier().isPublic() && field.getName().equals(name)) {
198-
types.add(field.getType());
207+
String foundType = field.getType().toString();
208+
for (String text : field.getType().getTypesWithParametrisedParts()) {
209+
if (text.contains("<")) {
210+
foundType = text;
211+
}
212+
}
213+
types.add(foundType);
199214
}
200215
}
201216
}
@@ -207,11 +222,17 @@ private static class Detector {
207222
Collection<PhpClass> phpClasses = type.getPhpClasses(project);
208223
String name = constant.getConstantName();
209224

210-
List<PhpType> types = new ArrayList<>();
225+
List<String> types = new ArrayList<>();
211226
for (PhpClass phpClass : phpClasses) {
212227
for (Field field : phpClass.getFields()) {
213228
if (field.isConstant() && field.getModifier().isPublic() && field.getName().equals(name)) {
214-
types.add(field.getType());
229+
String foundType = field.getType().toString();
230+
for (String text : field.getType().getTypesWithParametrisedParts()) {
231+
if (text.contains("<")) {
232+
foundType = text;
233+
}
234+
}
235+
types.add(foundType);
215236
}
216237
}
217238
}
@@ -257,7 +278,15 @@ public NettePhpType detectVariableTypeFromTemplateType(@NotNull LattePhpCachedVa
257278
for (PhpClass phpClass : classes) {
258279
for (Field field : phpClass.getFields()) {
259280
if (!field.isConstant() && field.getModifier().isPublic() && variableName.equals(field.getName())) {
260-
return NettePhpType.create(field.getName(), field.getType().toString(), LattePhpUtil.isNullable(field.getType()));
281+
String type = field.getType().toString();
282+
283+
for (String text : field.getType().getTypesWithParametrisedParts()) {
284+
if (text.contains("<")) {
285+
type = text;
286+
}
287+
}
288+
289+
return NettePhpType.create(field.getName(), type, LattePhpUtil.isNullable(field.getType()));
261290
}
262291
}
263292
}

src/main/java/org/nette/latte/php/LattePhpUtil.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,9 +227,16 @@ public static String normalizeClassName(@Nullable String className) {
227227
if (normalized.contains("|null")) {
228228
normalized = normalized.replace("|null", "");
229229
}
230+
230231
if (normalized.contains("|NULL")) {
231232
normalized = normalized.replace("|NULL", "");
232233
}
234+
235+
// Collection<Test> - we need only Collection
236+
if (normalized.contains("<")) {
237+
normalized = normalized.substring(0, normalized.indexOf("<"));
238+
}
239+
233240
return normalized;
234241
}
235242
}

src/main/java/org/nette/latte/php/NettePhpType.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,9 @@ public class NettePhpType {
7272
return create(null, String.join("|", phpType.getTypes()), LattePhpUtil.isNullable(phpType));
7373
}
7474

75-
public static @NotNull NettePhpType create(final @NotNull List<PhpType> phpTypes) {
76-
List<String> typesStrings = new ArrayList<>();
77-
for (PhpType type : phpTypes) {
78-
typesStrings.add(type.toString());
79-
}
75+
public static @NotNull NettePhpType create(final @NotNull List<String> types) {
8076
Set<String> temp = new LinkedHashSet<>(
81-
Arrays.asList(String.join("|", typesStrings).split("\\|"))
77+
Arrays.asList(String.join("|", types).split("\\|"))
8278
);
8379
return create(null, String.join("|", temp));
8480
}
@@ -131,6 +127,17 @@ private NettePhpType(final @Nullable String name, @NotNull String typeString, fi
131127
typeString = typeString.substring(1);
132128
}
133129

130+
// check for iterable types
131+
for (String part : typeString.split("\\|")) {
132+
String clean = part.trim();
133+
if (clean.contains("<") && clean.endsWith(">")) {
134+
String inner = clean.substring(clean.indexOf('<') + 1, clean.length() - 1);
135+
String[] generics = inner.split(",", -1);
136+
String type = generics.length == 2 ? generics[1].trim() : generics[0].trim();
137+
parts.add(type + "[]");
138+
}
139+
}
140+
134141
parts.addAll(Arrays.asList(typeString.split("\\|")));
135142
Map<Integer, Boolean> isObjectOnly = new HashMap<>();
136143
for (String part : parts) {

0 commit comments

Comments
 (0)