Skip to content

Commit

Permalink
[pigeon] Implement Screaming Snake Case Conversion for Kotlin Enum Ca…
Browse files Browse the repository at this point in the history
…ses (flutter#5918)

This pull request addresses issue flutter#140938 in the Pigeon package, related to the naming convention of Kotlin enum cases generated from lower camel case Dart enums. The current implementation concatenates the enum cases in uppercase, deviating from the Kotlin naming convention, specifically when dealing with multi-word names.

Changes

- Kotlin Enum Generation: Modified the writeEnum function in the Pigeon package to ensure the Kotlin generator produces enum cases in SCREAMING_SNAKE_CASE. This adheres to the Kotlin coding conventions and allows a consistent cross-platform enum naming convention across Dart, Kotlin, and Swift.
- Regex Handling: Enhanced the regex pattern to correctly transform lower camel case names to screaming snake case, considering edge cases involving numbers and special characters.
- Testing: Updated the Dart unit tests to include cases for validating the correct transformation of multi-word and complex enum names from lower camel case to screaming snake case.
  • Loading branch information
erdzan12 authored Feb 9, 2024
1 parent cd45100 commit 90baeee
Show file tree
Hide file tree
Showing 18 changed files with 112 additions and 18 deletions.
8 changes: 8 additions & 0 deletions packages/pigeon/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## 17.0.0

* **Breaking Change** [kotlin] Converts Kotlin enum case generation to SCREAMING_SNAKE_CASE.
* Updates `writeEnum` function to adhere to Kotlin naming conventions.
* Improves handling of complex names with enhanced regex patterns.
* Expands unit tests for comprehensive name conversion validation.
* **Migration Note**: This change modifies the naming convention of Kotlin enum cases generated from the Pigeon package. It is recommended to review the impact on your existing codebase and update any dependent code accordingly.

## 16.0.5

* Adds ProxyApi to AST generation.
Expand Down
2 changes: 1 addition & 1 deletion packages/pigeon/lib/generator_tools.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import 'ast.dart';
/// The current version of pigeon.
///
/// This must match the version in pubspec.yaml.
const String pigeonVersion = '16.0.5';
const String pigeonVersion = '17.0.0';

/// Read all the content from [stdin] to a String.
String readStdin() {
Expand Down
6 changes: 5 additions & 1 deletion packages/pigeon/lib/kotlin_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,11 @@ class KotlinGenerator extends StructuredGenerator<KotlinOptions> {
enumerate(anEnum.members, (int index, final EnumMember member) {
addDocumentationComments(
indent, member.documentationComments, _docCommentSpec);
indent.write('${member.name.toUpperCase()}($index)');
final String nameScreamingSnakeCase = member.name
.replaceAllMapped(
RegExp(r'(?<=[a-z])[A-Z]'), (Match m) => '_${m.group(0)}')
.toUpperCase();
indent.write('$nameScreamingSnakeCase($index)');
if (index != anEnum.members.length - 1) {
indent.addln(',');
} else {
Expand Down
2 changes: 2 additions & 0 deletions packages/pigeon/pigeons/core_tests.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ enum AnEnum {
one,
two,
three,
fortyTwo,
fourHundredTwentyTwo,
}

/// A class containing all supported types.
Expand Down
3 changes: 3 additions & 0 deletions packages/pigeon/pigeons/enum.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ enum EnumState {

/// This comment is to test enum member (Error) documentation comments.
Error,

/// This comment is to test enum member (SnakeCase) documentation comments.
SnakeCase,
}

/// This comment is to test class documentation comments.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ protected static FlutterError createConnectionError(@NonNull String channelName)
public enum AnEnum {
ONE(0),
TWO(1),
THREE(2);
THREE(2),
FORTY_TWO(3),
FOUR_HUNDRED_TWENTY_TWO(4);

final int index;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ typedef NS_ENUM(NSUInteger, AnEnum) {
AnEnumOne = 0,
AnEnumTwo = 1,
AnEnumThree = 2,
AnEnumFortyTwo = 3,
AnEnumFourHundredTwentyTwo = 4,
};

/// Wrapper for AnEnum to allow for nullability.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ typedef NS_ENUM(NSUInteger, AnEnum) {
AnEnumOne = 0,
AnEnumTwo = 1,
AnEnumThree = 2,
AnEnumFortyTwo = 3,
AnEnumFourHundredTwentyTwo = 4,
};

/// Wrapper for AnEnum to allow for nullability.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) {
'd': false,
'e': null
},
anEnum: AnEnum.two,
anEnum: AnEnum.fortyTwo,
anObject: 1,
);

Expand Down Expand Up @@ -166,7 +166,7 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) {
],
nullableMapWithAnnotations: <String?, String?>{},
nullableMapWithObject: <String?, Object?>{},
aNullableEnum: AnEnum.two,
aNullableEnum: AnEnum.fourHundredTwentyTwo,
aNullableObject: 0,
);

Expand Down Expand Up @@ -443,6 +443,15 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) {
expect(receivedEnum, sentEnum);
});

testWidgets('multi word enums serialize and deserialize correctly',
(WidgetTester _) async {
final HostIntegrationCoreApi api = HostIntegrationCoreApi();

const AnEnum sentEnum = AnEnum.fortyTwo;
final AnEnum receivedEnum = await api.echoEnum(sentEnum);
expect(receivedEnum, sentEnum);
});

testWidgets('required named parameter', (WidgetTester _) async {
final HostIntegrationCoreApi api = HostIntegrationCoreApi();
// This number corresponds with the default value of this method.
Expand Down Expand Up @@ -648,6 +657,15 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) {
expect(echoEnum, sentEnum);
});

testWidgets('multi word nullable enums serialize and deserialize correctly',
(WidgetTester _) async {
final HostIntegrationCoreApi api = HostIntegrationCoreApi();

const AnEnum sentEnum = AnEnum.fourHundredTwentyTwo;
final AnEnum? echoEnum = await api.echoNullableEnum(sentEnum);
expect(echoEnum, sentEnum);
});

testWidgets('null lists serialize and deserialize correctly',
(WidgetTester _) async {
final HostIntegrationCoreApi api = HostIntegrationCoreApi();
Expand Down Expand Up @@ -887,6 +905,15 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) {
expect(echoEnum, sentEnum);
});

testWidgets('multi word enums serialize and deserialize correctly',
(WidgetTester _) async {
final HostIntegrationCoreApi api = HostIntegrationCoreApi();

const AnEnum sentEnum = AnEnum.fourHundredTwentyTwo;
final AnEnum echoEnum = await api.echoAsyncEnum(sentEnum);
expect(echoEnum, sentEnum);
});

testWidgets('nullable Int async serialize and deserialize correctly',
(WidgetTester _) async {
final HostIntegrationCoreApi api = HostIntegrationCoreApi();
Expand Down Expand Up @@ -1004,6 +1031,15 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) {
expect(echoEnum, sentEnum);
});

testWidgets('nullable enums serialize and deserialize correctly',
(WidgetTester _) async {
final HostIntegrationCoreApi api = HostIntegrationCoreApi();

const AnEnum sentEnum = AnEnum.fortyTwo;
final AnEnum? echoEnum = await api.echoAsyncNullableEnum(sentEnum);
expect(echoEnum, sentEnum);
});

testWidgets('null Ints async serialize and deserialize correctly',
(WidgetTester _) async {
final HostIntegrationCoreApi api = HostIntegrationCoreApi();
Expand Down Expand Up @@ -1243,6 +1279,15 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) {
expect(echoEnum, sentEnum);
});

testWidgets('multi word enums serialize and deserialize correctly',
(WidgetTester _) async {
final HostIntegrationCoreApi api = HostIntegrationCoreApi();

const AnEnum sentEnum = AnEnum.fortyTwo;
final AnEnum echoEnum = await api.callFlutterEchoEnum(sentEnum);
expect(echoEnum, sentEnum);
});

testWidgets('nullable booleans serialize and deserialize correctly',
(WidgetTester _) async {
final HostIntegrationCoreApi api = HostIntegrationCoreApi();
Expand Down Expand Up @@ -1407,6 +1452,15 @@ void runPigeonIntegrationTests(TargetGenerator targetGenerator) {
expect(echoEnum, sentEnum);
});

testWidgets('multi word nullable enums serialize and deserialize correctly',
(WidgetTester _) async {
final HostIntegrationCoreApi api = HostIntegrationCoreApi();

const AnEnum sentEnum = AnEnum.fourHundredTwentyTwo;
final AnEnum? echoEnum = await api.callFlutterEchoNullableEnum(sentEnum);
expect(echoEnum, sentEnum);
});

testWidgets('null enums serialize and deserialize correctly',
(WidgetTester _) async {
final HostIntegrationCoreApi api = HostIntegrationCoreApi();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ enum AnEnum {
one,
two,
three,
fortyTwo,
fourHundredTwentyTwo,
}

/// A class containing all supported types.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ enum EnumState {

/// This comment is to test enum member (Error) documentation comments.
Error,

/// This comment is to test enum member (SnakeCase) documentation comments.
SnakeCase,
}

/// This comment is to test class documentation comments.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ class CoreTestsError(
enum class AnEnum(val raw: Int) {
ONE(0),
TWO(1),
THREE(2);
THREE(2),
FORTY_TWO(3),
FOUR_HUNDRED_TWENTY_TWO(4);

companion object {
fun ofRaw(raw: Int): AnEnum? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ internal class EnumTest : TestCase() {
val api = mockk<EnumApi2Host>()

val channelName = "dev.flutter.pigeon.pigeon_integration_tests.EnumApi2Host.echo"
val input = DataWithEnum(EnumState.SUCCESS)
val input = DataWithEnum(EnumState.SNAKE_CASE)

val handlerSlot = slot<BinaryMessenger.BinaryMessageHandler>()

Expand Down Expand Up @@ -52,7 +52,7 @@ internal class EnumTest : TestCase() {
val binaryMessenger = mockk<BinaryMessenger>()
val api = EnumApi2Flutter(binaryMessenger)

val input = DataWithEnum(EnumState.SUCCESS)
val input = DataWithEnum(EnumState.SNAKE_CASE)

every { binaryMessenger.send(any(), any(), any()) } answers
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ enum AnEnum: Int {
case one = 0
case two = 1
case three = 2
case fortyTwo = 3
case fourHundredTwentyTwo = 4
}

/// A class containing all supported types.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ enum AnEnum: Int {
case one = 0
case two = 1
case three = 2
case fortyTwo = 3
case fourHundredTwentyTwo = 4
}

/// A class containing all supported types.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,13 @@ class ErrorOr {
std::variant<T, FlutterError> v_;
};

enum class AnEnum { one = 0, two = 1, three = 2 };
enum class AnEnum {
one = 0,
two = 1,
three = 2,
fortyTwo = 3,
fourHundredTwentyTwo = 4
};

// A class containing all supported types.
//
Expand Down
2 changes: 1 addition & 1 deletion packages/pigeon/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: pigeon
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%3A%22p%3A+pigeon%22
version: 16.0.5 # This must match the version in lib/generator_tools.dart
version: 17.0.0 # This must match the version in lib/generator_tools.dart

environment:
sdk: ">=3.0.0 <4.0.0"
Expand Down
16 changes: 8 additions & 8 deletions packages/pigeon/test/kotlin_generator_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -856,22 +856,22 @@ void main() {

test('gen one enum class', () {
final Enum anEnum = Enum(
name: 'Enum1',
name: 'SampleEnum',
members: <EnumMember>[
EnumMember(name: 'one'),
EnumMember(name: 'two'),
EnumMember(name: 'sampleVersion'),
EnumMember(name: 'sampleTest'),
],
);
final Class classDefinition = Class(
name: 'EnumClass',
fields: <NamedType>[
NamedType(
type: TypeDeclaration(
baseName: 'Enum1',
baseName: 'SampleEnum',
associatedEnum: emptyEnum,
isNullable: true,
),
name: 'enum1',
name: 'sampleEnum',
),
],
);
Expand All @@ -890,9 +890,9 @@ void main() {
dartPackageName: DEFAULT_PACKAGE_NAME,
);
final String code = sink.toString();
expect(code, contains('enum class Enum1(val raw: Int)'));
expect(code, contains('ONE(0)'));
expect(code, contains('TWO(1)'));
expect(code, contains('enum class SampleEnum(val raw: Int)'));
expect(code, contains('SAMPLE_VERSION(0)'));
expect(code, contains('SAMPLE_TEST(1)'));
});

Iterable<String> makeIterable(String string) sync* {
Expand Down

0 comments on commit 90baeee

Please sign in to comment.