Skip to content

Commit 3045a56

Browse files
authored
[Property Editor] Better error handling (#9101)
1 parent d2d7962 commit 3045a56

File tree

3 files changed

+77
-30
lines changed

3 files changed

+77
-30
lines changed

packages/devtools_app/lib/src/shared/analytics/constants/_property_editor_sidebar_constants.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ class PropertyEditorSidebar {
99
/// Analytics id to track events that come from the DTD editor sidebar.
1010
static String get id => 'propertyEditorSidebar';
1111

12+
/// Identifier for errors returned from the getEditableArguments API.
13+
static String get getEditableArgumentsIdentifier =>
14+
'${id}Error-getEditableArguments';
15+
16+
/// Identifier for errors returned from the editArgument API.
17+
static String get editArgumentIdentifier => '${id}Error-editArgument';
18+
1219
/// Analytics id for opening the documentation.
1320
static String get documentationLink => 'propertyEditorDocumentation';
1421

packages/devtools_app/lib/src/shared/editor/api_classes.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,16 @@ abstract class Field {
140140
static const vmServiceUri = 'vmServiceUri';
141141
}
142142

143+
/// Multi-purpose errors used by the Analysis Server.
144+
enum AnalysisServerError {
145+
/// The document content was modified before the request was completed.
146+
contentModifiedError(code: -32801);
147+
148+
const AnalysisServerError({required this.code});
149+
150+
final int code;
151+
}
152+
143153
/// A base class for all known events that an editor can produce.
144154
///
145155
/// The set of subclasses is not guaranteed to match actual events from any

packages/devtools_app/lib/src/shared/editor/editor_client.dart

Lines changed: 60 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,11 @@ import 'package:devtools_app_shared/utils.dart';
88
import 'package:dtd/dtd.dart';
99
import 'package:flutter/foundation.dart';
1010
import 'package:json_rpc_2/json_rpc_2.dart';
11-
import 'package:logging/logging.dart';
1211

1312
import '../analytics/constants.dart';
13+
import '../framework/app_error_handling.dart';
1414
import 'api_classes.dart';
1515

16-
final _log = Logger('editor_client');
17-
1816
/// A client wrapper that connects to an editor over DTD.
1917
///
2018
/// Changes made to the editor services/events should be considered carefully to
@@ -262,18 +260,47 @@ class EditorClient extends DisposableController
262260
}) async {
263261
final method = editableArgumentsMethodName.value;
264262
if (method == null) return null;
265-
final response = await _callLspApi(
266-
method,
267-
params: {
268-
'type': 'Object', // This is required by DTD.
269-
'textDocument': textDocument.toJson(),
270-
'position': position.toJson(),
271-
},
272-
);
273-
final result = response.result[Field.result];
274-
return result != null
275-
? EditableArgumentsResult.fromJson(result as Map<String, Object?>)
276-
: null;
263+
264+
String? errorMessage;
265+
StackTrace? stack;
266+
EditableArgumentsResult? result;
267+
try {
268+
final response = await _callLspApi(
269+
method,
270+
params: {
271+
'type': 'Object', // This is required by DTD.
272+
'textDocument': textDocument.toJson(),
273+
'position': position.toJson(),
274+
},
275+
);
276+
final rawResult = response.result[Field.result];
277+
result =
278+
rawResult != null
279+
? EditableArgumentsResult.fromJson(
280+
rawResult as Map<String, Object?>,
281+
)
282+
: null;
283+
} on RpcException catch (e, st) {
284+
// We expect content modified errors if a user edits their code before the
285+
// request completes. Therefore it is safe to ignore.
286+
if (e.code != AnalysisServerError.contentModifiedError.code) {
287+
errorMessage = e.message;
288+
stack = st;
289+
}
290+
} catch (e, st) {
291+
errorMessage = 'Unknown error: $e';
292+
stack = st;
293+
} finally {
294+
if (errorMessage != null) {
295+
reportError(
296+
errorMessage,
297+
errorType: PropertyEditorSidebar.getEditableArgumentsIdentifier,
298+
stack: stack,
299+
);
300+
}
301+
}
302+
303+
return result;
277304
}
278305

279306
/// Requests that the Analysis Server makes a code edit for an argument.
@@ -290,6 +317,9 @@ class EditorClient extends DisposableController
290317
errorMessage: 'API is unavailable.',
291318
);
292319
}
320+
321+
String? errorMessage;
322+
StackTrace? stack;
293323
try {
294324
await _callLspApi(
295325
method,
@@ -301,22 +331,22 @@ class EditorClient extends DisposableController
301331
},
302332
);
303333
return EditArgumentResponse(success: true);
304-
} on RpcException catch (e) {
305-
final errorMessage = e.message;
306-
_log.severe(errorMessage);
307-
return EditArgumentResponse(
308-
success: false,
309-
errorCode: e.code,
310-
errorMessage: errorMessage,
311-
);
312-
} catch (e) {
313-
final errorMessage = 'Unknown error: $e';
314-
_log.severe(errorMessage);
315-
return EditArgumentResponse(
316-
success: false,
317-
errorMessage: 'Unknown error: $e',
318-
);
334+
} on RpcException catch (e, st) {
335+
errorMessage = e.message;
336+
stack = st;
337+
} catch (e, st) {
338+
errorMessage = 'Unknown error: $e';
339+
stack = st;
340+
} finally {
341+
if (errorMessage != null) {
342+
reportError(
343+
errorMessage,
344+
errorType: PropertyEditorSidebar.editArgumentIdentifier,
345+
stack: stack,
346+
);
347+
}
319348
}
349+
return EditArgumentResponse(success: false, errorMessage: errorMessage);
320350
}
321351

322352
Future<DTDResponse> _call(

0 commit comments

Comments
 (0)