mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-02-28 19:40:54 +00:00
Fix markdown apply to text with emoji.
This commit is contained in:
parent
122ab94f3d
commit
dca6e10beb
@ -609,19 +609,21 @@ void MessageLinksParser::parse() {
|
||||
const auto markdownTagsEnd = markdownTags.end();
|
||||
const auto markdownTagsAllow = [&](int from, int length) {
|
||||
while (markdownTag != markdownTagsEnd
|
||||
&& (markdownTag->start + markdownTag->length <= from
|
||||
&& (markdownTag->adjustedStart
|
||||
+ markdownTag->adjustedLength <= from
|
||||
|| !markdownTag->closed)) {
|
||||
++markdownTag;
|
||||
continue;
|
||||
}
|
||||
if (markdownTag == markdownTagsEnd
|
||||
|| markdownTag->start >= from + length) {
|
||||
|| markdownTag->adjustedStart >= from + length) {
|
||||
return true;
|
||||
}
|
||||
// Ignore http-links that are completely inside some tags.
|
||||
// This will allow sending http://test.com/__test__/test correctly.
|
||||
return (markdownTag->start > from
|
||||
|| markdownTag->start + markdownTag->length < from + length);
|
||||
return (markdownTag->adjustedStart > from)
|
||||
|| (markdownTag->adjustedStart
|
||||
+ markdownTag->adjustedLength < from + length);
|
||||
};
|
||||
|
||||
const auto len = text.size();
|
||||
|
@ -272,12 +272,22 @@ public:
|
||||
, _items(_expressions.size()) {
|
||||
}
|
||||
|
||||
void feed(const QString &text, const QString &textTag) {
|
||||
// Here we use the fact that text either contains only emoji
|
||||
// { adjustedTextLength = text.size() * (emojiLength - 1) }
|
||||
// or contains no emoji at all and can have tag edges in the middle
|
||||
// { adjustedTextLength = 0 }.
|
||||
//
|
||||
// Otherwise we would have to pass emoji positions inside text.
|
||||
void feed(
|
||||
const QString &text,
|
||||
int adjustedTextLength,
|
||||
const QString &textTag) {
|
||||
if (!_tags) {
|
||||
return;
|
||||
}
|
||||
const auto guard = gsl::finally([&] {
|
||||
_currentLength += text.size();
|
||||
_currentInternalLength += text.size();
|
||||
_currentAdjustedLength += adjustedTextLength;
|
||||
});
|
||||
if (!textTag.isEmpty()) {
|
||||
finishTags();
|
||||
@ -290,7 +300,7 @@ public:
|
||||
while (true) {
|
||||
for (; tryFinishTag != _currentFreeTag; ++tryFinishTag) {
|
||||
auto &tag = (*_tags)[tryFinishTag];
|
||||
if (tag.length >= 0) {
|
||||
if (tag.internalLength >= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -298,8 +308,12 @@ public:
|
||||
Assert(i != end(_tagIndices));
|
||||
const auto tagIndex = i->second;
|
||||
|
||||
_items[tagIndex].applyOffset(
|
||||
tag.start + tag.tag.size() + 1 - _currentLength);
|
||||
const auto atLeastOffset =
|
||||
tag.internalStart
|
||||
+ tag.tag.size()
|
||||
+ 1
|
||||
- _currentInternalLength;
|
||||
_items[tagIndex].applyOffset(atLeastOffset);
|
||||
|
||||
fillItem(
|
||||
tagIndex,
|
||||
@ -311,10 +325,7 @@ public:
|
||||
const auto position = matchPosition(tagIndex, Edge::Close);
|
||||
if (position < kInvalidPosition) {
|
||||
const auto till = position + tag.tag.size();
|
||||
finishTag(
|
||||
tryFinishTag,
|
||||
_currentLength + till,
|
||||
true);
|
||||
finishTag(tryFinishTag, till, true);
|
||||
_items[tagIndex].applyOffset(till);
|
||||
}
|
||||
}
|
||||
@ -325,9 +336,7 @@ public:
|
||||
if (min < 0) {
|
||||
return;
|
||||
}
|
||||
startTag(
|
||||
_currentLength + matchPosition(min, Edge::Open),
|
||||
_expressions[min].tag);
|
||||
startTag(matchPosition(min, Edge::Open), _expressions[min].tag);
|
||||
}
|
||||
}
|
||||
|
||||
@ -342,13 +351,18 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void finishTag(int index, int end, bool closed) {
|
||||
void finishTag(int index, int offsetFromAccumulated, bool closed) {
|
||||
Expects(_tags != nullptr);
|
||||
Expects(index >= 0 && index < _tags->size());
|
||||
|
||||
auto &tag = (*_tags)[index];
|
||||
if (tag.length < 0) {
|
||||
tag.length = end - tag.start;
|
||||
if (tag.internalLength < 0) {
|
||||
tag.internalLength = _currentInternalLength
|
||||
+ offsetFromAccumulated
|
||||
- tag.internalStart;
|
||||
tag.adjustedLength = _currentAdjustedLength
|
||||
+ offsetFromAccumulated
|
||||
- tag.adjustedStart;
|
||||
tag.closed = closed;
|
||||
}
|
||||
if (index == _currentTag) {
|
||||
@ -369,25 +383,33 @@ private:
|
||||
}
|
||||
const auto endPosition = newlinePosition(
|
||||
text,
|
||||
std::max(0, tag.start + 1 - _currentLength));
|
||||
std::max(0, tag.internalStart + 1 - _currentInternalLength));
|
||||
if (matchPosition(tagIndex, Edge::Close) <= endPosition) {
|
||||
return false;
|
||||
}
|
||||
finishTag(index, _currentLength + endPosition, false);
|
||||
finishTag(index, endPosition, false);
|
||||
return true;
|
||||
}
|
||||
void finishTags() {
|
||||
while (_currentTag != _currentFreeTag) {
|
||||
finishTag(_currentTag, _currentLength, false);
|
||||
finishTag(_currentTag, 0, false);
|
||||
}
|
||||
}
|
||||
void startTag(int offset, const QString &tag) {
|
||||
void startTag(int offsetFromAccumulated, const QString &tag) {
|
||||
Expects(_tags != nullptr);
|
||||
|
||||
const auto newTag = InputField::MarkdownTag{
|
||||
_currentInternalLength + offsetFromAccumulated,
|
||||
-1,
|
||||
_currentAdjustedLength + offsetFromAccumulated,
|
||||
-1,
|
||||
false,
|
||||
tag
|
||||
};
|
||||
if (_currentFreeTag < _tags->size()) {
|
||||
(*_tags)[_currentFreeTag] = { offset, -1, false, tag };
|
||||
(*_tags)[_currentFreeTag] = newTag;
|
||||
} else {
|
||||
_tags->push_back({ offset, -1, false, tag });
|
||||
_tags->push_back(newTag);
|
||||
}
|
||||
++_currentFreeTag;
|
||||
}
|
||||
@ -447,7 +469,8 @@ private:
|
||||
|
||||
int _currentTag = 0;
|
||||
int _currentFreeTag = 0;
|
||||
int _currentLength = 0;
|
||||
int _currentInternalLength = 0;
|
||||
int _currentAdjustedLength = 0;
|
||||
|
||||
};
|
||||
|
||||
@ -1762,11 +1785,11 @@ QString InputField::getTextPart(
|
||||
if (full || !text.isEmpty()) {
|
||||
lastTag = format.property(kTagProperty).toString();
|
||||
tagAccumulator.feed(lastTag, result.size());
|
||||
markdownTagAccumulator.feed(text, lastTag);
|
||||
}
|
||||
|
||||
auto begin = text.data();
|
||||
auto ch = begin;
|
||||
auto adjustedLength = text.size();
|
||||
for (const auto end = begin + text.size(); ch != end; ++ch) {
|
||||
if (IsNewline(*ch) && ch->unicode() != '\r') {
|
||||
*ch = QLatin1Char('\n');
|
||||
@ -1778,6 +1801,7 @@ QString InputField::getTextPart(
|
||||
if (ch > begin) {
|
||||
result.append(begin, ch - begin);
|
||||
}
|
||||
adjustedLength += (emojiText.size() - 1);
|
||||
if (!emojiText.isEmpty()) {
|
||||
result.append(emojiText);
|
||||
}
|
||||
@ -1788,12 +1812,16 @@ QString InputField::getTextPart(
|
||||
if (ch > begin) {
|
||||
result.append(begin, ch - begin);
|
||||
}
|
||||
|
||||
if (full || !text.isEmpty()) {
|
||||
markdownTagAccumulator.feed(text, adjustedLength, lastTag);
|
||||
}
|
||||
}
|
||||
|
||||
block = block.next();
|
||||
if (block != till) {
|
||||
result.append('\n');
|
||||
markdownTagAccumulator.feed(newline, lastTag);
|
||||
markdownTagAccumulator.feed(newline, 1, lastTag);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2151,14 +2179,17 @@ void InputField::highlightMarkdown() {
|
||||
from = b;
|
||||
};
|
||||
for (const auto &tag : _lastMarkdownTags) {
|
||||
if (tag.start > from) {
|
||||
applyColor(from, tag.start, QColor(0, 0, 0));
|
||||
} else if (tag.start < from) {
|
||||
if (tag.internalStart > from) {
|
||||
applyColor(from, tag.internalStart, QColor(0, 0, 0));
|
||||
} else if (tag.internalStart < from) {
|
||||
continue;
|
||||
}
|
||||
applyColor(tag.start, tag.start + tag.length, tag.closed
|
||||
? QColor(0, 128, 0)
|
||||
: QColor(128, 0, 0));
|
||||
applyColor(
|
||||
tag.internalStart,
|
||||
tag.internalStart + tag.internalLength,
|
||||
(tag.closed
|
||||
? QColor(0, 128, 0)
|
||||
: QColor(128, 0, 0)));
|
||||
}
|
||||
auto cursor = textCursor();
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
@ -2352,36 +2383,38 @@ TextWithTags InputField::getTextWithAppliedMarkdown() const {
|
||||
const auto linksEnd = links.end();
|
||||
for (const auto &tag : _lastMarkdownTags) {
|
||||
const auto tagLength = int(tag.tag.size());
|
||||
if (!tag.closed || tag.start < from) {
|
||||
if (!tag.closed || tag.adjustedStart < from) {
|
||||
continue;
|
||||
}
|
||||
const auto entityLength = tag.length - 2 * tagLength;
|
||||
const auto entityLength = tag.adjustedLength - 2 * tagLength;
|
||||
if (entityLength <= 0) {
|
||||
continue;
|
||||
}
|
||||
addOriginalTagsUpTill(tag.start);
|
||||
addOriginalTagsUpTill(tag.adjustedStart);
|
||||
const auto tagAdjustedEnd = tag.adjustedStart + tag.adjustedLength;
|
||||
if (originalTag != originalTagsEnd
|
||||
&& originalTag->offset < tag.start + tag.length) {
|
||||
&& originalTag->offset < tagAdjustedEnd) {
|
||||
continue;
|
||||
}
|
||||
while (link != linksEnd
|
||||
&& link->offset() + link->length() <= tag.start) {
|
||||
&& link->offset() + link->length() <= tag.adjustedStart) {
|
||||
++link;
|
||||
}
|
||||
if (link != linksEnd
|
||||
&& link->offset() < tag.start + tag.length
|
||||
&& (link->offset() + link->length() > tag.start + tag.length
|
||||
|| link->offset() < tag.start)) {
|
||||
&& link->offset() < tagAdjustedEnd
|
||||
&& (link->offset() + link->length() > tagAdjustedEnd
|
||||
|| link->offset() < tag.adjustedStart)) {
|
||||
continue;
|
||||
}
|
||||
addOriginalTextUpTill(tag.start);
|
||||
addOriginalTextUpTill(tag.adjustedStart);
|
||||
result.tags.push_back(TextWithTags::Tag{
|
||||
int(result.text.size()),
|
||||
entityLength,
|
||||
tag.tag });
|
||||
result.text.append(
|
||||
originalText.midRef(tag.start + tagLength, entityLength));
|
||||
from = tag.start + tag.length;
|
||||
result.text.append(originalText.midRef(
|
||||
tag.adjustedStart + tagLength,
|
||||
entityLength));
|
||||
from = tag.adjustedStart + tag.adjustedLength;
|
||||
removed += 2 * tagLength;
|
||||
}
|
||||
addOriginalTagsUpTill(originalText.size());
|
||||
@ -2749,8 +2782,8 @@ void InputField::processInstantReplaces(const QString &appended) {
|
||||
}
|
||||
const auto position = textCursor().position();
|
||||
for (const auto &tag : _lastMarkdownTags) {
|
||||
if (tag.start < position
|
||||
&& tag.start + tag.length >= position
|
||||
if (tag.internalStart < position
|
||||
&& tag.internalStart + tag.internalLength >= position
|
||||
&& (tag.tag == kTagCode || tag.tag == kTagPre)) {
|
||||
return;
|
||||
}
|
||||
|
@ -125,8 +125,14 @@ public:
|
||||
using TagList = TextWithTags::Tags;
|
||||
|
||||
struct MarkdownTag {
|
||||
int start = 0;
|
||||
int length = 0;
|
||||
// With each emoji being QChar::ObjectReplacementCharacter.
|
||||
int internalStart = 0;
|
||||
int internalLength = 0;
|
||||
|
||||
// Adjusted by emoji to match _lastTextWithTags.
|
||||
int adjustedStart = 0;
|
||||
int adjustedLength = 0;
|
||||
|
||||
bool closed = false;
|
||||
QString tag;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user