diff --git a/lib/src/intl_suggestors/message_parser.dart b/lib/src/intl_suggestors/message_parser.dart index 5abe6847..9322dfde 100644 --- a/lib/src/intl_suggestors/message_parser.dart +++ b/lib/src/intl_suggestors/message_parser.dart @@ -88,16 +88,17 @@ class MessageParser { /// partially rewritten. String withCorrectFunctionTypes( MethodDeclaration declaration, String currentSource) { - // Split out the body and just do a simple string replace. The conditions - // for a false positive on this seem unlikely, so just do it and cross our - // fingers. If it's in a function name it will be followed by a parenthesis. - // This is only used for formattedMessage, so it won't be a getter. And if - // you name a parameter that ends in Function it'll presumably be followed - // by either a comma or a close-paren. - var declarationParts = currentSource.split('=>'); + // Split out the body and just do a simple string replace on the header. The + // conditions for a false positive on this seem unlikely, so just do it and + // cross our fingers. If it's in a function name it will be followed by a + // parenthesis. This is only used for formattedMessage, so it won't be a + // getter. And if you name a parameter that ends in Function it'll + // presumably be followed by either a comma or a close-paren. + var splitString = (declaration.body is BlockFunctionBody) ? '{' : '=>'; + var declarationParts = currentSource.split(splitString); var newBeginning = declarationParts.first.replaceAll('Function ', 'Object '); - return '$newBeginning=>${declarationParts.last}'; + return '$newBeginning$splitString${declarationParts.last}'; } /// Find the parameter `name:` from the invocation, or return null if there @@ -125,16 +126,23 @@ class MessageParser { } /// The invocation of the internal Intl method. That is, the part after the - /// '=>'. We know there's only ever one. Used for determining what sort of - /// method this is message/plural/select/formattedMessage. + /// '=>' or the first statement inside the {}. We expect only one. Used for + /// determining what sort of method this is + /// message/plural/select/formattedMessage. MethodInvocation intlMethodInvocation(MethodDeclaration method) { - var invocation = method.body.childEntities.toList()[1]; - if (invocation is MethodInvocation) { - return invocation; + var node = method.body; + if (node is ExpressionFunctionBody) { + return node.expression as MethodInvocation; + } else if (node is BlockFunctionBody) { + var children = node.block.statements.first.childEntities.toList(); + var methods = children.whereType().toList(); + if (methods.length > 1) + throw ArgumentError( + 'A message can only contain a single call, which must be to an Intl function'); + return methods.first; } else { - print('ERROR: Invalid Intl method: $method'); - // We expect this to throw - return invocation as MethodInvocation; + throw ArgumentError( + 'Cannot parse $node. It needs to be a function with a single expression which is an Intl method invocation'); } } diff --git a/test/intl_suggestors/intl_messages_test.dart b/test/intl_suggestors/intl_messages_test.dart index f667f22f..2dcd5a9d 100644 --- a/test/intl_suggestors/intl_messages_test.dart +++ b/test/intl_suggestors/intl_messages_test.dart @@ -24,6 +24,7 @@ void main() { 'function', 'aPlural', 'formatted', + 'formattedNonArrow', 'someSelect' ]); }); @@ -117,7 +118,7 @@ void main() { }); test('messages found', () { - expect(messages.methods.length, 7); + expect(messages.methods.length, 8); expect(messages.methods.keys, [ 'orange', 'aquamarine', @@ -125,6 +126,7 @@ void main() { 'function', 'aPlural', 'formatted', + 'formattedNonArrow', 'someSelect' ]); expect(messages.methods.values.map((each) => each.source).toList(), @@ -182,7 +184,7 @@ void main() { var otherName = messages.nameForString('function', r'www${x}def'); tweakedMore = tweakedMore.replaceAll('function', otherName); messages.addMethod(tweakedMore); - expect(messages.methods.length, 9); + expect(messages.methods.length, 10); expect(messages.methods['function']?.source, sampleMethods[3]); expect(messages.methods['function1']?.source, tweaked); expect(messages.methods['function2']?.source, contains(r'www${x}def')); @@ -202,7 +204,7 @@ class TestProjectIntl {${methods.isNotEmpty ? '\n' : ''}$methods }'''; List sampleMethods = [ - " static String get orange => Intl.message('orange', name: 'TestProjectIntl_orange', desc: 'The color.');", + " static String get orange {return Intl.message('orange', name: 'TestProjectIntl_orange', desc: 'The color.');}", " static String get aquamarine => Intl.message('aquamarine', name: 'TestProjectIntl_aquamarine', desc: 'The color', meaning: 'blueish');", """ static String get long => Intl.message('''multi line @@ -210,12 +212,13 @@ string''', name: 'TestProjectIntl_long');""", """ static String function(String x) => Intl.message('abc\${x}def', name: 'TestProjectIntl_function');""", """ static String aPlural(int n) => Intl.plural(n, zero: 'zero', other: 'other', name: 'TestProjectIntl_aPlural', args: [n]);""", """ static List formatted(Object f) => Intl.formattedMessage([f, 'foo'], name: 'TestProjectIntl_formatted', args: [f]);""", + """ static List formattedNonArrow(Object f) {Intl.formattedMessage([f, 'foo'], name: 'TestProjectIntl_formattedNonArrow', args: [f]);}""", """ static String someSelect(Object choice) => Intl.select(choice, {'a' : 'b'}, name: 'TestProjectIntl_someSelect', args: [choice]);""" ]; // The sample methods in a hard-coded sorted order. List get sortedSampleMethods => - [4, 1, 5, 3, 2, 0, 6].map((i) => sampleMethods[i]).toList(); + [4, 1, 5, 6, 3, 2, 0, 7].map((i) => sampleMethods[i]).toList(); // A test utility to be invoked from the debug console to see where subtly-different long strings differ. void firstDifference(String a, String b) {