From 1784bf8ab2d34175ec71a913d0b3e6055d5c465f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Fri, 29 Dec 2023 10:37:53 +0100 Subject: [PATCH 1/9] Refactor and test doc comment generation Currently we have 3 places where we generate a doc comment, and each of these does it differently. With this they now generate the comments the same way. The doc comment generator functions are documented with the invariants, namely that they never return an empty string and the output is always trimmed (i.e. no trailing whitespace). --- protoc_plugin/lib/src/enum_generator.dart | 6 +++--- protoc_plugin/lib/src/message_generator.dart | 11 +++++------ protoc_plugin/lib/src/shared.dart | 12 ++++++++++++ 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/protoc_plugin/lib/src/enum_generator.dart b/protoc_plugin/lib/src/enum_generator.dart index 03c80b0a..8126b9ec 100644 --- a/protoc_plugin/lib/src/enum_generator.dart +++ b/protoc_plugin/lib/src/enum_generator.dart @@ -103,9 +103,9 @@ 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 {', diff --git a/protoc_plugin/lib/src/message_generator.dart b/protoc_plugin/lib/src/message_generator.dart index 95963d55..b15f49b7 100644 --- a/protoc_plugin/lib/src/message_generator.dart +++ b/protoc_plugin/lib/src/message_generator.dart @@ -319,13 +319,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) @@ -539,7 +538,7 @@ class MessageGenerator extends ProtobufContainer { final commentBlock = fileGen.commentBlock(memberFieldPath); if (commentBlock != null) { - out.println(commentBlock.trim()); + out.println(commentBlock); } _emitDeprecatedIf(field.isDeprecated, out); diff --git a/protoc_plugin/lib/src/shared.dart b/protoc_plugin/lib/src/shared.dart index b01fcdb4..8fe66bb5 100644 --- a/protoc_plugin/lib/src/shared.dart +++ b/protoc_plugin/lib/src/shared.dart @@ -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 path) { final bits = descriptor.sourceCodeInfo.location .where((element) => element.path.toString() == path.toString()) @@ -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; From 269e708319b322c8cee5abf6f13dc1a192327489 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Fri, 29 Dec 2023 11:10:16 +0100 Subject: [PATCH 2/9] Generate doc comments for enum values --- protoc_plugin/lib/src/enum_generator.dart | 22 ++++++++++++-------- protoc_plugin/lib/src/message_generator.dart | 1 - 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/protoc_plugin/lib/src/enum_generator.dart b/protoc_plugin/lib/src/enum_generator.dart index 8126b9ec..a9f7536b 100644 --- a/protoc_plugin/lib/src/enum_generator.dart +++ b/protoc_plugin/lib/src/enum_generator.dart @@ -32,9 +32,8 @@ class EnumGenerator extends ProtobufContainer { List? _fieldPath; final List _fieldPathSegment; - /// See [[ProtobufContainer] @override - List? get fieldPath => + List get fieldPath => _fieldPath ??= List.from(parent!.fieldPath!)..addAll(_fieldPathSegment); EnumGenerator._(EnumDescriptorProto descriptor, this.parent, @@ -103,7 +102,7 @@ class EnumGenerator extends ProtobufContainer { static const int _enumValueTag = 2; void generate(IndentingWriter out) { - final commentBlock = fileGen?.commentBlock(fieldPath!); + final commentBlock = fileGen?.commentBlock(fieldPath); if (commentBlock != null) { out.println(commentBlock); } @@ -111,9 +110,7 @@ class EnumGenerator extends ProtobufContainer { 'class $classname extends $protobufImportPrefix.ProtobufEnum {', '}\n', [ NamedLocation( - name: classname!, - fieldPathSegment: fieldPath!, - start: 'class '.length) + name: classname!, fieldPathSegment: fieldPath, start: 'class '.length) ], () { // ----------------------------------------------------------------- // Define enum types. @@ -124,14 +121,21 @@ class EnumGenerator extends ProtobufContainer { out.addSuffix( omitEnumNames.constFieldName, omitEnumNames.constDefinition); final conditionalValName = omitEnumNames.createTernary(val.name); + final fieldPathSegment = List.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) ]); } @@ -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) ]); diff --git a/protoc_plugin/lib/src/message_generator.dart b/protoc_plugin/lib/src/message_generator.dart index b15f49b7..035caf7e 100644 --- a/protoc_plugin/lib/src/message_generator.dart +++ b/protoc_plugin/lib/src/message_generator.dart @@ -67,7 +67,6 @@ class MessageGenerator extends ProtobufContainer { final List _fieldPathSegment; - /// See [[ProtobufContainer] @override late final List fieldPath = List.from(parent!.fieldPath!) ..addAll(_fieldPathSegment); From 7496ca5ffc2c8c3d8455b456924b6cae325a70a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Fri, 29 Dec 2023 11:16:54 +0100 Subject: [PATCH 3/9] Make EnumGenerator.className non nullable --- protoc_plugin/lib/src/enum_generator.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/protoc_plugin/lib/src/enum_generator.dart b/protoc_plugin/lib/src/enum_generator.dart index a9f7536b..5c656768 100644 --- a/protoc_plugin/lib/src/enum_generator.dart +++ b/protoc_plugin/lib/src/enum_generator.dart @@ -15,7 +15,7 @@ class EnumGenerator extends ProtobufContainer { final ProtobufContainer? parent; @override - final String? classname; + final String classname; @override final String fullName; @@ -110,7 +110,7 @@ class EnumGenerator extends ProtobufContainer { 'class $classname extends $protobufImportPrefix.ProtobufEnum {', '}\n', [ NamedLocation( - name: classname!, fieldPathSegment: fieldPath, start: 'class '.length) + name: classname, fieldPathSegment: fieldPath, start: 'class '.length) ], () { // ----------------------------------------------------------------- // Define enum types. From 2071fe1160bebb770a35499e751f94953ede06f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Fri, 29 Dec 2023 11:17:45 +0100 Subject: [PATCH 4/9] Make EnumGenerator.parent non nullable --- protoc_plugin/lib/src/enum_generator.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/protoc_plugin/lib/src/enum_generator.dart b/protoc_plugin/lib/src/enum_generator.dart index 5c656768..145b8e80 100644 --- a/protoc_plugin/lib/src/enum_generator.dart +++ b/protoc_plugin/lib/src/enum_generator.dart @@ -12,7 +12,7 @@ class EnumAlias { class EnumGenerator extends ProtobufContainer { @override - final ProtobufContainer? parent; + final ProtobufContainer parent; @override final String classname; @@ -34,13 +34,13 @@ class EnumGenerator extends ProtobufContainer { @override List get fieldPath => - _fieldPath ??= List.from(parent!.fieldPath!)..addAll(_fieldPathSegment); + _fieldPath ??= List.from(parent.fieldPath!)..addAll(_fieldPathSegment); EnumGenerator._(EnumDescriptorProto descriptor, this.parent, Set 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}', @@ -79,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) { From 1f7638dba4f237e97789a378c5e87841e35ee267 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Fri, 29 Dec 2023 11:33:28 +0100 Subject: [PATCH 5/9] Add tests --- protoc_plugin/Makefile | 1 + protoc_plugin/test/doc_comments_test.dart | 22 +++ protoc_plugin/test/goldens/doc_comments | 137 ++++++++++++++++++ .../test/goldens/doc_comments.pbenum | 36 +++++ protoc_plugin/test/protos/doc_comments.proto | 31 ++++ 5 files changed, 227 insertions(+) create mode 100644 protoc_plugin/test/doc_comments_test.dart create mode 100644 protoc_plugin/test/goldens/doc_comments create mode 100644 protoc_plugin/test/goldens/doc_comments.pbenum create mode 100644 protoc_plugin/test/protos/doc_comments.proto diff --git a/protoc_plugin/Makefile b/protoc_plugin/Makefile index 69bca65d..77dca0ba 100644 --- a/protoc_plugin/Makefile +++ b/protoc_plugin/Makefile @@ -24,6 +24,7 @@ TEST_PROTO_LIST = \ google/protobuf/wrappers \ custom_option \ dart_name \ + doc_comments \ default_value_escape \ entity \ enum_extension \ diff --git a/protoc_plugin/test/doc_comments_test.dart b/protoc_plugin/test/doc_comments_test.dart new file mode 100644 index 00000000..20468a33 --- /dev/null +++ b/protoc_plugin/test/doc_comments_test.dart @@ -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'); + }); +} diff --git a/protoc_plugin/test/goldens/doc_comments b/protoc_plugin/test/goldens/doc_comments new file mode 100644 index 00000000..0a2f4ed9 --- /dev/null +++ b/protoc_plugin/test/goldens/doc_comments @@ -0,0 +1,137 @@ +// +// Generated code. Do not modify. +// source: doc_comments.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +import 'dart:async' as $async; +import 'dart:core' as $core; + +import 'package:protobuf/protobuf.dart' as $pb; + +export 'doc_comments.pbenum.dart'; + +/// This is a message. +class HelloRequest extends $pb.GeneratedMessage { + factory HelloRequest() => create(); + HelloRequest._() : super(); + factory HelloRequest.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + factory HelloRequest.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'HelloRequest', + package: const $pb.PackageName(_omitMessageNames ? '' : 'service'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'name') + ..hasRequiredFields = false; + + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + HelloRequest clone() => HelloRequest()..mergeFromMessage(this); + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + HelloRequest copyWith(void Function(HelloRequest) updates) => + super.copyWith((message) => updates(message as HelloRequest)) + as HelloRequest; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static HelloRequest create() => HelloRequest._(); + HelloRequest createEmptyInstance() => create(); + static $pb.PbList createRepeated() => + $pb.PbList(); + @$core.pragma('dart2js:noInline') + static HelloRequest getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static HelloRequest? _defaultInstance; + + /// This is a message field. + @$pb.TagNumber(1) + $core.String get name => $_getSZ(0); + @$pb.TagNumber(1) + set name($core.String v) { + $_setString(0, v); + } + + @$pb.TagNumber(1) + $core.bool hasName() => $_has(0); + @$pb.TagNumber(1) + void clearName() => clearField(1); +} + +class HelloReply extends $pb.GeneratedMessage { + factory HelloReply() => create(); + HelloReply._() : super(); + factory HelloReply.fromBuffer($core.List<$core.int> i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromBuffer(i, r); + factory HelloReply.fromJson($core.String i, + [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => + create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo( + _omitMessageNames ? '' : 'HelloReply', + package: const $pb.PackageName(_omitMessageNames ? '' : 'service'), + createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'message') + ..hasRequiredFields = false; + + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + HelloReply clone() => HelloReply()..mergeFromMessage(this); + @$core.Deprecated('Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + HelloReply copyWith(void Function(HelloReply) updates) => + super.copyWith((message) => updates(message as HelloReply)) as HelloReply; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static HelloReply create() => HelloReply._(); + HelloReply createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static HelloReply getDefault() => _defaultInstance ??= + $pb.GeneratedMessage.$_defaultFor(create); + static HelloReply? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get message => $_getSZ(0); + @$pb.TagNumber(1) + set message($core.String v) { + $_setString(0, v); + } + + @$pb.TagNumber(1) + $core.bool hasMessage() => $_has(0); + @$pb.TagNumber(1) + void clearMessage() => clearField(1); +} + +class GreeterApi { + $pb.RpcClient _client; + GreeterApi(this._client); + + $async.Future sayHello( + $pb.ClientContext? ctx, HelloRequest request) => + _client.invoke( + ctx, 'Greeter', 'SayHello', request, HelloReply()); +} + +const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); +const _omitMessageNames = + $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/protoc_plugin/test/goldens/doc_comments.pbenum b/protoc_plugin/test/goldens/doc_comments.pbenum new file mode 100644 index 00000000..3a400053 --- /dev/null +++ b/protoc_plugin/test/goldens/doc_comments.pbenum @@ -0,0 +1,36 @@ +// +// Generated code. Do not modify. +// source: doc_comments.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +import 'dart:core' as $core; + +import 'package:protobuf/protobuf.dart' as $pb; + +/// This is an enum. +class A extends $pb.ProtobufEnum { + /// This is an enum variant. + static const A A1 = A._(0, _omitEnumNames ? '' : 'A1'); + + /// This is another enum variant. + static const A A2 = A._(1, _omitEnumNames ? '' : 'A2'); + + static const $core.List values = [ + A1, + A2, + ]; + + static final $core.Map<$core.int, A> _byValue = + $pb.ProtobufEnum.initByValue(values); + static A? valueOf($core.int value) => _byValue[value]; + + const A._($core.int v, $core.String n) : super(v, n); +} + +const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names'); diff --git a/protoc_plugin/test/protos/doc_comments.proto b/protoc_plugin/test/protos/doc_comments.proto new file mode 100644 index 00000000..e4021c5f --- /dev/null +++ b/protoc_plugin/test/protos/doc_comments.proto @@ -0,0 +1,31 @@ +// 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. + +syntax = "proto3"; + +package service; + +// This is a service. +service Greeter { + // This is a service method. + rpc SayHello (HelloRequest) returns (HelloReply) {} +} + +// This is a message. +message HelloRequest { + // This is a message field. + string name = 1; +} + +message HelloReply { + string message = 1; +} + +// This is an enum. +enum A { + // This is an enum variant. + A1 = 0; + // This is another enum variant. + A2 = 1; +} From f8cb7bb1e07a50a7d9bd7a15b8da6aab25783b32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Fri, 29 Dec 2023 14:09:10 +0100 Subject: [PATCH 6/9] Generate grpc client docs --- protoc_plugin/lib/src/client_generator.dart | 40 ++++++++++++++++++-- protoc_plugin/lib/src/file_generator.dart | 7 ++-- protoc_plugin/lib/src/grpc_generator.dart | 8 +++- protoc_plugin/lib/src/message_generator.dart | 5 +++ protoc_plugin/test/goldens/doc_comments | 2 + 5 files changed, 54 insertions(+), 8 deletions(-) diff --git a/protoc_plugin/lib/src/client_generator.dart b/protoc_plugin/lib/src/client_generator.dart index f927a1f9..2f94b549 100644 --- a/protoc_plugin/lib/src/client_generator.dart +++ b/protoc_plugin/lib/src/client_generator.dart @@ -10,7 +10,29 @@ class ClientApiGenerator { final String className; final Set usedMethodNames = {...reservedMemberNames}; - ClientApiGenerator(this.service, Set 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 get _serviceDescriptorPath => [ + ...service.fileGen.fieldPath, + _fileDescriptorServiceTag, + _repeatedFieldIndex + ]; + + List _methodDescriptorPath(int methodRepeatedFieldIndex) => [ + ..._serviceDescriptorPath, + _serviceDescriptorMethodTag, + methodRepeatedFieldIndex + ]; + + ClientApiGenerator( + this.service, Set usedNames, this._repeatedFieldIndex) : className = disambiguateName( avoidInitialUnderscore(service._descriptor.name), usedNames, @@ -20,20 +42,25 @@ 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, @@ -41,6 +68,11 @@ class ClientApiGenerator { 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) =>', diff --git a/protoc_plugin/lib/src/file_generator.dart b/protoc_plugin/lib/src/file_generator.dart index c1be6731..660f16e8 100644 --- a/protoc_plugin/lib/src/file_generator.dart +++ b/protoc_plugin/lib/src/file_generator.dart @@ -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)); + grpcGenerators.add(GrpcServiceGenerator(service, this, i)); } else { final serviceGen = ServiceGenerator(service, this, usedTopLevelServiceNames); serviceGenerators.add(serviceGen); clientApiGenerators - .add(ClientApiGenerator(serviceGen, usedTopLevelNames)); + .add(ClientApiGenerator(serviceGen, usedTopLevelNames, i)); } } } diff --git a/protoc_plugin/lib/src/grpc_generator.dart b/protoc_plugin/lib/src/grpc_generator.dart index 219a10e5..cf96a0f7 100644 --- a/protoc_plugin/lib/src/grpc_generator.dart +++ b/protoc_plugin/lib/src/grpc_generator.dart @@ -33,7 +33,13 @@ class GrpcServiceGenerator { /// List of gRPC methods. final _methods = <_GrpcMethod>[]; - GrpcServiceGenerator(this._descriptor, this.fileGen) { + final List _fieldPathSegment; + + late final List _fieldPath = List.from(fileGen.fieldPath) + ..addAll(_fieldPathSegment); + + GrpcServiceGenerator(this._descriptor, this.fileGen, int repeatedFieldIndex) + : _fieldPathSegment = [6, repeatedFieldIndex] { final name = _descriptor.name; final package = fileGen.package; diff --git a/protoc_plugin/lib/src/message_generator.dart b/protoc_plugin/lib/src/message_generator.dart index 035caf7e..bd04d797 100644 --- a/protoc_plugin/lib/src/message_generator.dart +++ b/protoc_plugin/lib/src/message_generator.dart @@ -117,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( diff --git a/protoc_plugin/test/goldens/doc_comments b/protoc_plugin/test/goldens/doc_comments index 0a2f4ed9..406ef045 100644 --- a/protoc_plugin/test/goldens/doc_comments +++ b/protoc_plugin/test/goldens/doc_comments @@ -122,10 +122,12 @@ class HelloReply extends $pb.GeneratedMessage { void clearMessage() => clearField(1); } +/// This is a service. class GreeterApi { $pb.RpcClient _client; GreeterApi(this._client); + /// This is a service method. $async.Future sayHello( $pb.ClientContext? ctx, HelloRequest request) => _client.invoke( From 4ad053c0cca00f1140f21eb2ecffc2c7412e5484 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Fri, 29 Dec 2023 14:11:17 +0100 Subject: [PATCH 7/9] Revert changes in grpc generator --- protoc_plugin/lib/src/grpc_generator.dart | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/protoc_plugin/lib/src/grpc_generator.dart b/protoc_plugin/lib/src/grpc_generator.dart index cf96a0f7..219a10e5 100644 --- a/protoc_plugin/lib/src/grpc_generator.dart +++ b/protoc_plugin/lib/src/grpc_generator.dart @@ -33,13 +33,7 @@ class GrpcServiceGenerator { /// List of gRPC methods. final _methods = <_GrpcMethod>[]; - final List _fieldPathSegment; - - late final List _fieldPath = List.from(fileGen.fieldPath) - ..addAll(_fieldPathSegment); - - GrpcServiceGenerator(this._descriptor, this.fileGen, int repeatedFieldIndex) - : _fieldPathSegment = [6, repeatedFieldIndex] { + GrpcServiceGenerator(this._descriptor, this.fileGen) { final name = _descriptor.name; final package = fileGen.package; From 7a62bed4e13315708e1467b8bb2239625eef9557 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Fri, 29 Dec 2023 14:13:30 +0100 Subject: [PATCH 8/9] Fix build --- protoc_plugin/lib/src/file_generator.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/protoc_plugin/lib/src/file_generator.dart b/protoc_plugin/lib/src/file_generator.dart index 660f16e8..9c358dc7 100644 --- a/protoc_plugin/lib/src/file_generator.dart +++ b/protoc_plugin/lib/src/file_generator.dart @@ -176,7 +176,7 @@ class FileGenerator extends ProtobufContainer { for (var i = 0; i < descriptor.service.length; i++) { final service = descriptor.service[i]; if (options.useGrpc) { - grpcGenerators.add(GrpcServiceGenerator(service, this, i)); + grpcGenerators.add(GrpcServiceGenerator(service, this)); } else { final serviceGen = ServiceGenerator(service, this, usedTopLevelServiceNames); From b998f8225e503eeadc24630b370eb4aebea5af50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Sinan=20A=C4=9Facan?= Date: Fri, 29 Dec 2023 14:48:32 +0100 Subject: [PATCH 9/9] Update changelog --- protoc_plugin/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/protoc_plugin/CHANGELOG.md b/protoc_plugin/CHANGELOG.md index 0684291b..97b8aa77 100644 --- a/protoc_plugin/CHANGELOG.md +++ b/protoc_plugin/CHANGELOG.md @@ -5,9 +5,13 @@ fields is now `PbMap` (instead of `Map`). ([#903]) This change requires protobuf-4.0.0. +* 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 +[#900]: https://github.com/google/protobuf.dart/issues/900 +[#909]: https://github.com/google/protobuf.dart/pull/909 ## 21.1.2