Files
sdk/pkg/analysis_server/tool/spec/codegen_dart_notification_handler.dart
T
Paul Berry fb2d1171e5 Reference all genearted file paths to pkg directory.
Modifies `GeneratedDirectory.outputDirPath` and
`GeneratedFile.outputPath` to be relative to the SDK's `pkg` directory
rather than relative to the containing package. Accordingly, modifies
the `GeneratedContent` methods `check`, `checkAll`, `generate`,
`generateAll`, `output`, as well as the `DirectoryContentsComputer`
and `FileContentsComputer` callbacks, so that their first parameter is
the path to the `pkg` directory rather than the path to the containing
package.

Also modifies the `readApi` functions in `pkg/analysis_server` and
`pkg/analyzer_plugin` to accept a path to the `pkg` directory rather
than a path to the containing package, since these functions are
called by code generation callbacks.

These changes should make code generation logic easier to reason
about. They also will make it easier to move the outputs of code
generation from one package to another, which will pave the way for
some follow-up work in which I intend to start sharing error message
representations belonging to `pkg/analyzer`, `pkg/front_end`, and
`pkg/_fe_analyzer_shared`.

Change-Id: Ia9b369b16f2df931c8a472f91400f2c5a0b8be9d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/438480
Reviewed-by: Samuel Rawlins <srawlins@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
2025-07-04 05:04:50 -07:00

159 lines
4.9 KiB
Dart

// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:analyzer_utilities/html_dom.dart';
import 'package:analyzer_utilities/html_generator.dart';
import 'package:analyzer_utilities/tools.dart';
import 'api.dart';
import 'codegen_dart.dart';
import 'codegen_protocol_constants.dart' show generateConstName;
import 'from_html.dart';
GeneratedFile clientTarget() {
return GeneratedFile(
'analysis_server_client/lib/handler/notification_handler.dart',
(pkgRoot) async {
var visitor = CodegenNotificationHandlerVisitor(readApi(pkgRoot));
return visitor.collectCode(visitor.visitApi);
},
);
}
String _capitalize(String name) =>
'${name.substring(0, 1).toUpperCase()}${name.substring(1)}';
List<String> _generateDartDoc(Element html) =>
html.children
.where((Element elem) => elem.name == 'p')
.map<String>((Element elem) => innerText(elem).trim())
.toList();
String _generateNotificationMethodName(String domainName, String event) =>
'on${_capitalize(domainName)}${_capitalize(event)}';
String _generateParamTypeName(String domainName, String event) =>
'${_capitalize(domainName)}${_capitalize(event)}Params';
/// Visitor which produces Dart code representing the API.
class CodegenNotificationHandlerVisitor extends DartCodegenVisitor
with CodeGenerator {
CodegenNotificationHandlerVisitor(super.api) {
codeGeneratorSettings.commentLineLength = 79;
codeGeneratorSettings.docCommentStartMarker = null;
codeGeneratorSettings.docCommentLineLeader = '/// ';
codeGeneratorSettings.docCommentEndMarker = null;
codeGeneratorSettings.languageName = 'dart';
}
void emitDartdoc(List<String> dartdoc) {
var first = true;
for (var paragraph in dartdoc) {
if (first) {
first = false;
} else {
writeln(' ///');
}
for (var line in paragraph.split(RegExp('\r?\n'))) {
writeln(' /// ${line.trim()}');
}
}
}
void emitImports() {
writeln("import 'package:analysis_server_client/protocol.dart';");
}
void emitNotificationHandler() {
var visitor = _NotificationVisitor(api)..visitApi();
var notifications = visitor.notificationConstants;
notifications.sort((n1, n2) => n1.constName.compareTo(n2.constName));
writeln('''
/// [NotificationHandler] processes analysis server notifications
/// and dispatches those notifications to different methods based upon
/// the type of notification. Clients may override
/// any of the "on<EventName>" methods that are of interest.
///
/// Clients may mix-in this class, but may not implement it.
mixin NotificationHandler {
void handleEvent(Notification notification) {
var params = notification.params;
var decoder = ResponseDecoder(null);
switch (notification.event) {
''');
for (var notification in notifications) {
writeln(' case ${notification.constName}:');
writeln(' ${notification.methodName}(');
writeln(' ${notification.paramsTypeName}');
writeln(" .fromJson(decoder, 'params', params));");
writeln(' break;');
}
writeln(' default:');
writeln(' onUnknownNotification(notification.event, params);');
writeln(' break;');
writeln(' }');
writeln(' }');
for (var notification in notifications) {
writeln();
emitDartdoc(notification.dartdoc);
writeln(' void ${notification.methodName}(');
writeln(' ${notification.paramsTypeName} params) {');
writeln(' }');
}
writeln();
writeln(' /// Reports a notification that is not processed');
writeln(' /// by any other notification handlers.');
writeln(' void onUnknownNotification(String event, params) {}');
writeln('}');
}
@override
void visitApi() {
outputHeader(year: '2018');
writeln();
emitImports();
emitNotificationHandler();
}
}
class _Notification {
final String constName;
final String methodName;
final String paramsTypeName;
final List<String> dartdoc;
_Notification(
this.constName,
this.methodName,
this.paramsTypeName,
this.dartdoc,
);
}
class _NotificationVisitor extends HierarchicalApiVisitor {
final notificationConstants = <_Notification>[];
_NotificationVisitor(super.api);
@override
void visitNotification(Notification notification) {
notificationConstants.add(
_Notification(
generateConstName(
notification.domainName,
'notification',
notification.event,
),
_generateNotificationMethodName(
notification.domainName,
notification.event,
),
_generateParamTypeName(notification.domainName, notification.event),
_generateDartDoc(notification.html!),
),
);
}
}