mirror of
https://github.com/block/buzz.git
synced 2026-10-30 22:11:36 +00:00
fix(mobile): bound preview scanning and honor escaped backticks
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tom Brow <tomb@block.xyz>
This commit is contained in:
@@ -145,8 +145,10 @@ String messagePreviewSource(String content, {required int maxLines}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The first `maxLines + 1` lines that render, trimmed and without block
|
/// The first `maxLines + 1` lines that render, trimmed and without block
|
||||||
/// syntax, read lazily so the rest of [content] is never scanned. [code] marks
|
/// syntax, read lazily so the rest of [content] is never scanned. Lines that
|
||||||
/// a line inside a fenced block, and [end] is its end offset in [content].
|
/// don't render still cost a scan, so reading also stops after
|
||||||
|
/// [_maxScanLength] characters. [code] marks a line inside a fenced block, and
|
||||||
|
/// [end] is its end offset in [content].
|
||||||
Iterable<({String text, bool code, int end})> _keptLines(
|
Iterable<({String text, bool code, int end})> _keptLines(
|
||||||
String content,
|
String content,
|
||||||
int maxLines,
|
int maxLines,
|
||||||
@@ -154,7 +156,9 @@ Iterable<({String text, bool code, int end})> _keptLines(
|
|||||||
var kept = 0;
|
var kept = 0;
|
||||||
var fenceOpen = false;
|
var fenceOpen = false;
|
||||||
var start = 0;
|
var start = 0;
|
||||||
while (start <= content.length && kept <= maxLines) {
|
while (start <= content.length &&
|
||||||
|
start <= _maxScanLength &&
|
||||||
|
kept <= maxLines) {
|
||||||
var end = content.indexOf('\n', start);
|
var end = content.indexOf('\n', start);
|
||||||
if (end < 0) end = content.length;
|
if (end < 0) end = content.length;
|
||||||
// A long line wraps past the visible lines well before this length.
|
// A long line wraps past the visible lines well before this length.
|
||||||
@@ -183,6 +187,8 @@ Iterable<({String text, bool code, int end})> _keptLines(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const _maxLineLength = 2000;
|
const _maxLineLength = 2000;
|
||||||
|
const _maxScanLength = 8 * _maxLineLength;
|
||||||
|
const _backslash = 0x5C;
|
||||||
const _backtick = 0x60;
|
const _backtick = 0x60;
|
||||||
|
|
||||||
final _rule = RegExp(r'^([-*_])(?:\s*\1){2,}$');
|
final _rule = RegExp(r'^([-*_])(?:\s*\1){2,}$');
|
||||||
@@ -301,13 +307,18 @@ class _PreviewBuilder {
|
|||||||
piece is PreviewText ? text(piece.text) : pieces.add(piece);
|
piece is PreviewText ? text(piece.text) : pieces.add(piece);
|
||||||
|
|
||||||
/// One line of prose: inline code is literal, everything else is inline
|
/// One line of prose: inline code is literal, everything else is inline
|
||||||
/// Markdown. A code span opens at a run of backticks and closes at the next
|
/// Markdown. A code span opens at an unescaped run of backticks and closes
|
||||||
/// run of the same length; a run with no match is literal.
|
/// at the next run of the same length; a run with no match is literal.
|
||||||
void line(String line) {
|
void line(String line) {
|
||||||
var prose = 0;
|
var prose = 0;
|
||||||
var cursor = 0;
|
var cursor = 0;
|
||||||
while (cursor < line.length) {
|
while (cursor < line.length) {
|
||||||
if (line.codeUnitAt(cursor) != _backtick) {
|
final unit = line.codeUnitAt(cursor);
|
||||||
|
if (unit == _backslash) {
|
||||||
|
cursor += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (unit != _backtick) {
|
||||||
cursor++;
|
cursor++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ void main() {
|
|||||||
('escaped emphasis', r'\*literal\* and \_x\_', '*literal* and _x_'),
|
('escaped emphasis', r'\*literal\* and \_x\_', '*literal* and _x_'),
|
||||||
('escaped backslash', r'a \\ b', r'a \ b'),
|
('escaped backslash', r'a \\ b', r'a \ b'),
|
||||||
('escaped link opener', r'\[docs](https://x)', '[docs](https://x)'),
|
('escaped link opener', r'\[docs](https://x)', '[docs](https://x)'),
|
||||||
|
('escaped backticks', r'\`literal\` and `code`', '`literal` and code'),
|
||||||
]) {
|
]) {
|
||||||
test(name, () => expect(_text(source), expected));
|
test(name, () => expect(_text(source), expected));
|
||||||
}
|
}
|
||||||
@@ -99,6 +100,15 @@ void main() {
|
|||||||
expect(_text('# a\n\n---\n- b\nc\nd'), 'a\nb\nc');
|
expect(_text('# a\n\n---\n- b\nc\nd'), 'a\nb\nc');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('stops reading after a long run of lines that do not render', () {
|
||||||
|
final content = '\n' * 1000000 + 'late';
|
||||||
|
expect(_text(content), '');
|
||||||
|
expect(
|
||||||
|
messagePreviewSource(content, maxLines: 2).length,
|
||||||
|
lessThan(20000),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test('caps a very long line', () {
|
test('caps a very long line', () {
|
||||||
expect(_text('x' * 5000).length, 2000);
|
expect(_text('x' * 5000).length, 2000);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user