[pigeon] Adds Kotlin documentation comment support (#2665)
diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md
index 8d6aa74..5e32c04 100644
--- a/packages/pigeon/CHANGELOG.md
+++ b/packages/pigeon/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 4.2.1
+
+* Adds documentation comment support for Kotlin.
+
## 4.2.0
* Adds experimental support for Kotlin generation.
diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart
index fc32d23..2d8afea 100644
--- a/packages/pigeon/lib/generator_tools.dart
+++ b/packages/pigeon/lib/generator_tools.dart
@@ -9,7 +9,7 @@
import 'ast.dart';
/// The current version of pigeon. This must match the version in pubspec.yaml.
-const String pigeonVersion = '4.2.0';
+const String pigeonVersion = '4.2.1';
/// Read all the content from [stdin] to a String.
String readStdin() {
diff --git a/packages/pigeon/lib/kotlin_generator.dart b/packages/pigeon/lib/kotlin_generator.dart
index dc70534..a940d40 100644
--- a/packages/pigeon/lib/kotlin_generator.dart
+++ b/packages/pigeon/lib/kotlin_generator.dart
@@ -7,6 +7,23 @@
import 'generator_tools.dart';
import 'pigeon_lib.dart' show TaskQueueType;
+/// Documentation open symbol.
+const String _docCommentPrefix = '/**';
+
+/// Documentation continuation symbol.
+const String _docCommentContinuation = ' *';
+
+/// Documentation close symbol.
+const String _docCommentSuffix = ' */';
+
+/// Documentation comment spec.
+const DocumentCommentSpecification _docCommentSpec =
+ DocumentCommentSpecification(
+ _docCommentPrefix,
+ closeCommentToken: _docCommentSuffix,
+ blockContinuationToken: _docCommentContinuation,
+);
+
/// Options that control how Kotlin code will be generated.
class KotlinOptions {
/// Creates a [KotlinOptions] object
@@ -112,8 +129,12 @@
final String apiName = api.name;
- indent.writeln(
- '/** Generated interface from Pigeon that represents a handler of messages from Flutter. */');
+ const List<String> generatedMessages = <String>[
+ ' Generated interface from Pigeon that represents a handler of messages from Flutter.'
+ ];
+ addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
+ generatorComments: generatedMessages);
+
indent.write('interface $apiName ');
indent.scoped('{', '}', () {
for (final Method method in api.methods) {
@@ -132,6 +153,10 @@
final String returnType = method.returnType.isVoid
? ''
: _nullsafeKotlinTypeForDartType(method.returnType);
+
+ addDocumentationComments(
+ indent, method.documentationComments, _docCommentSpec);
+
if (method.isAsynchronous) {
argSignature.add('callback: ($returnType) -> Unit');
indent.writeln('fun ${method.name}(${argSignature.join(', ')})');
@@ -256,8 +281,12 @@
/// }
void _writeFlutterApi(Indent indent, Api api) {
assert(api.location == ApiLocation.flutter);
- indent.writeln(
- '/** Generated class from Pigeon that represents Flutter messages that can be called from Kotlin. */');
+ const List<String> generatedMessages = <String>[
+ ' Generated class from Pigeon that represents Flutter messages that can be called from Kotlin.'
+ ];
+ addDocumentationComments(indent, api.documentationComments, _docCommentSpec,
+ generatorComments: generatedMessages);
+
final String apiName = api.name;
indent.writeln('@Suppress("UNCHECKED_CAST")');
indent.write('class $apiName(private val binaryMessenger: BinaryMessenger) ');
@@ -277,6 +306,10 @@
? ''
: _nullsafeKotlinTypeForDartType(func.returnType);
String sendArgument;
+
+ addDocumentationComments(
+ indent, func.documentationComments, _docCommentSpec);
+
if (func.arguments.isEmpty) {
indent.write('fun ${func.name}(callback: ($returnType) -> Unit) ');
sendArgument = 'null';
@@ -430,6 +463,8 @@
}
void writeEnum(Enum anEnum) {
+ addDocumentationComments(
+ indent, anEnum.documentationComments, _docCommentSpec);
indent.write('enum class ${anEnum.name}(val raw: Int) ');
indent.scoped('{', '}', () {
// We use explicit indexing here as use of the ordinal() method is
@@ -460,6 +495,8 @@
void writeDataClass(Class klass) {
void writeField(NamedType field) {
+ addDocumentationComments(
+ indent, field.documentationComments, _docCommentSpec);
indent.write(
'val ${field.name}: ${_nullsafeKotlinTypeForDartType(field.type)}');
final String defaultNil = field.type.isNullable ? ' = null' : '';
@@ -566,8 +603,13 @@
});
}
- indent.writeln(
- '/** Generated class from Pigeon that represents data sent in messages. */');
+ const List<String> generatedMessages = <String>[
+ ' Generated class from Pigeon that represents data sent in messages.'
+ ];
+ addDocumentationComments(
+ indent, klass.documentationComments, _docCommentSpec,
+ generatorComments: generatedMessages);
+
indent.write('data class ${klass.name}(');
indent.scoped('', '', () {
for (final NamedType element in klass.fields) {
diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml
index bc7d9d5..cb524d5 100644
--- a/packages/pigeon/pubspec.yaml
+++ b/packages/pigeon/pubspec.yaml
@@ -2,7 +2,7 @@
description: Code generator tool to make communication between Flutter and the host platform type-safe and easier.
repository: https://github.com/flutter/packages/tree/main/packages/pigeon
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3Apigeon
-version: 4.2.0 # This must match the version in lib/generator_tools.dart
+version: 4.2.1 # This must match the version in lib/generator_tools.dart
environment:
sdk: ">=2.12.0 <3.0.0"
diff --git a/packages/pigeon/test/kotlin_generator_test.dart b/packages/pigeon/test/kotlin_generator_test.dart
index d9d5501..35cd887 100644
--- a/packages/pigeon/test/kotlin_generator_test.dart
+++ b/packages/pigeon/test/kotlin_generator_test.dart
@@ -993,4 +993,81 @@
final String code = sink.toString();
expect(code, contains('val input: String\n'));
});
+
+ test('transfers documentation comments', () {
+ final List<String> comments = <String>[
+ ' api comment',
+ ' api method comment',
+ ' class comment',
+ ' class field comment',
+ ' enum comment',
+ ];
+ int count = 0;
+
+ final Root root = Root(
+ apis: <Api>[
+ Api(
+ name: 'api',
+ location: ApiLocation.flutter,
+ documentationComments: <String>[comments[count++]],
+ methods: <Method>[
+ Method(
+ name: 'method',
+ returnType: const TypeDeclaration.voidDeclaration(),
+ documentationComments: <String>[comments[count++]],
+ arguments: <NamedType>[
+ NamedType(
+ name: 'field',
+ type: const TypeDeclaration(
+ baseName: 'int',
+ isNullable: true,
+ ),
+ ),
+ ],
+ )
+ ],
+ )
+ ],
+ classes: <Class>[
+ Class(
+ name: 'class',
+ documentationComments: <String>[comments[count++]],
+ fields: <NamedType>[
+ NamedType(
+ documentationComments: <String>[comments[count++]],
+ type: const TypeDeclaration(
+ baseName: 'Map',
+ isNullable: true,
+ typeArguments: <TypeDeclaration>[
+ TypeDeclaration(baseName: 'String', isNullable: true),
+ TypeDeclaration(baseName: 'int', isNullable: true),
+ ]),
+ name: 'field1',
+ ),
+ ],
+ ),
+ ],
+ enums: <Enum>[
+ Enum(
+ name: 'enum',
+ documentationComments: <String>[comments[count++]],
+ members: <String>[
+ 'one',
+ 'two',
+ ],
+ ),
+ ],
+ );
+ final StringBuffer sink = StringBuffer();
+ const KotlinOptions kotlinOptions = KotlinOptions();
+ generateKotlin(kotlinOptions, root, sink);
+ final String code = sink.toString();
+ for (final String comment in comments) {
+ // This regex finds the comment only between the open and close comment block
+ expect(
+ RegExp(r'(?<=\/\*\*.*?)' + comment + r'(?=.*?\*\/)', dotAll: true)
+ .hasMatch(code),
+ true);
+ }
+ });
}