44
55import 'package:analysis_server/lsp_protocol/protocol.dart' hide MessageType;
66import 'package:analysis_server/lsp_protocol/protocol.dart' ;
7+ import 'package:analysis_server/src/lsp/client_configuration.dart' ;
78import 'package:analysis_server/src/lsp/error_or.dart' ;
89import 'package:analysis_server/src/lsp/extensions/positions.dart' ;
910import 'package:analysis_server/src/lsp/handlers/handlers.dart' ;
1011import 'package:analysis_server/src/lsp/mapping.dart' ;
1112import 'package:analysis_server/src/lsp/registration/feature_registration.dart' ;
13+ import 'package:analysis_server/src/services/correction/dart/convert_null_check_to_null_aware_element_or_entry.dart' ;
1214import 'package:analyzer/dart/ast/ast.dart' ;
1315import 'package:analyzer/dart/ast/visitor.dart' ;
1416import 'package:analyzer/dart/element/element2.dart' ;
@@ -66,13 +68,19 @@ class InlineValueHandler
6668 // Find the function that is executing. We will only show values for
6769 // this single function expression.
6870 var node = await server.getNodeAtOffset (filePath, stoppedOffset);
69- var function = node? .thisOrAncestorOfType <FunctionExpression >();
71+ var function = node? .thisOrAncestorMatching (
72+ (node) => node is FunctionExpression || node is MethodDeclaration ,
73+ );
7074 if (function == null ) {
7175 return success (null );
7276 }
7377
7478 var collector = _InlineValueCollector (lineInfo, applicableRange);
75- var visitor = _InlineValueVisitor (collector, function);
79+ var visitor = _InlineValueVisitor (
80+ server.lspClientConfiguration,
81+ collector,
82+ function,
83+ );
7684 function.accept (visitor);
7785
7886 return success (collector.values.values.toList ());
@@ -117,54 +125,93 @@ class _InlineValueCollector {
117125
118126 _InlineValueCollector (this .lineInfo, this .applicableRange);
119127
128+ /// Records an expression inline value for [element] with [offset] /[length] .
129+ ///
130+ /// Expression values are sent to the client without expressions because the
131+ /// client can use the range from the source to get the expression.
132+ void recordExpression (Element2 ? element, int offset, int length) {
133+ assert (offset >= 0 );
134+ assert (length > 0 );
135+ if (element == null ) return ;
136+
137+ var range = toRange (lineInfo, offset, length);
138+
139+ var value = InlineValue .t1 (
140+ InlineValueEvaluatableExpression (
141+ range: range,
142+ // We don't provide expression, because it always matches the source
143+ // code and can be inferred.
144+ ),
145+ );
146+ _record (value, element);
147+ }
148+
120149 /// Records a variable inline value for [element] with [offset] /[length] .
121150 ///
122- /// Variable inline values are sent to the client without expressions/ names
123- /// because the client can infer the name from the range and look it up from
124- /// the debuggers Scopes/Variables.
151+ /// Variable inline values are sent to the client without names because the
152+ /// client can infer the name from the range and look it up from the debuggers
153+ /// Scopes/Variables.
125154 void recordVariableLookup (Element2 ? element, int offset, int length) {
126- if (element == null || element.isWildcardVariable) return ;
127-
128155 assert (offset >= 0 );
129156 assert (length > 0 );
157+ if (element == null || element.isWildcardVariable) return ;
130158
131159 var range = toRange (lineInfo, offset, length);
132160
161+ var value = InlineValue .t3 (
162+ InlineValueVariableLookup (
163+ caseSensitiveLookup: true ,
164+ range: range,
165+ // We don't provide name, because it always matches the source code
166+ // for a variable and can be inferred.
167+ ),
168+ );
169+ _record (value, element);
170+ }
171+
172+ /// Extracts the range from an [InlineValue] .
173+ Range _getRange (InlineValue value) {
174+ return value.map (
175+ (expression) => expression.range,
176+ (text) => text.range,
177+ (variable) => variable.range,
178+ );
179+ }
180+
181+ /// Records an inline value [value] for [element] if it is within range and is
182+ /// the latest one in the source for that element.
183+ void _record (InlineValue value, Element2 element) {
184+ var range = _getRange (value);
185+
133186 // Never record anything outside of the visible range.
134187 if (! range.intersects (applicableRange)) {
135188 return ;
136189 }
137190
138191 // We only want to show each variable once, so keep only the one furthest
139192 // into the source (closest to the execution pointer).
140- var existingPosition = values[element]? .map (
141- (expression) => expression.range.start,
142- (text) => text.range.start,
143- (variable) => variable.range.start,
144- );
145- if (existingPosition != null &&
146- existingPosition.isAfterOrEqual (range.start)) {
147- return ;
193+ if (values[element] case var existingValue? ) {
194+ var existingPosition = _getRange (existingValue).start;
195+ if (existingPosition.isAfterOrEqual (range.start)) {
196+ return ;
197+ }
148198 }
149199
150- values[element] = InlineValue .t3 (
151- InlineValueVariableLookup (
152- caseSensitiveLookup: true ,
153- range: range,
154- // We don't provide name, because it always matches the source code
155- // for a variable and can be inferred.
156- ),
157- );
200+ values[element] = value;
158201 }
159202}
160203
161204/// Visits a function expression and reports nodes that should have inline
162205/// values to [collector] .
163206class _InlineValueVisitor extends GeneralizingAstVisitor <void > {
207+ final LspClientConfiguration clientConfiguration;
164208 final _InlineValueCollector collector;
165- final FunctionExpression rootFunction;
209+ final AstNode rootNode;
210+
211+ _InlineValueVisitor (this .clientConfiguration, this .collector, this .rootNode);
166212
167- _InlineValueVisitor (this .collector, this .rootFunction);
213+ bool get experimentalInlineValuesProperties =>
214+ clientConfiguration.global.experimentalInlineValuesProperties;
168215
169216 @override
170217 void visitFormalParameter (FormalParameter node) {
@@ -182,20 +229,61 @@ class _InlineValueVisitor extends GeneralizingAstVisitor<void> {
182229 @override
183230 void visitFunctionExpression (FunctionExpression node) {
184231 // Don't descend into nested functions.
185- if (node != rootFunction ) {
232+ if (node != rootNode ) {
186233 return ;
187234 }
188235
189236 super .visitFunctionExpression (node);
190237 }
191238
239+ @override
240+ void visitPrefixedIdentifier (PrefixedIdentifier node) {
241+ if (experimentalInlineValuesProperties) {
242+ var parent = node.parent;
243+
244+ // Never produce values for the left side of a property access.
245+ var isTarget = parent is PropertyAccess && node == parent.realTarget;
246+ if (! isTarget) {
247+ collector.recordExpression (node.element, node.offset, node.length);
248+ }
249+ }
250+
251+ super .visitPrefixedIdentifier (node);
252+ }
253+
254+ @override
255+ void visitPropertyAccess (PropertyAccess node) {
256+ if (experimentalInlineValuesProperties && node.target is Identifier ) {
257+ collector.recordExpression (
258+ node.canonicalElement,
259+ node.offset,
260+ node.length,
261+ );
262+ }
263+
264+ super .visitPropertyAccess (node);
265+ }
266+
192267 @override
193268 void visitSimpleIdentifier (SimpleIdentifier node) {
194- switch (node.element) {
195- case LocalVariableElement2 (name3: _? ):
196- case FormalParameterElement ():
197- collector.recordVariableLookup (node.element, node.offset, node.length);
269+ var parent = node.parent;
270+
271+ // Never produce values for the left side of a prefixed identifier.
272+ // Or parts of an invocation.
273+ var isTarget = parent is PrefixedIdentifier && node == parent.prefix;
274+ var isInvocation = parent is InvocationExpression ;
275+ if (! isTarget && ! isInvocation) {
276+ switch (node.element) {
277+ case LocalVariableElement2 (name3: _? ):
278+ case FormalParameterElement ():
279+ collector.recordVariableLookup (
280+ node.element,
281+ node.offset,
282+ node.length,
283+ );
284+ }
198285 }
286+
199287 super .visitSimpleIdentifier (node);
200288 }
201289
0 commit comments