Removed text commands.

This commit is contained in:
23rd 2022-01-07 02:02:41 +03:00
parent 09b56b019b
commit 7e279bd83a
4 changed files with 29 additions and 484 deletions

View file

@ -156,112 +156,6 @@ bool IsBad(QChar ch) {
} // namespace Text
} // 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 = {
TextParseLinks | TextParseMultiline, // flags
0, // maxw
@ -328,15 +222,11 @@ private:
void createBlock(int32 skipBack = 0);
void createSkipBlock(int32 w, int32 h);
void createNewlineBlock();
bool checkCommand();
// Returns true if at least one entity was parsed in the current position.
bool checkEntities();
bool readSkipBlockCommand();
bool readCommand();
void parseCurrentChar();
void parseEmojiFromCurrent();
void checkForElidedSkipBlock();
void finalize(const TextParseOptions &options);
void finishEntities();
@ -528,17 +418,6 @@ void Parser::createNewlineBlock() {
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() {
while (!_startedEntities.empty()
&& (_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() {
_ch = ((_ptr < _end) ? *_ptr : 0);
_emojiLookback = 0;
@ -964,7 +700,7 @@ void Parser::parse(const TextParseOptions &options) {
_t->_text.reserve(_end - _ptr);
for (; _ptr <= _end; ++_ptr) {
while (checkEntities() || (_rich && checkCommand())) {
while (checkEntities()) {
}
parseCurrentChar();
parseEmojiFromCurrent();
@ -974,7 +710,6 @@ void Parser::parse(const TextParseOptions &options) {
}
}
createBlock();
checkForElidedSkipBlock();
finalize(options);
}
@ -983,25 +718,25 @@ void Parser::trimSourceRange() {
_source.entities,
_end - _start);
while (_ptr != _end && IsTrimmed(*_ptr, _rich) && _ptr != _start + firstMonospaceOffset) {
while (_ptr != _end && IsTrimmed(*_ptr) && _ptr != _start + firstMonospaceOffset) {
++_ptr;
}
while (_ptr != _end && IsTrimmed(*(_end - 1), _rich)) {
while (_ptr != _end && IsTrimmed(*(_end - 1))) {
--_end;
}
}
void Parser::checkForElidedSkipBlock() {
if (!_sumFinished || !_rich) {
return;
}
// We could've skipped the final skip block command.
for (; _ptr < _end; ++_ptr) {
if (*_ptr == TextCommand && readSkipBlockCommand()) {
break;
}
}
}
// void Parser::checkForElidedSkipBlock() {
// if (!_sumFinished || !_rich) {
// return;
// }
// // We could've skipped the final skip block command.
// for (; _ptr < _end; ++_ptr) {
// if (*_ptr == TextCommand && readSkipBlockCommand()) {
// break;
// }
// }
// }
void Parser::finalize(const TextParseOptions &options) {
_t->_links.resize(_maxLnkIndex);
@ -3795,8 +3530,7 @@ bool IsAlmostLinkEnd(QChar ch) {
}
bool IsLinkEnd(QChar ch) {
return (ch == TextCommand)
|| IsBad(ch)
return IsBad(ch)
|| IsSpace(ch)
|| IsNewline(ch)
|| ch.isLowSurrogate()
@ -3808,9 +3542,9 @@ bool IsNewline(QChar ch) {
|| (ch == 156);
}
bool IsSpace(QChar ch, bool rich) {
bool IsSpace(QChar ch) {
return ch.isSpace()
|| (ch < 32 && !(rich && ch == TextCommand))
|| (ch < 32)
|| (ch == QChar::ParagraphSeparator)
|| (ch == QChar::LineSeparator)
|| (ch == QChar::ObjectReplacementCharacter)
@ -3840,9 +3574,8 @@ bool IsReplacedBySpace(QChar ch) {
|| (ch >= 8232 && ch <= 8237);
}
bool IsTrimmed(QChar ch, bool rich) {
return (!rich || ch != TextCommand)
&& (IsSpace(ch) || IsBad(ch));
bool IsTrimmed(QChar ch) {
return (IsSpace(ch) || IsBad(ch));
}
} // namespace Text

View file

@ -22,25 +22,6 @@ static const auto kQEllipsis = QStringLiteral("...");
} // namespace Ui
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 {
int32 flags;
@ -253,10 +234,10 @@ private:
[[nodiscard]] bool IsAlmostLinkEnd(QChar ch);
[[nodiscard]] bool IsLinkEnd(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 IsReplacedBySpace(QChar ch);
[[nodiscard]] bool IsTrimmed(QChar ch, bool rich = false);
[[nodiscard]] bool IsTrimmed(QChar ch);
} // namespace Text
} // 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) {
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);

View file

@ -1293,35 +1293,17 @@ bool IsValidTopDomain(const QString &protocol) {
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 result;
result.reserve(text.size());
auto s = text.constData(), ch = s;
for (const QChar *e = s + text.size(); ch != e; ++ch) {
if (*ch == TextCommand) {
if (ch > s) result.append(s, ch - s);
result.append(QChar::Space);
s = ch + 1;
continue;
}
// if (*ch == TextCommand) {
// if (ch > s) result.append(s, ch - s);
// result.append(QChar::Space);
// s = ch + 1;
// continue;
// }
if (ch->unicode() == '\\' || ch->unicode() == '[') {
if (ch > s) result.append(s, ch - s);
result.append('\\');
@ -1349,7 +1331,7 @@ QString SingleLine(const QString &text) {
}
for (auto ch = s; ch != e; ++ch) {
if (IsNewline(*ch) || *ch == TextCommand) {
if (IsNewline(*ch)/* || *ch == TextCommand*/) {
result[int(ch - s)] = QChar::Space;
}
}
@ -1545,47 +1527,6 @@ bool CutPart(TextWithEntities &sending, TextWithEntities &left, int32 limit) {
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) {
const auto rich = ((flags & TextParseRichText) != 0);
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 existingEntityEnd = 0;
int32 len = result.text.size(), commandOffset = rich ? 0 : len;
bool inLink = false, commandIsLink = false;
int32 len = result.text.size();
const auto start = result.text.constData();
const auto end = start + result.text.size();
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 mExplicitDomain = qthelp::RegExpDomainExplicit().match(result.text, matchOffset);
auto mHashtag = withHashtags ? RegExpHashtag().match(result.text, matchOffset) : QRegularExpressionMatch();
@ -1706,17 +1637,6 @@ void ParseEntities(TextWithEntities &result, int32 flags, bool rich) {
offset = matchOffset = mentionEnd;
continue;
}
const auto inCommand = checkTagStartInCommand(
start,
len,
mentionStart,
commandOffset,
commandIsLink,
inLink);
if (inCommand || inLink) {
offset = matchOffset = commandOffset;
continue;
}
lnkType = EntityType::Mention;
lnkStart = mentionStart;
@ -1727,50 +1647,15 @@ void ParseEntities(TextWithEntities &result, int32 flags, bool rich) {
offset = matchOffset = hashtagEnd;
continue;
}
const auto inCommand = checkTagStartInCommand(
start,
len,
hashtagStart,
commandOffset,
commandIsLink,
inLink);
if (inCommand || inLink) {
offset = matchOffset = commandOffset;
continue;
}
lnkType = EntityType::Hashtag;
lnkStart = hashtagStart;
lnkLength = hashtagEnd - hashtagStart;
} else if (botCommandStart < domainStart) {
const auto inCommand = checkTagStartInCommand(
start,
len,
botCommandStart,
commandOffset,
commandIsLink,
inLink);
if (inCommand || inLink) {
offset = matchOffset = commandOffset;
continue;
}
lnkType = EntityType::BotCommand;
lnkStart = botCommandStart;
lnkLength = botCommandEnd - botCommandStart;
} 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 topDomain = mDomain.captured(3).toLower();
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
EntityInText::EntityInText(

View file

@ -297,7 +297,6 @@ QString MarkdownSpoilerGoodBefore();
QString MarkdownSpoilerBadAfter();
// Text preprocess.
QString Clean(const QString &text, bool keepSpoilers = false);
QString EscapeForRichParsing(const QString &text);
QString SingleLine(const QString &text);
TextWithEntities SingleLine(const TextWithEntities &text);
@ -383,12 +382,4 @@ void SetClipboardText(
const TextForMimeData &text,
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