Skip to content

Commit

Permalink
fix: parse rich texts with interpolation braces and double_braces
Browse files Browse the repository at this point in the history
… correctly
  • Loading branch information
Tienisto committed Jul 28, 2022
1 parent 1d1a3d0 commit 52c51b8
Show file tree
Hide file tree
Showing 7 changed files with 58 additions and 9 deletions.
5 changes: 5 additions & 0 deletions slang/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 2.6.2

- feat: add Russian plural resolver (thanks to @LuckyWins)
- fix: parse rich text with interpolation `braces` and `double_braces` correctly

## 2.6.1

- fix: remove `const` if rich text has links
Expand Down
2 changes: 2 additions & 0 deletions slang/example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ dependencies:

dev_dependencies:
lints: ^2.0.0
# build_runner: any
# slang_build_runner: any

flutter:
uses-material-design: true
2 changes: 2 additions & 0 deletions slang/example/pubspec_overrides.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ dependency_overrides:
path: ..
slang_flutter:
path: ../../slang_flutter
# slang_build_runner:
# path: ../../slang_build_runner
4 changes: 3 additions & 1 deletion slang/lib/api/plural_resolver_map.dart
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,9 @@ final Map<String, _Resolvers> _resolverMap = {
if (fr10.clamp(2, 4) == fr10 && fr100.clamp(12, 14) != fr100) {
return few ?? other!;
}
if (fr10 == 0 || fr10.clamp(5, 9) == fr10 || fr100.clamp(11, 14) == fr100) {
if (fr10 == 0 ||
fr10.clamp(5, 9) == fr10 ||
fr100.clamp(11, 14) == fr100) {
return many ?? other!;
}
return other!;
Expand Down
24 changes: 16 additions & 8 deletions slang/lib/builder/model/node.dart
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ class RichTextNode extends TextNode {
raw: _escapeContent(raw, interpolation),
interpolation: interpolation,
paramCase: paramCase,
allowBracketsInsideParameters: true,
);

final parsedParams =
Expand Down Expand Up @@ -376,6 +377,7 @@ _ParseInterpolationResult _parseInterpolation({
required String raw,
required StringInterpolation interpolation,
required CaseStyle? paramCase,
bool allowBracketsInsideParameters = false,
}) {
final String parsedContent;
final params = Set<String>();
Expand All @@ -398,37 +400,43 @@ _ParseInterpolationResult _parseInterpolation({
});
break;
case StringInterpolation.braces:
parsedContent =
raw.replaceAllMapped(RegexUtils.argumentsBracesRegex, (match) {
parsedContent = raw.replaceAllMapped(
allowBracketsInsideParameters
? RegexUtils.argumentsBracesAdvancedRegex
: RegexUtils.argumentsBracesRegex, (match) {
if (match.group(1) == '\\') {
return '{${match.group(2)}}'; // escape
}

final param = match.group(2)!.toCase(paramCase);
params.add(param);

if (match.group(3) != null) {
if (match.group(3) != null || param.contains('(')) {
// ${...} because a word follows
return '${match.group(1)}\${$param}${match.group(3)}';
// ... or contains brackets '(' which is needed for rich text, otherwise this would be invalid syntax anyways
return '${match.group(1)}\${$param}${match.group(3) ?? ''}';
} else {
// $...
return '${match.group(1)}\$$param';
}
});
break;
case StringInterpolation.doubleBraces:
parsedContent =
raw.replaceAllMapped(RegexUtils.argumentsDoubleBracesRegex, (match) {
parsedContent = raw.replaceAllMapped(
allowBracketsInsideParameters
? RegexUtils.argumentsDoubleBracesAdvancedRegex
: RegexUtils.argumentsDoubleBracesRegex, (match) {
if (match.group(1) == '\\') {
return '{{${match.group(2)}}}'; // escape
}

final param = match.group(2)!.toCase(paramCase);
params.add(param);

if (match.group(3) != null) {
if (match.group(3) != null || param.contains('(')) {
// ${...} because a word follows
return '${match.group(1)}\${$param}${match.group(3)}';
// ... or contains brackets '(' which is needed for rich text, otherwise this would be invalid syntax anyways
return '${match.group(1)}\${$param}${match.group(3) ?? ''}';
} else {
// $...
return '${match.group(1)}\$$param';
Expand Down
10 changes: 10 additions & 0 deletions slang/lib/builder/utils/regex_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,20 @@ class RegexUtils {
/// 3 = post character is word, therefore ${...}
static RegExp argumentsBracesRegex = RegExp(r'(.|^)\{(\w+)\}(\w)?');

/// Same as [argumentsBracesRegex]
/// But allowing brackets: {hello(my text)}
static RegExp argumentsBracesAdvancedRegex =
RegExp(r'(.|^)\{([\w\s()]+)\}(\w)?');

/// matches {{argument}}
/// similar group indices like [argumentsBracesRegex]
static RegExp argumentsDoubleBracesRegex = RegExp(r'(.|^)\{\{(\w+)\}\}(\w)?');

/// Same as [argumentsDoubleBracesRegex]
/// But allowing brackets: {{hello(my text)}}
static RegExp argumentsDoubleBracesAdvancedRegex =
RegExp(r'(.|^)\{\{([\w\s()]+)\}\}(\w)?');

/// matches @:translation.key
static RegExp linkedRegex = RegExp(r'@:(\w[\w|.]*\w|\w)');

Expand Down
20 changes: 20 additions & 0 deletions slang/test/unit/model/node_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,16 @@ void main() {
expect(node.spans[2].code, 'const TextSpan(text: \'!\')');
expect(node.params, {'yey'});
});

test('one argument with default text', () {
final test = r'Hello {yey(my text)}!';
final node = richTextNode(test, StringInterpolation.braces);
expect(node.spans.length, 3);
expect(node.spans[0].code, 'const TextSpan(text: \'Hello \')');
expect(node.spans[1].code, 'yey(\'my text\')');
expect(node.spans[2].code, 'const TextSpan(text: \'!\')');
expect(node.params, {'yey'});
});
});

group(StringInterpolation.doubleBraces, () {
Expand All @@ -372,6 +382,16 @@ void main() {
expect(node.spans[2].code, 'const TextSpan(text: \'!\')');
expect(node.params, {'yey'});
});

test('one argument with default text', () {
final test = r'Hello {{yey(my text)}}!';
final node = richTextNode(test, StringInterpolation.doubleBraces);
expect(node.spans.length, 3);
expect(node.spans[0].code, 'const TextSpan(text: \'Hello \')');
expect(node.spans[1].code, 'yey(\'my text\')');
expect(node.spans[2].code, 'const TextSpan(text: \'!\')');
expect(node.params, {'yey'});
});
});
});
}

0 comments on commit 52c51b8

Please sign in to comment.