Track Ui::Text::String modifications on parse.

This commit is contained in:
John Preston 2023-10-11 21:49:00 +04:00
parent 84cc457af9
commit 45ffbdeef9
5 changed files with 45 additions and 32 deletions

View file

@ -293,6 +293,9 @@ public:
[[nodiscard]] OnlyCustomEmoji toOnlyCustomEmoji() const; [[nodiscard]] OnlyCustomEmoji toOnlyCustomEmoji() const;
[[nodiscard]] bool hasNotEmojiAndSpaces() const; [[nodiscard]] bool hasNotEmojiAndSpaces() const;
[[nodiscard]] const base::flat_map<int, int> &modifications() const {
return _modifications;
}
[[nodiscard]] const style::TextStyle *style() const { [[nodiscard]] const style::TextStyle *style() const {
return _st; return _st;
@ -350,6 +353,7 @@ private:
bool _hasNotEmojiAndSpaces : 1 = false; bool _hasNotEmojiAndSpaces : 1 = false;
QString _text; QString _text;
base::flat_map<int, int> _modifications;
const style::TextStyle *_st = nullptr; const style::TextStyle *_st = nullptr;
TextBlocks _blocks; TextBlocks _blocks;

View file

@ -1890,7 +1890,7 @@ void Trim(TextWithEntities &result) {
} }
int SerializeTagsSize(const TextWithTags::Tags &tags) { int SerializeTagsSize(const TextWithTags::Tags &tags) {
auto result = qint32(0); auto result = int(sizeof(qint32)); // QByteArray size
if (tags.isEmpty()) { if (tags.isEmpty()) {
return result; return result;
} }

View file

@ -50,10 +50,17 @@ struct EntityLinkData {
QString data; QString data;
EntityType type = EntityType::Invalid; EntityType type = EntityType::Invalid;
EntityLinkShown shown = EntityLinkShown::Full; EntityLinkShown shown = EntityLinkShown::Full;
friend inline auto operator<=>(
const EntityLinkData &,
const EntityLinkData &) = default;
friend inline bool operator==(
const EntityLinkData &,
const EntityLinkData &) = default;
}; };
class EntityInText; class EntityInText;
using EntitiesInText = QList<EntityInText>; using EntitiesInText = QVector<EntityInText>;
class EntityInText { class EntityInText {
public: public:
@ -113,6 +120,13 @@ public:
return type() != EntityType::Invalid; return type() != EntityType::Invalid;
} }
friend inline auto operator<=>(
const EntityInText &,
const EntityInText &) = default;
friend inline bool operator==(
const EntityInText &,
const EntityInText &) = default;
private: private:
EntityType _type = EntityType::Invalid; EntityType _type = EntityType::Invalid;
int _offset = 0; int _offset = 0;
@ -121,17 +135,6 @@ private:
}; };
inline bool operator==(const EntityInText &a, const EntityInText &b) {
return (a.type() == b.type())
&& (a.offset() == b.offset())
&& (a.length() == b.length())
&& (a.data() == b.data());
}
inline bool operator!=(const EntityInText &a, const EntityInText &b) {
return !(a == b);
}
struct TextWithEntities { struct TextWithEntities {
QString text; QString text;
EntitiesInText entities; EntitiesInText entities;
@ -182,20 +185,15 @@ struct TextWithEntities {
result.text = simple; result.text = simple;
return result; return result;
} }
friend inline auto operator<=>(
const TextWithEntities &,
const TextWithEntities &) = default;
friend inline bool operator==(
const TextWithEntities &,
const TextWithEntities &) = default;
}; };
inline bool operator==(
const TextWithEntities &a,
const TextWithEntities &b) {
return (a.text == b.text) && (a.entities == b.entities);
}
inline bool operator!=(
const TextWithEntities &a,
const TextWithEntities &b) {
return !(a == b);
}
struct TextForMimeData { struct TextForMimeData {
QString expanded; QString expanded;
TextWithEntities rich; TextWithEntities rich;

View file

@ -182,6 +182,7 @@ void Parser::createBlock(int32 skipBack) {
if (_newlineAwaited) { if (_newlineAwaited) {
_newlineAwaited = false; _newlineAwaited = false;
if (!newline) { if (!newline) {
++_t->_modifications[_blockStart];
_t->_text.insert(_blockStart, QChar::LineFeed); _t->_text.insert(_blockStart, QChar::LineFeed);
createBlock(skipBack - length); createBlock(skipBack - length);
} }
@ -220,20 +221,23 @@ void Parser::createBlock(int32 skipBack) {
blockCreated(); blockCreated();
} }
void Parser::createNewlineBlock() { void Parser::createNewlineBlock(bool fromOriginalText) {
createBlock(); if (!fromOriginalText) {
++_t->_modifications[_t->_text.size()];
}
_t->_text.push_back(QChar::LineFeed); _t->_text.push_back(QChar::LineFeed);
_allowDiacritic = false; _allowDiacritic = false;
createBlock(); createBlock();
} }
void Parser::ensureAtNewline() { void Parser::ensureAtNewline() {
createBlock();
const auto lastType = _t->_blocks.empty() const auto lastType = _t->_blocks.empty()
? TextBlockType::Newline ? TextBlockType::Newline
: _t->_blocks.back()->type(); : _t->_blocks.back()->type();
if (lastType != TextBlockType::Newline) { if (lastType != TextBlockType::Newline) {
auto saved = base::take(_customEmojiData); auto saved = base::take(_customEmojiData);
createNewlineBlock(); createNewlineBlock(false);
_customEmojiData = base::take(saved); _customEmojiData = base::take(saved);
} }
} }
@ -335,7 +339,6 @@ bool Parser::checkEntities() {
flags = TextBlockFlag::Code; flags = TextBlockFlag::Code;
} else { } else {
flags = TextBlockFlag::Pre; flags = TextBlockFlag::Pre;
createBlock();
ensureAtNewline(); ensureAtNewline();
} }
const auto text = QString(entityBegin, entityLength); const auto text = QString(entityBegin, entityLength);
@ -352,7 +355,6 @@ bool Parser::checkEntities() {
} }
} else if (entityType == EntityType::Blockquote) { } else if (entityType == EntityType::Blockquote) {
flags = TextBlockFlag::Blockquote; flags = TextBlockFlag::Blockquote;
createBlock();
ensureAtNewline(); ensureAtNewline();
} else if (entityType == EntityType::Url } else if (entityType == EntityType::Url
|| entityType == EntityType::Email || entityType == EntityType::Email
@ -498,6 +500,7 @@ void Parser::parseCurrentChar() {
} }
if (skip) { if (skip) {
--_t->_modifications[_t->_text.size()];
_ch = 0; _ch = 0;
_allowDiacritic = false; _allowDiacritic = false;
} else { } else {
@ -513,7 +516,8 @@ void Parser::parseCurrentChar() {
} }
} }
if (isNewLine) { if (isNewLine) {
createNewlineBlock(); createBlock();
createNewlineBlock(true);
} else if (replaceWithSpace) { } else if (replaceWithSpace) {
_t->_text.push_back(QChar::Space); _t->_text.push_back(QChar::Space);
_allowDiacritic = false; _allowDiacritic = false;
@ -545,6 +549,7 @@ void Parser::parseEmojiFromCurrent() {
Assert(!_t->_text.isEmpty()); Assert(!_t->_text.isEmpty());
const auto last = _t->_text[_t->_text.size() - 1]; const auto last = _t->_text[_t->_text.size() - 1];
if (last.unicode() != Emoji::kPostfix) { if (last.unicode() != Emoji::kPostfix) {
++_t->_modifications[_t->_text.size()];
_t->_text.push_back(QChar(Emoji::kPostfix)); _t->_text.push_back(QChar(Emoji::kPostfix));
++len; ++len;
} }
@ -579,8 +584,13 @@ void Parser::parse(const TextParseOptions &options) {
trimSourceRange(); trimSourceRange();
_t->_text.resize(0); _t->_text.resize(0);
_t->_modifications = {};
_t->_text.reserve(_end - _ptr); _t->_text.reserve(_end - _ptr);
if (_ptr > _start) {
_t->_modifications[0] = -(_ptr - _start);
}
for (; _ptr <= _end; ++_ptr) { for (; _ptr <= _end; ++_ptr) {
while (checkEntities()) { while (checkEntities()) {
} }
@ -758,6 +768,7 @@ void Parser::finalize(const TextParseOptions &options) {
_t->_links.squeeze(); _t->_links.squeeze();
_t->_blocks.shrink_to_fit(); _t->_blocks.shrink_to_fit();
_t->_text.squeeze(); _t->_text.squeeze();
_t->_modifications.shrink_to_fit();
} }
void Parser::computeLinkText( void Parser::computeLinkText(

View file

@ -56,7 +56,7 @@ private:
void trimSourceRange(); void trimSourceRange();
void blockCreated(); void blockCreated();
void createBlock(int32 skipBack = 0); void createBlock(int32 skipBack = 0);
void createNewlineBlock(); void createNewlineBlock(bool fromOriginalText);
void ensureAtNewline(); void ensureAtNewline();
// Returns true if at least one entity was parsed in the current position. // Returns true if at least one entity was parsed in the current position.