Removed text commands.
This commit is contained in:
parent
09b56b019b
commit
7e279bd83a
4 changed files with 29 additions and 484 deletions
305
ui/text/text.cpp
305
ui/text/text.cpp
|
|
@ -156,112 +156,6 @@ bool IsBad(QChar ch) {
|
||||||
} // namespace Text
|
} // namespace Text
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
QString textcmdSkipBlock(ushort w, ushort h) {
|
|
||||||
static QString cmd(5, TextCommand);
|
|
||||||
cmd[1] = QChar(TextCommandSkipBlock);
|
|
||||||
cmd[2] = QChar(w);
|
|
||||||
cmd[3] = QChar(h);
|
|
||||||
return cmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString textcmdStartLink(ushort lnkIndex) {
|
|
||||||
static QString cmd(4, TextCommand);
|
|
||||||
cmd[1] = QChar(TextCommandLinkIndex);
|
|
||||||
cmd[2] = QChar(lnkIndex);
|
|
||||||
return cmd;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString textcmdStartLink(const QString &url) {
|
|
||||||
if (url.size() >= 4096) return QString();
|
|
||||||
|
|
||||||
QString result;
|
|
||||||
result.reserve(url.size() + 4);
|
|
||||||
return result.append(TextCommand).append(QChar(TextCommandLinkText)).append(QChar(int(url.size()))).append(url).append(TextCommand);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString textcmdStopLink() {
|
|
||||||
return textcmdStartLink(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString textcmdLink(ushort lnkIndex, const QString &text) {
|
|
||||||
QString result;
|
|
||||||
result.reserve(4 + text.size() + 4);
|
|
||||||
return result.append(textcmdStartLink(lnkIndex)).append(text).append(textcmdStopLink());
|
|
||||||
}
|
|
||||||
|
|
||||||
QString textcmdLink(const QString &url, const QString &text) {
|
|
||||||
QString result;
|
|
||||||
result.reserve(4 + url.size() + text.size() + 4);
|
|
||||||
return result.append(textcmdStartLink(url)).append(text).append(textcmdStopLink());
|
|
||||||
}
|
|
||||||
|
|
||||||
QString textcmdStartSemibold() {
|
|
||||||
QString result;
|
|
||||||
result.reserve(3);
|
|
||||||
return result.append(TextCommand).append(QChar(TextCommandSemibold)).append(TextCommand);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString textcmdStopSemibold() {
|
|
||||||
QString result;
|
|
||||||
result.reserve(3);
|
|
||||||
return result.append(TextCommand).append(QChar(TextCommandNoSemibold)).append(TextCommand);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString textcmdStartSpoiler() {
|
|
||||||
QString result;
|
|
||||||
result.reserve(3);
|
|
||||||
return result.append(TextCommand).append(QChar(TextCommandSpoiler)).append(TextCommand);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString textcmdStopSpoiler() {
|
|
||||||
QString result;
|
|
||||||
result.reserve(3);
|
|
||||||
return result.append(TextCommand).append(QChar(TextCommandNoSpoiler)).append(TextCommand);
|
|
||||||
}
|
|
||||||
|
|
||||||
const QChar *textSkipCommand(const QChar *from, const QChar *end, bool canLink) {
|
|
||||||
const QChar *result = from + 1;
|
|
||||||
if (*from != TextCommand || result >= end) return from;
|
|
||||||
|
|
||||||
ushort cmd = result->unicode();
|
|
||||||
++result;
|
|
||||||
if (result >= end) return from;
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
case TextCommandBold:
|
|
||||||
case TextCommandNoBold:
|
|
||||||
case TextCommandSemibold:
|
|
||||||
case TextCommandNoSemibold:
|
|
||||||
case TextCommandItalic:
|
|
||||||
case TextCommandNoItalic:
|
|
||||||
case TextCommandUnderline:
|
|
||||||
case TextCommandNoUnderline:
|
|
||||||
case TextCommandSpoiler:
|
|
||||||
case TextCommandNoSpoiler:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TextCommandLinkIndex:
|
|
||||||
if (result->unicode() > 0x7FFF) return from;
|
|
||||||
++result;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TextCommandLinkText: {
|
|
||||||
ushort len = result->unicode();
|
|
||||||
if (len >= 4096 || !canLink) return from;
|
|
||||||
result += len + 1;
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case TextCommandSkipBlock:
|
|
||||||
result += 2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TextCommandLangTag:
|
|
||||||
result += 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return (result < end && *result == TextCommand) ? (result + 1) : from;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TextParseOptions _defaultOptions = {
|
const TextParseOptions _defaultOptions = {
|
||||||
TextParseLinks | TextParseMultiline, // flags
|
TextParseLinks | TextParseMultiline, // flags
|
||||||
0, // maxw
|
0, // maxw
|
||||||
|
|
@ -328,15 +222,11 @@ private:
|
||||||
void createBlock(int32 skipBack = 0);
|
void createBlock(int32 skipBack = 0);
|
||||||
void createSkipBlock(int32 w, int32 h);
|
void createSkipBlock(int32 w, int32 h);
|
||||||
void createNewlineBlock();
|
void createNewlineBlock();
|
||||||
bool checkCommand();
|
|
||||||
|
|
||||||
// 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.
|
||||||
bool checkEntities();
|
bool checkEntities();
|
||||||
bool readSkipBlockCommand();
|
|
||||||
bool readCommand();
|
|
||||||
void parseCurrentChar();
|
void parseCurrentChar();
|
||||||
void parseEmojiFromCurrent();
|
void parseEmojiFromCurrent();
|
||||||
void checkForElidedSkipBlock();
|
|
||||||
void finalize(const TextParseOptions &options);
|
void finalize(const TextParseOptions &options);
|
||||||
|
|
||||||
void finishEntities();
|
void finishEntities();
|
||||||
|
|
@ -528,17 +418,6 @@ void Parser::createNewlineBlock() {
|
||||||
createBlock();
|
createBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Parser::checkCommand() {
|
|
||||||
bool result = false;
|
|
||||||
for (QChar c = ((_ptr < _end) ? *_ptr : 0); c == TextCommand; c = ((_ptr < _end) ? *_ptr : 0)) {
|
|
||||||
if (!readCommand()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
result = true;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Parser::finishEntities() {
|
void Parser::finishEntities() {
|
||||||
while (!_startedEntities.empty()
|
while (!_startedEntities.empty()
|
||||||
&& (_ptr >= _startedEntities.begin()->first || _ptr >= _end)) {
|
&& (_ptr >= _startedEntities.begin()->first || _ptr >= _end)) {
|
||||||
|
|
@ -691,149 +570,6 @@ void Parser::skipBadEntities() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Parser::readSkipBlockCommand() {
|
|
||||||
const QChar *afterCmd = textSkipCommand(_ptr, _end, _links.size() < 0x7FFF);
|
|
||||||
if (afterCmd == _ptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ushort cmd = (++_ptr)->unicode();
|
|
||||||
++_ptr;
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
case TextCommandSkipBlock:
|
|
||||||
createSkipBlock(_ptr->unicode(), (_ptr + 1)->unicode());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
_ptr = afterCmd;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Parser::readCommand() {
|
|
||||||
const QChar *afterCmd = textSkipCommand(_ptr, _end, _links.size() < 0x7FFF);
|
|
||||||
if (afterCmd == _ptr) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ushort cmd = (++_ptr)->unicode();
|
|
||||||
++_ptr;
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
case TextCommandBold:
|
|
||||||
if (!(_flags & TextBlockFBold)) {
|
|
||||||
createBlock();
|
|
||||||
_flags |= TextBlockFBold;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TextCommandNoBold:
|
|
||||||
if (_flags & TextBlockFBold) {
|
|
||||||
createBlock();
|
|
||||||
_flags &= ~TextBlockFBold;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TextCommandSemibold:
|
|
||||||
if (!(_flags & TextBlockFSemibold)) {
|
|
||||||
createBlock();
|
|
||||||
_flags |= TextBlockFSemibold;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TextCommandNoSemibold:
|
|
||||||
if (_flags & TextBlockFSemibold) {
|
|
||||||
createBlock();
|
|
||||||
_flags &= ~TextBlockFSemibold;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TextCommandItalic:
|
|
||||||
if (!(_flags & TextBlockFItalic)) {
|
|
||||||
createBlock();
|
|
||||||
_flags |= TextBlockFItalic;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TextCommandNoItalic:
|
|
||||||
if (_flags & TextBlockFItalic) {
|
|
||||||
createBlock();
|
|
||||||
_flags &= ~TextBlockFItalic;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TextCommandUnderline:
|
|
||||||
if (!(_flags & TextBlockFUnderline)) {
|
|
||||||
createBlock();
|
|
||||||
_flags |= TextBlockFUnderline;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TextCommandNoUnderline:
|
|
||||||
if (_flags & TextBlockFUnderline) {
|
|
||||||
createBlock();
|
|
||||||
_flags &= ~TextBlockFUnderline;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TextCommandStrikeOut:
|
|
||||||
if (!(_flags & TextBlockFStrikeOut)) {
|
|
||||||
createBlock();
|
|
||||||
_flags |= TextBlockFStrikeOut;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TextCommandNoStrikeOut:
|
|
||||||
if (_flags & TextBlockFStrikeOut) {
|
|
||||||
createBlock();
|
|
||||||
_flags &= ~TextBlockFStrikeOut;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TextCommandLinkIndex:
|
|
||||||
if (_ptr->unicode() != _lnkIndex) {
|
|
||||||
createBlock();
|
|
||||||
_lnkIndex = _ptr->unicode();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TextCommandLinkText: {
|
|
||||||
createBlock();
|
|
||||||
int32 len = _ptr->unicode();
|
|
||||||
_links.push_back(EntityLinkData{
|
|
||||||
.data = QString(++_ptr, len),
|
|
||||||
.type = EntityType::CustomUrl
|
|
||||||
});
|
|
||||||
_lnkIndex = kStringLinkIndexShift + _links.size();
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case TextCommandSpoiler: {
|
|
||||||
if (!_spoilerIndex) {
|
|
||||||
createBlock();
|
|
||||||
_spoilers.push_back(EntityLinkData{
|
|
||||||
.data = QString::number(_spoilers.size() + 1),
|
|
||||||
.type = EntityType::Spoiler,
|
|
||||||
});
|
|
||||||
_spoilerIndex = _spoilers.size();
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case TextCommandNoSpoiler:
|
|
||||||
if (_spoilerIndex == _spoilers.size()) {
|
|
||||||
createBlock();
|
|
||||||
_spoilerIndex = 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TextCommandSkipBlock:
|
|
||||||
createSkipBlock(_ptr->unicode(), (_ptr + 1)->unicode());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
_ptr = afterCmd;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Parser::parseCurrentChar() {
|
void Parser::parseCurrentChar() {
|
||||||
_ch = ((_ptr < _end) ? *_ptr : 0);
|
_ch = ((_ptr < _end) ? *_ptr : 0);
|
||||||
_emojiLookback = 0;
|
_emojiLookback = 0;
|
||||||
|
|
@ -964,7 +700,7 @@ void Parser::parse(const TextParseOptions &options) {
|
||||||
_t->_text.reserve(_end - _ptr);
|
_t->_text.reserve(_end - _ptr);
|
||||||
|
|
||||||
for (; _ptr <= _end; ++_ptr) {
|
for (; _ptr <= _end; ++_ptr) {
|
||||||
while (checkEntities() || (_rich && checkCommand())) {
|
while (checkEntities()) {
|
||||||
}
|
}
|
||||||
parseCurrentChar();
|
parseCurrentChar();
|
||||||
parseEmojiFromCurrent();
|
parseEmojiFromCurrent();
|
||||||
|
|
@ -974,7 +710,6 @@ void Parser::parse(const TextParseOptions &options) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
createBlock();
|
createBlock();
|
||||||
checkForElidedSkipBlock();
|
|
||||||
finalize(options);
|
finalize(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -983,25 +718,25 @@ void Parser::trimSourceRange() {
|
||||||
_source.entities,
|
_source.entities,
|
||||||
_end - _start);
|
_end - _start);
|
||||||
|
|
||||||
while (_ptr != _end && IsTrimmed(*_ptr, _rich) && _ptr != _start + firstMonospaceOffset) {
|
while (_ptr != _end && IsTrimmed(*_ptr) && _ptr != _start + firstMonospaceOffset) {
|
||||||
++_ptr;
|
++_ptr;
|
||||||
}
|
}
|
||||||
while (_ptr != _end && IsTrimmed(*(_end - 1), _rich)) {
|
while (_ptr != _end && IsTrimmed(*(_end - 1))) {
|
||||||
--_end;
|
--_end;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Parser::checkForElidedSkipBlock() {
|
// void Parser::checkForElidedSkipBlock() {
|
||||||
if (!_sumFinished || !_rich) {
|
// if (!_sumFinished || !_rich) {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
// We could've skipped the final skip block command.
|
// // We could've skipped the final skip block command.
|
||||||
for (; _ptr < _end; ++_ptr) {
|
// for (; _ptr < _end; ++_ptr) {
|
||||||
if (*_ptr == TextCommand && readSkipBlockCommand()) {
|
// if (*_ptr == TextCommand && readSkipBlockCommand()) {
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
void Parser::finalize(const TextParseOptions &options) {
|
void Parser::finalize(const TextParseOptions &options) {
|
||||||
_t->_links.resize(_maxLnkIndex);
|
_t->_links.resize(_maxLnkIndex);
|
||||||
|
|
@ -3795,8 +3530,7 @@ bool IsAlmostLinkEnd(QChar ch) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsLinkEnd(QChar ch) {
|
bool IsLinkEnd(QChar ch) {
|
||||||
return (ch == TextCommand)
|
return IsBad(ch)
|
||||||
|| IsBad(ch)
|
|
||||||
|| IsSpace(ch)
|
|| IsSpace(ch)
|
||||||
|| IsNewline(ch)
|
|| IsNewline(ch)
|
||||||
|| ch.isLowSurrogate()
|
|| ch.isLowSurrogate()
|
||||||
|
|
@ -3808,9 +3542,9 @@ bool IsNewline(QChar ch) {
|
||||||
|| (ch == 156);
|
|| (ch == 156);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsSpace(QChar ch, bool rich) {
|
bool IsSpace(QChar ch) {
|
||||||
return ch.isSpace()
|
return ch.isSpace()
|
||||||
|| (ch < 32 && !(rich && ch == TextCommand))
|
|| (ch < 32)
|
||||||
|| (ch == QChar::ParagraphSeparator)
|
|| (ch == QChar::ParagraphSeparator)
|
||||||
|| (ch == QChar::LineSeparator)
|
|| (ch == QChar::LineSeparator)
|
||||||
|| (ch == QChar::ObjectReplacementCharacter)
|
|| (ch == QChar::ObjectReplacementCharacter)
|
||||||
|
|
@ -3840,9 +3574,8 @@ bool IsReplacedBySpace(QChar ch) {
|
||||||
|| (ch >= 8232 && ch <= 8237);
|
|| (ch >= 8232 && ch <= 8237);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsTrimmed(QChar ch, bool rich) {
|
bool IsTrimmed(QChar ch) {
|
||||||
return (!rich || ch != TextCommand)
|
return (IsSpace(ch) || IsBad(ch));
|
||||||
&& (IsSpace(ch) || IsBad(ch));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Text
|
} // namespace Text
|
||||||
|
|
|
||||||
|
|
@ -22,25 +22,6 @@ static const auto kQEllipsis = QStringLiteral("...");
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
static const QChar TextCommand(0x0010);
|
static const QChar TextCommand(0x0010);
|
||||||
enum TextCommands {
|
|
||||||
TextCommandBold = 0x01,
|
|
||||||
TextCommandNoBold = 0x02,
|
|
||||||
TextCommandItalic = 0x03,
|
|
||||||
TextCommandNoItalic = 0x04,
|
|
||||||
TextCommandUnderline = 0x05,
|
|
||||||
TextCommandNoUnderline = 0x06,
|
|
||||||
TextCommandStrikeOut = 0x07,
|
|
||||||
TextCommandNoStrikeOut = 0x08,
|
|
||||||
TextCommandSemibold = 0x09,
|
|
||||||
TextCommandNoSemibold = 0x0A,
|
|
||||||
TextCommandLinkIndex = 0x0B, // 0 - NoLink
|
|
||||||
TextCommandLinkText = 0x0C,
|
|
||||||
TextCommandSkipBlock = 0x0D,
|
|
||||||
TextCommandSpoiler = 0x0E,
|
|
||||||
TextCommandNoSpoiler = 0x0F,
|
|
||||||
|
|
||||||
TextCommandLangTag = 0x20,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct TextParseOptions {
|
struct TextParseOptions {
|
||||||
int32 flags;
|
int32 flags;
|
||||||
|
|
@ -253,10 +234,10 @@ private:
|
||||||
[[nodiscard]] bool IsAlmostLinkEnd(QChar ch);
|
[[nodiscard]] bool IsAlmostLinkEnd(QChar ch);
|
||||||
[[nodiscard]] bool IsLinkEnd(QChar ch);
|
[[nodiscard]] bool IsLinkEnd(QChar ch);
|
||||||
[[nodiscard]] bool IsNewline(QChar ch);
|
[[nodiscard]] bool IsNewline(QChar ch);
|
||||||
[[nodiscard]] bool IsSpace(QChar ch, bool rich = false);
|
[[nodiscard]] bool IsSpace(QChar ch);
|
||||||
[[nodiscard]] bool IsDiac(QChar ch);
|
[[nodiscard]] bool IsDiac(QChar ch);
|
||||||
[[nodiscard]] bool IsReplacedBySpace(QChar ch);
|
[[nodiscard]] bool IsReplacedBySpace(QChar ch);
|
||||||
[[nodiscard]] bool IsTrimmed(QChar ch, bool rich = false);
|
[[nodiscard]] bool IsTrimmed(QChar ch);
|
||||||
|
|
||||||
} // namespace Text
|
} // namespace Text
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
@ -276,18 +257,3 @@ inline TextSelection shiftSelection(TextSelection selection, const Ui::Text::Str
|
||||||
inline TextSelection unshiftSelection(TextSelection selection, const Ui::Text::String &byText) {
|
inline TextSelection unshiftSelection(TextSelection selection, const Ui::Text::String &byText) {
|
||||||
return unshiftSelection(selection, byText.length());
|
return unshiftSelection(selection, byText.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
// textcmd
|
|
||||||
QString textcmdSkipBlock(ushort w, ushort h);
|
|
||||||
QString textcmdStartLink(ushort lnkIndex);
|
|
||||||
QString textcmdStartLink(const QString &url);
|
|
||||||
QString textcmdStopLink();
|
|
||||||
QString textcmdLink(ushort lnkIndex, const QString &text);
|
|
||||||
QString textcmdLink(const QString &url, const QString &text);
|
|
||||||
QString textcmdStartSemibold();
|
|
||||||
QString textcmdStopSemibold();
|
|
||||||
|
|
||||||
QString textcmdStartSpoiler();
|
|
||||||
QString textcmdStopSpoiler();
|
|
||||||
|
|
||||||
const QChar *textSkipCommand(const QChar *from, const QChar *end, bool canLink = true);
|
|
||||||
|
|
|
||||||
|
|
@ -1293,35 +1293,17 @@ bool IsValidTopDomain(const QString &protocol) {
|
||||||
return list.contains(base::crc32(protocol.constData(), protocol.size() * sizeof(QChar)));
|
return list.contains(base::crc32(protocol.constData(), protocol.size() * sizeof(QChar)));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString Clean(const QString &text, bool keepSpoilers) {
|
|
||||||
auto result = text;
|
|
||||||
for (auto s = text.unicode(), ch = s, e = text.unicode() + text.size(); ch != e; ++ch) {
|
|
||||||
if (keepSpoilers && (*ch == TextCommand)) {
|
|
||||||
if ((*(ch + 1) == TextCommandSpoiler)
|
|
||||||
|| (*(ch - 1) == TextCommandSpoiler)
|
|
||||||
|| (*(ch + 1) == TextCommandNoSpoiler)
|
|
||||||
|| (*(ch - 1) == TextCommandNoSpoiler)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (*ch == TextCommand) {
|
|
||||||
result[int(ch - s)] = QChar::Space;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString EscapeForRichParsing(const QString &text) {
|
QString EscapeForRichParsing(const QString &text) {
|
||||||
QString result;
|
QString result;
|
||||||
result.reserve(text.size());
|
result.reserve(text.size());
|
||||||
auto s = text.constData(), ch = s;
|
auto s = text.constData(), ch = s;
|
||||||
for (const QChar *e = s + text.size(); ch != e; ++ch) {
|
for (const QChar *e = s + text.size(); ch != e; ++ch) {
|
||||||
if (*ch == TextCommand) {
|
// if (*ch == TextCommand) {
|
||||||
if (ch > s) result.append(s, ch - s);
|
// if (ch > s) result.append(s, ch - s);
|
||||||
result.append(QChar::Space);
|
// result.append(QChar::Space);
|
||||||
s = ch + 1;
|
// s = ch + 1;
|
||||||
continue;
|
// continue;
|
||||||
}
|
// }
|
||||||
if (ch->unicode() == '\\' || ch->unicode() == '[') {
|
if (ch->unicode() == '\\' || ch->unicode() == '[') {
|
||||||
if (ch > s) result.append(s, ch - s);
|
if (ch > s) result.append(s, ch - s);
|
||||||
result.append('\\');
|
result.append('\\');
|
||||||
|
|
@ -1349,7 +1331,7 @@ QString SingleLine(const QString &text) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto ch = s; ch != e; ++ch) {
|
for (auto ch = s; ch != e; ++ch) {
|
||||||
if (IsNewline(*ch) || *ch == TextCommand) {
|
if (IsNewline(*ch)/* || *ch == TextCommand*/) {
|
||||||
result[int(ch - s)] = QChar::Space;
|
result[int(ch - s)] = QChar::Space;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1545,47 +1527,6 @@ bool CutPart(TextWithEntities &sending, TextWithEntities &left, int32 limit) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool textcmdStartsLink(const QChar *start, int32 len, int32 commandOffset) {
|
|
||||||
if (commandOffset + 2 < len) {
|
|
||||||
if (*(start + commandOffset + 1) == TextCommandLinkIndex) {
|
|
||||||
return (*(start + commandOffset + 2) != 0);
|
|
||||||
}
|
|
||||||
return (*(start + commandOffset + 1) != TextCommandLinkText);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool checkTagStartInCommand(const QChar *start, int32 len, int32 tagStart, int32 &commandOffset, bool &commandIsLink, bool &inLink) {
|
|
||||||
bool inCommand = false;
|
|
||||||
const QChar *commandEnd = start + commandOffset;
|
|
||||||
while (commandOffset < len && tagStart > commandOffset) { // skip commands, evaluating are we in link or not
|
|
||||||
commandEnd = textSkipCommand(start + commandOffset, start + len);
|
|
||||||
if (commandEnd > start + commandOffset) {
|
|
||||||
if (tagStart < (commandEnd - start)) {
|
|
||||||
inCommand = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
for (commandOffset = commandEnd - start; commandOffset < len; ++commandOffset) {
|
|
||||||
if (*(start + commandOffset) == TextCommand) {
|
|
||||||
inLink = commandIsLink;
|
|
||||||
commandIsLink = textcmdStartsLink(start, len, commandOffset);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (commandOffset >= len) {
|
|
||||||
inLink = commandIsLink;
|
|
||||||
commandIsLink = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (inCommand) {
|
|
||||||
commandOffset = commandEnd - start;
|
|
||||||
}
|
|
||||||
return inCommand;
|
|
||||||
}
|
|
||||||
|
|
||||||
TextWithEntities ParseEntities(const QString &text, int32 flags) {
|
TextWithEntities ParseEntities(const QString &text, int32 flags) {
|
||||||
const auto rich = ((flags & TextParseRichText) != 0);
|
const auto rich = ((flags & TextParseRichText) != 0);
|
||||||
auto result = TextWithEntities{ text, EntitiesInText() };
|
auto result = TextWithEntities{ text, EntitiesInText() };
|
||||||
|
|
@ -1605,20 +1546,10 @@ void ParseEntities(TextWithEntities &result, int32 flags, bool rich) {
|
||||||
int existingEntityIndex = 0, existingEntitiesCount = result.entities.size();
|
int existingEntityIndex = 0, existingEntitiesCount = result.entities.size();
|
||||||
int existingEntityEnd = 0;
|
int existingEntityEnd = 0;
|
||||||
|
|
||||||
int32 len = result.text.size(), commandOffset = rich ? 0 : len;
|
int32 len = result.text.size();
|
||||||
bool inLink = false, commandIsLink = false;
|
|
||||||
const auto start = result.text.constData();
|
const auto start = result.text.constData();
|
||||||
const auto end = start + result.text.size();
|
const auto end = start + result.text.size();
|
||||||
for (int32 offset = 0, matchOffset = offset, mentionSkip = 0; offset < len;) {
|
for (int32 offset = 0, matchOffset = offset, mentionSkip = 0; offset < len;) {
|
||||||
if (commandOffset <= offset) {
|
|
||||||
for (commandOffset = offset; commandOffset < len; ++commandOffset) {
|
|
||||||
if (*(start + commandOffset) == TextCommand) {
|
|
||||||
inLink = commandIsLink;
|
|
||||||
commandIsLink = textcmdStartsLink(start, len, commandOffset);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto mDomain = qthelp::RegExpDomain().match(result.text, matchOffset);
|
auto mDomain = qthelp::RegExpDomain().match(result.text, matchOffset);
|
||||||
auto mExplicitDomain = qthelp::RegExpDomainExplicit().match(result.text, matchOffset);
|
auto mExplicitDomain = qthelp::RegExpDomainExplicit().match(result.text, matchOffset);
|
||||||
auto mHashtag = withHashtags ? RegExpHashtag().match(result.text, matchOffset) : QRegularExpressionMatch();
|
auto mHashtag = withHashtags ? RegExpHashtag().match(result.text, matchOffset) : QRegularExpressionMatch();
|
||||||
|
|
@ -1706,17 +1637,6 @@ void ParseEntities(TextWithEntities &result, int32 flags, bool rich) {
|
||||||
offset = matchOffset = mentionEnd;
|
offset = matchOffset = mentionEnd;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto inCommand = checkTagStartInCommand(
|
|
||||||
start,
|
|
||||||
len,
|
|
||||||
mentionStart,
|
|
||||||
commandOffset,
|
|
||||||
commandIsLink,
|
|
||||||
inLink);
|
|
||||||
if (inCommand || inLink) {
|
|
||||||
offset = matchOffset = commandOffset;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
lnkType = EntityType::Mention;
|
lnkType = EntityType::Mention;
|
||||||
lnkStart = mentionStart;
|
lnkStart = mentionStart;
|
||||||
|
|
@ -1727,50 +1647,15 @@ void ParseEntities(TextWithEntities &result, int32 flags, bool rich) {
|
||||||
offset = matchOffset = hashtagEnd;
|
offset = matchOffset = hashtagEnd;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto inCommand = checkTagStartInCommand(
|
|
||||||
start,
|
|
||||||
len,
|
|
||||||
hashtagStart,
|
|
||||||
commandOffset,
|
|
||||||
commandIsLink,
|
|
||||||
inLink);
|
|
||||||
if (inCommand || inLink) {
|
|
||||||
offset = matchOffset = commandOffset;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
lnkType = EntityType::Hashtag;
|
lnkType = EntityType::Hashtag;
|
||||||
lnkStart = hashtagStart;
|
lnkStart = hashtagStart;
|
||||||
lnkLength = hashtagEnd - hashtagStart;
|
lnkLength = hashtagEnd - hashtagStart;
|
||||||
} else if (botCommandStart < domainStart) {
|
} else if (botCommandStart < domainStart) {
|
||||||
const auto inCommand = checkTagStartInCommand(
|
|
||||||
start,
|
|
||||||
len,
|
|
||||||
botCommandStart,
|
|
||||||
commandOffset,
|
|
||||||
commandIsLink,
|
|
||||||
inLink);
|
|
||||||
if (inCommand || inLink) {
|
|
||||||
offset = matchOffset = commandOffset;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
lnkType = EntityType::BotCommand;
|
lnkType = EntityType::BotCommand;
|
||||||
lnkStart = botCommandStart;
|
lnkStart = botCommandStart;
|
||||||
lnkLength = botCommandEnd - botCommandStart;
|
lnkLength = botCommandEnd - botCommandStart;
|
||||||
} else {
|
} else {
|
||||||
const auto inCommand = checkTagStartInCommand(
|
|
||||||
start,
|
|
||||||
len,
|
|
||||||
domainStart,
|
|
||||||
commandOffset,
|
|
||||||
commandIsLink,
|
|
||||||
inLink);
|
|
||||||
if (inCommand || inLink) {
|
|
||||||
offset = matchOffset = commandOffset;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto protocol = mDomain.captured(1).toLower();
|
auto protocol = mDomain.captured(1).toLower();
|
||||||
auto topDomain = mDomain.captured(3).toLower();
|
auto topDomain = mDomain.captured(3).toLower();
|
||||||
auto isProtocolValid = protocol.isEmpty() || IsValidProtocol(protocol);
|
auto isProtocolValid = protocol.isEmpty() || IsValidProtocol(protocol);
|
||||||
|
|
@ -2332,36 +2217,6 @@ void SetClipboardText(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString TextWithSpoilerCommands(const TextWithEntities &textWithEntities) {
|
|
||||||
auto text = textWithEntities.text;
|
|
||||||
auto offset = 0;
|
|
||||||
const auto start = textcmdStartSpoiler();
|
|
||||||
const auto stop = textcmdStopSpoiler();
|
|
||||||
for (const auto &e : textWithEntities.entities) {
|
|
||||||
if (e.type() == EntityType::Spoiler) {
|
|
||||||
text.insert(e.offset() + offset, start);
|
|
||||||
offset += start.size();
|
|
||||||
text.insert(e.offset() + e.length() + offset, stop);
|
|
||||||
offset += stop.size();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString CutTextWithCommands(
|
|
||||||
QString text,
|
|
||||||
int length,
|
|
||||||
const QString &start,
|
|
||||||
const QString &stop) {
|
|
||||||
text = text.mid(0, length);
|
|
||||||
const auto lastStart = text.lastIndexOf(start);
|
|
||||||
const auto lastStop = text.lastIndexOf(stop);
|
|
||||||
const auto additional = ((lastStart == -1) || (lastStart < lastStop))
|
|
||||||
? QString()
|
|
||||||
: stop;
|
|
||||||
return text + additional + Ui::kQEllipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace TextUtilities
|
} // namespace TextUtilities
|
||||||
|
|
||||||
EntityInText::EntityInText(
|
EntityInText::EntityInText(
|
||||||
|
|
|
||||||
|
|
@ -297,7 +297,6 @@ QString MarkdownSpoilerGoodBefore();
|
||||||
QString MarkdownSpoilerBadAfter();
|
QString MarkdownSpoilerBadAfter();
|
||||||
|
|
||||||
// Text preprocess.
|
// Text preprocess.
|
||||||
QString Clean(const QString &text, bool keepSpoilers = false);
|
|
||||||
QString EscapeForRichParsing(const QString &text);
|
QString EscapeForRichParsing(const QString &text);
|
||||||
QString SingleLine(const QString &text);
|
QString SingleLine(const QString &text);
|
||||||
TextWithEntities SingleLine(const TextWithEntities &text);
|
TextWithEntities SingleLine(const TextWithEntities &text);
|
||||||
|
|
@ -383,12 +382,4 @@ void SetClipboardText(
|
||||||
const TextForMimeData &text,
|
const TextForMimeData &text,
|
||||||
QClipboard::Mode mode = QClipboard::Clipboard);
|
QClipboard::Mode mode = QClipboard::Clipboard);
|
||||||
|
|
||||||
[[nodiscard]] QString TextWithSpoilerCommands(
|
|
||||||
const TextWithEntities &textWithEntities);
|
|
||||||
[[nodiscard]] QString CutTextWithCommands(
|
|
||||||
QString text,
|
|
||||||
int length,
|
|
||||||
const QString &start,
|
|
||||||
const QString &stop);
|
|
||||||
|
|
||||||
} // namespace TextUtilities
|
} // namespace TextUtilities
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue