5
5
*/
6
6
package io .flutter .editor ;
7
7
8
- import static io .flutter .dart .DartPsiUtil .getNamedArgumentExpression ;
9
- import static io .flutter .dart .DartPsiUtil .getNewExprFromType ;
10
- import static io .flutter .dart .DartPsiUtil .getValueOfNamedArgument ;
11
- import static io .flutter .dart .DartPsiUtil .getValueOfPositionalArgument ;
12
- import static io .flutter .dart .DartPsiUtil .parseLiteralNumber ;
13
- import static io .flutter .dart .DartPsiUtil .topmostReferenceExpression ;
14
-
15
8
import com .intellij .codeInsight .daemon .GutterName ;
16
9
import com .intellij .codeInsight .daemon .LineMarkerInfo ;
17
10
import com .intellij .codeInsight .daemon .LineMarkerProviderDescriptor ;
18
- import com .intellij .openapi .application .ApplicationManager ;
19
11
import com .intellij .openapi .diagnostic .Logger ;
20
12
import com .intellij .openapi .editor .markup .GutterIconRenderer ;
21
13
import com .intellij .openapi .project .Project ;
30
22
import com .intellij .psi .PsiManager ;
31
23
import com .intellij .psi .impl .source .tree .AstBufferUtil ;
32
24
import com .jetbrains .lang .dart .DartTokenTypes ;
33
- import com .jetbrains .lang .dart .psi .DartArgumentList ;
34
- import com .jetbrains .lang .dart .psi .DartArguments ;
35
- import com .jetbrains .lang .dart .psi .DartCallExpression ;
36
- import com .jetbrains .lang .dart .psi .DartExpression ;
37
- import com .jetbrains .lang .dart .psi .DartNewExpression ;
38
- import com .jetbrains .lang .dart .psi .DartRecursiveVisitor ;
39
- import com .jetbrains .lang .dart .psi .DartReference ;
40
- import com .jetbrains .lang .dart .psi .DartStringLiteralExpression ;
41
- import com .jetbrains .lang .dart .psi .DartType ;
42
- import com .jetbrains .lang .dart .psi .DartVarAccessDeclaration ;
43
- import com .jetbrains .lang .dart .psi .DartVarInit ;
25
+ import com .jetbrains .lang .dart .psi .*;
44
26
import com .jetbrains .lang .dart .psi .impl .DartCallExpressionImpl ;
45
27
import com .jetbrains .lang .dart .util .DartPsiImplUtil ;
46
28
import com .jetbrains .lang .dart .util .DartResolveUtil ;
47
29
import gnu .trove .THashSet ;
48
30
import info .debatty .java .stringsimilarity .JaroWinkler ;
49
31
import io .flutter .FlutterBundle ;
50
32
import io .flutter .utils .IconPreviewGenerator ;
51
-
52
- import java .util .*;
53
- import javax .swing .Icon ;
54
33
import org .jetbrains .annotations .NotNull ;
55
34
import org .jetbrains .annotations .Nullable ;
56
35
import org .jetbrains .yaml .psi .YAMLCompoundValue ;
57
36
import org .jetbrains .yaml .psi .YAMLKeyValue ;
58
37
import org .jetbrains .yaml .psi .YamlRecursivePsiElementVisitor ;
59
38
39
+ import javax .swing .*;
40
+ import java .util .*;
41
+
42
+ import static io .flutter .dart .DartPsiUtil .*;
43
+
44
+ // Style note: Normally we put control flow changing statements on a separate line in a block.
45
+ // When working with PSI elements nearly everything can return null. There are a lot of null checks
46
+ // that could return null, but they seldom trigger, so the return is on the same line as the if statement.
60
47
public class FlutterIconLineMarkerProvider extends LineMarkerProviderDescriptor {
61
48
62
49
public static final Map <String , Set <String >> KnownPaths = new HashMap <>();
63
50
private static final Logger LOG = Logger .getInstance (FlutterIconLineMarkerProvider .class );
64
51
65
52
static {
66
- KnownPaths .put ("Icons" , new THashSet <String >(Collections .singleton ("packages/flutter/lib/src/material" )));
67
- KnownPaths .put ("IconData" , new THashSet <String >(Collections .singleton ("packages/flutter/lib/src/widgets" )));
68
- KnownPaths .put ("CupertinoIcons" , new THashSet <String >(Collections .singleton ("packages/flutter/lib/src/cupertino" )));
53
+ KnownPaths .put ("Icons" , new THashSet <>(Collections .singleton ("packages/flutter/lib/src/material" )));
54
+ KnownPaths .put ("IconData" , new THashSet <>(Collections .singleton ("packages/flutter/lib/src/widgets" )));
55
+ KnownPaths .put ("CupertinoIcons" , new THashSet <>(Collections .singleton ("packages/flutter/lib/src/cupertino" )));
69
56
}
70
57
71
58
@ Nullable ("null means disabled" )
@@ -88,34 +75,32 @@ public LineMarkerInfo<?> getLineMarkerInfo(@NotNull PsiElement element) {
88
75
String knownPath = null ;
89
76
90
77
// Resolve the class reference and check that it is one of the known, cached classes.
91
- if (!ApplicationManager .getApplication ().isUnitTestMode ()) {
92
- final PsiElement symbol = "IconData" .equals (name ) ? refExpr : refExpr .getFirstChild ();
93
- if (!(symbol instanceof DartReference )) return null ;
94
- final PsiElement result = ((DartReference )symbol ).resolve ();
95
- if (result == null ) return null ;
96
- final List <VirtualFile > library = DartResolveUtil .findLibrary (result .getContainingFile ());
97
- for (VirtualFile file : library ) {
98
- final VirtualFile dir = file .getParent ();
99
- if (dir .isInLocalFileSystem ()) {
100
- final String path = dir .getPath ();
101
- String trimmedPath = path ;
102
- if (!path .endsWith ("lib" )) {
103
- final int index = path .indexOf ("lib" );
104
- if (index >= 0 ) {
105
- trimmedPath = path .substring (0 , index + 3 );
106
- }
78
+ final PsiElement symbol = "IconData" .equals (name ) ? refExpr : refExpr .getFirstChild ();
79
+ if (!(symbol instanceof DartReference )) return null ;
80
+ final PsiElement result = ((DartReference )symbol ).resolve ();
81
+ if (result == null ) return null ;
82
+ final List <VirtualFile > library = DartResolveUtil .findLibrary (result .getContainingFile ());
83
+ for (VirtualFile file : library ) {
84
+ final VirtualFile dir = file .getParent ();
85
+ if (dir .isInLocalFileSystem ()) {
86
+ final String path = dir .getPath ();
87
+ String trimmedPath = path ;
88
+ if (!path .endsWith ("lib" )) {
89
+ final int index = path .indexOf ("lib" );
90
+ if (index >= 0 ) {
91
+ trimmedPath = path .substring (0 , index + 3 );
107
92
}
108
- final Set <String > knownPaths = KnownPaths .get (name );
109
- if (knownPaths .contains (path ) || knownPaths .contains (trimmedPath )) {
93
+ }
94
+ final Set <String > knownPaths = KnownPaths .get (name );
95
+ if (knownPaths .contains (path ) || knownPaths .contains (trimmedPath )) {
96
+ knownPath = file .getPath ();
97
+ break ;
98
+ }
99
+ for (String aPath : knownPaths ) {
100
+ if (path .endsWith (aPath ) || aPath .contains (path ) || trimmedPath .endsWith (aPath ) || aPath .contains (trimmedPath )) {
110
101
knownPath = file .getPath ();
111
102
break ;
112
103
}
113
- for (String aPath : knownPaths ) {
114
- if (path .endsWith (aPath ) || aPath .contains (path ) || trimmedPath .endsWith (aPath ) || aPath .contains (trimmedPath )) {
115
- knownPath = file .getPath ();
116
- break ;
117
- }
118
- }
119
104
}
120
105
}
121
106
}
@@ -126,24 +111,18 @@ public LineMarkerInfo<?> getLineMarkerInfo(@NotNull PsiElement element) {
126
111
final DartArguments arguments = DartPsiImplUtil .getArguments ((DartCallExpression )parent );
127
112
if (arguments == null ) return null ;
128
113
final String family = getValueOfNamedArgument (arguments , "fontFamily" );
129
- //if (family != null) {
130
- // // TODO https://github.com/flutter/flutter-intellij/issues/2334
131
- // if (!"MaterialIcons".equals(family)) return null;
132
- //}
133
114
final PsiElement fontPackage = getNamedArgumentExpression (arguments , "fontPackage" );
134
- //if (fontPackage != null) return null; // See previous TODO
135
115
final String argument = getValueOfPositionalArgument (arguments , 0 );
136
116
if (argument == null ) return null ;
117
+ final Icon icon ;
137
118
if (family == null || "MaterialIcons" .equals (family )) {
138
- final Icon icon = getIconFromCode (argument );
139
- if (icon != null ) {
140
- return createLineMarker (element , icon );
141
- }
142
- } else {
143
- final Icon icon = getIconFromPackage (fontPackage , family , argument );
144
- if (icon != null ) {
145
- return createLineMarker (element , icon );
146
- }
119
+ icon = getIconFromCode (argument );
120
+ }
121
+ else {
122
+ icon = getIconFromPackage (fontPackage , family , argument );
123
+ }
124
+ if (icon != null ) {
125
+ return createLineMarker (element , icon );
147
126
}
148
127
}
149
128
else if (parent .getNode ().getElementType () == DartTokenTypes .SIMPLE_TYPE ) {
@@ -188,9 +167,9 @@ else if (name.equals("CupertinoIcons")) {
188
167
// final HashMap<String, String> assetMap = visitor.assetMap;
189
168
// }
190
169
//}
191
- final PsiElement symbol = refExpr .getLastChild ();
192
- if (symbol == null ) return null ; // TODO check for instance creation with codepoint
193
- final String iconName = symbol .getText ();
170
+ final PsiElement iconElement = refExpr .getLastChild ();
171
+ if (iconElement == null ) return null ; // TODO check for instance creation with codepoint
172
+ final String iconName = iconElement .getText ();
194
173
final IconInfo iconDef = findDefinition (name , iconName , element .getProject (), knownPath );
195
174
if (iconDef == null ) return null ;
196
175
icon = findIconFromDef (name , iconDef , knownPath );
@@ -203,6 +182,8 @@ else if (name.equals("CupertinoIcons")) {
203
182
return null ;
204
183
}
205
184
185
+ // TODO Implement this if we ever support the package flutter_icons.
186
+ // That package is not currently supported because it takes forever to analyze it.
206
187
private Icon getIconFromPackage (PsiElement aPackage , String family , String argument ) {
207
188
if (aPackage == null || family == null || argument == null ) {
208
189
return null ;
@@ -265,7 +246,7 @@ private Icon findIconFromDef(@NotNull String iconClassName, @NotNull IconInfo ic
265
246
return null ;
266
247
}
267
248
final List <VirtualFile > ttfFiles = new ArrayList <>();
268
- VfsUtilCore .visitChildrenRecursively (parent , new VirtualFileVisitor <Object >() {
249
+ VfsUtilCore .visitChildrenRecursively (parent , new VirtualFileVisitor <>() {
269
250
@ Override
270
251
public boolean visitFile (@ NotNull VirtualFile file ) {
271
252
if ("ttf" .equals (file .getExtension ())) {
@@ -288,7 +269,7 @@ public boolean visitFile(@NotNull VirtualFile file) {
288
269
bestFileMatch = file ;
289
270
}
290
271
}
291
- }
272
+ } // If the family is null we could do a search for font files named similar to the package.
292
273
if (bestFileMatch != null ) {
293
274
final IconPreviewGenerator generator = new IconPreviewGenerator (bestFileMatch .getPath ());
294
275
final Icon icon = generator .convert (iconDef .codepoint );
@@ -303,7 +284,8 @@ public boolean visitFile(@NotNull VirtualFile file) {
303
284
}
304
285
305
286
public double findPattern (@ NotNull String t , @ NotNull String p ) {
306
- // TODO Experiment with https://github.com/tdebatty/java-string-similarity
287
+ // This is from https://github.com/tdebatty/java-string-similarity
288
+ // It's MIT license file is: https://github.com/tdebatty/java-string-similarity/blob/master/LICENSE.md
307
289
final JaroWinkler jw = new JaroWinkler ();
308
290
return jw .similarity (t , p );
309
291
}
@@ -363,7 +345,9 @@ public void visitVarAccessDeclaration(@NotNull DartVarAccessDeclaration o) {
363
345
className = type .getText ();
364
346
arguments = newExpr .getArguments ();
365
347
}
366
- } else if (expression instanceof DartCallExpression ) {
348
+ }
349
+ else if (expression instanceof DartCallExpression ) {
350
+ // The Dart parser sometimes generates a call expression where we expect a new expression.
367
351
final DartCallExpressionImpl callExpr = (DartCallExpressionImpl )expression ;
368
352
arguments = callExpr .getArguments ();
369
353
className = callExpr .getExpression ().getText ();
@@ -385,6 +369,9 @@ public void visitVarAccessDeclaration(@NotNull DartVarAccessDeclaration o) {
385
369
}
386
370
else {
387
371
if (o .getFirstChild ().getText ().trim ().equals ("static" )) {
372
+ // Fortunately, in all packages checked so far, static variables defining font family and
373
+ // package names appear at the beginning of the file. So this simple visitor works, but it
374
+ // will fail if the static variables are defined at the end of the file.
388
375
final String varName = o .getComponentName ().getText ().trim ();
389
376
final DartVarInit init = (DartVarInit )o .getParent ().getLastChild ();
390
377
final DartExpression expression = init .getExpression ();
@@ -397,7 +384,7 @@ public void visitVarAccessDeclaration(@NotNull DartVarAccessDeclaration o) {
397
384
}
398
385
}
399
386
400
- @ Deprecated
387
+ @ Deprecated // This might be useful if we eliminate the preference pane that defines packages to analyze.
401
388
static class YamlAssetMapVisitor extends YamlRecursivePsiElementVisitor {
402
389
HashMap <String , String > assetMap = new HashMap <>();
403
390
List <String > iconClassNames = new ArrayList <>();
0 commit comments