Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/FindBar.qml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Rectangle {
replacing = replace
visible = true
const area = editor.area
if (area.selectionStart !== area.selectionEnd && area.selectedText.indexOf("
") < 0)
if (area.selectionStart !== area.selectionEnd && area.selectedText.indexOf("\u2029") < 0)
findField.text = area.selectedText
findField.forceActiveFocus()
findField.selectAll()
Expand Down
15 changes: 11 additions & 4 deletions src/document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,16 @@ Document::Document(QObject *parent) : QObject(parent) {
connect(&m_debounce, &QTimer::timeout, this, &Document::diskChanged);
}

// QTextDocument separates its blocks with U+2029, so one in the file would
// become a line break. It's held in the editor as U+FDEF instead, and turned
// back on save: a noncharacter, which Unicode sets aside for a program's own
// use, so text typed or pasted in won't hold one.
static const QChar ParagraphStandIn(0xFDEF);

// Bytes to editable text, or not ok when saving the text again couldn't give
// back the same file: invalid UTF-8 (a sequence cut off at the end too), NUL
// bytes, carriage returns that aren't part of CRLF, or U+2029, which
// QTextDocument turns into a line break.
// bytes, carriage returns that aren't part of CRLF, or the U+2029 stand-in
// itself, which saving would turn into U+2029.
Document::Decoded Document::decode(const QByteArray &input) {
Decoded result;
QByteArray bytes = input;
Expand All @@ -91,8 +97,9 @@ Document::Decoded Document::decode(const QByteArray &input) {
QStringDecoder decoder(QStringConverter::Utf8,
QStringConverter::Flag::Stateless | QStringConverter::Flag::ConvertInitialBom);
QString text = decoder.decode(bytes);
if (decoder.hasError() || text.contains(QChar::ParagraphSeparator))
if (decoder.hasError() || text.contains(ParagraphStandIn))
return result;
text.replace(QChar::ParagraphSeparator, ParagraphStandIn);

int crlf = 0, lf = 0;
for (qsizetype i = 0; i < text.size(); ++i) {
Expand Down Expand Up @@ -121,7 +128,7 @@ QByteArray Document::encode(const QTextDocument *document, bool bom, bool crlf)
QString text;
const QString ending = crlf ? "\r\n" : "\n";
for (QTextBlock block = document->begin(); block.isValid(); block = block.next()) {
text += block.text();
text += block.text().replace(ParagraphStandIn, QChar::ParagraphSeparator);
Comment thread
dhh marked this conversation as resolved.
Resolved
if (block.next().isValid())
text += ending;
}
Expand Down
2 changes: 1 addition & 1 deletion src/document.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class Document : public QObject {
NotFound, // the path doesn't exist and its folder doesn't either
NotRegular, // a folder, device or pipe
NotReadable, // permission denied: belongs to root or someone else
NotText, // not UTF-8, NUL bytes, lone CRs, or U+2029
NotText, // not UTF-8, NUL bytes, lone CRs, or U+FDEF
TooLarge, // big enough that TextEdit would be slow; asks first
MixedEndings, // opened fine; will be saved with the majority ending
ChangedOnDisk, // changed elsewhere while there are unsaved edits
Expand Down
3 changes: 2 additions & 1 deletion tests/plain_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ private slots:
QVERIFY(!Document::decode(QByteArray("a\0b", 3)).ok);
QVERIFY(!Document::decode("caf\xE9\n").ok); // Latin-1, not UTF-8
QVERIFY(!Document::decode("a\rb\n").ok); // lone CR
QVERIFY(!Document::decode("a\xE2\x80\xA9" "b").ok); // U+2029
QVERIFY(!Document::decode("a\xEF\xB7\xAF" "b").ok); // U+FDEF, U+2029's stand-in
QVERIFY(Document::decode("").ok);
// A sequence cut off at the end, which a stateful decoder holds back
// without an error, so saving would drop it.
Expand All @@ -295,6 +295,7 @@ private slots:
QTest::newRow("double bom") << QByteArray("\xEF\xBB\xBF\xEF\xBB\xBF" "x\n");
QTest::newRow("nbsp and tabs") << QByteArray("a\xC2\xA0" "b\n\tc\n");
QTest::newRow("line separator") << QByteArray("a\xE2\x80\xA8" "b\n");
QTest::newRow("paragraph separator") << QByteArray("a\xE2\x80\xA9" "b\n");
QTest::newRow("empty") << QByteArray();
QTest::newRow("only newline") << QByteArray("\n");
QTest::newRow("unicode") << QByteArray("名前 = \"ø\" # 🎉\n");
Expand Down
Loading