@@ -9,6 +9,11 @@ import jsonrpc.Types.NoData;
9
9
import languageServerProtocol .Types .InlineValue ;
10
10
import languageServerProtocol .protocol .InlineValue ;
11
11
import refactor .discover .Identifier ;
12
+ import refactor .discover .IdentifierPos ;
13
+ import refactor .discover .Type ;
14
+ import tokentree .utils .TokenTreeCheckUtils ;
15
+
16
+ using tokentree .TokenTreeAccessHelper ;
12
17
13
18
class InlineValueFeature {
14
19
final context : Context ;
@@ -26,56 +31,203 @@ class InlineValueFeature {
26
31
}
27
32
28
33
function onInlineValue (params : InlineValueParams , token : CancellationToken , resolve : Array <InlineValue >-> Void , reject : ResponseError <NoData >-> Void ) {
34
+ final onResolve = context .startTimer (" textDocument/inlineValue" );
29
35
if (context .config .user .disableRefactorCache || context .config .user .disableInlineValue ) {
30
36
resolve ([]);
37
+ onResolve ();
31
38
return ;
32
39
}
33
40
34
41
var file = refactorCache .fileList .getFile (params .textDocument .uri .toFsPath ().toString ());
35
42
if (file == null ) {
36
43
reject .handler ()(" file not found" );
44
+ onResolve ();
37
45
return ;
38
46
}
39
47
40
48
var editDoc = new EditDoc (params .textDocument .uri .toFsPath (), new EditList (), context , converter );
41
49
42
50
var localScopedNames : Array <String > = [];
51
+ var outOfScope : Array <Identifier > = [];
52
+ var functionStartLine : Int = params .context .stoppedLocation .start .line ;
43
53
44
54
function matchLocalScoped (identifier : Identifier ): Bool {
55
+ switch (identifier .name ) {
56
+ case " this" | " super" :
57
+ return false ;
58
+ default :
59
+ }
45
60
return switch (identifier .type ) {
46
61
case ScopedLocal (scopeStart , scopeEnd , scopeType ):
62
+ var pos : IdentifierPos = {
63
+ fileName : identifier .pos .fileName ,
64
+ start : scopeStart ,
65
+ end : scopeEnd
66
+ };
67
+ var range = editDoc .posToRange (pos );
68
+ if (! range .contains (params .context .stoppedLocation )) {
69
+ outOfScope .push (identifier );
70
+ return false ;
71
+ }
47
72
localScopedNames .push (identifier .name );
48
73
true ;
49
- case Access : true ;
74
+ case Access :
75
+ for (scoped in outOfScope ) {
76
+ switch (scoped .type ) {
77
+ case ScopedLocal (scopeStart , scopeEnd , scopeType ):
78
+ if ((scoped .name == identifier .name ) || identifier .name .startsWith (' ${scoped .name }.' )) {
79
+ if (scopeStart <= identifier .pos .start && scopeEnd >= identifier .pos .end ) {
80
+ return false ;
81
+ }
82
+ }
83
+ default :
84
+ }
85
+ }
86
+ true ;
87
+ case Method (_ ):
88
+ var functionRange : Range = editDoc .posToRange (identifier .pos );
89
+ if (functionRange .start .line <= params .context .stoppedLocation .start .line ) {
90
+ functionStartLine = functionRange .start .line ;
91
+ }
92
+ false ;
50
93
default : false ;
51
94
}
52
95
}
53
96
final identifiers : Array <Identifier > = file .findAllIdentifiers (matchLocalScoped );
54
97
final inlineValueVars : Array <InlineValue > = [];
55
98
for (identifier in identifiers ) {
56
99
var identifierRange = editDoc .posToRange (identifier .pos );
100
+ if (identifierRange .start .line < functionStartLine ) {
101
+ continue ;
102
+ }
57
103
if (! params .range .contains (identifierRange )) {
58
104
continue ;
59
105
}
60
- var needsExpression : Bool = identifier .name .contains (" ." );
61
- if ((identifier .type == Access ) && ! localScopedNames .contains (identifier .name )) {
62
- needsExpression = true ;
106
+ if (params .context .stoppedLocation .end .line < identifierRange .start .line ) {
107
+ continue ;
63
108
}
64
-
65
- if (needsExpression ) {
66
- inlineValueVars .push ({
67
- range : identifierRange ,
68
- expression : identifier .name
69
- });
70
- } else {
71
- inlineValueVars .push ({
72
- range : identifierRange ,
73
- variableName : identifier .name ,
74
- caseSensitiveLookup : true
75
- });
109
+ if (isSharpCondition (params , identifier )) {
110
+ continue ;
76
111
}
112
+ if (isTypeParam (params , identifier )) {
113
+ continue ;
114
+ }
115
+ if (isLocalFunctionName (params , identifier )) {
116
+ continue ;
117
+ }
118
+ var hasDot : Bool = identifier .name .contains (" ." );
119
+ if (! hasDot ) {
120
+ if (skipIdentifier (identifier )) {
121
+ continue ;
122
+ }
123
+ }
124
+ inlineValueVars .push ({
125
+ range : identifierRange ,
126
+ expression : identifier .name
127
+ });
77
128
}
78
129
79
130
resolve (inlineValueVars );
131
+ onResolve ();
132
+ }
133
+
134
+ function isSharpCondition (params : InlineValueParams , identifier : Identifier ): Bool {
135
+ final doc : Null <HaxeDocument > = context .documents .getHaxe (params .textDocument .uri );
136
+ final token = doc ?. tokens ?. getTokenAtOffset (identifier .pos .start );
137
+
138
+ if (token == null ) {
139
+ return false ;
140
+ }
141
+ var parent = token .parent ;
142
+ while (parent != null ) {
143
+ switch (parent .tok ) {
144
+ case Dot :
145
+ case Const (CIdent (_ )):
146
+ case POpen :
147
+ case Unop (OpNot ):
148
+ case Binop (OpBoolAnd ) | Binop (OpBoolOr ):
149
+ case Sharp (" if" ) | Sharp (" elseif" ):
150
+ return true ;
151
+ default :
152
+ return false ;
153
+ }
154
+ parent = parent .parent ;
155
+ }
156
+
157
+ return false ;
158
+ }
159
+
160
+ function isLocalFunctionName (params : InlineValueParams , identifier : Identifier ): Bool {
161
+ final doc : Null <HaxeDocument > = context .documents .getHaxe (params .textDocument .uri );
162
+ final token = doc ?. tokens ?. getTokenAtOffset (identifier .pos .start );
163
+
164
+ if (token == null ) {
165
+ return false ;
166
+ }
167
+ switch (token .parent ?. tok ) {
168
+ case Kwd (KwdFunction ):
169
+ return true ;
170
+ default :
171
+ return false ;
172
+ }
173
+ }
174
+
175
+ function isTypeParam (params : InlineValueParams , identifier : Identifier ): Bool {
176
+ final doc : Null <HaxeDocument > = context .documents .getHaxe (params .textDocument .uri );
177
+ final token = doc ?. tokens ?. getTokenAtOffset (identifier .pos .end );
178
+
179
+ if (token == null ) {
180
+ return false ;
181
+ }
182
+ var parent = token .parent ;
183
+ while (parent != null ) {
184
+ switch (parent .tok ) {
185
+ case Dot :
186
+ case DblDot :
187
+ case BrOpen :
188
+ case Const (CIdent (_ )):
189
+ case Binop (OpAssign ):
190
+ case Sharp (_ ):
191
+ return true ;
192
+ case Binop (OpLt ):
193
+ return parent .access ().firstOf (Binop (OpGt )).exists ();
194
+ default :
195
+ return false ;
196
+ }
197
+ parent = parent .parent ;
198
+ }
199
+
200
+ return false ;
201
+ }
202
+
203
+ function skipIdentifier (identifier : Identifier ): Bool {
204
+ if (isTypeUsed (identifier .defineType , identifier .name )) {
205
+ return true ;
206
+ }
207
+ var allUses : Array <Identifier > = refactorCache .nameMap .getIdentifiers (identifier .name );
208
+ for (use in allUses ) {
209
+ switch (use .type ) {
210
+ case EnumField (_ ):
211
+ return true ;
212
+ default :
213
+ }
214
+ }
215
+ return false ;
216
+ }
217
+
218
+ function isTypeUsed (containerType : Null <Type >, name : String ): Bool {
219
+ if (containerType == null ) {
220
+ return false ;
221
+ }
222
+ final types : Array <Type > = refactorCache .typeList .findTypeName (name );
223
+ for (type in types ) {
224
+ switch (containerType .file .importsModule (type .file .getPackage (), type .file .getMainModulName (), name )) {
225
+ case None :
226
+ case ImportedWithAlias (_ ):
227
+ case Global | ParentPackage | SamePackage | Imported | StarImported :
228
+ return true ;
229
+ }
230
+ }
231
+ return false ;
80
232
}
81
233
}
0 commit comments