Skip to content

Commit 16749f9

Browse files
authored
🐛 Resolves the referenced element before parsing colors (#6782)
Fixes #5436 #6777. The PR does: - Resolves PSI elements if they are `VAR_INIT` or `FUNCTION_BODY` before parsing, which can always lead to a correct definition eventually. - Improve `const Color.from*` constructors handling. - Only handle the last child of an element's children to avoid too many requests when gathering line markers, thus `Colors.blue.shade700` will only resolve color on `shade700` instead of `Colors`, `blue`, and `shade700`. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read the [Flutter Style Guide] _recently_, and have followed its advice. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] All existing and new tests are passing. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat
1 parent ce623fd commit 16749f9

File tree

2 files changed

+87
-12
lines changed

2 files changed

+87
-12
lines changed

flutter-idea/src/io/flutter/editor/FlutterColorProvider.java

+84-9
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,7 @@
1414
import com.intellij.psi.impl.source.tree.AstBufferUtil;
1515
import com.jetbrains.lang.dart.DartLanguage;
1616
import com.jetbrains.lang.dart.DartTokenTypes;
17-
import com.jetbrains.lang.dart.psi.DartArgumentList;
18-
import com.jetbrains.lang.dart.psi.DartArguments;
19-
import com.jetbrains.lang.dart.psi.DartExpression;
20-
import com.jetbrains.lang.dart.psi.DartLiteralExpression;
17+
import com.jetbrains.lang.dart.psi.*;
2118
import io.flutter.FlutterBundle;
2219
import org.jetbrains.annotations.NotNull;
2320
import org.jetbrains.annotations.Nullable;
@@ -38,7 +35,7 @@ public Color getColorFrom(@NotNull PsiElement element) {
3835
if (element.getNode().getElementType() != DartTokenTypes.IDENTIFIER) return null;
3936

4037
final String name = element.getText();
41-
if (!(name.equals("Colors") || name.equals("CupertinoColors") || name.equals("Color"))) return null;
38+
if (name == null) return null;
4239

4340
final PsiElement refExpr = topmostReferenceExpression(element);
4441
if (refExpr == null) return null;
@@ -47,21 +44,66 @@ public Color getColorFrom(@NotNull PsiElement element) {
4744

4845
if (parent.getNode().getElementType() == DartTokenTypes.ARRAY_ACCESS_EXPRESSION) {
4946
// Colors.blue[200]
47+
if (name.equals(refExpr.getFirstChild().getText()) && refExpr.getChildren().length > 1) {
48+
// Avoid duplicate resolves.
49+
return null;
50+
}
5051
final String code = AstBufferUtil.getTextSkippingWhitespaceComments(parent.getNode());
51-
return parseColorText(code.substring(code.indexOf(name) + name.length() + 1), name);
52+
return parseColorText(code.substring(code.indexOf(name)), name);
5253
}
5354
else if (parent.getNode().getElementType() == DartTokenTypes.CALL_EXPRESSION) {
5455
// foo(Color.fromRGBO(0, 255, 0, 0.5))
56+
if (name.matches("fromARGB") || name.matches("fromRGBO")) {
57+
// Avoid duplicate resolves.
58+
return null;
59+
}
60+
if (parent.getLastChild() instanceof DartArguments && !name.matches("(Cupertino)?Color")) {
61+
// Avoid duplicate resolves.
62+
final DartExpression colorExpression = ((DartArguments)parent.getLastChild()).getArgumentList().getExpressionList().get(0);
63+
final Color color = parseColorElements(colorExpression, colorExpression.getFirstChild());
64+
if (color != null) return null;
65+
}
66+
// Color.fromRGBO(0, 255, 0, 0.5)
5567
return parseColorElements(parent, refExpr);
5668
}
5769
else if (parent.getNode().getElementType() == DartTokenTypes.SIMPLE_TYPE) {
5870
// const Color.fromARGB(100, 255, 0, 0)
71+
if (name.matches("fromARGB") || name.matches("fromRGBO")) {
72+
// Avoid duplicate resolves.
73+
return null;
74+
}
5975
// parent.getParent().getParent() is a new expr
6076
parent = getNewExprFromType(parent);
6177
if (parent == null) return null;
6278
return parseColorElements(parent, refExpr);
6379
}
6480
else {
81+
if (parent.getNode().getElementType() == DartTokenTypes.VAR_INIT ||
82+
parent.getNode().getElementType() == DartTokenTypes.FUNCTION_BODY) {
83+
if (name.equals(refExpr.getFirstChild().getText()) && refExpr.getChildren().length > 1) {
84+
// Avoid duplicate resolves.
85+
return null;
86+
}
87+
final PsiElement reference = resolveReferencedElement(refExpr);
88+
if (reference != null && reference.getLastChild() != null) {
89+
Color tryParseColor = null;
90+
if (reference instanceof DartCallExpression) {
91+
final DartExpression expression = ((DartCallExpression)reference).getExpression();
92+
if (expression != null && expression.getLastChild() instanceof DartReferenceExpression) {
93+
tryParseColor = parseColorElements(reference, expression);
94+
if (tryParseColor != null) return tryParseColor;
95+
}
96+
}
97+
final PsiElement lastChild = reference.getLastChild();
98+
if (lastChild instanceof DartArguments && reference.getParent() != null) {
99+
tryParseColor = parseColorElements(reference, reference.getParent());
100+
}
101+
else {
102+
tryParseColor = parseColorElements(reference, reference.getLastChild());
103+
}
104+
if (tryParseColor != null) return tryParseColor;
105+
}
106+
}
65107
// name.equals(refExpr.getFirstChild().getText()) -> Colors.blue
66108
final PsiElement idNode = refExpr.getFirstChild();
67109
if (idNode == null) return null;
@@ -75,13 +117,46 @@ else if (parent.getNode().getElementType() == DartTokenTypes.SIMPLE_TYPE) {
75117
final PsiElement child = refExpr.getLastChild();
76118
if (child == null) return null;
77119
if (child.getText().startsWith("shade")) {
78-
final String code = AstBufferUtil.getTextSkippingWhitespaceComments(refExpr.getNode());
79-
return parseColorText(code.substring(code.indexOf(name) + name.length() + 1), name);
120+
if (idNode.getText().contains(name)) return null; // Avoid duplicate resolves.
121+
String code = AstBufferUtil.getTextSkippingWhitespaceComments(refExpr.getNode());
122+
code = code.replaceFirst("(Cupertino)?Colors\\.", "");
123+
return parseColorText(code, name);
80124
}
81125
}
82126
return null;
83127
}
84128

129+
@Nullable
130+
private PsiElement resolveReferencedElement(@NotNull PsiElement element) {
131+
if (element instanceof DartCallExpression && element.getFirstChild().getText().equals("Color")) {
132+
return element;
133+
}
134+
final PsiElement symbol = element.getLastChild();
135+
final PsiElement result;
136+
if (symbol instanceof DartReference) {
137+
result = ((DartReference)symbol).resolve();
138+
}
139+
else if (element instanceof DartReference) {
140+
result = ((DartReference)element).resolve();
141+
}
142+
else {
143+
return null;
144+
}
145+
if (!(result instanceof DartComponentName) || result.getParent() == null) return null;
146+
final PsiElement declaration = result.getParent().getParent();
147+
if (declaration instanceof DartClassMembers) return declaration;
148+
if (!(declaration instanceof DartVarDeclarationList)) return null;
149+
final PsiElement lastChild = declaration.getLastChild();
150+
if (!(lastChild instanceof DartVarInit)) return null;
151+
152+
final PsiElement effectiveElement = lastChild.getLastChild();
153+
// Recursively determine reference if the initialization is still a `DartReference`.
154+
if (effectiveElement instanceof DartReference && !(effectiveElement instanceof DartCallExpression)) {
155+
return resolveReferencedElement(effectiveElement);
156+
}
157+
return effectiveElement;
158+
}
159+
85160
@Nullable
86161
private Color parseColorElements(@NotNull PsiElement parent, @NotNull PsiElement refExpr) {
87162
final PsiElement selectorNode = refExpr.getLastChild();
@@ -91,7 +166,7 @@ private Color parseColorElements(@NotNull PsiElement parent, @NotNull PsiElement
91166
final boolean isFromRGBO = "fromRGBO".equals(selector);
92167
if (isFromARGB || isFromRGBO) {
93168
String code = AstBufferUtil.getTextSkippingWhitespaceComments(parent.getNode());
94-
if (code.startsWith("constColor(")) {
169+
if (code.startsWith("constColor(") || code.startsWith("constColor.")) {
95170
code = code.substring(5);
96171
}
97172
return ExpressionParsingUtils.parseColorComponents(code.substring(code.indexOf(selector)), selector + "(", isFromARGB);

flutter-idea/testSrc/unit/io/flutter/editor/FlutterColorProviderTest.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public void locatesRGBOColor() throws Exception {
122122
@Test
123123
public void locatesColorShadeReference() throws Exception {
124124
run(() -> {
125-
final PsiElement testIdentifier = setUpDartElement("main() { Colors.blue.shade700; }", "Colors", LeafPsiElement.class);
125+
final PsiElement testIdentifier = setUpDartElement("main() { Colors.blue.shade700; }", "shade700", LeafPsiElement.class);
126126
final Color color = new FlutterColorProvider().getColorFrom(testIdentifier);
127127
assertNotNull(color);
128128
final DartReferenceExpression element = DartSyntax.findEnclosingReferenceExpression(testIdentifier);
@@ -133,7 +133,7 @@ public void locatesColorShadeReference() throws Exception {
133133
@Test
134134
public void locatesColorArrayReference() throws Exception {
135135
run(() -> {
136-
final PsiElement testIdentifier = setUpDartElement("main() { Colors.blue[200]]; }", "Colors", LeafPsiElement.class);
136+
final PsiElement testIdentifier = setUpDartElement("main() { Colors.blue[200]; }", "blue", LeafPsiElement.class);
137137
final Color color = new FlutterColorProvider().getColorFrom(testIdentifier);
138138
assertNotNull(color);
139139
final DartReferenceExpression element = DartSyntax.findEnclosingReferenceExpression(testIdentifier);
@@ -155,7 +155,7 @@ public void locatesCuppertinoColorReference() throws Exception {
155155
@Test
156156
public void locatesColorReferenceWithComment() throws Exception {
157157
run(() -> {
158-
final PsiElement testIdentifier = setUpDartElement("main() { Colors . blue . /* darkish */ shade700; }", "Colors", LeafPsiElement.class);
158+
final PsiElement testIdentifier = setUpDartElement("main() { Colors . blue . /* darkish */ shade700; }", "shade700", LeafPsiElement.class);
159159
final Color color = new FlutterColorProvider().getColorFrom(testIdentifier);
160160
assertNotNull(color);
161161
final DartReferenceExpression element = DartSyntax.findEnclosingReferenceExpression(testIdentifier);

0 commit comments

Comments
 (0)