2
0
mirror of https://github.com/telegramdesktop/tdesktop synced 2025-09-02 07:25:46 +00:00

version 0.5.8 prepared, hashtag search support, clearhistory crash fix

This commit is contained in:
John Preston
2014-07-16 10:58:36 +04:00
parent 54095eb515
commit c93e4e27fe
18 changed files with 182 additions and 83 deletions

View File

@@ -19,6 +19,7 @@ Copyright (c) 2014 John Preston, https://tdesktop.com
#include "text.h"
#include "lang.h"
#include "app.h"
#include <private/qharfbuzz_p.h>
@@ -111,8 +112,9 @@ namespace {
}
const QRegularExpression reDomain(QString::fromUtf8("(?<![A-Za-z\\$0-9А-Яа-яёЁ\\-\\_%=])(?:([a-zA-Z]+)://)?((?:[A-Za-zА-яА-ЯёЁ0-9\\-\\_]+\\.){1,5}([A-Za-zрф\\-\\d]{2,22}))"));
const QRegularExpression reMailName(QString::fromUtf8("[a-zA-Z\\-_\\.0-9]{1,256}$"));
const QRegularExpression reMailStart(QString::fromUtf8("^[a-zA-Z\\-_\\.0-9]{1,256}\\@"));
const QRegularExpression reMailName(qsl("[a-zA-Z\\-_\\.0-9]{1,256}$"));
const QRegularExpression reMailStart(qsl("^[a-zA-Z\\-_\\.0-9]{1,256}\\@"));
const QRegularExpression reHashtag(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])#[A-Za-z_\\.0-9]{4,20}([\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10]|$)"));
QSet<int32> validProtocols, validTopDomains;
void initLinkSets();
@@ -298,7 +300,7 @@ public:
return Qt::LayoutDirectionAuto;
}
void prepareLinks() { // support emails!
void prepareLinks() { // support emails and hashtags!
if (validProtocols.empty()) {
initLinkSets();
}
@@ -313,73 +315,98 @@ public:
}
}
QRegularExpressionMatch mDomain = reDomain.match(src, offset);
if (!mDomain.hasMatch()) break;
int32 domainOffset = mDomain.capturedStart(), domainEnd = mDomain.capturedEnd();
if (domainOffset > nextCmd) {
const QChar *after = skipCommand(srcData + nextCmd, srcData + len);
if (after > srcData + nextCmd && domainOffset < (after - srcData)) {
nextCmd = offset = after - srcData;
continue;
}
}
QString protocol = mDomain.captured(1).toLower();
QString topDomain = mDomain.captured(3).toLower();
bool isProtocolValid = protocol.isEmpty() || validProtocols.contains(hashCrc32(protocol.constData(), protocol.size() * sizeof(QChar)));
bool isTopDomainValid = validTopDomains.contains(hashCrc32(topDomain.constData(), topDomain.size() * sizeof(QChar)));
if (!isProtocolValid || !isTopDomainValid) {
offset = domainEnd;
continue;
}
QRegularExpressionMatch mHashtag = reHashtag.match(src, offset);
if (!mDomain.hasMatch() && !mHashtag.hasMatch()) break;
LinkRange link;
if (protocol.isEmpty() && domainOffset > offset + 1 && *(start + domainOffset - 1) == QChar('@')) {
QString forMailName = src.mid(offset, domainOffset - offset - 1);
QRegularExpressionMatch mMailName = reMailName.match(forMailName);
if (mMailName.hasMatch()) {
int32 mailOffset = offset + mMailName.capturedStart();
if (mailOffset < offset) {
mailOffset = offset;
}
link.from = start + mailOffset;
link.len = domainEnd - mailOffset;
int32 domainOffset = mDomain.hasMatch() ? mDomain.capturedStart() : INT_MAX,
domainEnd = mDomain.hasMatch() ? mDomain.capturedEnd() : INT_MAX,
hashtagOffset = mHashtag.hasMatch() ? mHashtag.capturedStart() : INT_MAX,
hashtagEnd = mHashtag.hasMatch() ? mHashtag.capturedEnd() : INT_MAX;
if (mHashtag.hasMatch()) {
if (!mHashtag.capturedRef(1).isEmpty()) {
++hashtagOffset;
}
if (!mHashtag.capturedRef(2).isEmpty()) {
--hashtagEnd;
}
}
if (!link.from || !link.len) {
link.from = start + domainOffset;
QStack<const QChar*> parenth;
const QChar *p = start + mDomain.capturedEnd();
for (; p < end; ++p) {
QChar ch(*p);
if (chIsLinkEnd(ch)) break; // link finished
if (chIsAlmostLinkEnd(ch)) {
const QChar *endTest = p + 1;
while (endTest < end && chIsAlmostLinkEnd(*endTest)) {
++endTest;
}
if (endTest >= end || chIsLinkEnd(*endTest)) {
break; // link finished at p
}
p = endTest;
ch = *p;
}
if (ch == '(' || ch == '[' || ch == '{' || ch == '<') {
parenth.push(p);
} else if (ch == ')' || ch == ']' || ch == '}' || ch == '>') {
if (parenth.isEmpty()) break;
const QChar *q = parenth.pop(), open(*q);
if ((ch == ')' && open != '(') || (ch == ']' && open != '[') || (ch == '}' && open != '{') || (ch == '>' && open != '<')) {
p = q;
break;
}
if (hashtagOffset < domainOffset) {
if (hashtagOffset > nextCmd) {
const QChar *after = skipCommand(srcData + nextCmd, srcData + len);
if (after > srcData + nextCmd && hashtagOffset < (after - srcData)) {
nextCmd = offset = after - srcData;
continue;
}
}
link.len = p - link.from;
link.from = start + hashtagOffset;
link.len = start + hashtagEnd - link.from;
} else {
if (domainOffset > nextCmd) {
const QChar *after = skipCommand(srcData + nextCmd, srcData + len);
if (after > srcData + nextCmd && domainOffset < (after - srcData)) {
nextCmd = offset = after - srcData;
continue;
}
}
QString protocol = mDomain.captured(1).toLower();
QString topDomain = mDomain.captured(3).toLower();
bool isProtocolValid = protocol.isEmpty() || validProtocols.contains(hashCrc32(protocol.constData(), protocol.size() * sizeof(QChar)));
bool isTopDomainValid = validTopDomains.contains(hashCrc32(topDomain.constData(), topDomain.size() * sizeof(QChar)));
if (!isProtocolValid || !isTopDomainValid) {
offset = domainEnd;
continue;
}
if (protocol.isEmpty() && domainOffset > offset + 1 && *(start + domainOffset - 1) == QChar('@')) {
QString forMailName = src.mid(offset, domainOffset - offset - 1);
QRegularExpressionMatch mMailName = reMailName.match(forMailName);
if (mMailName.hasMatch()) {
int32 mailOffset = offset + mMailName.capturedStart();
if (mailOffset < offset) {
mailOffset = offset;
}
link.from = start + mailOffset;
link.len = domainEnd - mailOffset;
}
}
if (!link.from || !link.len) {
link.from = start + domainOffset;
QStack<const QChar*> parenth;
const QChar *p = start + mDomain.capturedEnd();
for (; p < end; ++p) {
QChar ch(*p);
if (chIsLinkEnd(ch)) break; // link finished
if (chIsAlmostLinkEnd(ch)) {
const QChar *endTest = p + 1;
while (endTest < end && chIsAlmostLinkEnd(*endTest)) {
++endTest;
}
if (endTest >= end || chIsLinkEnd(*endTest)) {
break; // link finished at p
}
p = endTest;
ch = *p;
}
if (ch == '(' || ch == '[' || ch == '{' || ch == '<') {
parenth.push(p);
} else if (ch == ')' || ch == ']' || ch == '}' || ch == '>') {
if (parenth.isEmpty()) break;
const QChar *q = parenth.pop(), open(*q);
if ((ch == ')' && open != '(') || (ch == ']' && open != '[') || (ch == '}' && open != '{') || (ch == '>' && open != '<')) {
p = q;
break;
}
}
}
link.len = p - link.from;
}
}
lnkRanges.push_back(link);
@@ -421,9 +448,12 @@ public:
}
void getLinkData(const QString &original, QString &result, int32 &fullDisplayed) {
if (reMailStart.match(original).hasMatch()) {
if (!original.isEmpty() && original.at(0) == '#') {
result = original;
fullDisplayed = -1;
fullDisplayed = -2; // hashtag
} else if (reMailStart.match(original).hasMatch()) {
result = original;
fullDisplayed = -1; // email
} else {
QUrl url(original), good(url.isValid() ? url.toEncoded() : "");
QString readable = good.isValid() ? good.toDisplayString() : original;
@@ -725,7 +755,9 @@ public:
_t->_links.resize(lnkIndex);
const TextLinkData &data(links[lnkIndex - maxLnkIndex - 1]);
TextLinkPtr lnk;
if (data.fullDisplayed < 0) { // email
if (data.fullDisplayed < -1) { // hashtag
lnk = TextLinkPtr(new HashtagLink(data.url));
} else if (data.fullDisplayed < 0) { // email
lnk = TextLinkPtr(new EmailLink(data.url));
} else {
lnk = TextLinkPtr(new TextLink(data.url, data.fullDisplayed > 0));
@@ -755,7 +787,7 @@ private:
TextLinkData(const QString &url = QString(), int32 fullDisplayed = 1) : url(url), fullDisplayed(fullDisplayed) {
}
QString url;
int32 fullDisplayed; // < 0 - email
int32 fullDisplayed; // -2 - hashtag, -1 - email
};
typedef QVector<TextLinkData> TextLinks;
TextLinks links;
@@ -874,6 +906,12 @@ namespace {
}
void HashtagLink::onClick(Qt::MouseButton button) const {
if (button == Qt::LeftButton || button == Qt::MiddleButton) {
App::searchByHashtag(_tag);
}
}
class TextPainter {
public: