[web] Don't get break type from v8BreakIterator (flutter/engine#43053)
In some languages, when the text contains a new line "\n", the `v8BreakIterator` starts returning different values from `breakType()`. This caused our code to think that those line breaks were hard line breaks when in fact they were soft line breaks. Still unclear if this is a `v8BreakIterator` bug or a wrong expectation on our side regarding how `breakType()` is supposed to work. Instead of using `v8BreakIterator.breakType()`, let's do the `soft` vs `hard` detection ourselves (we already have all the necessary pieces). Fixes https://github.com/flutter/flutter/issues/127379
This commit is contained in:
@@ -84,8 +84,6 @@ List<LineBreakFragment> breakLinesUsingV8BreakIterator(String text, JSString jsT
|
||||
iterator.adoptText(jsText);
|
||||
iterator.first();
|
||||
while (iterator.next() != -1) {
|
||||
final LineBreakType type = _getV8BreakType(text, iterator);
|
||||
|
||||
final int fragmentEnd = iterator.current().toInt();
|
||||
int trailingNewlines = 0;
|
||||
int trailingSpaces = 0;
|
||||
@@ -115,6 +113,15 @@ List<LineBreakFragment> breakLinesUsingV8BreakIterator(String text, JSString jsT
|
||||
}
|
||||
}
|
||||
|
||||
final LineBreakType type;
|
||||
if (trailingNewlines > 0) {
|
||||
type = LineBreakType.mandatory;
|
||||
} else if (fragmentEnd == text.length) {
|
||||
type = LineBreakType.endOfText;
|
||||
} else {
|
||||
type = LineBreakType.opportunity;
|
||||
}
|
||||
|
||||
breaks.add(LineBreakFragment(
|
||||
fragmentStart,
|
||||
fragmentEnd,
|
||||
@@ -132,20 +139,6 @@ List<LineBreakFragment> breakLinesUsingV8BreakIterator(String text, JSString jsT
|
||||
return breaks;
|
||||
}
|
||||
|
||||
/// Gets break type from v8BreakIterator.
|
||||
LineBreakType _getV8BreakType(String text, DomV8BreakIterator iterator) {
|
||||
final int fragmentEnd = iterator.current().toInt();
|
||||
|
||||
// I don't know why v8BreakIterator uses the type "none" to mean "soft break".
|
||||
if (iterator.breakType() != 'none') {
|
||||
return LineBreakType.mandatory;
|
||||
}
|
||||
if (fragmentEnd == text.length) {
|
||||
return LineBreakType.endOfText;
|
||||
}
|
||||
return LineBreakType.opportunity;
|
||||
}
|
||||
|
||||
class LineBreakFragment extends TextFragment {
|
||||
const LineBreakFragment(super.start, super.end, this.type, {
|
||||
required this.trailingNewlines,
|
||||
|
||||
@@ -410,6 +410,46 @@ void testMain() {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
group('v8BreakIterator hard line breaks', () {
|
||||
List<Line> split(String text) {
|
||||
return V8LineBreakFragmenter(text)
|
||||
.fragment()
|
||||
.map((LineBreakFragment fragment) => Line.fromLineBreakFragment(text, fragment))
|
||||
.toList();
|
||||
}
|
||||
|
||||
test('thai text with hard line breaks', () {
|
||||
const String thaiText = '\u0E1A\u0E38\u0E1C\u0E25\u0E01\u0E32\u0E23';
|
||||
expect(split(thaiText), <Line>[
|
||||
Line('\u0E1A\u0E38', opportunity),
|
||||
Line('\u0E1C\u0E25', opportunity),
|
||||
Line('\u0E01\u0E32\u0E23', endOfText),
|
||||
]);
|
||||
expect(split('$thaiText\n'), <Line>[
|
||||
Line('\u0E1A\u0E38', opportunity),
|
||||
Line('\u0E1C\u0E25', opportunity),
|
||||
Line('\u0E01\u0E32\u0E23\n', mandatory, nl: 1, sp: 1),
|
||||
Line('', endOfText),
|
||||
]);
|
||||
});
|
||||
|
||||
test('khmer text with hard line breaks', () {
|
||||
const String khmerText =
|
||||
'\u179B\u1792\u17D2\u179C\u17BE\u17B2\u17D2\u1799';
|
||||
expect(split(khmerText), <Line>[
|
||||
Line('\u179B', opportunity),
|
||||
Line('\u1792\u17D2\u179C\u17BE', opportunity),
|
||||
Line('\u17B2\u17D2\u1799', endOfText),
|
||||
]);
|
||||
expect(split('$khmerText\n'), <Line>[
|
||||
Line('\u179B', opportunity),
|
||||
Line('\u1792\u17D2\u179C\u17BE', opportunity),
|
||||
Line('\u17B2\u17D2\u1799\n', mandatory, nl: 1, sp: 1),
|
||||
Line('', endOfText),
|
||||
]);
|
||||
});
|
||||
}, skip: domIntl.v8BreakIterator == null);
|
||||
}
|
||||
|
||||
typedef CreateLineBreakFragmenter = LineBreakFragmenter Function(String text);
|
||||
|
||||
Reference in New Issue
Block a user