Skip to content

Commit 34e54a4

Browse files
DanTupCommit Queue
authored andcommitted
[analysis_server] Support properties in line values behind a flag
+ fix them not showing up inside methods Change-Id: I885afe68d56ab60c20b816373fbd071592cfd463 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/412644 Reviewed-by: Phil Quitslund <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Brian Wilkerson <[email protected]>
1 parent 0865762 commit 34e54a4

File tree

3 files changed

+320
-47
lines changed

3 files changed

+320
-47
lines changed

pkg/analysis_server/lib/src/lsp/client_configuration.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,10 @@ class LspGlobalClientConfiguration extends LspResourceClientConfiguration {
191191
bool get completeFunctionCalls =>
192192
_settings['completeFunctionCalls'] as bool? ?? false;
193193

194+
/// A flag for including property access in Inline Values.
195+
bool get experimentalInlineValuesProperties =>
196+
_settings['experimentalInlineValuesProperties'] as bool? ?? false;
197+
194198
/// A flag for enabling interactive refactors flagged as experimental.
195199
///
196200
/// This flag is likely to be used by both analysis server developers (working

pkg/analysis_server/lib/src/lsp/handlers/handler_inline_value.dart

Lines changed: 118 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44

55
import 'package:analysis_server/lsp_protocol/protocol.dart' hide MessageType;
66
import 'package:analysis_server/lsp_protocol/protocol.dart';
7+
import 'package:analysis_server/src/lsp/client_configuration.dart';
78
import 'package:analysis_server/src/lsp/error_or.dart';
89
import 'package:analysis_server/src/lsp/extensions/positions.dart';
910
import 'package:analysis_server/src/lsp/handlers/handlers.dart';
1011
import 'package:analysis_server/src/lsp/mapping.dart';
1112
import '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';
1214
import 'package:analyzer/dart/ast/ast.dart';
1315
import 'package:analyzer/dart/ast/visitor.dart';
1416
import '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].
163206
class _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

Comments
 (0)