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>()));
});
});
}
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