Unverified Commit 5a8de59a authored by Alexandre Ardhuin's avatar Alexandre Ardhuin Committed by GitHub

refactor generateString to emit simpler dart code (#49334)

parent 94210a66
......@@ -139,17 +139,17 @@ String _jsonToMap(dynamic json) {
if (json == null || json is num || json is bool)
return '$json';
if (json is String) {
if (currentLocale == 'kn')
return generateEncodedString(json);
else if (json.contains("'"))
return 'r"""$json"""';
else
return "r'''$json'''";
}
if (json is String)
return generateEncodedString(currentLocale, json);
if (json is Iterable)
return '<dynamic>[${json.map<String>(_jsonToMap).join(',')}]';
if (json is Iterable) {
final StringBuffer buffer = StringBuffer('<dynamic>[');
for (final dynamic value in json) {
buffer.writeln('${_jsonToMap(value)},');
}
buffer.write(']');
return buffer.toString();
}
if (json is Map<String, dynamic>) {
final StringBuffer buffer = StringBuffer('<String, dynamic>{');
......
......@@ -464,10 +464,7 @@ String generateValue(String value, Map<String, dynamic> attributes, LocaleInfo l
return _scriptCategoryToEnum[value];
}
}
// Localization strings for the Kannada locale ('kn') are encoded because
// some of the localized strings contain characters that can crash Emacs on Linux.
// See packages/flutter_localizations/lib/src/l10n/README for more information.
return locale.languageCode == 'kn' ? generateEncodedString(value) : generateString(value);
return generateEncodedString(locale.languageCode, value);
}
/// Combines [generateType], [generateKey], and [generateValue] to return
......
......@@ -225,8 +225,8 @@ String genSimpleMethod(Message message) {
for (final Placeholder placeholder in message.placeholders) {
messageValue = messageValue.replaceAll('{${placeholder.name}}', '\${${placeholder.name}}');
}
final String rawMessage = generateString(messageValue); // "r'...'"
return rawMessage.substring(1);
final String generatedMessage = generateString(messageValue); // "r'...'"
return generatedMessage.startsWith('r') ? generatedMessage.substring(1) : generatedMessage;
}
List<String> genMethodParameters([String type]) {
......
......@@ -371,46 +371,46 @@ String generateClassDeclaration(
class $classNamePrefix$camelCaseName extends $superClass {''';
}
/// Return `s` as a Dart-parseable raw string in single or double quotes.
/// Return `s` as a Dart-parseable string.
///
/// Double quotes are expanded:
/// The result tries to avoid character escaping:
///
/// ```
/// foo => r'foo'
/// foo "bar" => r'foo "bar"'
/// foo 'bar' => r'foo ' "'" r'bar' "'"
/// foo => 'foo'
/// foo "bar" => 'foo "bar"'
/// foo 'bar' => "foo 'bar'"
/// foo 'bar' "baz" => '''foo 'bar' "baz"'''
/// foo\bar => r'foo\bar'
/// ```
String generateString(String s) {
if (!s.contains("'"))
return "r'$s'";
final StringBuffer output = StringBuffer();
bool started = false; // Have we started writing a raw string.
for (int i = 0; i < s.length; i++) {
if (s[i] == "'") {
if (started)
output.write("'");
output.write(' "\'" ');
started = false;
} else if (!started) {
output.write("r'${s[i]}");
started = true;
} else {
output.write(s[i]);
}
}
if (started)
output.write("'");
return output.toString();
///
/// Strings with newlines are not supported.
String generateString(String value) {
assert(!value.contains('\n'));
final String rawPrefix = value.contains(r'$') || value.contains(r'\') ? 'r' : '';
if (!value.contains("'"))
return "$rawPrefix'$value'";
if (!value.contains('"'))
return '$rawPrefix"$value"';
if (!value.contains("'''"))
return "$rawPrefix'''$value'''";
if (!value.contains('"""'))
return '$rawPrefix"""$value"""';
return value.split("'''")
.map(generateString)
// If value contains more than 6 consecutive single quotes some empty strings may be generated.
// The following map removes them.
.map((String part) => part == "''" ? '' : part)
.join(" \"'''\" ");
}
/// Only used to generate localization strings for the Kannada locale ('kn') because
/// some of the localized strings contain characters that can crash Emacs on Linux.
/// See packages/flutter_localizations/lib/src/l10n/README for more information.
String generateEncodedString(String s) {
if (s.runes.every((int code) => code <= 0xFF))
return generateString(s);
String generateEncodedString(String locale, String value) {
if (locale != 'kn' || value.runes.every((int code) => code <= 0xFF))
return generateString(value);
final String unicodeEscapes = s.runes.map((int code) => '\\u{${code.toRadixString(16)}}').join();
final String unicodeEscapes = value.runes.map((int code) => '\\u{${code.toRadixString(16)}}').join();
return "'$unicodeEscapes'";
}
......@@ -615,7 +615,7 @@ void main() {
'Title',
locale: _localeName,
name: 'title',
desc: r'Title for the application'
desc: 'Title for the application'
);
}
''');
......@@ -657,10 +657,10 @@ void main() {
generator.classMethods.first,
''' String itemNumber(Object value) {
return Intl.message(
\'Item \${value}\',
'Item \${value}',
locale: _localeName,
name: 'itemNumber',
desc: r\'Item placement in list.\',
desc: 'Item placement in list.',
args: <Object>[value]
);
}
......@@ -710,10 +710,10 @@ void main() {
String springBegins(Object springStartDate) {
return Intl.message(
\'Spring begins on \${springStartDate}\',
'Spring begins on \${springStartDate}',
locale: _localeName,
name: \'springBegins\',
desc: r\'The first day of spring\',
name: 'springBegins',
desc: 'The first day of spring',
args: <Object>[springStartDate]
);
}
......@@ -837,10 +837,10 @@ void main() {
String springGreetings(Object springStartDate, Object helloWorld) {
return Intl.message(
\'Since it\' "\'" r\'s \${springStartDate}, it\' "\'" r\'s finally spring! \${helloWorld}!\',
"Since it's \${springStartDate}, it's finally spring! \${helloWorld}!",
locale: _localeName,
name: \'springGreetings\',
desc: r\'A realization that it\' "\'" r\'s finally the spring season, followed by a greeting.\',
name: 'springGreetings',
desc: "A realization that it's finally the spring season, followed by a greeting.",
args: <Object>[springStartDate, helloWorld]
);
}
......@@ -897,10 +897,10 @@ void main() {
String springRange(Object springStartDate, Object springEndDate) {
return Intl.message(
\'Spring begins on \${springStartDate} and ends on \${springEndDate}\',
'Spring begins on \${springStartDate} and ends on \${springEndDate}',
locale: _localeName,
name: \'springRange\',
desc: r\'The range of dates for spring in the year\',
name: 'springRange',
desc: 'The range of dates for spring in the year',
args: <Object>[springStartDate, springEndDate]
);
}
......@@ -1009,10 +1009,10 @@ void main() {
String courseCompletion(Object progress) {
return Intl.message(
\'You have completed \${progress} of the course.\',
'You have completed \${progress} of the course.',
locale: _localeName,
name: \'courseCompletion\',
desc: r\'The amount of progress the student has made in their class.\',
name: 'courseCompletion',
desc: 'The amount of progress the student has made in their class.',
args: <Object>[progress]
);
}
......@@ -1079,10 +1079,10 @@ void main() {
String courseCompletion(Object progress) {
return Intl.message(
\'You have completed \${progress} of the course.\',
'You have completed \${progress} of the course.',
locale: _localeName,
name: \'courseCompletion\',
desc: r\'The amount of progress the student has made in their class.\',
name: 'courseCompletion',
desc: 'The amount of progress the student has made in their class.',
args: <Object>[progress]
);
}
......@@ -1139,10 +1139,10 @@ void main() {
String courseCompletion(Object progress) {
return Intl.message(
\'You have completed \${progress} of the course.\',
'You have completed \${progress} of the course.',
locale: _localeName,
name: \'courseCompletion\',
desc: r\'The amount of progress the student has made in their class.\',
name: 'courseCompletion',
desc: 'The amount of progress the student has made in their class.',
args: <Object>[progress]
);
}
......@@ -1706,4 +1706,37 @@ void main() {
expect(outputFileString, contains('class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations>'));
});
});
group('generateString', () {
test('handles simple string', () {
expect(generateString('abc'), "'abc'");
});
test('handles string with quote', () {
expect(generateString("ab'c"), '''"ab'c"''');
});
test('handles string with double quote', () {
expect(generateString('ab"c'), """'ab"c'""");
});
test('handles string with both single and double quote', () {
expect(generateString('''a'b"c'''), """'''a'b"c'''""");
});
test('handles string with a triple single quote and a double quote', () {
expect(generateString("""a"b'''c"""), '''"""a"b\'''c"""''');
});
test('handles string with a triple double quote and a single quote', () {
expect(generateString('''a'b"""c'''), """'''a'b\"""c'''""");
});
test('handles string with both triple single and triple double quote', () {
expect(generateString('''a\'''\'''\''b"""c'''), """'a' "'''" "'''" '''''b\"""c'''""");
});
test('handles dollar', () {
expect(generateString(r'ab$c'), r"r'ab$c'");
});
test('handles back slash', () {
expect(generateString(r'ab\c'), r"r'ab\c'");
});
test("doesn't support multiline strings", () {
expect(() => generateString('ab\nc'), throwsA(isA<AssertionError>()));
});
});
}
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment