Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate docs of enums and rpc clients, some refactoring #909

Merged
merged 10 commits into from
Jan 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions protoc_plugin/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@
This change requires protobuf-4.0.0.
* Generated files now export `GeneratedMessageGenericExtensions` from the
protobuf library. ([#503], [#907])
* Generate doc comments for enum types and values, rpc services and methods.
([#900], [#909])

[#738]: https://github.com/google/protobuf.dart/issues/738
[#903]: https://github.com/google/protobuf.dart/pull/903
[#503]: https://github.com/google/protobuf.dart/issues/503
[#907]: https://github.com/google/protobuf.dart/pull/907
[#900]: https://github.com/google/protobuf.dart/issues/900
[#909]: https://github.com/google/protobuf.dart/pull/909

## 21.1.2

Expand Down
1 change: 1 addition & 0 deletions protoc_plugin/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ TEST_PROTO_LIST = \
google/protobuf/wrappers \
custom_option \
dart_name \
doc_comments \
default_value_escape \
entity \
enum_extension \
Expand Down
40 changes: 36 additions & 4 deletions protoc_plugin/lib/src/client_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,29 @@ class ClientApiGenerator {
final String className;
final Set<String> usedMethodNames = {...reservedMemberNames};

ClientApiGenerator(this.service, Set<String> usedNames)
/// Tag of `FileDescriptorProto.service`.
static const _fileDescriptorServiceTag = 6;

/// Tag of `ServiceDescriptorProto.method`.
static const _serviceDescriptorMethodTag = 2;

/// Index of the service in `FileDescriptorProto.service` repeated field.
final int _repeatedFieldIndex;

List<int> get _serviceDescriptorPath => [
...service.fileGen.fieldPath,
_fileDescriptorServiceTag,
_repeatedFieldIndex
];

List<int> _methodDescriptorPath(int methodRepeatedFieldIndex) => [
..._serviceDescriptorPath,
_serviceDescriptorMethodTag,
methodRepeatedFieldIndex
];

ClientApiGenerator(
this.service, Set<String> usedNames, this._repeatedFieldIndex)
: className = disambiguateName(
avoidInitialUnderscore(service._descriptor.name),
usedNames,
Expand All @@ -20,27 +42,37 @@ class ClientApiGenerator {
String get _clientType => '$protobufImportPrefix.RpcClient';

void generate(IndentingWriter out) {
final commentBlock = service.fileGen.commentBlock(_serviceDescriptorPath);
if (commentBlock != null) {
out.println(commentBlock);
}
out.addBlock('class ${className}Api {', '}', () {
out.println('$_clientType _client;');
out.println('${className}Api(this._client);');
out.println();

for (final m in service._descriptor.method) {
generateMethod(out, m);
for (var i = 0; i < service._descriptor.method.length; i++) {
generateMethod(out, service._descriptor.method[i], i);
}
});
out.println();
}

// Subclasses can override this.
void generateMethod(IndentingWriter out, MethodDescriptorProto m) {
void generateMethod(IndentingWriter out, MethodDescriptorProto m,
int methodRepeatedFieldIndex) {
final methodName = disambiguateName(
avoidInitialUnderscore(service._methodName(m.name)),
usedMethodNames,
defaultSuffixes());
final inputType = service._getDartClassName(m.inputType, forMainFile: true);
final outputType =
service._getDartClassName(m.outputType, forMainFile: true);
final commentBlock = service.fileGen
.commentBlock(_methodDescriptorPath(methodRepeatedFieldIndex));
if (commentBlock != null) {
out.println(commentBlock);
}
out.addBlock(
'$asyncImportPrefix.Future<$outputType> $methodName('
'$protobufImportPrefix.ClientContext? ctx, $inputType request) =>',
Expand Down
38 changes: 21 additions & 17 deletions protoc_plugin/lib/src/enum_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ class EnumAlias {

class EnumGenerator extends ProtobufContainer {
@override
final ProtobufContainer? parent;
final ProtobufContainer parent;

@override
final String? classname;
final String classname;

@override
final String fullName;
Expand All @@ -32,16 +32,15 @@ class EnumGenerator extends ProtobufContainer {
List<int>? _fieldPath;
final List<int> _fieldPathSegment;

/// See [[ProtobufContainer]
@override
List<int>? get fieldPath =>
_fieldPath ??= List.from(parent!.fieldPath!)..addAll(_fieldPathSegment);
List<int> get fieldPath =>
_fieldPath ??= List.from(parent.fieldPath!)..addAll(_fieldPathSegment);

EnumGenerator._(EnumDescriptorProto descriptor, this.parent,
Set<String> usedClassNames, int repeatedFieldIndex, int fieldIdTag)
: _fieldPathSegment = [fieldIdTag, repeatedFieldIndex],
classname = messageOrEnumClassName(descriptor.name, usedClassNames,
parent: parent!.classname ?? ''),
parent: parent.classname ?? ''),
fullName = parent.fullName == ''
? descriptor.name
: '${parent.fullName}.${descriptor.name}',
Expand Down Expand Up @@ -80,10 +79,10 @@ class EnumGenerator extends ProtobufContainer {
_nestedFieldTag);

@override
String get package => parent!.package;
String get package => parent.package;

@override
FileGenerator? get fileGen => parent!.fileGen;
FileGenerator? get fileGen => parent.fileGen;

/// Make this enum available as a field type.
void register(GenerationContext ctx) {
Expand All @@ -103,17 +102,15 @@ class EnumGenerator extends ProtobufContainer {
static const int _enumValueTag = 2;

void generate(IndentingWriter out) {
final comment = fileGen?.commentBlock(fieldPath!);
if (comment != null) {
out.println(comment);
final commentBlock = fileGen?.commentBlock(fieldPath);
if (commentBlock != null) {
out.println(commentBlock);
}
out.addAnnotatedBlock(
'class $classname extends $protobufImportPrefix.ProtobufEnum {',
'}\n', [
NamedLocation(
name: classname!,
fieldPathSegment: fieldPath!,
start: 'class '.length)
name: classname, fieldPathSegment: fieldPath, start: 'class '.length)
], () {
// -----------------------------------------------------------------
// Define enum types.
Expand All @@ -124,14 +121,21 @@ class EnumGenerator extends ProtobufContainer {
out.addSuffix(
omitEnumNames.constFieldName, omitEnumNames.constDefinition);
final conditionalValName = omitEnumNames.createTernary(val.name);
final fieldPathSegment = List<int>.from(fieldPath)
..addAll([_enumValueTag, _originalCanonicalIndices[i]]);

final commentBlock = fileGen?.commentBlock(fieldPathSegment);
if (commentBlock != null) {
out.println(commentBlock);
}

out.printlnAnnotated(
'static const $classname $name = '
'$classname._(${val.number}, $conditionalValName);',
[
NamedLocation(
name: name,
fieldPathSegment: List.from(fieldPath!)
..addAll([_enumValueTag, _originalCanonicalIndices[i]]),
fieldPathSegment: fieldPathSegment,
start: 'static const $classname '.length)
]);
}
Expand All @@ -146,7 +150,7 @@ class EnumGenerator extends ProtobufContainer {
[
NamedLocation(
name: name,
fieldPathSegment: List.from(fieldPath!)
fieldPathSegment: List.from(fieldPath)
..addAll([_enumValueTag, _originalAliasIndices[i]]),
start: 'static const $classname '.length)
]);
Expand Down
5 changes: 3 additions & 2 deletions protoc_plugin/lib/src/file_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -173,15 +173,16 @@ class FileGenerator extends ProtobufContainer {
extensionGenerators.add(ExtensionGenerator.topLevel(
descriptor.extension[i], this, usedExtensionNames, i));
}
for (final service in descriptor.service) {
for (var i = 0; i < descriptor.service.length; i++) {
final service = descriptor.service[i];
if (options.useGrpc) {
grpcGenerators.add(GrpcServiceGenerator(service, this));
} else {
final serviceGen =
ServiceGenerator(service, this, usedTopLevelServiceNames);
serviceGenerators.add(serviceGen);
clientApiGenerators
.add(ClientApiGenerator(serviceGen, usedTopLevelNames));
.add(ClientApiGenerator(serviceGen, usedTopLevelNames, i));
}
}
}
Expand Down
17 changes: 10 additions & 7 deletions protoc_plugin/lib/src/message_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ class MessageGenerator extends ProtobufContainer {

final List<int> _fieldPathSegment;

/// See [[ProtobufContainer]
@override
late final List<int> fieldPath = List.from(parent!.fieldPath!)
..addAll(_fieldPathSegment);
Expand Down Expand Up @@ -118,8 +117,13 @@ class MessageGenerator extends ProtobufContainer {
}
}

/// Tag of `FileDescriptorProto.message_type`.
static const _topLevelMessageTag = 4;

/// Tag of `DescriptorProto.nested_type`.
static const _nestedMessageTag = 3;

/// Tag of `DescriptorProto.field`.
static const _messageFieldTag = 2;

MessageGenerator.topLevel(
Expand Down Expand Up @@ -319,13 +323,12 @@ class MessageGenerator extends ProtobufContainer {
extendedClass = 'GeneratedMessage';
}

var commentBlock = fileGen.commentBlock(fieldPath) ?? '';
if (commentBlock.isNotEmpty) {
commentBlock = '$commentBlock\n';
final commentBlock = fileGen.commentBlock(fieldPath);
if (commentBlock != null) {
out.println(commentBlock);
}

out.addAnnotatedBlock(
'${commentBlock}class $classname extends $protobufImportPrefix.$extendedClass$mixinClause {',
'class $classname extends $protobufImportPrefix.$extendedClass$mixinClause {',
'}', [
NamedLocation(
name: classname, fieldPathSegment: fieldPath, start: 'class '.length)
Expand Down Expand Up @@ -539,7 +542,7 @@ class MessageGenerator extends ProtobufContainer {

final commentBlock = fileGen.commentBlock(memberFieldPath);
if (commentBlock != null) {
out.println(commentBlock.trim());
out.println(commentBlock);
}

_emitDeprecatedIf(field.isDeprecated, out);
Expand Down
12 changes: 12 additions & 0 deletions protoc_plugin/lib/src/shared.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ const grpcImportPrefix = r'$grpc';
const mixinImportPrefix = r'$mixin';

extension FileDescriptorProtoExt on FileGenerator {
/// Convert leading comments of a definition at [path] to Dart doc comment
/// syntax.
///
/// This never returns an empty string: if the comment is empty it returns
/// `null`.
///
/// The output can contain multiple lines. None of the lines will have
/// trailing whitespace.
String? commentBlock(List<int> path) {
final bits = descriptor.sourceCodeInfo.location
.where((element) => element.path.toString() == path.toString())
Expand All @@ -33,6 +41,10 @@ extension FileDescriptorProtoExt on FileGenerator {
}
}

/// Convert a comment to Dart doc comment syntax.
///
/// This is the internal method for [FileDescriptorProtoExt.commentBlock],
/// public to be able to test.
String? toDartComment(String value) {
if (value.isEmpty) return null;

Expand Down
22 changes: 22 additions & 0 deletions protoc_plugin/test/doc_comments_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) 2023, 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 'dart:io';

import 'package:test/test.dart';

import 'golden_file.dart';

void main() {
test('Doc comment generation for messages', () {
final actual = File('out/protos/doc_comments.pb.dart').readAsStringSync();
expectMatchesGoldenFile(actual, 'test/goldens/doc_comments');
});

test('Doc comment generation for enums', () {
final actual = File('out/protos/constructor_args/doc_comments.pbenum.dart')
.readAsStringSync();
expectMatchesGoldenFile(actual, 'test/goldens/doc_comments.pbenum');
});
}
Loading
Loading