Track Ui::Text::String modifications on parse.
This commit is contained in:
parent
84cc457af9
commit
45ffbdeef9
5 changed files with 45 additions and 32 deletions
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue