diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 4801cce00..099d7c6ba 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -87,7 +87,7 @@ void ApiWrap::addLocalChangelogs(int oldAppVersion) { auto addedSome = false; auto addLocalChangelog = [this, &addedSome](const QString &text) { auto textWithEntities = TextWithEntities { text }; - textParseEntities(textWithEntities.text, TextParseLinks, &textWithEntities.entities); + TextUtilities::ParseEntities(textWithEntities, TextParseLinks); App::wnd()->serviceNotification(textWithEntities, MTP_messageMediaEmpty(), unixtime()); addedSome = true; }; @@ -1177,7 +1177,7 @@ void ApiWrap::saveDraftsToCloud() { if (!textWithTags.tags.isEmpty()) { flags |= MTPmessages_SaveDraft::Flag::f_entities; } - auto entities = linksToMTP(ConvertTextTagsToEntities(textWithTags.tags), true); + auto entities = TextUtilities::EntitiesToMTP(ConvertTextTagsToEntities(textWithTags.tags), TextUtilities::ConvertOption::SkipLocal); cloudDraft->saveRequestId = request(MTPmessages_SaveDraft(MTP_flags(flags), MTP_int(cloudDraft->msgId), history->peer->input, MTP_string(textWithTags.text), entities)).done([this, history](const MTPBool &result, mtpRequestId requestId) { if (auto cloudDraft = history->cloudDraft()) { diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index 4975bb951..e26e65bcb 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -459,11 +459,11 @@ namespace { // apply first_name and last_name from minimal user only if we don't have // local values for first name and last name already, otherwise skip bool noLocalName = data->firstName.isEmpty() && data->lastName.isEmpty(); - QString fname = (!minimal || noLocalName) ? (d.has_first_name() ? textOneLine(qs(d.vfirst_name)) : QString()) : data->firstName; - QString lname = (!minimal || noLocalName) ? (d.has_last_name() ? textOneLine(qs(d.vlast_name)) : QString()) : data->lastName; + QString fname = (!minimal || noLocalName) ? (d.has_first_name() ? TextUtilities::SingleLine(qs(d.vfirst_name)) : QString()) : data->firstName; + QString lname = (!minimal || noLocalName) ? (d.has_last_name() ? TextUtilities::SingleLine(qs(d.vlast_name)) : QString()) : data->lastName; QString phone = minimal ? data->phone() : (d.has_phone() ? qs(d.vphone) : QString()); - QString uname = minimal ? data->username : (d.has_username() ? textOneLine(qs(d.vusername)) : QString()); + QString uname = minimal ? data->username : (d.has_username() ? TextUtilities::SingleLine(qs(d.vusername)) : QString()); bool phoneChanged = (data->phone() != phone); if (phoneChanged) { @@ -714,7 +714,7 @@ namespace { } cdata->flagsUpdated(); - QString uname = d.has_username() ? textOneLine(qs(d.vusername)) : QString(); + QString uname = d.has_username() ? TextUtilities::SingleLine(qs(d.vusername)) : QString(); cdata->setName(qs(d.vtitle), uname); cdata->setIsForbidden(false); @@ -1096,7 +1096,7 @@ namespace { } if (auto existing = App::histItemById(peerToChannel(peerId), m.vid.v)) { auto text = qs(m.vmessage); - auto entities = m.has_entities() ? entitiesFromMTP(m.ventities.v) : EntitiesInText(); + auto entities = m.has_entities() ? TextUtilities::EntitiesFromMTP(m.ventities.v) : EntitiesInText(); existing->setText({ text, entities }); existing->updateMedia(m.has_media() ? (&m.vmedia) : nullptr); existing->updateReplyMarkup(m.has_reply_markup() ? (&m.vreply_markup) : nullptr); @@ -1334,7 +1334,7 @@ namespace { bool showPhone = !isServiceUser(user->id) && !user->isSelf() && !user->contact; bool showPhoneChanged = !isServiceUser(user->id) && !user->isSelf() && ((showPhone && !wasShowPhone) || (!showPhone && wasShowPhone)); if (showPhoneChanged) { - user->setName(textOneLine(user->firstName), textOneLine(user->lastName), showPhone ? App::formatPhone(user->phone()) : QString(), textOneLine(user->username)); + user->setName(TextUtilities::SingleLine(user->firstName), TextUtilities::SingleLine(user->lastName), showPhone ? App::formatPhone(user->phone()) : QString(), TextUtilities::SingleLine(user->username)); } markPeerUpdated(user); } @@ -1497,13 +1497,13 @@ namespace { } WebPageData *feedWebPage(const MTPDwebPage &webpage, WebPageData *convert) { - auto description = TextWithEntities { webpage.has_description() ? textClean(qs(webpage.vdescription)) : QString() }; + auto description = TextWithEntities { webpage.has_description() ? TextUtilities::Clean(qs(webpage.vdescription)) : QString() }; auto siteName = webpage.has_site_name() ? qs(webpage.vsite_name) : QString(); auto parseFlags = TextParseLinks | TextParseMultiline | TextParseRichText; if (siteName == qstr("Twitter") || siteName == qstr("Instagram")) { parseFlags |= TextParseHashtags | TextParseMentions; } - textParseEntities(description.text, parseFlags, &description.entities); + TextUtilities::ParseEntities(description, parseFlags); return App::webPageSet(webpage.vid.v, convert, webpage.has_type() ? qs(webpage.vtype) : qsl("article"), qs(webpage.vurl), qs(webpage.vdisplay_url), siteName, webpage.has_title() ? qs(webpage.vtitle) : QString(), description, webpage.has_photo() ? App::feedPhoto(webpage.vphoto) : nullptr, webpage.has_document() ? App::feedDocument(webpage.vdocument) : nullptr, webpage.has_duration() ? webpage.vduration.v : 0, webpage.has_author() ? qs(webpage.vauthor) : QString(), 0); } @@ -1800,15 +1800,15 @@ namespace { } if ((convert->url.isEmpty() && !url.isEmpty()) || (convert->pendingTill && convert->pendingTill != pendingTill && pendingTill >= -1)) { convert->type = toWebPageType(type); - convert->url = textClean(url); - convert->displayUrl = textClean(displayUrl); - convert->siteName = textClean(siteName); - convert->title = textOneLine(textClean(title)); + convert->url = TextUtilities::Clean(url); + convert->displayUrl = TextUtilities::Clean(displayUrl); + convert->siteName = TextUtilities::Clean(siteName); + convert->title = TextUtilities::SingleLine(title); convert->description = description; convert->photo = photo; convert->document = document; convert->duration = duration; - convert->author = textClean(author); + convert->author = TextUtilities::Clean(author); if (convert->pendingTill > 0 && pendingTill <= 0 && api()) api()->clearWebPageRequest(convert); convert->pendingTill = pendingTill; if (App::main()) App::main()->webPageUpdated(convert); @@ -1831,15 +1831,15 @@ namespace { if (result != convert) { if ((result->url.isEmpty() && !url.isEmpty()) || (result->pendingTill && result->pendingTill != pendingTill && pendingTill >= -1)) { result->type = toWebPageType(type); - result->url = textClean(url); - result->displayUrl = textClean(displayUrl); - result->siteName = textClean(siteName); - result->title = textOneLine(textClean(title)); + result->url = TextUtilities::Clean(url); + result->displayUrl = TextUtilities::Clean(displayUrl); + result->siteName = TextUtilities::Clean(siteName); + result->title = TextUtilities::SingleLine(title); result->description = description; result->photo = photo; result->document = document; result->duration = duration; - result->author = textClean(author); + result->author = TextUtilities::Clean(author); if (result->pendingTill > 0 && pendingTill <= 0 && api()) api()->clearWebPageRequest(result); result->pendingTill = pendingTill; if (App::main()) App::main()->webPageUpdated(result); @@ -1869,9 +1869,9 @@ namespace { } if (!convert->accessHash && accessHash) { convert->accessHash = accessHash; - convert->shortName = textClean(shortName); - convert->title = textOneLine(textClean(title)); - convert->description = textClean(description); + convert->shortName = TextUtilities::Clean(shortName); + convert->title = TextUtilities::SingleLine(title); + convert->description = TextUtilities::Clean(description); convert->photo = photo; convert->document = document; if (App::main()) App::main()->gameUpdated(convert); @@ -1891,9 +1891,9 @@ namespace { if (result != convert) { if (!result->accessHash && accessHash) { result->accessHash = accessHash; - result->shortName = textClean(shortName); - result->title = textOneLine(textClean(title)); - result->description = textClean(description); + result->shortName = TextUtilities::Clean(shortName); + result->title = TextUtilities::SingleLine(title); + result->description = TextUtilities::Clean(description); result->photo = photo; result->document = document; if (App::main()) App::main()->gameUpdated(result); diff --git a/Telegram/SourceFiles/boxes/add_contact_box.cpp b/Telegram/SourceFiles/boxes/add_contact_box.cpp index 131288c38..62536dd69 100644 --- a/Telegram/SourceFiles/boxes/add_contact_box.cpp +++ b/Telegram/SourceFiles/boxes/add_contact_box.cpp @@ -173,9 +173,9 @@ void AddContactBox::onSubmit() { void AddContactBox::onSave() { if (_addRequest) return; - QString firstName = prepareText(_first->getLastText()); - QString lastName = prepareText(_last->getLastText()); - QString phone = _phone->getLastText().trimmed(); + auto firstName = TextUtilities::PrepareForSending(_first->getLastText()); + auto lastName = TextUtilities::PrepareForSending(_last->getLastText()); + auto phone = _phone->getLastText().trimmed(); if (firstName.isEmpty() && lastName.isEmpty()) { if (_invertOrder) { _last->setFocus(); @@ -368,8 +368,8 @@ void GroupInfoBox::onNameSubmit() { void GroupInfoBox::onNext() { if (_creationRequestId) return; - auto title = prepareText(_title->getLastText()); - auto description = _description ? prepareText(_description->getLastText(), true) : QString(); + auto title = TextUtilities::PrepareForSending(_title->getLastText()); + auto description = _description ? TextUtilities::PrepareForSending(_description->getLastText(), TextUtilities::PrepareTextOption::CheckLinks) : QString(); if (title.isEmpty()) { _title->setFocus(); _title->showError(); @@ -695,7 +695,7 @@ void SetupChannelBox::privacyChanged(Privacy value) { } void SetupChannelBox::onUpdateDone(const MTPBool &result) { - _channel->setName(textOneLine(_channel->name), _sentUsername); + _channel->setName(TextUtilities::SingleLine(_channel->name), _sentUsername); closeBox(); } @@ -705,7 +705,7 @@ bool SetupChannelBox::onUpdateFail(const RPCError &error) { _saveRequestId = 0; QString err(error.type()); if (err == "USERNAME_NOT_MODIFIED" || _sentUsername == _channel->username) { - _channel->setName(textOneLine(_channel->name), textOneLine(_sentUsername)); + _channel->setName(TextUtilities::SingleLine(_channel->name), TextUtilities::SingleLine(_sentUsername)); closeBox(); return true; } else if (err == "USERNAME_INVALID") { @@ -872,7 +872,8 @@ void EditNameTitleBox::resizeEvent(QResizeEvent *e) { void EditNameTitleBox::onSave() { if (_requestId) return; - QString first = prepareText(_first->getLastText()), last = prepareText(_last->getLastText()); + auto first = TextUtilities::PrepareForSending(_first->getLastText()); + auto last = TextUtilities::PrepareForSending(_last->getLastText()); if (first.isEmpty() && last.isEmpty()) { if (_invertOrder) { _last->setFocus(); @@ -904,10 +905,11 @@ void EditNameTitleBox::onSaveSelfDone(const MTPUser &user) { bool EditNameTitleBox::onSaveSelfFail(const RPCError &error) { if (MTP::isDefaultHandledError(error)) return false; - QString err(error.type()); - QString first = textOneLine(_first->getLastText().trimmed()), last = textOneLine(_last->getLastText().trimmed()); + auto err = error.type(); + auto first = TextUtilities::SingleLine(_first->getLastText().trimmed()); + auto last = TextUtilities::SingleLine(_last->getLastText().trimmed()); if (err == "NAME_NOT_MODIFIED") { - App::self()->setName(first, last, QString(), textOneLine(App::self()->username)); + App::self()->setName(first, last, QString(), TextUtilities::SingleLine(App::self()->username)); closeBox(); return true; } else if (err == "FIRSTNAME_INVALID") { @@ -1073,7 +1075,8 @@ void EditChannelBox::paintEvent(QPaintEvent *e) { void EditChannelBox::onSave() { if (_saveTitleRequestId || _saveDescriptionRequestId || _saveSignRequestId || _saveInvitesRequestId) return; - QString title = prepareText(_title->getLastText()), description = prepareText(_description->getLastText(), true); + auto title = TextUtilities::PrepareForSending(_title->getLastText()); + auto description = TextUtilities::PrepareForSending(_description->getLastText(), TextUtilities::PrepareTextOption::CheckLinks); if (title.isEmpty()) { _title->setFocus(); _title->showError(); diff --git a/Telegram/SourceFiles/boxes/contacts_box.cpp b/Telegram/SourceFiles/boxes/contacts_box.cpp index 6d866a32f..0b428469c 100644 --- a/Telegram/SourceFiles/boxes/contacts_box.cpp +++ b/Telegram/SourceFiles/boxes/contacts_box.cpp @@ -1526,22 +1526,11 @@ void ContactsBox::Inner::updateSelection() { void ContactsBox::Inner::updateFilter(QString filter) { _lastQuery = filter.toLower().trimmed(); - filter = textSearchKey(filter); + + auto words = TextUtilities::PrepareSearchWords(_lastQuery); + filter = words.isEmpty() ? QString() : words.join(' '); _time = unixtime(); - QStringList f; - if (!filter.isEmpty()) { - QStringList filterList = filter.split(cWordSplit(), QString::SkipEmptyParts); - int l = filterList.size(); - - f.reserve(l); - for (int i = 0; i < l; ++i) { - QString filterName = filterList[i].trimmed(); - if (filterName.isEmpty()) continue; - f.push_back(filterName); - } - filter = f.join(' '); - } if (_filter != filter) { _filter = filter; @@ -1560,10 +1549,10 @@ void ContactsBox::Inner::updateFilter(QString filter) { } else { if (!_addContactLnk->isHidden()) _addContactLnk->hide(); if (!_allAdmins->isHidden()) _allAdmins->hide(); - QStringList::const_iterator fb = f.cbegin(), fe = f.cend(), fi; + QStringList::const_iterator fb = words.cbegin(), fe = words.cend(), fi; _filtered.clear(); - if (!f.isEmpty()) { + if (!words.isEmpty()) { const Dialogs::List *toFilter = nullptr; if (!_contacts->isEmpty()) { for (fi = fb; fi != fe; ++fi) { diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp index 4af336592..771d9429e 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp @@ -984,7 +984,7 @@ void PeerListBox::Inner::checkScrollForPreload() { } void PeerListBox::Inner::searchQueryChanged(QString query) { - auto searchWordsList = query.isEmpty() ? QStringList() : query.split(cWordSplit(), QString::SkipEmptyParts); + auto searchWordsList = TextUtilities::PrepareSearchWords(query); auto normalizedQuery = searchWordsList.isEmpty() ? QString() : searchWordsList.join(' '); if (_normalizedSearchQuery != normalizedQuery) { setSearchQuery(query, normalizedQuery); diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index e8321c12f..12aad319d 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -438,7 +438,7 @@ void SendFilesBox::onSend(bool ctrlShiftEnter) { _confirmed = true; if (_confirmedCallback) { auto compressed = _compressed ? _compressed->checked() : false; - auto caption = _caption ? prepareText(_caption->getLastText(), true) : QString(); + auto caption = _caption ? TextUtilities::PrepareForSending(_caption->getLastText(), TextUtilities::PrepareTextOption::CheckLinks) : QString(); _confirmedCallback(_files, _animated ? QImage() : _image, std::move(_information), compressed, caption, ctrlShiftEnter); } closeBox(); @@ -766,7 +766,7 @@ void EditCaptionBox::onSave(bool ctrlShiftEnter) { if (!sentEntities.v.isEmpty()) { flags |= MTPmessages_EditMessage::Flag::f_entities; } - auto text = prepareText(_field->getLastText(), true); + auto text = TextUtilities::PrepareForSending(_field->getLastText(), TextUtilities::PrepareTextOption::CheckLinks); _saveRequestId = MTP::send(MTPmessages_EditMessage(MTP_flags(flags), item->history()->peer->input, MTP_int(item->id), MTP_string(text), MTPnullMarkup, sentEntities), rpcDone(&EditCaptionBox::saveDone), rpcFail(&EditCaptionBox::saveFail)); } diff --git a/Telegram/SourceFiles/boxes/share_box.cpp b/Telegram/SourceFiles/boxes/share_box.cpp index 6de786b7f..b6de616ff 100644 --- a/Telegram/SourceFiles/boxes/share_box.cpp +++ b/Telegram/SourceFiles/boxes/share_box.cpp @@ -674,21 +674,9 @@ bool ShareBox::Inner::hasSelected() const { void ShareBox::Inner::updateFilter(QString filter) { _lastQuery = filter.toLower().trimmed(); - filter = textSearchKey(filter); - QStringList f; - if (!filter.isEmpty()) { - QStringList filterList = filter.split(cWordSplit(), QString::SkipEmptyParts); - int l = filterList.size(); - - f.reserve(l); - for (int i = 0; i < l; ++i) { - QString filterName = filterList[i].trimmed(); - if (filterName.isEmpty()) continue; - f.push_back(filterName); - } - filter = f.join(' '); - } + auto words = TextUtilities::PrepareSearchWords(_lastQuery); + filter = words.isEmpty() ? QString() : words.join(' '); if (_filter != filter) { _filter = filter; @@ -701,10 +689,10 @@ void ShareBox::Inner::updateFilter(QString filter) { if (_filter.isEmpty()) { refresh(); } else { - QStringList::const_iterator fb = f.cbegin(), fe = f.cend(), fi; + QStringList::const_iterator fb = words.cbegin(), fe = words.cend(), fi; _filtered.clear(); - if (!f.isEmpty()) { + if (!words.isEmpty()) { const Dialogs::List *toFilter = nullptr; if (!_chatsIndexed->isEmpty()) { for (fi = fb; fi != fe; ++fi) { diff --git a/Telegram/SourceFiles/boxes/sticker_set_box.cpp b/Telegram/SourceFiles/boxes/sticker_set_box.cpp index c16ef37e9..fd0972f32 100644 --- a/Telegram/SourceFiles/boxes/sticker_set_box.cpp +++ b/Telegram/SourceFiles/boxes/sticker_set_box.cpp @@ -428,18 +428,17 @@ bool StickerSetBox::Inner::official() const { } base::lambda StickerSetBox::Inner::title() const { - auto text = _setTitle; - auto entities = EntitiesInText(); + auto text = TextWithEntities { _setTitle }; if (_loaded) { if (_pack.isEmpty()) { return [] { return TextWithEntities { lang(lng_attach_failed), EntitiesInText() }; }; } else { - textParseEntities(text, TextParseMentions, &entities); + TextUtilities::ParseEntities(text, TextParseMentions); } } else { return [] { return TextWithEntities { lang(lng_contacts_loading), EntitiesInText() }; }; } - return [text, entities] { return TextWithEntities { text, entities }; }; + return [text] { return text; }; } QString StickerSetBox::Inner::shortName() const { diff --git a/Telegram/SourceFiles/boxes/username_box.cpp b/Telegram/SourceFiles/boxes/username_box.cpp index cd3890d02..013ef5b6b 100644 --- a/Telegram/SourceFiles/boxes/username_box.cpp +++ b/Telegram/SourceFiles/boxes/username_box.cpp @@ -175,7 +175,7 @@ bool UsernameBox::onUpdateFail(const RPCError &error) { _saveRequestId = 0; QString err(error.type()); if (err == qstr("USERNAME_NOT_MODIFIED") || _sentUsername == App::self()->username) { - App::self()->setName(textOneLine(App::self()->firstName), textOneLine(App::self()->lastName), textOneLine(App::self()->nameOrPhone), textOneLine(_sentUsername)); + App::self()->setName(TextUtilities::SingleLine(App::self()->firstName), TextUtilities::SingleLine(App::self()->lastName), TextUtilities::SingleLine(App::self()->nameOrPhone), TextUtilities::SingleLine(_sentUsername)); closeBox(); return true; } else if (err == qstr("USERNAME_INVALID")) { diff --git a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp index bd9d6c15a..970f8e145 100644 --- a/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp +++ b/Telegram/SourceFiles/chat_helpers/field_autocomplete.cpp @@ -99,7 +99,7 @@ void FieldAutocomplete::showFiltered(PeerData *peer, QString query, bool addInli bool resetScroll = (_type != type || _filter != plainQuery); if (resetScroll) { _type = type; - _filter = textAccentFold(plainQuery.toString()); + _filter = TextUtilities::RemoveAccents(plainQuery.toString()); } _addInlineBots = addInlineBots; diff --git a/Telegram/SourceFiles/config.h b/Telegram/SourceFiles/config.h index b6b68bd4e..6a075905c 100644 --- a/Telegram/SourceFiles/config.h +++ b/Telegram/SourceFiles/config.h @@ -336,11 +336,6 @@ enum { ForwardOnAdd = 100, // how many messages from chat history server should forward to user, that was added to this chat }; -inline const QRegularExpression &cWordSplit() { - static QRegularExpression regexp(qsl("[\\@\\s\\-\\+\\(\\)\\[\\]\\{\\}\\<\\>\\,\\.\\:\\!\\_\\;\\\"\\'\\x0]")); - return regexp; -} - inline const QRegularExpression &cRussianLetters() { static QRegularExpression regexp(QString::fromUtf8("[а-яА-ЯёЁ]")); return regexp; diff --git a/Telegram/SourceFiles/data/data_drafts.cpp b/Telegram/SourceFiles/data/data_drafts.cpp index bf540747a..8c4b24408 100644 --- a/Telegram/SourceFiles/data/data_drafts.cpp +++ b/Telegram/SourceFiles/data/data_drafts.cpp @@ -40,10 +40,9 @@ Draft::Draft(const Ui::FlatTextarea *field, MsgId msgId, bool previewCancelled, void applyPeerCloudDraft(PeerId peerId, const MTPDdraftMessage &draft) { auto history = App::history(peerId); - auto text = qs(draft.vmessage); - auto entities = draft.has_entities() ? entitiesFromMTP(draft.ventities.v) : EntitiesInText(); - TextWithTags textWithTags = { textApplyEntities(text, entities), ConvertEntitiesToTextTags(entities) }; - MsgId replyTo = draft.has_reply_to_msg_id() ? draft.vreply_to_msg_id.v : 0; + auto text = TextWithEntities { qs(draft.vmessage), draft.has_entities() ? TextUtilities::EntitiesFromMTP(draft.ventities.v) : EntitiesInText() }; + auto textWithTags = TextWithTags { TextUtilities::ApplyEntities(text), ConvertEntitiesToTextTags(text.entities) }; + auto replyTo = draft.has_reply_to_msg_id() ? draft.vreply_to_msg_id.v : MsgId(0); auto cloudDraft = std::make_unique(textWithTags, replyTo, MessageCursor(QFIXED_MAX, QFIXED_MAX, QFIXED_MAX), draft.is_no_webpage()); cloudDraft->date = ::date(draft.vdate); diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp index 0c7a2a3f6..998d10bad 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp @@ -99,7 +99,7 @@ void paintRow(Painter &p, const RippleRow *row, History *history, HistoryItem *i if (!history->paintSendAction(p, nameleft, texttop, availableWidth, fullWidth, color, ms)) { if (history->cloudDraftTextCache.isEmpty()) { auto draftWrapped = textcmdLink(1, lng_dialogs_text_from_wrapped(lt_from, lang(lng_from_draft))); - auto draftText = lng_dialogs_text_with_from(lt_from_part, draftWrapped, lt_message, textClean(draft->textWithTags.text)); + auto draftText = lng_dialogs_text_with_from(lt_from_part, draftWrapped, lt_message, TextUtilities::Clean(draft->textWithTags.text)); history->cloudDraftTextCache.setText(st::dialogsTextStyle, draftText, _textDlgOptions); } p.setPen(active ? st::dialogsTextFgActive : (selected ? st::dialogsTextFgOver : st::dialogsTextFg)); diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 4f7c5b97a..4e98574f6 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -1263,104 +1263,90 @@ void DialogsInner::onPeerPhotoChanged(PeerData *peer) { } void DialogsInner::onFilterUpdate(QString newFilter, bool force) { - newFilter = textSearchKey(newFilter); + auto words = TextUtilities::PrepareSearchWords(newFilter); + newFilter = words.isEmpty() ? QString() : words.join(' '); if (newFilter != _filter || force) { - QStringList f; - if (!newFilter.isEmpty()) { - QStringList filterList = newFilter.split(cWordSplit(), QString::SkipEmptyParts); - int l = filterList.size(); + _filter = newFilter; + if (!_searchInPeer && _filter.isEmpty()) { + _state = DefaultState; + _hashtagResults.clear(); + _filterResults.clear(); + _peerSearchResults.clear(); + _searchResults.clear(); + _lastSearchDate = 0; + _lastSearchPeer = 0; + _lastSearchId = _lastSearchMigratedId = 0; + } else { + QStringList::const_iterator fb = words.cbegin(), fe = words.cend(), fi; - f.reserve(l); - for (int i = 0; i < l; ++i) { - QString filterName = filterList[i].trimmed(); - if (filterName.isEmpty()) continue; - f.push_back(filterName); - } - newFilter = f.join(' '); - } - if (newFilter != _filter || force) { - _filter = newFilter; - if (!_searchInPeer && _filter.isEmpty()) { - _state = DefaultState; - _hashtagResults.clear(); - _filterResults.clear(); - _peerSearchResults.clear(); - _searchResults.clear(); - _lastSearchDate = 0; - _lastSearchPeer = 0; - _lastSearchId = _lastSearchMigratedId = 0; - } else { - QStringList::const_iterator fb = f.cbegin(), fe = f.cend(), fi; - - _state = FilteredState; - _filterResults.clear(); - if (!_searchInPeer && !f.isEmpty()) { - const Dialogs::List *toFilter = nullptr; - if (!_dialogs->isEmpty()) { - for (fi = fb; fi != fe; ++fi) { - auto found = _dialogs->filtered(fi->at(0)); - if (found->isEmpty()) { - toFilter = nullptr; - break; - } - if (!toFilter || toFilter->size() > found->size()) { - toFilter = found; - } + _state = FilteredState; + _filterResults.clear(); + if (!_searchInPeer && !words.isEmpty()) { + const Dialogs::List *toFilter = nullptr; + if (!_dialogs->isEmpty()) { + for (fi = fb; fi != fe; ++fi) { + auto found = _dialogs->filtered(fi->at(0)); + if (found->isEmpty()) { + toFilter = nullptr; + break; + } + if (!toFilter || toFilter->size() > found->size()) { + toFilter = found; } } - const Dialogs::List *toFilterContacts = nullptr; - if (!_contactsNoDialogs->isEmpty()) { - for (fi = fb; fi != fe; ++fi) { - auto found = _contactsNoDialogs->filtered(fi->at(0)); - if (found->isEmpty()) { - toFilterContacts = nullptr; - break; - } - if (!toFilterContacts || toFilterContacts->size() > found->size()) { - toFilterContacts = found; - } + } + const Dialogs::List *toFilterContacts = nullptr; + if (!_contactsNoDialogs->isEmpty()) { + for (fi = fb; fi != fe; ++fi) { + auto found = _contactsNoDialogs->filtered(fi->at(0)); + if (found->isEmpty()) { + toFilterContacts = nullptr; + break; + } + if (!toFilterContacts || toFilterContacts->size() > found->size()) { + toFilterContacts = found; } } - _filterResults.reserve((toFilter ? toFilter->size() : 0) + (toFilterContacts ? toFilterContacts->size() : 0)); - if (toFilter) { - for_const (auto row, *toFilter) { - const PeerData::Names &names(row->history()->peer->names); - PeerData::Names::const_iterator nb = names.cbegin(), ne = names.cend(), ni; - for (fi = fb; fi != fe; ++fi) { - QString filterName(*fi); - for (ni = nb; ni != ne; ++ni) { - if (ni->startsWith(*fi)) { - break; - } - } - if (ni == ne) { + } + _filterResults.reserve((toFilter ? toFilter->size() : 0) + (toFilterContacts ? toFilterContacts->size() : 0)); + if (toFilter) { + for_const (auto row, *toFilter) { + const PeerData::Names &names(row->history()->peer->names); + PeerData::Names::const_iterator nb = names.cbegin(), ne = names.cend(), ni; + for (fi = fb; fi != fe; ++fi) { + QString filterName(*fi); + for (ni = nb; ni != ne; ++ni) { + if (ni->startsWith(*fi)) { break; } } - if (fi == fe) { - _filterResults.push_back(row); + if (ni == ne) { + break; } } + if (fi == fe) { + _filterResults.push_back(row); + } } - if (toFilterContacts) { - for_const (auto row, *toFilterContacts) { - const PeerData::Names &names(row->history()->peer->names); - PeerData::Names::const_iterator nb = names.cbegin(), ne = names.cend(), ni; - for (fi = fb; fi != fe; ++fi) { - QString filterName(*fi); - for (ni = nb; ni != ne; ++ni) { - if (ni->startsWith(*fi)) { - break; - } - } - if (ni == ne) { + } + if (toFilterContacts) { + for_const (auto row, *toFilterContacts) { + const PeerData::Names &names(row->history()->peer->names); + PeerData::Names::const_iterator nb = names.cbegin(), ne = names.cend(), ni; + for (fi = fb; fi != fe; ++fi) { + QString filterName(*fi); + for (ni = nb; ni != ne; ++ni) { + if (ni->startsWith(*fi)) { break; } } - if (fi == fe) { - _filterResults.push_back(row); + if (ni == ne) { + break; } } + if (fi == fe) { + _filterResults.push_back(row); + } } } } @@ -2038,10 +2024,10 @@ bool DialogsInner::choosePeer() { } void DialogsInner::saveRecentHashtags(const QString &text) { - bool found = false; + auto found = false; QRegularExpressionMatch m; - RecentHashtagPack recent(cRecentSearchHashtags()); - for (int32 i = 0, next = 0; (m = reHashtag().match(text, i)).hasMatch(); i = next) { + auto recent = cRecentSearchHashtags(); + for (int32 i = 0, next = 0; (m = TextUtilities::RegExpHashtag().match(text, i)).hasMatch(); i = next) { i = m.capturedStart(); next = m.capturedEnd(); if (m.hasMatch()) { diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index 9f75c8b2b..d4dbabf81 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -56,11 +56,10 @@ constexpr auto kNewBlockEachMessage = 50; auto GlobalPinnedIndex = 0; HistoryItem *createUnsupportedMessage(History *history, MsgId msgId, MTPDmessage::Flags flags, MsgId replyTo, int32 viaBotId, QDateTime date, int32 from) { - QString text(lng_message_unsupported(lt_link, qsl("https://desktop.telegram.org"))); - EntitiesInText entities; - textParseEntities(text, _historyTextNoMonoOptions.flags, &entities); - entities.push_front(EntityInText(EntityInTextItalic, 0, text.size())); - return HistoryMessage::create(history, msgId, flags, replyTo, viaBotId, date, from, { text, entities }); + auto text = TextWithEntities { lng_message_unsupported(lt_link, qsl("https://desktop.telegram.org")) }; + TextUtilities::ParseEntities(text, _historyTextNoMonoOptions.flags); + text.entities.push_front(EntityInText(EntityInTextItalic, 0, text.text.size())); + return HistoryMessage::create(history, msgId, flags, replyTo, viaBotId, date, from, text); } } // namespace diff --git a/Telegram/SourceFiles/history/history_admin_log_inner.cpp b/Telegram/SourceFiles/history/history_admin_log_inner.cpp index 271044523..26ec82a66 100644 --- a/Telegram/SourceFiles/history/history_admin_log_inner.cpp +++ b/Telegram/SourceFiles/history/history_admin_log_inner.cpp @@ -392,7 +392,7 @@ void InnerWidget::updateEmptyText() { auto text = TextWithEntities { lang((hasSearch || hasFilter) ? lng_admin_log_no_results_title : lng_admin_log_no_events_title) }; text.entities.append(EntityInText(EntityInTextBold, 0, text.text.size())); auto description = hasSearch - ? lng_admin_log_no_results_search_text(lt_query, textClean(_searchQuery)) + ? lng_admin_log_no_results_search_text(lt_query, TextUtilities::Clean(_searchQuery)) : lang(hasFilter ? lng_admin_log_no_results_text : lng_admin_log_no_events_text); text.text.append(qstr("\n\n") + description); _emptyText.setMarkedText(st::defaultTextStyle, text, options); diff --git a/Telegram/SourceFiles/history/history_admin_log_item.cpp b/Telegram/SourceFiles/history/history_admin_log_item.cpp index 54dddc693..048a402aa 100644 --- a/Telegram/SourceFiles/history/history_admin_log_item.cpp +++ b/Telegram/SourceFiles/history/history_admin_log_item.cpp @@ -30,14 +30,14 @@ namespace AdminLog { namespace { TextWithEntities PrepareText(const QString &value, const QString &emptyValue) { - auto result = TextWithEntities { textClean(value) }; + auto result = TextWithEntities { TextUtilities::Clean(value) }; if (result.text.isEmpty()) { result.text = emptyValue; if (!emptyValue.isEmpty()) { result.entities.push_back(EntityInText(EntityInTextItalic, 0, emptyValue.size())); } } else { - textParseEntities(result.text, TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands, &result.entities); + TextUtilities::ParseEntities(result, TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands); } return result; } @@ -79,8 +79,8 @@ TextWithEntities ExtractEditedText(const MTPMessage &message) { } else if (mediaType == mtpc_messageMediaPhoto) { return PrepareText(qs(data.vmedia.c_messageMediaPhoto().vcaption), QString()); } - auto text = textClean(qs(data.vmessage)); - auto entities = data.has_entities() ? entitiesFromMTP(data.ventities.v) : EntitiesInText(); + auto text = TextUtilities::Clean(qs(data.vmessage)); + auto entities = data.has_entities() ? TextUtilities::EntitiesFromMTP(data.ventities.v) : EntitiesInText(); return { text, entities }; } diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index b788f99f1..ba3ef666c 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -1522,7 +1522,7 @@ TextWithEntities HistoryInner::getSelectedText() const { int y = itemTop(item); if (y >= 0) { part.text.append(item->author()->name).append(time); - appendTextWithEntities(part, std::move(unwrapped)); + TextUtilities::Append(part, std::move(unwrapped)); texts.insert(y, part); fullSize += size; } @@ -1532,7 +1532,7 @@ TextWithEntities HistoryInner::getSelectedText() const { auto sep = qsl("\n\n"); result.text.reserve(fullSize + (texts.size() - 1) * sep.size()); for (auto i = texts.begin(), e = texts.end(); i != e; ++i) { - appendTextWithEntities(result, std::move(i.value())); + TextUtilities::Append(result, std::move(i.value())); if (i + 1 != e) { result.text.append(sep); } diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 0aa284223..fe5170a45 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -115,7 +115,7 @@ ReplyKeyboard::ReplyKeyboard(const HistoryItem *item, StylePtr &&s) auto str = row.at(j).text; button.type = row.at(j).type; button.link = MakeShared(item, i, j); - button.text.setText(_st->textStyle(), textOneLine(str), _textPlainOptions); + button.text.setText(_st->textStyle(), TextUtilities::SingleLine(str), _textPlainOptions); button.characters = str.isEmpty() ? 1 : str.size(); } _rows.push_back(newRow); @@ -1141,12 +1141,12 @@ QString HistoryItem::inDialogsText() const { if (emptyText()) { return _media ? _media->inDialogsText() : QString(); } - return textClean(_text.originalText()); + return TextUtilities::Clean(_text.originalText()); }; auto plainText = getText(); if ((!_history->peer->isUser() || out()) && !isPost() && !isEmpty()) { auto fromText = author()->isSelf() ? lang(lng_from_you) : author()->shortName(); - auto fromWrapped = textcmdLink(1, lng_dialogs_text_from_wrapped(lt_from, textClean(fromText))); + auto fromWrapped = textcmdLink(1, lng_dialogs_text_from_wrapped(lt_from, TextUtilities::Clean(fromText))); return lng_dialogs_text_with_from(lt_from_part, fromWrapped, lt_message, plainText); } return plainText; diff --git a/Telegram/SourceFiles/history/history_media.h b/Telegram/SourceFiles/history/history_media.h index 2a571234f..122e142c4 100644 --- a/Telegram/SourceFiles/history/history_media.h +++ b/Telegram/SourceFiles/history/history_media.h @@ -42,7 +42,7 @@ public: // Example: "[link1-start]You:[link1-end] [link1-start]Photo,[link1-end] caption text" virtual QString inDialogsText() const { auto result = notificationText(); - return result.isEmpty() ? QString() : textcmdLink(1, textClean(result)); + return result.isEmpty() ? QString() : textcmdLink(1, TextUtilities::Clean(result)); } virtual TextWithEntities selectedText(TextSelection selection) const = 0; diff --git a/Telegram/SourceFiles/history/history_media_types.cpp b/Telegram/SourceFiles/history/history_media_types.cpp index b52401fcd..4e7ae013e 100644 --- a/Telegram/SourceFiles/history/history_media_types.cpp +++ b/Telegram/SourceFiles/history/history_media_types.cpp @@ -130,7 +130,7 @@ TextWithEntities captionedSelectedText(const QString &attachType, const Text &ca result.text.append(qstr("[ ")).append(attachType).append(qstr(" ]")); if (!caption.isEmpty()) { result.text.append(qstr("\n")); - appendTextWithEntities(result, std::move(original)); + TextUtilities::Append(result, std::move(original)); } return result; } @@ -147,11 +147,11 @@ QString captionedNotificationText(const QString &attachType, const Text &caption QString captionedInDialogsText(const QString &attachType, const Text &caption) { if (caption.isEmpty()) { - return textcmdLink(1, textClean(attachType)); + return textcmdLink(1, TextUtilities::Clean(attachType)); } - auto captionText = textClean(caption.originalText()); - auto attachTypeWrapped = textcmdLink(1, lng_dialogs_text_media_wrapped(lt_media, textClean(attachType))); + auto captionText = TextUtilities::Clean(caption.originalText()); + auto attachTypeWrapped = textcmdLink(1, lng_dialogs_text_media_wrapped(lt_media, TextUtilities::Clean(attachType))); return lng_dialogs_text_media(lt_media_part, attachTypeWrapped, lt_caption, captionText); } @@ -3164,7 +3164,7 @@ void HistoryWebPage::initDimensions() { } // init layout - auto title = textOneLine(_data->title.isEmpty() ? _data->author : _data->title); + auto title = TextUtilities::SingleLine(_data->title.isEmpty() ? _data->author : _data->title); if (!_data->description.text.isEmpty() && title.isEmpty() && _data->siteName.isEmpty() && !_data->url.isEmpty()) { _data->siteName = siteNameFromUrl(_data->url); } @@ -3642,7 +3642,7 @@ TextWithEntities HistoryWebPage::selectedText(TextSelection selection) const { } titleResult.text += '\n'; - appendTextWithEntities(titleResult, std::move(descriptionResult)); + TextUtilities::Append(titleResult, std::move(descriptionResult)); return titleResult; } @@ -3700,7 +3700,7 @@ void HistoryGame::initDimensions() { _openl = MakeShared(_parent, 0, 0); } - auto title = textOneLine(_data->title); + auto title = TextUtilities::SingleLine(_data->title); // init attach if (!_attach) { @@ -3728,7 +3728,7 @@ void HistoryGame::initDimensions() { } auto marked = TextWithEntities { text }; auto parseFlags = TextParseLinks | TextParseMultiline | TextParseRichText; - textParseEntities(marked.text, parseFlags, &marked.entities); + TextUtilities::ParseEntities(marked, parseFlags); _description.setMarkedText(st::webPageDescriptionStyle, marked, _webpageDescriptionOptions); } } @@ -4042,7 +4042,7 @@ TextWithEntities HistoryGame::selectedText(TextSelection selection) const { } titleResult.text += '\n'; - appendTextWithEntities(titleResult, std::move(descriptionResult)); + TextUtilities::Append(titleResult, std::move(descriptionResult)); return titleResult; } @@ -4156,10 +4156,10 @@ void HistoryInvoice::fillFromData(const MTPDmessageMediaInvoice &data) { if (!description.isEmpty()) { auto marked = TextWithEntities { description }; auto parseFlags = TextParseLinks | TextParseMultiline | TextParseRichText; - textParseEntities(marked.text, parseFlags, &marked.entities); + TextUtilities::ParseEntities(marked, parseFlags); _description.setMarkedText(st::webPageDescriptionStyle, marked, _webpageDescriptionOptions); } - auto title = textOneLine(qs(data.vtitle)); + auto title = TextUtilities::SingleLine(qs(data.vtitle)); if (!title.isEmpty()) { _title.setText(st::webPageTitleStyle, title, _webpageTitleOptions); } @@ -4435,7 +4435,7 @@ TextWithEntities HistoryInvoice::selectedText(TextSelection selection) const { } titleResult.text += '\n'; - appendTextWithEntities(titleResult, std::move(descriptionResult)); + TextUtilities::Append(titleResult, std::move(descriptionResult)); return titleResult; } @@ -4460,12 +4460,12 @@ HistoryLocation::HistoryLocation(gsl::not_null parent, const Locat , _description(st::msgMinWidth) , _link(MakeShared(coords)) { if (!title.isEmpty()) { - _title.setText(st::webPageTitleStyle, textClean(title), _webpageTitleOptions); + _title.setText(st::webPageTitleStyle, TextUtilities::Clean(title), _webpageTitleOptions); } if (!description.isEmpty()) { - auto marked = TextWithEntities { textClean(description) }; + auto marked = TextWithEntities { TextUtilities::Clean(description) }; auto parseFlags = TextParseLinks | TextParseMultiline | TextParseRichText; - textParseEntities(marked.text, parseFlags, &marked.entities); + TextUtilities::ParseEntities(marked, parseFlags); _description.setMarkedText(st::webPageDescriptionStyle, marked, _webpageDescriptionOptions); } } @@ -4700,7 +4700,7 @@ TextWithEntities HistoryLocation::selectedText(TextSelection selection) const { TextWithEntities result = { qsl("[ ") + lang(lng_maps_point) + qsl(" ]\n"), EntitiesInText() }; auto info = selectedText(AllTextSelection); if (!info.text.isEmpty()) { - appendTextWithEntities(result, std::move(info)); + TextUtilities::Append(result, std::move(info)); result.text.append('\n'); } result.text += _link->dragText(); @@ -4715,7 +4715,7 @@ TextWithEntities HistoryLocation::selectedText(TextSelection selection) const { return titleResult; } titleResult.text += '\n'; - appendTextWithEntities(titleResult, std::move(descriptionResult)); + TextUtilities::Append(titleResult, std::move(descriptionResult)); return titleResult; } diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 89bfa4a86..dd318655f 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -221,7 +221,7 @@ bool HistoryMessageReply::updateData(HistoryMessage *holder, bool force) { } if (replyToMsg) { - replyToText.setText(st::messageTextStyle, textClean(replyToMsg->inReplyText()), _textDlgOptions); + replyToText.setText(st::messageTextStyle, TextUtilities::Clean(replyToMsg->inReplyText()), _textDlgOptions); updateName(); @@ -432,8 +432,8 @@ HistoryMessage::HistoryMessage(gsl::not_null history, const MTPDmessag initMedia(msg.has_media() ? (&msg.vmedia) : nullptr); - auto text = textClean(qs(msg.vmessage)); - auto entities = msg.has_entities() ? entitiesFromMTP(msg.ventities.v) : EntitiesInText(); + auto text = TextUtilities::Clean(qs(msg.vmessage)); + auto entities = msg.has_entities() ? TextUtilities::EntitiesFromMTP(msg.ventities.v) : EntitiesInText(); setText({ text, entities }); } @@ -980,7 +980,7 @@ void HistoryMessage::applyEdition(const MTPDmessage &message) { TextWithEntities textWithEntities = { qs(message.vmessage), EntitiesInText() }; if (message.has_entities()) { - textWithEntities.entities = entitiesFromMTP(message.ventities.v); + textWithEntities.entities = TextUtilities::EntitiesFromMTP(message.ventities.v); } setText(textWithEntities); setReplyMarkup(message.has_reply_markup() ? (&message.vreply_markup) : nullptr); @@ -1075,13 +1075,13 @@ TextWithEntities HistoryMessage::selectedText(TextSelection selection) const { result = std::move(mediaResult); } else if (!mediaResult.text.isEmpty()) { result.text += qstr("\n\n"); - appendTextWithEntities(result, std::move(mediaResult)); + TextUtilities::Append(result, std::move(mediaResult)); } if (result.text.isEmpty()) { result = std::move(logEntryOriginalResult); } else if (!logEntryOriginalResult.text.isEmpty()) { result.text += qstr("\n\n"); - appendTextWithEntities(result, std::move(logEntryOriginalResult)); + TextUtilities::Append(result, std::move(logEntryOriginalResult)); } if (auto forwarded = Get()) { if (selection == FullSelection) { @@ -1090,9 +1090,9 @@ TextWithEntities HistoryMessage::selectedText(TextSelection selection) const { wrapped.text.reserve(fwdinfo.text.size() + 4 + result.text.size()); wrapped.entities.reserve(fwdinfo.entities.size() + result.entities.size()); wrapped.text.append('['); - appendTextWithEntities(wrapped, std::move(fwdinfo)); + TextUtilities::Append(wrapped, std::move(fwdinfo)); wrapped.text.append(qsl("]\n")); - appendTextWithEntities(wrapped, std::move(result)); + TextUtilities::Append(wrapped, std::move(result)); result = wrapped; } } @@ -1101,7 +1101,7 @@ TextWithEntities HistoryMessage::selectedText(TextSelection selection) const { TextWithEntities wrapped; wrapped.text.reserve(lang(lng_in_reply_to).size() + reply->replyToMsg->author()->name.size() + 4 + result.text.size()); wrapped.text.append('[').append(lang(lng_in_reply_to)).append(' ').append(reply->replyToMsg->author()->name).append(qsl("]\n")); - appendTextWithEntities(wrapped, std::move(result)); + TextUtilities::Append(wrapped, std::move(result)); result = wrapped; } } diff --git a/Telegram/SourceFiles/history/history_service.cpp b/Telegram/SourceFiles/history/history_service.cpp index 61f365eb0..7b1903b09 100644 --- a/Telegram/SourceFiles/history/history_service.cpp +++ b/Telegram/SourceFiles/history/history_service.cpp @@ -89,7 +89,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { auto prepareChatCreate = [this](const MTPDmessageActionChatCreate &action) { auto result = PreparedText {}; result.links.push_back(fromLink()); - result.text = lng_action_created_chat(lt_from, fromLinkText(), lt_title, textClean(qs(action.vtitle))); + result.text = lng_action_created_chat(lt_from, fromLinkText(), lt_title, TextUtilities::Clean(qs(action.vtitle))); return result; }; @@ -99,7 +99,7 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { result.text = lang(lng_action_created_channel); } else { result.links.push_back(fromLink()); - result.text = lng_action_created_chat(lt_from, fromLinkText(), lt_title, textClean(qs(action.vtitle))); + result.text = lng_action_created_chat(lt_from, fromLinkText(), lt_title, TextUtilities::Clean(qs(action.vtitle))); } return result; }; @@ -143,10 +143,10 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) { auto prepareChatEditTitle = [this](const MTPDmessageActionChatEditTitle &action) { auto result = PreparedText {}; if (isPost()) { - result.text = lng_action_changed_title_channel(lt_title, textClean(qs(action.vtitle))); + result.text = lng_action_changed_title_channel(lt_title, TextUtilities::Clean(qs(action.vtitle))); } else { result.links.push_back(fromLink()); - result.text = lng_action_changed_title(lt_from, fromLinkText(), lt_title, textClean(qs(action.vtitle))); + result.text = lng_action_changed_title(lt_from, fromLinkText(), lt_title, TextUtilities::Clean(qs(action.vtitle))); } return result; }; @@ -423,7 +423,7 @@ TextWithEntities HistoryService::selectedText(TextSelection selection) const { } QString HistoryService::inDialogsText() const { - return textcmdLink(1, textClean(notificationText())); + return textcmdLink(1, TextUtilities::Clean(notificationText())); } QString HistoryService::inReplyText() const { diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index fed4cd7e0..af8ee7fad 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -2873,14 +2873,15 @@ void HistoryWidget::saveEditMsg() { auto &textWithTags = _field->getTextWithTags(); auto prepareFlags = itemTextOptions(_history, App::self()).flags; - EntitiesInText sendingEntities, leftEntities = ConvertTextTagsToEntities(textWithTags.tags); - QString sendingText, leftText = prepareTextWithEntities(textWithTags.text, prepareFlags, &leftEntities); + auto sending = TextWithEntities(); + auto left = TextWithEntities { textWithTags.text, ConvertTextTagsToEntities(textWithTags.tags) }; + TextUtilities::PrepareForSending(left, prepareFlags); - if (!textSplit(sendingText, sendingEntities, leftText, leftEntities, MaxMessageSize)) { + if (!TextUtilities::CutPart(sending, left, MaxMessageSize)) { _field->selectAll(); _field->setFocus(); return; - } else if (!leftText.isEmpty()) { + } else if (!left.text.isEmpty()) { Ui::show(Box(lang(lng_edit_too_long))); return; } @@ -2889,12 +2890,12 @@ void HistoryWidget::saveEditMsg() { if (webPageId == CancelledWebPageId) { sendFlags |= MTPmessages_EditMessage::Flag::f_no_webpage; } - auto localEntities = linksToMTP(sendingEntities); - auto sentEntities = linksToMTP(sendingEntities, true); + auto localEntities = TextUtilities::EntitiesToMTP(sending.entities); + auto sentEntities = TextUtilities::EntitiesToMTP(sending.entities, TextUtilities::ConvertOption::SkipLocal); if (!sentEntities.v.isEmpty()) { sendFlags |= MTPmessages_EditMessage::Flag::f_entities; } - _saveEditMsgRequestId = MTP::send(MTPmessages_EditMessage(MTP_flags(sendFlags), _history->peer->input, MTP_int(_editMsgId), MTP_string(sendingText), MTPnullMarkup, sentEntities), rpcDone(&HistoryWidget::saveEditMsgDone, _history), rpcFail(&HistoryWidget::saveEditMsgFail, _history)); + _saveEditMsgRequestId = MTP::send(MTPmessages_EditMessage(MTP_flags(sendFlags), _history->peer->input, MTP_int(_editMsgId), MTP_string(sending.text), MTPnullMarkup, sentEntities), rpcDone(&HistoryWidget::saveEditMsgDone, _history), rpcFail(&HistoryWidget::saveEditMsgFail, _history)); } void HistoryWidget::saveEditMsgDone(History *history, const MTPUpdates &updates, mtpRequestId req) { @@ -3841,7 +3842,7 @@ void HistoryWidget::onKbToggle(bool manual) { _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard->forceReply()) ? App::histItemById(_keyboard->forMsgId()) : 0; if (_kbReplyTo && !_editMsgId && !_replyToId && fieldEnabled) { updateReplyToName(); - _replyEditMsgText.setText(st::messageTextStyle, textClean(_kbReplyTo->inReplyText()), _textDlgOptions); + _replyEditMsgText.setText(st::messageTextStyle, TextUtilities::Clean(_kbReplyTo->inReplyText()), _textDlgOptions); _fieldBarCancel->show(); updateMouseTracking(); } @@ -3860,7 +3861,7 @@ void HistoryWidget::onKbToggle(bool manual) { _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard->forceReply()) ? App::histItemById(_keyboard->forMsgId()) : 0; if (_kbReplyTo && !_editMsgId && !_replyToId) { updateReplyToName(); - _replyEditMsgText.setText(st::messageTextStyle, textClean(_kbReplyTo->inReplyText()), _textDlgOptions); + _replyEditMsgText.setText(st::messageTextStyle, TextUtilities::Clean(_kbReplyTo->inReplyText()), _textDlgOptions); _fieldBarCancel->show(); updateMouseTracking(); } @@ -5181,7 +5182,7 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) { _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard->forceReply()) ? App::histItemById(_keyboard->forMsgId()) : 0; if (_kbReplyTo && !_replyToId) { updateReplyToName(); - _replyEditMsgText.setText(st::messageTextStyle, textClean(_kbReplyTo->inReplyText()), _textDlgOptions); + _replyEditMsgText.setText(st::messageTextStyle, TextUtilities::Clean(_kbReplyTo->inReplyText()), _textDlgOptions); _fieldBarCancel->show(); updateMouseTracking(); } @@ -5440,7 +5441,7 @@ void HistoryWidget::updatePinnedBar(bool force) { _pinnedBar->msg = App::histItemById(_history->channelId(), _pinnedBar->msgId); } if (_pinnedBar->msg) { - _pinnedBar->text.setText(st::messageTextStyle, textClean(_pinnedBar->msg->notificationText()), _textDlgOptions); + _pinnedBar->text.setText(st::messageTextStyle, TextUtilities::Clean(_pinnedBar->msg->notificationText()), _textDlgOptions); update(); } else if (force) { if (_peer && _peer->isMegagroup()) { @@ -5667,7 +5668,7 @@ void HistoryWidget::onReplyToMessage() { } else { _replyEditMsg = to; _replyToId = to->id; - _replyEditMsgText.setText(st::messageTextStyle, textClean(_replyEditMsg->inReplyText()), _textDlgOptions); + _replyEditMsgText.setText(st::messageTextStyle, TextUtilities::Clean(_replyEditMsg->inReplyText()), _textDlgOptions); updateBotKeyboard(); @@ -5709,10 +5710,8 @@ void HistoryWidget::onEditMessage() { } auto original = to->originalText(); - auto editText = textApplyEntities(original.text, original.entities); - auto editTags = ConvertEntitiesToTextTags(original.entities); - TextWithTags editData = { editText, editTags }; - MessageCursor cursor = { editText.size(), editText.size(), QFIXED_MAX }; + auto editData = TextWithTags { TextUtilities::ApplyEntities(original), ConvertEntitiesToTextTags(original.entities) }; + auto cursor = MessageCursor { editData.text.size(), editData.text.size(), QFIXED_MAX }; _history->setEditDraft(std::make_unique(editData, to->id, cursor, false)); applyDraft(false); @@ -6013,7 +6012,7 @@ void HistoryWidget::updatePreview() { #else // OS_MAC_OLD auto linkText = _previewLinks.split(' ').at(0); #endif // OS_MAC_OLD - _previewDescription.setText(st::messageTextStyle, textClean(linkText), _textDlgOptions); + _previewDescription.setText(st::messageTextStyle, TextUtilities::Clean(linkText), _textDlgOptions); int32 t = (_previewData->pendingTill - unixtime()) * 1000; if (t <= 0) t = 1; @@ -6045,7 +6044,7 @@ void HistoryWidget::updatePreview() { } } _previewTitle.setText(st::msgNameStyle, title, _textNameOptions); - _previewDescription.setText(st::messageTextStyle, textClean(desc), _textDlgOptions); + _previewDescription.setText(st::messageTextStyle, TextUtilities::Clean(desc), _textDlgOptions); } } else if (!readyToForward() && !replyToId() && !_editMsgId) { _fieldBarCancel->hide(); @@ -6060,9 +6059,7 @@ void HistoryWidget::onCancel() { onInlineBotCancel(); } else if (_editMsgId) { auto original = _replyEditMsg ? _replyEditMsg->originalText() : TextWithEntities(); - auto editText = textApplyEntities(original.text, original.entities); - auto editTags = ConvertEntitiesToTextTags(original.entities); - TextWithTags editData = { editText, editTags }; + auto editData = TextWithTags { TextUtilities::ApplyEntities(original), ConvertEntitiesToTextTags(original.entities) }; if (_replyEditMsg && editData != _field->getTextWithTags()) { Ui::show(Box( lang(lng_cancel_edit_post_sure), @@ -6329,7 +6326,7 @@ void HistoryWidget::updateReplyEditTexts(bool force) { _replyEditMsg = App::histItemById(_channel, _editMsgId ? _editMsgId : _replyToId); } if (_replyEditMsg) { - _replyEditMsgText.setText(st::messageTextStyle, textClean(_replyEditMsg->inReplyText()), _textDlgOptions); + _replyEditMsgText.setText(st::messageTextStyle, TextUtilities::Clean(_replyEditMsg->inReplyText()), _textDlgOptions); updateBotKeyboard(); @@ -6390,7 +6387,7 @@ void HistoryWidget::updateForwardingTexts() { } } _toForwardFrom.setText(st::msgNameStyle, from, _textNameOptions); - _toForwardText.setText(st::messageTextStyle, textClean(text), _textDlgOptions); + _toForwardText.setText(st::messageTextStyle, TextUtilities::Clean(text), _textDlgOptions); _toForwardNameVersion = version; } diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index 68da37939..15c290685 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -570,7 +570,7 @@ void Video::initDimensions() { _maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft; int32 textWidth = _maxw - (withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : 0); TextParseOptions titleOpts = { 0, _maxw, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto }; - QString title = textOneLine(_result->getLayoutTitle()); + auto title = TextUtilities::SingleLine(_result->getLayoutTitle()); if (title.isEmpty()) { title = lang(lng_media_video); } @@ -685,7 +685,7 @@ void File::initDimensions() { int textWidth = _maxw - (st::msgFileSize + st::inlineThumbSkip); TextParseOptions titleOpts = { 0, _maxw, st::semiboldFont->height, Qt::LayoutDirectionAuto }; - _title.setText(st::semiboldTextStyle, textOneLine(_result->getLayoutTitle()), titleOpts); + _title.setText(st::semiboldTextStyle, TextUtilities::SingleLine(_result->getLayoutTitle()), titleOpts); TextParseOptions descriptionOpts = { TextParseMultiline, _maxw, st::normalFont->height, Qt::LayoutDirectionAuto }; _description.setText(st::defaultTextStyle, _result->getLayoutDescription(), descriptionOpts); @@ -890,7 +890,7 @@ void Contact::initDimensions() { _maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft; int32 textWidth = _maxw - (st::inlineThumbSize + st::inlineThumbSkip); TextParseOptions titleOpts = { 0, _maxw, st::semiboldFont->height, Qt::LayoutDirectionAuto }; - _title.setText(st::semiboldTextStyle, textOneLine(_result->getLayoutTitle()), titleOpts); + _title.setText(st::semiboldTextStyle, TextUtilities::SingleLine(_result->getLayoutTitle()), titleOpts); int32 titleHeight = qMin(_title.countHeight(_maxw), st::semiboldFont->height); TextParseOptions descriptionOpts = { TextParseMultiline, _maxw, st::normalFont->height, Qt::LayoutDirectionAuto }; @@ -987,7 +987,7 @@ void Article::initDimensions() { _maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft; int32 textWidth = _maxw - (_withThumb ? (st::inlineThumbSize + st::inlineThumbSkip) : 0); TextParseOptions titleOpts = { 0, _maxw, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto }; - _title.setText(st::semiboldTextStyle, textOneLine(_result->getLayoutTitle()), titleOpts); + _title.setText(st::semiboldTextStyle, TextUtilities::SingleLine(_result->getLayoutTitle()), titleOpts); int32 titleHeight = qMin(_title.countHeight(_maxw), 2 * st::semiboldFont->height); int32 descriptionLines = (_withThumb || _url) ? 2 : 3; @@ -1155,7 +1155,7 @@ void Game::initDimensions() { _maxw = st::emojiPanWidth - st::emojiScroll.width - st::inlineResultsLeft; int32 textWidth = _maxw - (st::inlineThumbSize + st::inlineThumbSkip); TextParseOptions titleOpts = { 0, _maxw, 2 * st::semiboldFont->height, Qt::LayoutDirectionAuto }; - _title.setText(st::semiboldTextStyle, textOneLine(_result->getLayoutTitle()), titleOpts); + _title.setText(st::semiboldTextStyle, TextUtilities::SingleLine(_result->getLayoutTitle()), titleOpts); int32 titleHeight = qMin(_title.countHeight(_maxw), 2 * st::semiboldFont->height); int32 descriptionLines = 2; diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp index 3a2babc85..0689f68c3 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp @@ -136,7 +136,7 @@ std::unique_ptr Result::create(uint64 queryId, const MTPBotInlineResult case mtpc_botInlineMessageText: { auto &r = message->c_botInlineMessageText(); - auto entities = r.has_entities() ? entitiesFromMTP(r.ventities.v) : EntitiesInText(); + auto entities = r.has_entities() ? TextUtilities::EntitiesFromMTP(r.ventities.v) : EntitiesInText(); result->sendData = std::make_unique(qs(r.vmessage), entities, r.is_no_webpage()); if (result->_type == Type::Photo) { result->createPhoto(); diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp index 80349a402..f6fedcca1 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp @@ -57,7 +57,7 @@ QString SendDataCommon::getErrorOnSend(const Result *owner, History *history) co SendDataCommon::SentMTPMessageFields SendText::getSentMessageFields() const { SentMTPMessageFields result; result.text = MTP_string(_message); - result.entities = linksToMTP(_entities); + result.entities = TextUtilities::EntitiesToMTP(_entities); return result; } diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 042bd13cd..555db1a96 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -1479,23 +1479,24 @@ void MainWidget::sendMessage(const MessageToSend &message) { } saveRecentHashtags(textWithTags.text); - EntitiesInText sendingEntities, leftEntities = ConvertTextTagsToEntities(textWithTags.tags); + auto sending = TextWithEntities(); + auto left = TextWithEntities { textWithTags.text, ConvertTextTagsToEntities(textWithTags.tags) }; auto prepareFlags = itemTextOptions(history, App::self()).flags; - QString sendingText, leftText = prepareTextWithEntities(textWithTags.text, prepareFlags, &leftEntities); + TextUtilities::PrepareForSending(left, prepareFlags); HistoryItem *lastMessage = nullptr; - MsgId replyTo = (message.replyTo < 0) ? _history->replyToId() : message.replyTo; - while (textSplit(sendingText, sendingEntities, leftText, leftEntities, MaxMessageSize)) { - FullMsgId newId(peerToChannel(history->peer->id), clientMsgId()); - uint64 randomId = rand_value(); + auto replyTo = (message.replyTo < 0) ? _history->replyToId() : message.replyTo; + while (TextUtilities::CutPart(sending, left, MaxMessageSize)) { + auto newId = FullMsgId(peerToChannel(history->peer->id), clientMsgId()); + auto randomId = rand_value(); - trimTextWithEntities(sendingText, &sendingEntities); + TextUtilities::Trim(sending); App::historyRegRandom(randomId, newId); - App::historyRegSentData(randomId, history->peer->id, sendingText); + App::historyRegSentData(randomId, history->peer->id, sending.text); - MTPstring msgText(MTP_string(sendingText)); + MTPstring msgText(MTP_string(sending.text)); auto flags = NewMessageFlags(history->peer) | MTPDmessage::Flag::f_entities; // unread, out auto sendFlags = MTPmessages_SendMessage::Flags(0); if (replyTo) { @@ -1523,8 +1524,8 @@ void MainWidget::sendMessage(const MessageToSend &message) { if (silentPost) { sendFlags |= MTPmessages_SendMessage::Flag::f_silent; } - auto localEntities = linksToMTP(sendingEntities); - auto sentEntities = linksToMTP(sendingEntities, true); + auto localEntities = TextUtilities::EntitiesToMTP(sending.entities); + auto sentEntities = TextUtilities::EntitiesToMTP(sending.entities, TextUtilities::ConvertOption::SkipLocal); if (!sentEntities.v.isEmpty()) { sendFlags |= MTPmessages_SendMessage::Flag::f_entities; } @@ -1546,7 +1547,7 @@ void MainWidget::saveRecentHashtags(const QString &text) { bool found = false; QRegularExpressionMatch m; RecentHashtagPack recent(cRecentWriteHashtags()); - for (int32 i = 0, next = 0; (m = reHashtag().match(text, i)).hasMatch(); i = next) { + for (int32 i = 0, next = 0; (m = TextUtilities::RegExpHashtag().match(text, i)).hasMatch(); i = next) { i = m.capturedStart(); next = m.capturedEnd(); if (m.hasMatch()) { @@ -2093,12 +2094,11 @@ void MainWidget::dialogsCancelled() { void MainWidget::insertCheckedServiceNotification(const TextWithEntities &message, const MTPMessageMedia &media, int32 date) { auto flags = MTPDmessage::Flag::f_entities | MTPDmessage::Flag::f_from_id | MTPDmessage_ClientFlag::f_clientside_unread; - QString sendingText, leftText = message.text; - EntitiesInText sendingEntities, leftEntities = message.entities; + auto sending = TextWithEntities(), left = message; HistoryItem *item = nullptr; - while (textSplit(sendingText, sendingEntities, leftText, leftEntities, MaxMessageSize)) { - MTPVector localEntities = linksToMTP(sendingEntities); - item = App::histories().addNewMessage(MTP_message(MTP_flags(flags), MTP_int(clientMsgId()), MTP_int(ServiceUserId), MTP_peerUser(MTP_int(AuthSession::CurrentUserId())), MTPnullFwdHeader, MTPint(), MTPint(), MTP_int(date), MTP_string(sendingText), media, MTPnullMarkup, localEntities, MTPint(), MTPint()), NewMessageUnread); + while (TextUtilities::CutPart(sending, left, MaxMessageSize)) { + auto localEntities = TextUtilities::EntitiesToMTP(sending.entities); + item = App::histories().addNewMessage(MTP_message(MTP_flags(flags), MTP_int(clientMsgId()), MTP_int(ServiceUserId), MTP_peerUser(MTP_int(AuthSession::CurrentUserId())), MTPnullFwdHeader, MTPint(), MTPint(), MTP_int(date), MTP_string(sending.text), media, MTPnullMarkup, localEntities, MTPint(), MTPint()), NewMessageUnread); } if (item) { _history->peerMessagesUpdated(item->history()->peer->id); @@ -4878,7 +4878,7 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) { if (d.has_entities() && !mentionUsersLoaded(d.ventities)) { AuthSession::Current().api().requestMessageData(item->history()->peer->asChannel(), item->id, ApiWrap::RequestMessageDataCallback()); } - auto entities = d.has_entities() ? entitiesFromMTP(d.ventities.v) : EntitiesInText(); + auto entities = d.has_entities() ? TextUtilities::EntitiesFromMTP(d.ventities.v) : EntitiesInText(); item->setText({ text, entities }); item->updateMedia(d.has_media() ? (&d.vmedia) : nullptr); item->addToOverview(AddToOverviewNew); @@ -5129,9 +5129,9 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { auto &d = update.c_updateUserName(); if (auto user = App::userLoaded(d.vuser_id.v)) { if (user->contact <= 0) { - user->setName(textOneLine(qs(d.vfirst_name)), textOneLine(qs(d.vlast_name)), user->nameOrPhone, textOneLine(qs(d.vusername))); + user->setName(TextUtilities::SingleLine(qs(d.vfirst_name)), TextUtilities::SingleLine(qs(d.vlast_name)), user->nameOrPhone, TextUtilities::SingleLine(qs(d.vusername))); } else { - user->setName(textOneLine(user->firstName), textOneLine(user->lastName), user->nameOrPhone, textOneLine(qs(d.vusername))); + user->setName(TextUtilities::SingleLine(user->firstName), TextUtilities::SingleLine(user->lastName), user->nameOrPhone, TextUtilities::SingleLine(qs(d.vusername))); } App::markPeerUpdated(user); } @@ -5234,7 +5234,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { if (d.is_popup()) { Ui::show(Box(qs(d.vmessage))); } else { - App::wnd()->serviceNotification({ qs(d.vmessage), entitiesFromMTP(d.ventities.v) }, d.vmedia); + App::wnd()->serviceNotification({ qs(d.vmessage), TextUtilities::EntitiesFromMTP(d.ventities.v) }, d.vmedia); emit App::wnd()->checkNewAuthorization(); } } break; diff --git a/Telegram/SourceFiles/media/player/media_player_cover.cpp b/Telegram/SourceFiles/media/player/media_player_cover.cpp index 365a23f4c..fae5c08dc 100644 --- a/Telegram/SourceFiles/media/player/media_player_cover.cpp +++ b/Telegram/SourceFiles/media/player/media_player_cover.cpp @@ -323,7 +323,7 @@ void CoverWidget::handleSongChange() { if (song->performer.isEmpty()) { textWithEntities.text = song->title.isEmpty() ? (current.audio()->name.isEmpty() ? qsl("Unknown Track") : current.audio()->name) : song->title; } else { - auto title = song->title.isEmpty() ? qsl("Unknown Track") : textClean(song->title); + auto title = song->title.isEmpty() ? qsl("Unknown Track") : TextUtilities::Clean(song->title); textWithEntities.text = song->performer + QString::fromUtf8(" \xe2\x80\x93 ") + title; textWithEntities.entities.append({ EntityInTextBold, 0, song->performer.size(), QString() }); } diff --git a/Telegram/SourceFiles/media/player/media_player_widget.cpp b/Telegram/SourceFiles/media/player/media_player_widget.cpp index f704da0b6..a8976c373 100644 --- a/Telegram/SourceFiles/media/player/media_player_widget.cpp +++ b/Telegram/SourceFiles/media/player/media_player_widget.cpp @@ -500,7 +500,7 @@ void Widget::handleSongChange() { if (!song || song->performer.isEmpty()) { textWithEntities.text = (!song || song->title.isEmpty()) ? (current.audio()->name.isEmpty() ? qsl("Unknown Track") : current.audio()->name) : song->title; } else { - auto title = song->title.isEmpty() ? qsl("Unknown Track") : textClean(song->title); + auto title = song->title.isEmpty() ? qsl("Unknown Track") : TextUtilities::Clean(song->title); textWithEntities.text = song->performer + QString::fromUtf8(" \xe2\x80\x93 ") + title; textWithEntities.entities.append({ EntityInTextBold, 0, song->performer.size(), QString() }); } diff --git a/Telegram/SourceFiles/overview/overview_layout.cpp b/Telegram/SourceFiles/overview/overview_layout.cpp index 2bfcc8bea..8ca69432e 100644 --- a/Telegram/SourceFiles/overview/overview_layout.cpp +++ b/Telegram/SourceFiles/overview/overview_layout.cpp @@ -506,7 +506,7 @@ Voice::Voice(DocumentData *voice, HistoryItem *parent, const style::OverviewFile setDocumentLinks(_data); updateName(); - QString d = textcmdLink(1, textRichPrepare(langDateTime(date(_data->date)))); + QString d = textcmdLink(1, TextUtilities::EscapeForRichParsing(langDateTime(date(_data->date)))); TextParseOptions opts = { TextParseRichText, 0, 0, Qt::LayoutDirectionAuto }; _details.setText(st::defaultTextStyle, lng_date_and_duration(lt_date, d, lt_duration, formatDurationText(_data->voice()->duration)), opts); _details.setLink(1, goToMessageClickHandler(parent)); diff --git a/Telegram/SourceFiles/profile/profile_block_group_members.cpp b/Telegram/SourceFiles/profile/profile_block_group_members.cpp index d7303f2df..312094b2e 100644 --- a/Telegram/SourceFiles/profile/profile_block_group_members.cpp +++ b/Telegram/SourceFiles/profile/profile_block_group_members.cpp @@ -309,9 +309,9 @@ void GroupMembersWidget::refreshLimitReached() { bool limitReachedShown = (itemsCount() >= Global::ChatSizeMax()) && chat->amCreator() && !emptyTitle(); if (limitReachedShown && !_limitReachedInfo) { _limitReachedInfo.create(this, st::profileLimitReachedLabel); - QString title = textRichPrepare(lng_profile_migrate_reached(lt_count, Global::ChatSizeMax())); - QString body = textRichPrepare(lang(lng_profile_migrate_body)); - QString link = textRichPrepare(lang(lng_profile_migrate_learn_more)); + QString title = TextUtilities::EscapeForRichParsing(lng_profile_migrate_reached(lt_count, Global::ChatSizeMax())); + QString body = TextUtilities::EscapeForRichParsing(lang(lng_profile_migrate_body)); + QString link = TextUtilities::EscapeForRichParsing(lang(lng_profile_migrate_learn_more)); QString text = qsl("%1%2%3\n%4 [a href=\"https://telegram.org/blog/supergroups5k\"]%5[/a]").arg(textcmdStartSemibold()).arg(title).arg(textcmdStopSemibold()).arg(body).arg(link); _limitReachedInfo->setRichText(text); _limitReachedInfo->setClickHandlerHook([this](const ClickHandlerPtr &handler, Qt::MouseButton button) { diff --git a/Telegram/SourceFiles/profile/profile_block_info.cpp b/Telegram/SourceFiles/profile/profile_block_info.cpp index 1994c68ec..b75fa574b 100644 --- a/Telegram/SourceFiles/profile/profile_block_info.cpp +++ b/Telegram/SourceFiles/profile/profile_block_info.cpp @@ -149,12 +149,12 @@ void InfoWidget::refreshAbout() { }; _about.destroy(); - auto aboutText = TextWithEntities { textClean(getAboutText()) }; + auto aboutText = TextWithEntities { TextUtilities::Clean(getAboutText()) }; if (!aboutText.text.isEmpty()) { _about.create(this, st::profileBlockTextPart); _about->show(); - textParseEntities(aboutText.text, TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands, &aboutText.entities); + TextUtilities::ParseEntities(aboutText, TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands); _about->setMarkedText(aboutText); _about->setSelectable(true); _about->setClickHandlerHook([this](const ClickHandlerPtr &handler, Qt::MouseButton button) { diff --git a/Telegram/SourceFiles/profile/profile_channel_controllers.cpp b/Telegram/SourceFiles/profile/profile_channel_controllers.cpp index 32e7f840b..862921ecd 100644 --- a/Telegram/SourceFiles/profile/profile_channel_controllers.cpp +++ b/Telegram/SourceFiles/profile/profile_channel_controllers.cpp @@ -1120,16 +1120,7 @@ void AddParticipantBoxSearchController::searchGlobalDone(mtpRequestId requestId, void AddParticipantBoxSearchController::addChatsContacts() { _chatsContactsAdded = true; - auto filterWordList = _query.split(cWordSplit(), QString::SkipEmptyParts); - auto wordsCount = filterWordList.size(); - auto wordList = QStringList(); - wordList.reserve(wordsCount); - for_const (auto &word, filterWordList) { - auto trimmed = word.trimmed(); - if (!trimmed.isEmpty()) { - wordList.push_back(trimmed); - } - } + auto wordList = TextUtilities::PrepareSearchWords(_query); if (wordList.empty()) { return; } diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index 8c6b68df6..d8441ae4e 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -461,22 +461,22 @@ void UserData::setPhoto(const MTPUserProfilePhoto &p) { // see Local::readPeer a void PeerData::fillNames() { names.clear(); chars.clear(); - QString toIndex = textAccentFold(name); + auto toIndex = TextUtilities::RemoveAccents(name); if (cRussianLetters().match(toIndex).hasMatch()) { toIndex += ' ' + translitRusEng(toIndex); } if (isUser()) { - if (!asUser()->nameOrPhone.isEmpty() && asUser()->nameOrPhone != name) toIndex += ' ' + textAccentFold(asUser()->nameOrPhone); - if (!asUser()->username.isEmpty()) toIndex += ' ' + textAccentFold(asUser()->username); + if (!asUser()->nameOrPhone.isEmpty() && asUser()->nameOrPhone != name) toIndex += ' ' + TextUtilities::RemoveAccents(asUser()->nameOrPhone); + if (!asUser()->username.isEmpty()) toIndex += ' ' + TextUtilities::RemoveAccents(asUser()->username); } else if (isChannel()) { - if (!asChannel()->username.isEmpty()) toIndex += ' ' + textAccentFold(asChannel()->username); + if (!asChannel()->username.isEmpty()) toIndex += ' ' + TextUtilities::RemoveAccents(asChannel()->username); } toIndex += ' ' + rusKeyboardLayoutSwitch(toIndex); - QStringList namesList = toIndex.toLower().split(cWordSplit(), QString::SkipEmptyParts); - for (QStringList::const_iterator i = namesList.cbegin(), e = namesList.cend(); i != e; ++i) { - names.insert(*i); - chars.insert(i->at(0)); + auto namesList = TextUtilities::PrepareSearchWords(toIndex); + for (auto &name : namesList) { + names.insert(name); + chars.insert(name[0]); } } diff --git a/Telegram/SourceFiles/ui/countryinput.cpp b/Telegram/SourceFiles/ui/countryinput.cpp index f6fdb9324..15c438add 100644 --- a/Telegram/SourceFiles/ui/countryinput.cpp +++ b/Telegram/SourceFiles/ui/countryinput.cpp @@ -417,21 +417,8 @@ void CountrySelectBox::Inner::mouseReleaseEvent(QMouseEvent *e) { } void CountrySelectBox::Inner::updateFilter(QString filter) { - filter = textSearchKey(filter); - - QStringList f; - if (!filter.isEmpty()) { - QStringList filterList = filter.split(cWordSplit(), QString::SkipEmptyParts); - int l = filterList.size(); - - f.reserve(l); - for (int i = 0; i < l; ++i) { - QString filterName = filterList[i].trimmed(); - if (filterName.isEmpty()) continue; - f.push_back(filterName); - } - filter = f.join(' '); - } + auto words = TextUtilities::PrepareSearchWords(filter); + filter = words.isEmpty() ? QString() : words.join(' '); if (_filter != filter) { _filter = filter; @@ -441,7 +428,7 @@ void CountrySelectBox::Inner::updateFilter(QString filter) { QChar first = _filter[0].toLower(); CountriesIds &ids(countriesByLetter[first]); - QStringList::const_iterator fb = f.cbegin(), fe = f.cend(), fi; + QStringList::const_iterator fb = words.cbegin(), fe = words.cend(), fi; countriesFiltered.clear(); for (CountriesIds::const_iterator i = ids.cbegin(), e = ids.cend(); i != e; ++i) { diff --git a/Telegram/SourceFiles/ui/emoji_config.h b/Telegram/SourceFiles/ui/emoji_config.h index 1da0bcc2d..7e2029d57 100644 --- a/Telegram/SourceFiles/ui/emoji_config.h +++ b/Telegram/SourceFiles/ui/emoji_config.h @@ -197,29 +197,30 @@ inline QString Filename(int index = Index()) { return QString::fromLatin1(EmojiNames[index]); } -inline void appendPartToResult(QString &result, const QChar *start, const QChar *from, const QChar *to, EntitiesInText *inOutEntities) { +inline void AppendPartToResult(TextWithEntities &result, const QChar *start, const QChar *from, const QChar *to) { if (to > from) { - for (auto &entity : *inOutEntities) { + for (auto &entity : result.entities) { if (entity.offset() >= to - start) break; if (entity.offset() + entity.length() < from - start) continue; if (entity.offset() >= from - start) { - entity.extendToLeft(from - start - result.size()); + entity.extendToLeft(from - start - result.text.size()); } if (entity.offset() + entity.length() <= to - start) { - entity.shrinkFromRight(from - start - result.size()); + entity.shrinkFromRight(from - start - result.text.size()); } } - result.append(from, to - from); + result.text.append(from, to - from); } } -inline QString ReplaceInText(const QString &text, EntitiesInText *inOutEntities) { - auto result = QString(); - auto currentEntity = inOutEntities->begin(); - auto entitiesEnd = inOutEntities->end(); - auto emojiStart = text.constData(); +inline void ReplaceInText(TextWithEntities &result) { + auto newText = TextWithEntities(); + newText.entities = std::move(result.entities); + auto currentEntity = newText.entities.begin(); + auto entitiesEnd = newText.entities.end(); + auto emojiStart = result.text.constData(); auto emojiEnd = emojiStart; - auto end = emojiStart + text.size(); + auto end = emojiStart + result.text.size(); auto canFindEmoji = true; for (auto ch = emojiEnd; ch != end;) { auto emojiLength = 0; @@ -234,9 +235,9 @@ inline QString ReplaceInText(const QString &text, EntitiesInText *inOutEntities) (newEmojiEnd == end || !newEmojiEnd->isLetterOrNumber() || newEmojiEnd == emojiStart || !(newEmojiEnd - 1)->isLetterOrNumber()) && (currentEntity == entitiesEnd || (ch < emojiStart + currentEntity->offset() && newEmojiEnd <= emojiStart + currentEntity->offset()) || (ch >= emojiStart + currentEntity->offset() + currentEntity->length() && newEmojiEnd > emojiStart + currentEntity->offset() + currentEntity->length())) ) { - if (result.isEmpty()) result.reserve(text.size()); + if (newText.text.isEmpty()) newText.text.reserve(result.text.size()); - appendPartToResult(result, emojiStart, emojiEnd, ch, inOutEntities); + AppendPartToResult(newText, emojiStart, emojiEnd, ch); if (emoji->hasVariants()) { auto it = cEmojiVariants().constFind(emoji->nonColoredId()); @@ -244,7 +245,7 @@ inline QString ReplaceInText(const QString &text, EntitiesInText *inOutEntities) emoji = emoji->variant(it.value()); } } - result.append(emoji->text()); + newText.text.append(emoji->text()); ch = emojiEnd = newEmojiEnd; canFindEmoji = true; @@ -257,11 +258,12 @@ inline QString ReplaceInText(const QString &text, EntitiesInText *inOutEntities) ++ch; } } - if (result.isEmpty()) return text; - - appendPartToResult(result, emojiStart, emojiEnd, end, inOutEntities); - - return result; + if (newText.text.isEmpty()) { + result.entities = std::move(newText.entities); + } else { + AppendPartToResult(newText, emojiStart, emojiEnd, end); + result = std::move(newText); + } } inline RecentEmojiPack &GetRecent() { diff --git a/Telegram/SourceFiles/ui/text/text.cpp b/Telegram/SourceFiles/ui/text/text.cpp index 044f87161..99eda4a7a 100644 --- a/Telegram/SourceFiles/ui/text/text.cpp +++ b/Telegram/SourceFiles/ui/text/text.cpp @@ -493,33 +493,32 @@ public: } TextParser(Text *t, const QString &text, const TextParseOptions &options) : _t(t), - src(text), + source { text }, rich(options.flags & TextParseRichText), multiline(options.flags & TextParseMultiline), stopAfterWidth(QFIXED_MAX) { if (options.flags & TextParseLinks) { - textParseEntities(src, options.flags, &entities, rich); + TextUtilities::ParseEntities(source, options.flags, rich); } parse(options); } TextParser(Text *t, const TextWithEntities &textWithEntities, const TextParseOptions &options) : _t(t), - src(textWithEntities.text), + source(textWithEntities), rich(options.flags & TextParseRichText), multiline(options.flags & TextParseMultiline), stopAfterWidth(QFIXED_MAX) { - auto preparsed = textWithEntities.entities; + auto &preparsed = textWithEntities.entities; if ((options.flags & TextParseLinks) && !preparsed.isEmpty()) { bool parseMentions = (options.flags & TextParseMentions); bool parseHashtags = (options.flags & TextParseHashtags); bool parseBotCommands = (options.flags & TextParseBotCommands); bool parseMono = (options.flags & TextParseMono); - if (parseMentions && parseHashtags && parseBotCommands && parseMono) { - entities = preparsed; - } else { + if (!parseMentions || !parseHashtags || !parseBotCommands || !parseMono) { int32 i = 0, l = preparsed.size(); - entities.reserve(l); - const QChar s = src.size(); + source.entities.clear(); + source.entities.reserve(l); + const QChar s = source.text.size(); for (; i < l; ++i) { auto type = preparsed.at(i).type(); if (((type == EntityInTextMention || type == EntityInTextMentionName) && !parseMentions) || @@ -528,7 +527,7 @@ public: ((type == EntityInTextBold || type == EntityInTextItalic || type == EntityInTextCode || type == EntityInTextPre) && !parseMono)) { continue; } - entities.push_back(preparsed.at(i)); + source.entities.push_back(preparsed.at(i)); } } } @@ -540,13 +539,13 @@ public: stopAfterWidth = ((options.maxh / _t->_st->font->height) + 1) * options.maxw; } - start = src.constData(); - end = start + src.size(); + start = source.text.constData(); + end = start + source.text.size(); - entitiesEnd = entities.cend(); - waitingEntity = entities.cbegin(); + entitiesEnd = source.entities.cend(); + waitingEntity = source.entities.cbegin(); while (waitingEntity != entitiesEnd && waitingEntity->length() <= 0) ++waitingEntity; - int firstMonospaceOffset = EntityInText::firstMonospaceOffset(entities, end - start); + int firstMonospaceOffset = EntityInText::firstMonospaceOffset(source.entities, end - start); ptr = start; while (ptr != end && chIsTrimmed(*ptr, rich) && ptr != start + firstMonospaceOffset) { @@ -587,50 +586,50 @@ public: removeFlags.clear(); _t->_links.resize(maxLnkIndex); - for (Text::TextBlocks::const_iterator i = _t->_blocks.cbegin(), e = _t->_blocks.cend(); i != e; ++i) { - ITextBlock *b = *i; + for (auto i = _t->_blocks.cbegin(), e = _t->_blocks.cend(); i != e; ++i) { + auto b = *i; if (b->lnkIndex() > 0x8000) { lnkIndex = maxLnkIndex + (b->lnkIndex() - 0x8000); if (_t->_links.size() < lnkIndex) { _t->_links.resize(lnkIndex); - const TextLinkData &link(links[lnkIndex - maxLnkIndex - 1]); + auto &link = links[lnkIndex - maxLnkIndex - 1]; ClickHandlerPtr handler; switch (link.type) { - case EntityInTextCustomUrl: handler.reset(new HiddenUrlClickHandler(link.data)); break; + case EntityInTextCustomUrl: handler = MakeShared(link.data); break; case EntityInTextEmail: - case EntityInTextUrl: handler.reset(new UrlClickHandler(link.data, link.displayStatus == LinkDisplayedFull)); break; - case EntityInTextBotCommand: handler.reset(new BotCommandClickHandler(link.data)); break; + case EntityInTextUrl: handler = MakeShared(link.data, link.displayStatus == LinkDisplayedFull); break; + case EntityInTextBotCommand: handler = MakeShared(link.data); break; case EntityInTextHashtag: if (options.flags & TextTwitterMentions) { - handler.reset(new UrlClickHandler(qsl("https://twitter.com/hashtag/") + link.data.mid(1) + qsl("?src=hash"), true)); + handler = MakeShared(qsl("https://twitter.com/hashtag/") + link.data.mid(1) + qsl("?src=hash"), true); } else if (options.flags & TextInstagramMentions) { - handler.reset(new UrlClickHandler(qsl("https://instagram.com/explore/tags/") + link.data.mid(1) + '/', true)); + handler = MakeShared(qsl("https://instagram.com/explore/tags/") + link.data.mid(1) + '/', true); } else { - handler.reset(new HashtagClickHandler(link.data)); + handler = MakeShared(link.data); } break; case EntityInTextMention: if (options.flags & TextTwitterMentions) { - handler.reset(new UrlClickHandler(qsl("https://twitter.com/") + link.data.mid(1), true)); + handler = MakeShared(qsl("https://twitter.com/") + link.data.mid(1), true); } else if (options.flags & TextInstagramMentions) { - handler.reset(new UrlClickHandler(qsl("https://instagram.com/") + link.data.mid(1) + '/', true)); + handler = MakeShared(qsl("https://instagram.com/") + link.data.mid(1) + '/', true); } else { - handler.reset(new MentionClickHandler(link.data)); + handler = MakeShared(link.data); } break; case EntityInTextMentionName: { - UserId userId = 0; - uint64 accessHash = 0; - if (mentionNameToFields(link.data, &userId, &accessHash)) { - handler.reset(new MentionNameClickHandler(link.text, userId, accessHash)); + auto fields = TextUtilities::MentionNameDataToFields(link.data); + if (fields.userId) { + handler = MakeShared(link.text, fields.userId, fields.accessHash); } else { LOG(("Bad mention name: %1").arg(link.data)); } } break; } - t_assert(!handler.isNull()); - _t->setLink(lnkIndex, handler); + if (!handler.isNull()) { + _t->setLink(lnkIndex, handler); + } } b->setLnkIndex(lnkIndex); } @@ -667,11 +666,9 @@ private: } Text *_t; - QString src; + TextWithEntities source; const QChar *start, *end, *ptr; bool rich, multiline; - - EntitiesInText entities; EntitiesInText::const_iterator waitingEntity, entitiesEnd; typedef QVector TextLinks; diff --git a/Telegram/SourceFiles/ui/text/text.h b/Telegram/SourceFiles/ui/text/text.h index 4eccd56c7..4c22681fd 100644 --- a/Telegram/SourceFiles/ui/text/text.h +++ b/Telegram/SourceFiles/ui/text/text.h @@ -258,15 +258,6 @@ inline TextSelection unshiftSelection(TextSelection selection, const Text &byTex return unshiftSelection(selection, byText.length()); } -void initLinkSets(); -const QSet &validProtocols(); -const QSet &validTopDomains(); -const QRegularExpression &reDomain(); -const QRegularExpression &reMailName(); -const QRegularExpression &reMailStart(); -const QRegularExpression &reHashtag(); -const QRegularExpression &reBotCommand(); - // textcmd QString textcmdSkipBlock(ushort w, ushort h); QString textcmdStartLink(ushort lnkIndex); diff --git a/Telegram/SourceFiles/ui/text/text_entity.cpp b/Telegram/SourceFiles/ui/text/text_entity.cpp index f09656506..be56202bd 100644 --- a/Telegram/SourceFiles/ui/text/text_entity.cpp +++ b/Telegram/SourceFiles/ui/text/text_entity.cpp @@ -23,391 +23,390 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "auth_session.h" #include "lang/lang_tag.h" +namespace TextUtilities { namespace { -const QRegularExpression _reDomain(QString::fromUtf8("(?|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])#[\\w]{2,64}([\\W]|$)"), QRegularExpression::UseUnicodePropertiesOption); -const QRegularExpression _reMention(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])@[A-Za-z_0-9]{1,32}([\\W]|$)"), QRegularExpression::UseUnicodePropertiesOption); -const QRegularExpression _reBotCommand(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])/[A-Za-z_0-9]{1,64}(@[A-Za-z_0-9]{5,32})?([\\W]|$)")); -const QRegularExpression _rePre(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\?\\%\\^\\*\\(\\)\\-\\+=\\x10])(````?)[\\s\\S]+?(````?)([\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\?\\%\\^\\*\\(\\)\\-\\+=\\x10]|$)"), QRegularExpression::UseUnicodePropertiesOption); -const QRegularExpression _reCode(qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\?\\%\\^\\*\\(\\)\\-\\+=\\x10])(`)[^\\n]+?(`)([\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\?\\%\\^\\*\\(\\)\\-\\+=\\x10]|$)"), QRegularExpression::UseUnicodePropertiesOption); -QSet _validProtocols, _validTopDomains; - -} // namespace - -const QRegularExpression &reDomain() { - return _reDomain; +QString ExpressionDomain() { + // Matches any domain name, containing at least one '.', including "file.txt". + return QString::fromUtf8("(?|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])#[\\w]{2,64}([\\W]|$)"); } -const QRegularExpression &reBotCommand() { - return _reBotCommand; +QString ExpressionMention() { + return qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])@[A-Za-z_0-9]{1,32}([\\W]|$)"); } -namespace { - -void regOneProtocol(const QString &protocol) { - _validProtocols.insert(hashCrc32(protocol.constData(), protocol.size() * sizeof(QChar))); +QString ExpressionBotCommand() { + return qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\%\\^\\*\\(\\)\\-\\+=\\x10])/[A-Za-z_0-9]{1,64}(@[A-Za-z_0-9]{5,32})?([\\W]|$)"); } -void regOneTopDomain(const QString &domain) { - _validTopDomains.insert(hashCrc32(domain.constData(), domain.size() * sizeof(QChar))); +QString ExpressionMonoInline() { // pre + return qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\?\\%\\^\\*\\(\\)\\-\\+=\\x10])(````?)[\\s\\S]+?(````?)([\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\?\\%\\^\\*\\(\\)\\-\\+=\\x10]|$)"); } -} // namespace - -const QSet &validProtocols() { - return _validProtocols; -} -const QSet &validTopDomains() { - return _validTopDomains; +QString ExpressionMonoBlock() { // code + return qsl("(^|[\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\?\\%\\^\\*\\(\\)\\-\\+=\\x10])(`)[^\\n]+?(`)([\\s\\.,:;<>|'\"\\[\\]\\{\\}`\\~\\!\\?\\%\\^\\*\\(\\)\\-\\+=\\x10]|$)"); } -void initLinkSets() { - if (!_validProtocols.isEmpty() || !_validTopDomains.isEmpty()) return; - - regOneProtocol(qsl("itmss")); // itunes - regOneProtocol(qsl("http")); - regOneProtocol(qsl("https")); - regOneProtocol(qsl("ftp")); - regOneProtocol(qsl("tg")); // local urls - - regOneTopDomain(qsl("ac")); - regOneTopDomain(qsl("ad")); - regOneTopDomain(qsl("ae")); - regOneTopDomain(qsl("af")); - regOneTopDomain(qsl("ag")); - regOneTopDomain(qsl("ai")); - regOneTopDomain(qsl("al")); - regOneTopDomain(qsl("am")); - regOneTopDomain(qsl("an")); - regOneTopDomain(qsl("ao")); - regOneTopDomain(qsl("aq")); - regOneTopDomain(qsl("ar")); - regOneTopDomain(qsl("as")); - regOneTopDomain(qsl("at")); - regOneTopDomain(qsl("au")); - regOneTopDomain(qsl("aw")); - regOneTopDomain(qsl("ax")); - regOneTopDomain(qsl("az")); - regOneTopDomain(qsl("ba")); - regOneTopDomain(qsl("bb")); - regOneTopDomain(qsl("bd")); - regOneTopDomain(qsl("be")); - regOneTopDomain(qsl("bf")); - regOneTopDomain(qsl("bg")); - regOneTopDomain(qsl("bh")); - regOneTopDomain(qsl("bi")); - regOneTopDomain(qsl("bj")); - regOneTopDomain(qsl("bm")); - regOneTopDomain(qsl("bn")); - regOneTopDomain(qsl("bo")); - regOneTopDomain(qsl("br")); - regOneTopDomain(qsl("bs")); - regOneTopDomain(qsl("bt")); - regOneTopDomain(qsl("bv")); - regOneTopDomain(qsl("bw")); - regOneTopDomain(qsl("by")); - regOneTopDomain(qsl("bz")); - regOneTopDomain(qsl("ca")); - regOneTopDomain(qsl("cc")); - regOneTopDomain(qsl("cd")); - regOneTopDomain(qsl("cf")); - regOneTopDomain(qsl("cg")); - regOneTopDomain(qsl("ch")); - regOneTopDomain(qsl("ci")); - regOneTopDomain(qsl("ck")); - regOneTopDomain(qsl("cl")); - regOneTopDomain(qsl("cm")); - regOneTopDomain(qsl("cn")); - regOneTopDomain(qsl("co")); - regOneTopDomain(qsl("cr")); - regOneTopDomain(qsl("cu")); - regOneTopDomain(qsl("cv")); - regOneTopDomain(qsl("cx")); - regOneTopDomain(qsl("cy")); - regOneTopDomain(qsl("cz")); - regOneTopDomain(qsl("de")); - regOneTopDomain(qsl("dj")); - regOneTopDomain(qsl("dk")); - regOneTopDomain(qsl("dm")); - regOneTopDomain(qsl("do")); - regOneTopDomain(qsl("dz")); - regOneTopDomain(qsl("ec")); - regOneTopDomain(qsl("ee")); - regOneTopDomain(qsl("eg")); - regOneTopDomain(qsl("eh")); - regOneTopDomain(qsl("er")); - regOneTopDomain(qsl("es")); - regOneTopDomain(qsl("et")); - regOneTopDomain(qsl("eu")); - regOneTopDomain(qsl("fi")); - regOneTopDomain(qsl("fj")); - regOneTopDomain(qsl("fk")); - regOneTopDomain(qsl("fm")); - regOneTopDomain(qsl("fo")); - regOneTopDomain(qsl("fr")); - regOneTopDomain(qsl("ga")); - regOneTopDomain(qsl("gd")); - regOneTopDomain(qsl("ge")); - regOneTopDomain(qsl("gf")); - regOneTopDomain(qsl("gg")); - regOneTopDomain(qsl("gh")); - regOneTopDomain(qsl("gi")); - regOneTopDomain(qsl("gl")); - regOneTopDomain(qsl("gm")); - regOneTopDomain(qsl("gn")); - regOneTopDomain(qsl("gp")); - regOneTopDomain(qsl("gq")); - regOneTopDomain(qsl("gr")); - regOneTopDomain(qsl("gs")); - regOneTopDomain(qsl("gt")); - regOneTopDomain(qsl("gu")); - regOneTopDomain(qsl("gw")); - regOneTopDomain(qsl("gy")); - regOneTopDomain(qsl("hk")); - regOneTopDomain(qsl("hm")); - regOneTopDomain(qsl("hn")); - regOneTopDomain(qsl("hr")); - regOneTopDomain(qsl("ht")); - regOneTopDomain(qsl("hu")); - regOneTopDomain(qsl("id")); - regOneTopDomain(qsl("ie")); - regOneTopDomain(qsl("il")); - regOneTopDomain(qsl("im")); - regOneTopDomain(qsl("in")); - regOneTopDomain(qsl("io")); - regOneTopDomain(qsl("iq")); - regOneTopDomain(qsl("ir")); - regOneTopDomain(qsl("is")); - regOneTopDomain(qsl("it")); - regOneTopDomain(qsl("je")); - regOneTopDomain(qsl("jm")); - regOneTopDomain(qsl("jo")); - regOneTopDomain(qsl("jp")); - regOneTopDomain(qsl("ke")); - regOneTopDomain(qsl("kg")); - regOneTopDomain(qsl("kh")); - regOneTopDomain(qsl("ki")); - regOneTopDomain(qsl("km")); - regOneTopDomain(qsl("kn")); - regOneTopDomain(qsl("kp")); - regOneTopDomain(qsl("kr")); - regOneTopDomain(qsl("kw")); - regOneTopDomain(qsl("ky")); - regOneTopDomain(qsl("kz")); - regOneTopDomain(qsl("la")); - regOneTopDomain(qsl("lb")); - regOneTopDomain(qsl("lc")); - regOneTopDomain(qsl("li")); - regOneTopDomain(qsl("lk")); - regOneTopDomain(qsl("lr")); - regOneTopDomain(qsl("ls")); - regOneTopDomain(qsl("lt")); - regOneTopDomain(qsl("lu")); - regOneTopDomain(qsl("lv")); - regOneTopDomain(qsl("ly")); - regOneTopDomain(qsl("ma")); - regOneTopDomain(qsl("mc")); - regOneTopDomain(qsl("md")); - regOneTopDomain(qsl("me")); - regOneTopDomain(qsl("mg")); - regOneTopDomain(qsl("mh")); - regOneTopDomain(qsl("mk")); - regOneTopDomain(qsl("ml")); - regOneTopDomain(qsl("mm")); - regOneTopDomain(qsl("mn")); - regOneTopDomain(qsl("mo")); - regOneTopDomain(qsl("mp")); - regOneTopDomain(qsl("mq")); - regOneTopDomain(qsl("mr")); - regOneTopDomain(qsl("ms")); - regOneTopDomain(qsl("mt")); - regOneTopDomain(qsl("mu")); - regOneTopDomain(qsl("mv")); - regOneTopDomain(qsl("mw")); - regOneTopDomain(qsl("mx")); - regOneTopDomain(qsl("my")); - regOneTopDomain(qsl("mz")); - regOneTopDomain(qsl("na")); - regOneTopDomain(qsl("nc")); - regOneTopDomain(qsl("ne")); - regOneTopDomain(qsl("nf")); - regOneTopDomain(qsl("ng")); - regOneTopDomain(qsl("ni")); - regOneTopDomain(qsl("nl")); - regOneTopDomain(qsl("no")); - regOneTopDomain(qsl("np")); - regOneTopDomain(qsl("nr")); - regOneTopDomain(qsl("nu")); - regOneTopDomain(qsl("nz")); - regOneTopDomain(qsl("om")); - regOneTopDomain(qsl("pa")); - regOneTopDomain(qsl("pe")); - regOneTopDomain(qsl("pf")); - regOneTopDomain(qsl("pg")); - regOneTopDomain(qsl("ph")); - regOneTopDomain(qsl("pk")); - regOneTopDomain(qsl("pl")); - regOneTopDomain(qsl("pm")); - regOneTopDomain(qsl("pn")); - regOneTopDomain(qsl("pr")); - regOneTopDomain(qsl("ps")); - regOneTopDomain(qsl("pt")); - regOneTopDomain(qsl("pw")); - regOneTopDomain(qsl("py")); - regOneTopDomain(qsl("qa")); - regOneTopDomain(qsl("re")); - regOneTopDomain(qsl("ro")); - regOneTopDomain(qsl("ru")); - regOneTopDomain(qsl("rs")); - regOneTopDomain(qsl("rw")); - regOneTopDomain(qsl("sa")); - regOneTopDomain(qsl("sb")); - regOneTopDomain(qsl("sc")); - regOneTopDomain(qsl("sd")); - regOneTopDomain(qsl("se")); - regOneTopDomain(qsl("sg")); - regOneTopDomain(qsl("sh")); - regOneTopDomain(qsl("si")); - regOneTopDomain(qsl("sj")); - regOneTopDomain(qsl("sk")); - regOneTopDomain(qsl("sl")); - regOneTopDomain(qsl("sm")); - regOneTopDomain(qsl("sn")); - regOneTopDomain(qsl("so")); - regOneTopDomain(qsl("sr")); - regOneTopDomain(qsl("ss")); - regOneTopDomain(qsl("st")); - regOneTopDomain(qsl("su")); - regOneTopDomain(qsl("sv")); - regOneTopDomain(qsl("sx")); - regOneTopDomain(qsl("sy")); - regOneTopDomain(qsl("sz")); - regOneTopDomain(qsl("tc")); - regOneTopDomain(qsl("td")); - regOneTopDomain(qsl("tf")); - regOneTopDomain(qsl("tg")); - regOneTopDomain(qsl("th")); - regOneTopDomain(qsl("tj")); - regOneTopDomain(qsl("tk")); - regOneTopDomain(qsl("tl")); - regOneTopDomain(qsl("tm")); - regOneTopDomain(qsl("tn")); - regOneTopDomain(qsl("to")); - regOneTopDomain(qsl("tp")); - regOneTopDomain(qsl("tr")); - regOneTopDomain(qsl("tt")); - regOneTopDomain(qsl("tv")); - regOneTopDomain(qsl("tw")); - regOneTopDomain(qsl("tz")); - regOneTopDomain(qsl("ua")); - regOneTopDomain(qsl("ug")); - regOneTopDomain(qsl("uk")); - regOneTopDomain(qsl("um")); - regOneTopDomain(qsl("us")); - regOneTopDomain(qsl("uy")); - regOneTopDomain(qsl("uz")); - regOneTopDomain(qsl("va")); - regOneTopDomain(qsl("vc")); - regOneTopDomain(qsl("ve")); - regOneTopDomain(qsl("vg")); - regOneTopDomain(qsl("vi")); - regOneTopDomain(qsl("vn")); - regOneTopDomain(qsl("vu")); - regOneTopDomain(qsl("wf")); - regOneTopDomain(qsl("ws")); - regOneTopDomain(qsl("ye")); - regOneTopDomain(qsl("yt")); - regOneTopDomain(qsl("yu")); - regOneTopDomain(qsl("za")); - regOneTopDomain(qsl("zm")); - regOneTopDomain(qsl("zw")); - regOneTopDomain(qsl("arpa")); - regOneTopDomain(qsl("aero")); - regOneTopDomain(qsl("asia")); - regOneTopDomain(qsl("biz")); - regOneTopDomain(qsl("cat")); - regOneTopDomain(qsl("com")); - regOneTopDomain(qsl("coop")); - regOneTopDomain(qsl("info")); - regOneTopDomain(qsl("int")); - regOneTopDomain(qsl("jobs")); - regOneTopDomain(qsl("mobi")); - regOneTopDomain(qsl("museum")); - regOneTopDomain(qsl("name")); - regOneTopDomain(qsl("net")); - regOneTopDomain(qsl("org")); - regOneTopDomain(qsl("post")); - regOneTopDomain(qsl("pro")); - regOneTopDomain(qsl("tel")); - regOneTopDomain(qsl("travel")); - regOneTopDomain(qsl("xxx")); - regOneTopDomain(qsl("edu")); - regOneTopDomain(qsl("gov")); - regOneTopDomain(qsl("mil")); - regOneTopDomain(qsl("local")); - regOneTopDomain(qsl("xn--lgbbat1ad8j")); - regOneTopDomain(qsl("xn--54b7fta0cc")); - regOneTopDomain(qsl("xn--fiqs8s")); - regOneTopDomain(qsl("xn--fiqz9s")); - regOneTopDomain(qsl("xn--wgbh1c")); - regOneTopDomain(qsl("xn--node")); - regOneTopDomain(qsl("xn--j6w193g")); - regOneTopDomain(qsl("xn--h2brj9c")); - regOneTopDomain(qsl("xn--mgbbh1a71e")); - regOneTopDomain(qsl("xn--fpcrj9c3d")); - regOneTopDomain(qsl("xn--gecrj9c")); - regOneTopDomain(qsl("xn--s9brj9c")); - regOneTopDomain(qsl("xn--xkc2dl3a5ee0h")); - regOneTopDomain(qsl("xn--45brj9c")); - regOneTopDomain(qsl("xn--mgba3a4f16a")); - regOneTopDomain(qsl("xn--mgbayh7gpa")); - regOneTopDomain(qsl("xn--80ao21a")); - regOneTopDomain(qsl("xn--mgbx4cd0ab")); - regOneTopDomain(qsl("xn--l1acc")); - regOneTopDomain(qsl("xn--mgbc0a9azcg")); - regOneTopDomain(qsl("xn--mgb9awbf")); - regOneTopDomain(qsl("xn--mgbai9azgqp6j")); - regOneTopDomain(qsl("xn--ygbi2ammx")); - regOneTopDomain(qsl("xn--wgbl6a")); - regOneTopDomain(qsl("xn--p1ai")); - regOneTopDomain(qsl("xn--mgberp4a5d4ar")); - regOneTopDomain(qsl("xn--90a3ac")); - regOneTopDomain(qsl("xn--yfro4i67o")); - regOneTopDomain(qsl("xn--clchc0ea0b2g2a9gcd")); - regOneTopDomain(qsl("xn--3e0b707e")); - regOneTopDomain(qsl("xn--fzc2c9e2c")); - regOneTopDomain(qsl("xn--xkc2al3hye2a")); - regOneTopDomain(qsl("xn--mgbtf8fl")); - regOneTopDomain(qsl("xn--kprw13d")); - regOneTopDomain(qsl("xn--kpry57d")); - regOneTopDomain(qsl("xn--o3cw4h")); - regOneTopDomain(qsl("xn--pgbs0dh")); - regOneTopDomain(qsl("xn--j1amh")); - regOneTopDomain(qsl("xn--mgbaam7a8h")); - regOneTopDomain(qsl("xn--mgb2ddes")); - regOneTopDomain(qsl("xn--ogbpf8fl")); - regOneTopDomain(QString::fromUtf8("\xd1\x80\xd1\x84")); +QRegularExpression CreateRegExp(const QString &expression) { + return QRegularExpression(expression, QRegularExpression::UseUnicodePropertiesOption); +} + +QSet CreateValidProtocols() { + auto result = QSet(); + auto addOne = [&result](const QString &string) { + result.insert(hashCrc32(string.constData(), string.size() * sizeof(QChar))); + }; + addOne(qsl("itmss")); // itunes + addOne(qsl("http")); + addOne(qsl("https")); + addOne(qsl("ftp")); + addOne(qsl("tg")); // local urls + return result; +} + +QSet CreateValidTopDomains() { + auto result = QSet(); + auto addOne = [&result](const QString &string) { + result.insert(hashCrc32(string.constData(), string.size() * sizeof(QChar))); + }; + addOne(qsl("ac")); + addOne(qsl("ad")); + addOne(qsl("ae")); + addOne(qsl("af")); + addOne(qsl("ag")); + addOne(qsl("ai")); + addOne(qsl("al")); + addOne(qsl("am")); + addOne(qsl("an")); + addOne(qsl("ao")); + addOne(qsl("aq")); + addOne(qsl("ar")); + addOne(qsl("as")); + addOne(qsl("at")); + addOne(qsl("au")); + addOne(qsl("aw")); + addOne(qsl("ax")); + addOne(qsl("az")); + addOne(qsl("ba")); + addOne(qsl("bb")); + addOne(qsl("bd")); + addOne(qsl("be")); + addOne(qsl("bf")); + addOne(qsl("bg")); + addOne(qsl("bh")); + addOne(qsl("bi")); + addOne(qsl("bj")); + addOne(qsl("bm")); + addOne(qsl("bn")); + addOne(qsl("bo")); + addOne(qsl("br")); + addOne(qsl("bs")); + addOne(qsl("bt")); + addOne(qsl("bv")); + addOne(qsl("bw")); + addOne(qsl("by")); + addOne(qsl("bz")); + addOne(qsl("ca")); + addOne(qsl("cc")); + addOne(qsl("cd")); + addOne(qsl("cf")); + addOne(qsl("cg")); + addOne(qsl("ch")); + addOne(qsl("ci")); + addOne(qsl("ck")); + addOne(qsl("cl")); + addOne(qsl("cm")); + addOne(qsl("cn")); + addOne(qsl("co")); + addOne(qsl("cr")); + addOne(qsl("cu")); + addOne(qsl("cv")); + addOne(qsl("cx")); + addOne(qsl("cy")); + addOne(qsl("cz")); + addOne(qsl("de")); + addOne(qsl("dj")); + addOne(qsl("dk")); + addOne(qsl("dm")); + addOne(qsl("do")); + addOne(qsl("dz")); + addOne(qsl("ec")); + addOne(qsl("ee")); + addOne(qsl("eg")); + addOne(qsl("eh")); + addOne(qsl("er")); + addOne(qsl("es")); + addOne(qsl("et")); + addOne(qsl("eu")); + addOne(qsl("fi")); + addOne(qsl("fj")); + addOne(qsl("fk")); + addOne(qsl("fm")); + addOne(qsl("fo")); + addOne(qsl("fr")); + addOne(qsl("ga")); + addOne(qsl("gd")); + addOne(qsl("ge")); + addOne(qsl("gf")); + addOne(qsl("gg")); + addOne(qsl("gh")); + addOne(qsl("gi")); + addOne(qsl("gl")); + addOne(qsl("gm")); + addOne(qsl("gn")); + addOne(qsl("gp")); + addOne(qsl("gq")); + addOne(qsl("gr")); + addOne(qsl("gs")); + addOne(qsl("gt")); + addOne(qsl("gu")); + addOne(qsl("gw")); + addOne(qsl("gy")); + addOne(qsl("hk")); + addOne(qsl("hm")); + addOne(qsl("hn")); + addOne(qsl("hr")); + addOne(qsl("ht")); + addOne(qsl("hu")); + addOne(qsl("id")); + addOne(qsl("ie")); + addOne(qsl("il")); + addOne(qsl("im")); + addOne(qsl("in")); + addOne(qsl("io")); + addOne(qsl("iq")); + addOne(qsl("ir")); + addOne(qsl("is")); + addOne(qsl("it")); + addOne(qsl("je")); + addOne(qsl("jm")); + addOne(qsl("jo")); + addOne(qsl("jp")); + addOne(qsl("ke")); + addOne(qsl("kg")); + addOne(qsl("kh")); + addOne(qsl("ki")); + addOne(qsl("km")); + addOne(qsl("kn")); + addOne(qsl("kp")); + addOne(qsl("kr")); + addOne(qsl("kw")); + addOne(qsl("ky")); + addOne(qsl("kz")); + addOne(qsl("la")); + addOne(qsl("lb")); + addOne(qsl("lc")); + addOne(qsl("li")); + addOne(qsl("lk")); + addOne(qsl("lr")); + addOne(qsl("ls")); + addOne(qsl("lt")); + addOne(qsl("lu")); + addOne(qsl("lv")); + addOne(qsl("ly")); + addOne(qsl("ma")); + addOne(qsl("mc")); + addOne(qsl("md")); + addOne(qsl("me")); + addOne(qsl("mg")); + addOne(qsl("mh")); + addOne(qsl("mk")); + addOne(qsl("ml")); + addOne(qsl("mm")); + addOne(qsl("mn")); + addOne(qsl("mo")); + addOne(qsl("mp")); + addOne(qsl("mq")); + addOne(qsl("mr")); + addOne(qsl("ms")); + addOne(qsl("mt")); + addOne(qsl("mu")); + addOne(qsl("mv")); + addOne(qsl("mw")); + addOne(qsl("mx")); + addOne(qsl("my")); + addOne(qsl("mz")); + addOne(qsl("na")); + addOne(qsl("nc")); + addOne(qsl("ne")); + addOne(qsl("nf")); + addOne(qsl("ng")); + addOne(qsl("ni")); + addOne(qsl("nl")); + addOne(qsl("no")); + addOne(qsl("np")); + addOne(qsl("nr")); + addOne(qsl("nu")); + addOne(qsl("nz")); + addOne(qsl("om")); + addOne(qsl("pa")); + addOne(qsl("pe")); + addOne(qsl("pf")); + addOne(qsl("pg")); + addOne(qsl("ph")); + addOne(qsl("pk")); + addOne(qsl("pl")); + addOne(qsl("pm")); + addOne(qsl("pn")); + addOne(qsl("pr")); + addOne(qsl("ps")); + addOne(qsl("pt")); + addOne(qsl("pw")); + addOne(qsl("py")); + addOne(qsl("qa")); + addOne(qsl("re")); + addOne(qsl("ro")); + addOne(qsl("ru")); + addOne(qsl("rs")); + addOne(qsl("rw")); + addOne(qsl("sa")); + addOne(qsl("sb")); + addOne(qsl("sc")); + addOne(qsl("sd")); + addOne(qsl("se")); + addOne(qsl("sg")); + addOne(qsl("sh")); + addOne(qsl("si")); + addOne(qsl("sj")); + addOne(qsl("sk")); + addOne(qsl("sl")); + addOne(qsl("sm")); + addOne(qsl("sn")); + addOne(qsl("so")); + addOne(qsl("sr")); + addOne(qsl("ss")); + addOne(qsl("st")); + addOne(qsl("su")); + addOne(qsl("sv")); + addOne(qsl("sx")); + addOne(qsl("sy")); + addOne(qsl("sz")); + addOne(qsl("tc")); + addOne(qsl("td")); + addOne(qsl("tf")); + addOne(qsl("tg")); + addOne(qsl("th")); + addOne(qsl("tj")); + addOne(qsl("tk")); + addOne(qsl("tl")); + addOne(qsl("tm")); + addOne(qsl("tn")); + addOne(qsl("to")); + addOne(qsl("tp")); + addOne(qsl("tr")); + addOne(qsl("tt")); + addOne(qsl("tv")); + addOne(qsl("tw")); + addOne(qsl("tz")); + addOne(qsl("ua")); + addOne(qsl("ug")); + addOne(qsl("uk")); + addOne(qsl("um")); + addOne(qsl("us")); + addOne(qsl("uy")); + addOne(qsl("uz")); + addOne(qsl("va")); + addOne(qsl("vc")); + addOne(qsl("ve")); + addOne(qsl("vg")); + addOne(qsl("vi")); + addOne(qsl("vn")); + addOne(qsl("vu")); + addOne(qsl("wf")); + addOne(qsl("ws")); + addOne(qsl("ye")); + addOne(qsl("yt")); + addOne(qsl("yu")); + addOne(qsl("za")); + addOne(qsl("zm")); + addOne(qsl("zw")); + addOne(qsl("arpa")); + addOne(qsl("aero")); + addOne(qsl("asia")); + addOne(qsl("biz")); + addOne(qsl("cat")); + addOne(qsl("com")); + addOne(qsl("coop")); + addOne(qsl("info")); + addOne(qsl("int")); + addOne(qsl("jobs")); + addOne(qsl("mobi")); + addOne(qsl("museum")); + addOne(qsl("name")); + addOne(qsl("net")); + addOne(qsl("org")); + addOne(qsl("post")); + addOne(qsl("pro")); + addOne(qsl("tel")); + addOne(qsl("travel")); + addOne(qsl("xxx")); + addOne(qsl("edu")); + addOne(qsl("gov")); + addOne(qsl("mil")); + addOne(qsl("local")); + addOne(qsl("xn--lgbbat1ad8j")); + addOne(qsl("xn--54b7fta0cc")); + addOne(qsl("xn--fiqs8s")); + addOne(qsl("xn--fiqz9s")); + addOne(qsl("xn--wgbh1c")); + addOne(qsl("xn--node")); + addOne(qsl("xn--j6w193g")); + addOne(qsl("xn--h2brj9c")); + addOne(qsl("xn--mgbbh1a71e")); + addOne(qsl("xn--fpcrj9c3d")); + addOne(qsl("xn--gecrj9c")); + addOne(qsl("xn--s9brj9c")); + addOne(qsl("xn--xkc2dl3a5ee0h")); + addOne(qsl("xn--45brj9c")); + addOne(qsl("xn--mgba3a4f16a")); + addOne(qsl("xn--mgbayh7gpa")); + addOne(qsl("xn--80ao21a")); + addOne(qsl("xn--mgbx4cd0ab")); + addOne(qsl("xn--l1acc")); + addOne(qsl("xn--mgbc0a9azcg")); + addOne(qsl("xn--mgb9awbf")); + addOne(qsl("xn--mgbai9azgqp6j")); + addOne(qsl("xn--ygbi2ammx")); + addOne(qsl("xn--wgbl6a")); + addOne(qsl("xn--p1ai")); + addOne(qsl("xn--mgberp4a5d4ar")); + addOne(qsl("xn--90a3ac")); + addOne(qsl("xn--yfro4i67o")); + addOne(qsl("xn--clchc0ea0b2g2a9gcd")); + addOne(qsl("xn--3e0b707e")); + addOne(qsl("xn--fzc2c9e2c")); + addOne(qsl("xn--xkc2al3hye2a")); + addOne(qsl("xn--mgbtf8fl")); + addOne(qsl("xn--kprw13d")); + addOne(qsl("xn--kpry57d")); + addOne(qsl("xn--o3cw4h")); + addOne(qsl("xn--pgbs0dh")); + addOne(qsl("xn--j1amh")); + addOne(qsl("xn--mgbaam7a8h")); + addOne(qsl("xn--mgb2ddes")); + addOne(qsl("xn--ogbpf8fl")); + addOne(QString::fromUtf8("\xd1\x80\xd1\x84")); + return result; } -namespace { // accent char list taken from https://github.com/aristus/accent-folding -inline QChar chNoAccent(int32 code) { +inline QChar RemoveOneAccent(uint32 code) { switch (code) { case 7834: return QChar(97); case 193: return QChar(97); @@ -1109,11 +1108,67 @@ inline QChar chNoAccent(int32 code) { } return QChar(0); } + +const QRegularExpression &RegExpWordSplit() { + static const auto result = QRegularExpression (qsl("[\\@\\s\\-\\+\\(\\)\\[\\]\\{\\}\\<\\>\\,\\.\\:\\!\\_\\;\\\"\\'\\x0]")); + return result; } -QString textClean(const QString &text) { - QString result(text); - for (const QChar *s = text.unicode(), *ch = s, *e = text.unicode() + text.size(); ch != e; ++ch) { +} // namespace + +const QRegularExpression &RegExpDomain() { + static const auto result = CreateRegExp(ExpressionDomain()); + return result; +} + +const QRegularExpression &RegExpDomainExplicit() { + static const auto result = CreateRegExp(ExpressionDomainExplicit()); + return result; +} + +const QRegularExpression &RegExpMailNameAtEnd() { + static const auto result = CreateRegExp(ExpressionMailNameAtEnd()); + return result; +} + +const QRegularExpression &RegExpHashtag() { + static const auto result = CreateRegExp(ExpressionHashtag()); + return result; +} + +const QRegularExpression &RegExpMention() { + static const auto result = CreateRegExp(ExpressionMention()); + return result; +} + +const QRegularExpression &RegExpBotCommand() { + static const auto result = CreateRegExp(ExpressionBotCommand()); + return result; +} + +const QRegularExpression &RegExpMonoInline() { + static const auto result = CreateRegExp(ExpressionMonoInline()); + return result; +} + +const QRegularExpression &RegExpMonoBlock() { + static const auto result = CreateRegExp(ExpressionMonoBlock()); + return result; +} + +bool IsValidProtocol(const QString &protocol) { + static const auto list = CreateValidProtocols(); + return list.contains(hashCrc32(protocol.constData(), protocol.size() * sizeof(QChar))); +} + +bool IsValidTopDomain(const QString &protocol) { + static const auto list = CreateValidTopDomains(); + return list.contains(hashCrc32(protocol.constData(), protocol.size() * sizeof(QChar))); +} + +QString Clean(const QString &text) { + auto result = text; + for (auto s = text.unicode(), ch = s, e = text.unicode() + text.size(); ch != e; ++ch) { if (*ch == TextCommand) { result[int(ch - s)] = QChar::Space; } @@ -1121,10 +1176,10 @@ QString textClean(const QString &text) { return result; } -QString textRichPrepare(const QString &text) { +QString EscapeForRichParsing(const QString &text) { QString result; result.reserve(text.size()); - const QChar *s = text.constData(), *ch = s; + 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); @@ -1143,33 +1198,34 @@ QString textRichPrepare(const QString &text) { return result; } -QString textOneLine(const QString &text, bool trim, bool rich) { - QString result(text); - const QChar *s = text.unicode(), *ch = s, *e = text.unicode() + text.size(); - if (trim) { - while (s < e && chIsTrimmed(*s)) { - ++s; - } - while (s < e && chIsTrimmed(*(e - 1))) { - --e; - } - if (e - s != text.size()) { - result = text.mid(s - text.unicode(), e - s); - } +QString SingleLine(const QString &text) { + auto result = text; + auto s = text.unicode(), ch = s, e = text.unicode() + text.size(); + + // Trim. + while (s < e && chIsTrimmed(*s)) { + ++s; } - for (const QChar *ch = s; ch != e; ++ch) { - if (chIsNewline(*ch)) { + while (s < e && chIsTrimmed(*(e - 1))) { + --e; + } + if (e - s != text.size()) { + result = text.mid(s - text.unicode(), e - s); + } + + for (auto ch = s; ch != e; ++ch) { + if (chIsNewline(*ch) || *ch == TextCommand) { result[int(ch - s)] = QChar::Space; } } return result; } -QString textAccentFold(const QString &text) { - QString result(text); - bool copying = false; - int32 i = 0; - for (const QChar *s = text.unicode(), *ch = s, *e = text.unicode() + text.size(); ch != e; ++ch, ++i) { +QString RemoveAccents(const QString &text) { + auto result = text; + auto copying = false; + auto i = 0; + for (auto s = text.unicode(), ch = s, e = text.unicode() + text.size(); ch != e; ++ch, ++i) { if (ch->unicode() < 128) { if (copying) result[i] = *ch; continue; @@ -1180,7 +1236,7 @@ QString textAccentFold(const QString &text) { continue; } if (ch->isHighSurrogate() && ch + 1 < e && (ch + 1)->isLowSurrogate()) { - QChar noAccent = chNoAccent(QChar::surrogateToUcs4(*ch, *(ch + 1))); + auto noAccent = RemoveOneAccent(QChar::surrogateToUcs4(*ch, *(ch + 1))); if (noAccent.unicode() > 0) { copying = true; result[i] = noAccent; @@ -1190,7 +1246,7 @@ QString textAccentFold(const QString &text) { if (copying) result[i] = *ch; } } else { - QChar noAccent = chNoAccent(ch->unicode()); + auto noAccent = RemoveOneAccent(ch->unicode()); if (noAccent.unicode() > 0 && noAccent != *ch) { result[i] = noAccent; } else if (copying) { @@ -1201,25 +1257,38 @@ QString textAccentFold(const QString &text) { return (i < result.size()) ? result.mid(0, i) : result; } -QString textSearchKey(const QString &text) { - return textAccentFold(text.trimmed().toLower()); +QStringList PrepareSearchWords(const QString &query, const QRegularExpression *SplitterOverride) { + auto clean = RemoveAccents(query.trimmed().toLower()); + auto result = QStringList(); + if (!clean.isEmpty()) { + auto list = clean.split(SplitterOverride ? *SplitterOverride : RegExpWordSplit(), QString::SkipEmptyParts); + auto size = list.size(); + result.reserve(list.size()); + for_const (auto &word, list) { + auto trimmed = word.trimmed(); + if (!trimmed.isEmpty()) { + result.push_back(trimmed); + } + } + } + return result; } -bool textSplit(QString &sendingText, EntitiesInText &sendingEntities, QString &leftText, EntitiesInText &leftEntities, int32 limit) { - if (leftText.isEmpty() || !limit) return false; +bool CutPart(TextWithEntities &sending, TextWithEntities &left, int32 limit) { + if (left.text.isEmpty() || !limit) return false; - int32 currentEntity = 0, goodEntity = currentEntity, entityCount = leftEntities.size(); + int32 currentEntity = 0, goodEntity = currentEntity, entityCount = left.entities.size(); bool goodInEntity = false, goodCanBreakEntity = false; int32 s = 0, half = limit / 2, goodLevel = 0; - for (const QChar *start = leftText.constData(), *ch = start, *end = leftText.constEnd(), *good = ch; ch != end; ++ch, ++s) { - while (currentEntity < entityCount && ch >= start + leftEntities.at(currentEntity).offset() + leftEntities.at(currentEntity).length()) { + for (const QChar *start = left.text.constData(), *ch = start, *end = left.text.constEnd(), *good = ch; ch != end; ++ch, ++s) { + while (currentEntity < entityCount && ch >= start + left.entities[currentEntity].offset() + left.entities[currentEntity].length()) { ++currentEntity; } if (s > half) { - bool inEntity = (currentEntity < entityCount) && (ch > start + leftEntities.at(currentEntity).offset()) && (ch < start + leftEntities.at(currentEntity).offset() + leftEntities.at(currentEntity).length()); - EntityInTextType entityType = (currentEntity < entityCount) ? leftEntities.at(currentEntity).type() : EntityInTextInvalid; + bool inEntity = (currentEntity < entityCount) && (ch > start + left.entities[currentEntity].offset()) && (ch < start + left.entities[currentEntity].offset() + left.entities[currentEntity].length()); + EntityInTextType entityType = (currentEntity < entityCount) ? left.entities[currentEntity].type() : EntityInTextInvalid; bool canBreakEntity = (entityType == EntityInTextPre || entityType == EntityInTextCode); int32 noEntityLevel = inEntity ? 0 : 1; @@ -1246,9 +1315,9 @@ bool textSplit(QString &sendingText, EntitiesInText &sendingEntities, QString &l } } else if (ch + 1 < end && chIsNewline(*(ch + 1))) { markGoodAsLevel(15); - } else if (currentEntity < entityCount && ch + 1 == start + leftEntities.at(currentEntity).offset() && leftEntities.at(currentEntity).type() == EntityInTextPre) { + } else if (currentEntity < entityCount && ch + 1 == start + left.entities[currentEntity].offset() && left.entities[currentEntity].type() == EntityInTextPre) { markGoodAsLevel(14); - } else if (currentEntity > 0 && ch == start + leftEntities.at(currentEntity - 1).offset() + leftEntities.at(currentEntity - 1).length() && leftEntities.at(currentEntity - 1).type() == EntityInTextPre) { + } else if (currentEntity > 0 && ch == start + left.entities[currentEntity - 1].offset() + left.entities[currentEntity - 1].length() && left.entities[currentEntity - 1].type() == EntityInTextPre) { markGoodAsLevel(14); } else { markGoodAsLevel(13); @@ -1283,34 +1352,34 @@ bool textSplit(QString &sendingText, EntitiesInText &sendingEntities, QString &l ++ch; } if (s >= limit) { - sendingText = leftText.mid(0, good - start); - leftText = leftText.mid(good - start); + sending.text = left.text.mid(0, good - start); + left.text = left.text.mid(good - start); if (goodInEntity) { if (goodCanBreakEntity) { - sendingEntities = leftEntities.mid(0, goodEntity + 1); - sendingEntities.back().updateTextEnd(good - start); - leftEntities = leftEntities.mid(goodEntity); - for (auto &entity : leftEntities) { + sending.entities = left.entities.mid(0, goodEntity + 1); + sending.entities.back().updateTextEnd(good - start); + left.entities = left.entities.mid(goodEntity); + for (auto &entity : left.entities) { entity.shiftLeft(good - start); } } else { - sendingEntities = leftEntities.mid(0, goodEntity); - leftEntities = leftEntities.mid(goodEntity + 1); + sending.entities = left.entities.mid(0, goodEntity); + left.entities = left.entities.mid(goodEntity + 1); } } else { - sendingEntities = leftEntities.mid(0, goodEntity); - leftEntities = leftEntities.mid(goodEntity); - for (auto &entity : leftEntities) { + sending.entities = left.entities.mid(0, goodEntity); + left.entities = left.entities.mid(goodEntity); + for (auto &entity : left.entities) { entity.shiftLeft(good - start); } } return true; } } - sendingText = leftText; - leftText = QString(); - sendingEntities = leftEntities; - leftEntities = EntitiesInText(); + sending.text = left.text; + left.text = QString(); + sending.entities = left.entities; + left.entities = EntitiesInText(); return true; } @@ -1355,33 +1424,35 @@ bool checkTagStartInCommand(const QChar *start, int32 len, int32 tagStart, int32 return inCommand; } -EntitiesInText entitiesFromMTP(const QVector &entities) { - EntitiesInText result; +EntitiesInText EntitiesFromMTP(const QVector &entities) { + auto result = EntitiesInText(); if (!entities.isEmpty()) { result.reserve(entities.size()); - for_const (const auto &entity, entities) { + for_const (auto &entity, entities) { switch (entity.type()) { - case mtpc_messageEntityUrl: { const auto &d(entity.c_messageEntityUrl()); result.push_back(EntityInText(EntityInTextUrl, d.voffset.v, d.vlength.v)); } break; - case mtpc_messageEntityTextUrl: { const auto &d(entity.c_messageEntityTextUrl()); result.push_back(EntityInText(EntityInTextCustomUrl, d.voffset.v, d.vlength.v, textClean(qs(d.vurl)))); } break; - case mtpc_messageEntityEmail: { const auto &d(entity.c_messageEntityEmail()); result.push_back(EntityInText(EntityInTextEmail, d.voffset.v, d.vlength.v)); } break; - case mtpc_messageEntityHashtag: { const auto &d(entity.c_messageEntityHashtag()); result.push_back(EntityInText(EntityInTextHashtag, d.voffset.v, d.vlength.v)); } break; - case mtpc_messageEntityMention: { const auto &d(entity.c_messageEntityMention()); result.push_back(EntityInText(EntityInTextMention, d.voffset.v, d.vlength.v)); } break; + case mtpc_messageEntityUrl: { auto &d = entity.c_messageEntityUrl(); result.push_back(EntityInText(EntityInTextUrl, d.voffset.v, d.vlength.v)); } break; + case mtpc_messageEntityTextUrl: { auto &d = entity.c_messageEntityTextUrl(); result.push_back(EntityInText(EntityInTextCustomUrl, d.voffset.v, d.vlength.v, Clean(qs(d.vurl)))); } break; + case mtpc_messageEntityEmail: { auto &d = entity.c_messageEntityEmail(); result.push_back(EntityInText(EntityInTextEmail, d.voffset.v, d.vlength.v)); } break; + case mtpc_messageEntityHashtag: { auto &d = entity.c_messageEntityHashtag(); result.push_back(EntityInText(EntityInTextHashtag, d.voffset.v, d.vlength.v)); } break; + case mtpc_messageEntityMention: { auto &d = entity.c_messageEntityMention(); result.push_back(EntityInText(EntityInTextMention, d.voffset.v, d.vlength.v)); } break; case mtpc_messageEntityMentionName: { auto &d = entity.c_messageEntityMentionName(); - auto data = QString::number(d.vuser_id.v); - if (auto user = App::userLoaded(peerFromUser(d.vuser_id))) { - data += '.' + QString::number(user->access); - } - result.push_back(EntityInText(EntityInTextMentionName, d.voffset.v, d.vlength.v, data)); + auto data = [&d] { + if (auto user = App::userLoaded(peerFromUser(d.vuser_id))) { + return MentionNameDataFromFields({ d.vuser_id.v, user->access }); + } + return MentionNameDataFromFields(d.vuser_id.v); + }; + result.push_back(EntityInText(EntityInTextMentionName, d.voffset.v, d.vlength.v, data())); } break; case mtpc_inputMessageEntityMentionName: { auto &d = entity.c_inputMessageEntityMentionName(); auto data = ([&d]() -> QString { if (d.vuser_id.type() == mtpc_inputUserSelf) { - return QString::number(AuthSession::CurrentUserId()); + return MentionNameDataFromFields(AuthSession::CurrentUserId()); } else if (d.vuser_id.type() == mtpc_inputUser) { - const auto &user(d.vuser_id.c_inputUser()); - return QString::number(user.vuser_id.v) + '.' + QString::number(user.vaccess_hash.v); + auto &user = d.vuser_id.c_inputUser(); + return MentionNameDataFromFields({ user.vuser_id.v, user.vaccess_hash.v }); } return QString(); })(); @@ -1389,23 +1460,23 @@ EntitiesInText entitiesFromMTP(const QVector &entities) { result.push_back(EntityInText(EntityInTextMentionName, d.voffset.v, d.vlength.v, data)); } } break; - case mtpc_messageEntityBotCommand: { const auto &d(entity.c_messageEntityBotCommand()); result.push_back(EntityInText(EntityInTextBotCommand, d.voffset.v, d.vlength.v)); } break; - case mtpc_messageEntityBold: { const auto &d(entity.c_messageEntityBold()); result.push_back(EntityInText(EntityInTextBold, d.voffset.v, d.vlength.v)); } break; - case mtpc_messageEntityItalic: { const auto &d(entity.c_messageEntityItalic()); result.push_back(EntityInText(EntityInTextItalic, d.voffset.v, d.vlength.v)); } break; - case mtpc_messageEntityCode: { const auto &d(entity.c_messageEntityCode()); result.push_back(EntityInText(EntityInTextCode, d.voffset.v, d.vlength.v)); } break; - case mtpc_messageEntityPre: { const auto &d(entity.c_messageEntityPre()); result.push_back(EntityInText(EntityInTextPre, d.voffset.v, d.vlength.v, textClean(qs(d.vlanguage)))); } break; + case mtpc_messageEntityBotCommand: { auto &d = entity.c_messageEntityBotCommand(); result.push_back(EntityInText(EntityInTextBotCommand, d.voffset.v, d.vlength.v)); } break; + case mtpc_messageEntityBold: { auto &d = entity.c_messageEntityBold(); result.push_back(EntityInText(EntityInTextBold, d.voffset.v, d.vlength.v)); } break; + case mtpc_messageEntityItalic: { auto &d = entity.c_messageEntityItalic(); result.push_back(EntityInText(EntityInTextItalic, d.voffset.v, d.vlength.v)); } break; + case mtpc_messageEntityCode: { auto &d = entity.c_messageEntityCode(); result.push_back(EntityInText(EntityInTextCode, d.voffset.v, d.vlength.v)); } break; + case mtpc_messageEntityPre: { auto &d = entity.c_messageEntityPre(); result.push_back(EntityInText(EntityInTextPre, d.voffset.v, d.vlength.v, Clean(qs(d.vlanguage)))); } break; } } } return result; } -MTPVector linksToMTP(const EntitiesInText &links, bool sending) { +MTPVector EntitiesToMTP(const EntitiesInText &links, ConvertOption option) { auto v = QVector(); v.reserve(links.size()); for_const (auto &link, links) { if (link.length() <= 0) continue; - if (sending + if (option == ConvertOption::SkipLocal && link.type() != EntityInTextCode && link.type() != EntityInTextPre && link.type() != EntityInTextMentionName) { @@ -1421,13 +1492,11 @@ MTPVector linksToMTP(const EntitiesInText &links, bool sending case EntityInTextMention: v.push_back(MTP_messageEntityMention(offset, length)); break; case EntityInTextMentionName: { auto inputUser = ([](const QString &data) -> MTPInputUser { - UserId userId = 0; - uint64 accessHash = 0; - if (mentionNameToFields(data, &userId, &accessHash)) { - if (userId == AuthSession::CurrentUserId()) { - return MTP_inputUserSelf(); - } - return MTP_inputUser(MTP_int(userId), MTP_long(accessHash)); + auto fields = MentionNameDataToFields(data); + if (fields.userId == AuthSession::CurrentUserId()) { + return MTP_inputUserSelf(); + } else if (fields.userId) { + return MTP_inputUser(MTP_int(fields.userId), MTP_long(fields.accessHash)); } return MTP_inputUserEmpty(); })(link.data()); @@ -1446,8 +1515,8 @@ MTPVector linksToMTP(const EntitiesInText &links, bool sending } // Some code is duplicated in flattextarea.cpp! -void textParseEntities(QString &text, int32 flags, EntitiesInText *inOutEntities, bool rich) { - EntitiesInText result; +void ParseEntities(TextWithEntities &result, int32 flags, bool rich) { + auto newEntities = EntitiesInText(); bool withHashtags = (flags & TextParseHashtags); bool withMentions = (flags & TextParseMentions); @@ -1455,14 +1524,14 @@ void textParseEntities(QString &text, int32 flags, EntitiesInText *inOutEntities bool withMono = (flags & TextParseMono); if (withMono) { // parse mono entities (code and pre) - int existingEntityIndex = 0, existingEntitiesCount = inOutEntities->size(); + int existingEntityIndex = 0, existingEntitiesCount = result.entities.size(); int existingEntityShiftLeft = 0; QString newText; - int32 offset = 0, matchOffset = offset, len = text.size(), commandOffset = rich ? 0 : len; + int32 offset = 0, matchOffset = offset, len = result.text.size(), commandOffset = rich ? 0 : len; bool inLink = false, commandIsLink = false; - const QChar *start = text.constData(); + const QChar *start = result.text.constData(); for (; matchOffset < len;) { if (commandOffset <= matchOffset) { for (commandOffset = matchOffset; commandOffset < len; ++commandOffset) { @@ -1477,8 +1546,8 @@ void textParseEntities(QString &text, int32 flags, EntitiesInText *inOutEntities commandIsLink = false; } } - auto mPre = _rePre.match(text, matchOffset); - auto mCode = _reCode.match(text, matchOffset); + auto mPre = RegExpMonoInline().match(result.text, matchOffset); + auto mCode = RegExpMonoBlock().match(result.text, matchOffset); if (!mPre.hasMatch() && !mCode.hasMatch()) break; int preStart = mPre.hasMatch() ? mPre.capturedStart() : INT_MAX, @@ -1525,7 +1594,7 @@ void textParseEntities(QString &text, int32 flags, EntitiesInText *inOutEntities // Check if start or end sequences intersect any existing entity. int intersectedEntityEnd = 0; - for_const (auto &entity, *inOutEntities) { + for_const (auto &entity, result.entities) { if (qMin(innerStart, entity.offset() + entity.length()) > qMax(outerStart, entity.offset()) || qMin(outerEnd, entity.offset() + entity.length()) > qMax(innerEnd, entity.offset())) { intersectedEntityEnd = entity.offset() + entity.length(); @@ -1537,7 +1606,7 @@ void textParseEntities(QString &text, int32 flags, EntitiesInText *inOutEntities continue; } - if (newText.isEmpty()) newText.reserve(text.size()); + if (newText.isEmpty()) newText.reserve(result.text.size()); if (pre) { while (outerStart > 0 && chIsSpace(*(start + outerStart - 1), rich) && !chIsNewline(*(start + outerStart - 1))) { --outerStart; @@ -1568,22 +1637,22 @@ void textParseEntities(QString &text, int32 flags, EntitiesInText *inOutEntities addNewlineAfter = (outerEnd < len && !chIsNewline(*(start + outerEnd))); } - for (; existingEntityIndex < existingEntitiesCount && inOutEntities->at(existingEntityIndex).offset() < innerStart; ++existingEntityIndex) { - auto &entity = inOutEntities->at(existingEntityIndex); - result.push_back(entity); - result.back().shiftLeft(existingEntityShiftLeft); + for (; existingEntityIndex < existingEntitiesCount && result.entities[existingEntityIndex].offset() < innerStart; ++existingEntityIndex) { + auto &entity = result.entities[existingEntityIndex]; + newEntities.push_back(entity); + newEntities.back().shiftLeft(existingEntityShiftLeft); } if (outerStart > offset) newText.append(start + offset, outerStart - offset); if (addNewlineBefore) newText.append('\n'); existingEntityShiftLeft += (innerStart - outerStart) - (addNewlineBefore ? 1 : 0); int entityStart = newText.size(), entityLength = innerEnd - innerStart; - result.push_back(EntityInText(pre ? EntityInTextPre : EntityInTextCode, entityStart, entityLength)); + newEntities.push_back(EntityInText(pre ? EntityInTextPre : EntityInTextCode, entityStart, entityLength)); - for (; existingEntityIndex < existingEntitiesCount && inOutEntities->at(existingEntityIndex).offset() <= innerEnd; ++existingEntityIndex) { - auto &entity = inOutEntities->at(existingEntityIndex); - result.push_back(entity); - result.back().shiftLeft(existingEntityShiftLeft); + for (; existingEntityIndex < existingEntitiesCount && result.entities[existingEntityIndex].offset() <= innerEnd; ++existingEntityIndex) { + auto &entity = result.entities[existingEntityIndex]; + newEntities.push_back(entity); + newEntities.back().shiftLeft(existingEntityShiftLeft); } newText.append(start + innerStart, entityLength); if (addNewlineAfter) newText.append('\n'); @@ -1593,26 +1662,25 @@ void textParseEntities(QString &text, int32 flags, EntitiesInText *inOutEntities } if (!newText.isEmpty()) { newText.append(start + offset, len - offset); - text = newText; + result.text = newText; } - if (!result.isEmpty()) { + if (!newEntities.isEmpty()) { for (; existingEntityIndex < existingEntitiesCount; ++existingEntityIndex) { - auto &entity = inOutEntities->at(existingEntityIndex); - result.push_back(entity); - result.back().shiftLeft(existingEntityShiftLeft); + auto &entity = result.entities[existingEntityIndex]; + newEntities.push_back(entity); + newEntities.back().shiftLeft(existingEntityShiftLeft); } - *inOutEntities = result; - result = EntitiesInText(); + result.entities = newEntities; + newEntities = EntitiesInText(); } } - int existingEntityIndex = 0, existingEntitiesCount = inOutEntities->size(); + int existingEntityIndex = 0, existingEntitiesCount = result.entities.size(); int existingEntityEnd = 0; - initLinkSets(); - int32 len = text.size(), commandOffset = rich ? 0 : len; + int32 len = result.text.size(), commandOffset = rich ? 0 : len; bool inLink = false, commandIsLink = false; - const QChar *start = text.constData(), *end = start + text.size(); + const QChar *start = result.text.constData(), *end = start + result.text.size(); for (int32 offset = 0, matchOffset = offset, mentionSkip = 0; offset < len;) { if (commandOffset <= offset) { for (commandOffset = offset; commandOffset < len; ++commandOffset) { @@ -1623,11 +1691,11 @@ void textParseEntities(QString &text, int32 flags, EntitiesInText *inOutEntities } } } - auto mDomain = _reDomain.match(text, matchOffset); - auto mExplicitDomain = _reExplicitDomain.match(text, matchOffset); - auto mHashtag = withHashtags ? _reHashtag.match(text, matchOffset) : QRegularExpressionMatch(); - auto mMention = withMentions ? _reMention.match(text, qMax(mentionSkip, matchOffset)) : QRegularExpressionMatch(); - auto mBotCommand = withBotCommands ? _reBotCommand.match(text, matchOffset) : QRegularExpressionMatch(); + auto mDomain = RegExpDomain().match(result.text, matchOffset); + auto mExplicitDomain = RegExpDomainExplicit().match(result.text, matchOffset); + auto mHashtag = withHashtags ? RegExpHashtag().match(result.text, matchOffset) : QRegularExpressionMatch(); + auto mMention = withMentions ? RegExpMention().match(result.text, qMax(mentionSkip, matchOffset)) : QRegularExpressionMatch(); + auto mBotCommand = withBotCommands ? RegExpBotCommand().match(result.text, matchOffset) : QRegularExpressionMatch(); EntityInTextType lnkType = EntityInTextUrl; int32 lnkStart = 0, lnkLength = 0; @@ -1658,7 +1726,7 @@ void textParseEntities(QString &text, int32 flags, EntitiesInText *inOutEntities } if (!(start + mentionStart + 1)->isLetter() || !(start + mentionEnd - 1)->isLetterOrNumber()) { mentionSkip = mentionEnd; - mMention = _reMention.match(text, qMax(mentionSkip, matchOffset)); + mMention = RegExpMention().match(result.text, qMax(mentionSkip, matchOffset)); if (mMention.hasMatch()) { mentionStart = mMention.capturedStart(); mentionEnd = mMention.capturedEnd(); @@ -1718,23 +1786,22 @@ void textParseEntities(QString &text, int32 flags, EntitiesInText *inOutEntities lnkStart = botCommandStart; lnkLength = botCommandEnd - botCommandStart; } else { - bool inCommand = checkTagStartInCommand(start, len, domainStart, commandOffset, commandIsLink, inLink); + auto inCommand = checkTagStartInCommand(start, len, domainStart, commandOffset, commandIsLink, inLink); if (inCommand || inLink) { offset = matchOffset = commandOffset; 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 = !protocol.isEmpty() || _validTopDomains.contains(hashCrc32(topDomain.constData(), topDomain.size() * sizeof(QChar))); + auto protocol = mDomain.captured(1).toLower(); + auto topDomain = mDomain.captured(3).toLower(); + auto isProtocolValid = protocol.isEmpty() || IsValidProtocol(protocol); + auto isTopDomainValid = !protocol.isEmpty() || IsValidTopDomain(topDomain); if (protocol.isEmpty() && domainStart > offset + 1 && *(start + domainStart - 1) == QChar('@')) { - QString forMailName = text.mid(offset, domainStart - offset - 1); - QRegularExpressionMatch mMailName = _reMailName.match(forMailName); + auto forMailName = result.text.mid(offset, domainStart - offset - 1); + auto mMailName = RegExpMailNameAtEnd().match(forMailName); if (mMailName.hasMatch()) { - int32 mailStart = offset + mMailName.capturedStart(); + auto mailStart = offset + mMailName.capturedStart(); if (mailStart < offset) { mailStart = offset; } @@ -1786,36 +1853,36 @@ void textParseEntities(QString &text, int32 flags, EntitiesInText *inOutEntities lnkLength = (p - start) - lnkStart; } } - for (; existingEntityIndex < existingEntitiesCount && inOutEntities->at(existingEntityIndex).offset() <= lnkStart; ++existingEntityIndex) { - auto &entity = inOutEntities->at(existingEntityIndex); + for (; existingEntityIndex < existingEntitiesCount && result.entities[existingEntityIndex].offset() <= lnkStart; ++existingEntityIndex) { + auto &entity = result.entities[existingEntityIndex]; accumulate_max(existingEntityEnd, entity.offset() + entity.length()); - result.push_back(entity); + newEntities.push_back(entity); } if (lnkStart >= existingEntityEnd) { - inOutEntities->push_back(EntityInText(lnkType, lnkStart, lnkLength)); + result.entities.push_back(EntityInText(lnkType, lnkStart, lnkLength)); } offset = matchOffset = lnkStart + lnkLength; } - if (!result.isEmpty()) { + if (!newEntities.isEmpty()) { for (; existingEntityIndex < existingEntitiesCount; ++existingEntityIndex) { - auto &entity = inOutEntities->at(existingEntityIndex); - result.push_back(entity); + auto &entity = result.entities[existingEntityIndex]; + newEntities.push_back(entity); } - *inOutEntities = result; + result.entities = newEntities; } } -QString textApplyEntities(const QString &text, const EntitiesInText &entities) { - if (entities.isEmpty()) return text; +QString ApplyEntities(const TextWithEntities &text) { + if (text.entities.isEmpty()) return text.text; QMultiMap closingTags; QString code(qsl("`")), pre(qsl("```")); QString result; - int32 size = text.size(); - const QChar *b = text.constData(), *already = b, *e = b + size; - auto entity = entities.cbegin(), end = entities.cend(); + int32 size = text.text.size(); + const QChar *b = text.text.constData(), *already = b, *e = b + size; + auto entity = text.entities.cbegin(), end = text.entities.cend(); while (entity != end && ((entity->type() != EntityInTextCode && entity->type() != EntityInTextPre) || entity->length() <= 0 || entity->offset() >= size)) { ++entity; } @@ -1824,7 +1891,7 @@ QString textApplyEntities(const QString &text, const EntitiesInText &entities) { int32 nextCloseEntity = closingTags.isEmpty() ? (size + 1) : closingTags.cbegin().key(); if (nextOpenEntity <= nextCloseEntity) { QString tag = (entity->type() == EntityInTextCode) ? code : pre; - if (result.isEmpty()) result.reserve(text.size() + entities.size() * pre.size() * 2); + if (result.isEmpty()) result.reserve(text.text.size() + text.entities.size() * pre.size() * 2); const QChar *offset = b + nextOpenEntity; if (offset > already) { @@ -1849,7 +1916,7 @@ QString textApplyEntities(const QString &text, const EntitiesInText &entities) { } } if (result.isEmpty()) { - return text; + return text.text; } const QChar *offset = b + size; if (offset > already) { @@ -1878,13 +1945,13 @@ void moveStringPart(QChar *start, int32 &to, int32 &from, int32 count, EntitiesI } } -void replaceStringWithEntities(const QLatin1String &from, QChar to, QString &result, EntitiesInText *inOutEntities, bool checkSpace = false) { - int32 len = from.size(), s = result.size(), offset = 0, length = 0; - EntitiesInText::iterator i = inOutEntities->begin(), e = inOutEntities->end(); - for (QChar *start = result.data(); offset < s;) { - int32 nextOffset = result.indexOf(from, offset); +void replaceStringWithEntities(const QLatin1String &from, QChar to, TextWithEntities &result, bool checkSpace = false) { + auto len = from.size(), s = result.text.size(), offset = 0, length = 0; + auto i = result.entities.begin(), e = result.entities.end(); + for (QChar *start = result.text.data(); offset < s;) { + int32 nextOffset = result.text.indexOf(from, offset); if (nextOffset < 0) { - moveStringPart(start, length, offset, s - offset, inOutEntities); + moveStringPart(start, length, offset, s - offset, &result.entities); break; } @@ -1892,7 +1959,7 @@ void replaceStringWithEntities(const QLatin1String &from, QChar to, QString &res bool spaceBefore = (nextOffset > 0) && (start + nextOffset - 1)->isSpace(); bool spaceAfter = (nextOffset + len < s) && (start + nextOffset + len)->isSpace(); if (!spaceBefore && !spaceAfter) { - moveStringPart(start, length, offset, nextOffset - offset + len + 1, inOutEntities); + moveStringPart(start, length, offset, nextOffset - offset + len + 1, &result.entities); continue; } } @@ -1905,97 +1972,96 @@ void replaceStringWithEntities(const QLatin1String &from, QChar to, QString &res } } if (skip) { - moveStringPart(start, length, offset, nextOffset - offset + len, inOutEntities); + moveStringPart(start, length, offset, nextOffset - offset + len, &result.entities); continue; } - moveStringPart(start, length, offset, nextOffset - offset, inOutEntities); + moveStringPart(start, length, offset, nextOffset - offset, &result.entities); *(start + length) = to; ++length; offset += len; } - if (length < s) result.resize(length); + if (length < s) result.text.resize(length); } -QString prepareTextWithEntities(QString result, int32 flags, EntitiesInText *inOutEntities) { - cleanTextWithEntities(result, inOutEntities); +void PrepareForSending(TextWithEntities &result, int32 flags) { + ApplyServerCleaning(result); if (flags) { - textParseEntities(result, flags, inOutEntities); + ParseEntities(result, flags); } - replaceStringWithEntities(qstr("--"), QChar(8212), result, inOutEntities, true); - replaceStringWithEntities(qstr("<<"), QChar(171), result, inOutEntities); - replaceStringWithEntities(qstr(">>"), QChar(187), result, inOutEntities); + replaceStringWithEntities(qstr("--"), QChar(8212), result, true); + replaceStringWithEntities(qstr("<<"), QChar(171), result); + replaceStringWithEntities(qstr(">>"), QChar(187), result); if (cReplaceEmojis()) { - result = Ui::Emoji::ReplaceInText(result, inOutEntities); + Ui::Emoji::ReplaceInText(result); } - trimTextWithEntities(result, inOutEntities); - - return result; + Trim(result); } -// replace bad symbols with space and remove \r -void cleanTextWithEntities(QString &result, EntitiesInText *inOutEntities) { - result = result.replace('\t', qstr(" ")); - int32 len = result.size(), to = 0, from = 0; - QChar *start = result.data(); +// Replace bad symbols with space and remove '\r'. +void ApplyServerCleaning(TextWithEntities &result) { + result.text = result.text.replace('\t', qstr(" ")); // TODO WTF? modify entities! + int32 len = result.text.size(), to = 0, from = 0; + QChar *start = result.text.data(); for (QChar *ch = start, *end = start + len; ch < end; ++ch) { if (ch->unicode() == '\r') { - moveStringPart(start, to, from, (ch - start) - from, inOutEntities); + moveStringPart(start, to, from, (ch - start) - from, &result.entities); ++from; } else if (chReplacedBySpace(*ch)) { *ch = ' '; } } - moveStringPart(start, to, from, len - from, inOutEntities); - if (to < len) result.resize(to); + moveStringPart(start, to, from, len - from, &result.entities); + if (to < len) result.text.resize(to); } -void trimTextWithEntities(QString &result, EntitiesInText *inOutEntities) { - bool foundNotTrimmedChar = false; +void Trim(TextWithEntities &result) { + auto foundNotTrimmedChar = false; // right trim - for (QChar *s = result.data(), *e = s + result.size(), *ch = e; ch != s;) { + for (auto s = result.text.data(), e = s + result.text.size(), ch = e; ch != s;) { --ch; if (!chIsTrimmed(*ch)) { if (ch + 1 < e) { - int32 l = ch + 1 - s; - for (auto &entity : *inOutEntities) { + auto l = ch + 1 - s; + for (auto &entity : result.entities) { entity.updateTextEnd(l); } - result.resize(l); + result.text.resize(l); } foundNotTrimmedChar = true; break; } } if (!foundNotTrimmedChar) { - result.clear(); - inOutEntities->clear(); + result = TextWithEntities(); return; } - int firstMonospaceOffset = EntityInText::firstMonospaceOffset(*inOutEntities, result.size()); + auto firstMonospaceOffset = EntityInText::firstMonospaceOffset(result.entities, result.text.size()); // left trim - for (QChar *s = result.data(), *ch = s, *e = s + result.size(); ch != e; ++ch) { + for (auto s = result.text.data(), ch = s, e = s + result.text.size(); ch != e; ++ch) { if (!chIsTrimmed(*ch) || (ch - s) == firstMonospaceOffset) { if (ch > s) { - int32 l = ch - s; - for (auto &entity : *inOutEntities) { + auto l = ch - s; + for (auto &entity : result.entities) { entity.shiftLeft(l); } - result = result.mid(l); + result.text = result.text.mid(l); } break; } } } +} // namespace TextUtilities + namespace Lang { TextWithEntities ReplaceTag::Call(TextWithEntities &&original, ushort tag, const TextWithEntities &replacement) { diff --git a/Telegram/SourceFiles/ui/text/text_entity.h b/Telegram/SourceFiles/ui/text/text_entity.h index 84319b6ac..3091787ee 100644 --- a/Telegram/SourceFiles/ui/text/text_entity.h +++ b/Telegram/SourceFiles/ui/text/text_entity.h @@ -116,22 +116,6 @@ struct TextWithEntities { QString text; EntitiesInText entities; }; -inline void appendTextWithEntities(TextWithEntities &to, TextWithEntities &&append) { - int entitiesShiftRight = to.text.size(); - for (auto &entity : append.entities) { - entity.shiftRight(entitiesShiftRight); - } - to.text += append.text; - to.entities += append.entities; -} - -// text preprocess -QString textClean(const QString &text); -QString textRichPrepare(const QString &text); -QString textOneLine(const QString &text, bool trim = true, bool rich = false); -QString textAccentFold(const QString &text); -QString textSearchKey(const QString &text); -bool textSplit(QString &sendingText, EntitiesInText &sendingEntities, QString &leftText, EntitiesInText &leftEntities, int32 limit); enum { TextParseMultiline = 0x001, @@ -148,39 +132,91 @@ enum { TextInstagramHashtags = 0x800, }; -inline bool mentionNameToFields(const QString &data, int32 *outUserId, uint64 *outAccessHash) { +// Parsing helpers. + +namespace TextUtilities { + +bool IsValidProtocol(const QString &protocol); +bool IsValidTopDomain(const QString &domain); + +const QRegularExpression &RegExpDomain(); +const QRegularExpression &RegExpDomainExplicit(); +const QRegularExpression &RegExpMailNameAtEnd(); +const QRegularExpression &RegExpHashtag(); +const QRegularExpression &RegExpMention(); +const QRegularExpression &RegExpBotCommand(); +const QRegularExpression &RegExpMonoInline(); +const QRegularExpression &RegExpMonoBlock(); + +inline void Append(TextWithEntities &to, TextWithEntities &&append) { + auto entitiesShiftRight = to.text.size(); + for (auto &entity : append.entities) { + entity.shiftRight(entitiesShiftRight); + } + to.text += append.text; + to.entities += append.entities; +} + +// Text preprocess. +QString Clean(const QString &text); +QString EscapeForRichParsing(const QString &text); +QString SingleLine(const QString &text); +QString RemoveAccents(const QString &text); +QStringList PrepareSearchWords(const QString &query, const QRegularExpression *SplitterOverride = nullptr); +bool CutPart(TextWithEntities &sending, TextWithEntities &left, int limit); + +struct MentionNameFields { + MentionNameFields(int32 userId = 0, uint64 accessHash = 0) : userId(userId), accessHash(accessHash) { + } + int32 userId = 0; + uint64 accessHash = 0; +}; +inline MentionNameFields MentionNameDataToFields(const QString &data) { auto components = data.split('.'); if (!components.isEmpty()) { - *outUserId = components.at(0).toInt(); - *outAccessHash = (components.size() > 1) ? components.at(1).toULongLong() : 0; - return (*outUserId != 0); + return { components.at(0).toInt(), (components.size() > 1) ? components.at(1).toULongLong() : 0 }; } - return false; + return MentionNameFields {}; } -inline QString mentionNameFromFields(int32 userId, uint64 accessHash) { - return QString::number(userId) + '.' + QString::number(accessHash); +inline QString MentionNameDataFromFields(const MentionNameFields &fields) { + auto result = QString::number(fields.userId); + if (fields.accessHash) { + result += '.' + QString::number(fields.accessHash); + } + return result; } -EntitiesInText entitiesFromMTP(const QVector &entities); -MTPVector linksToMTP(const EntitiesInText &links, bool sending = false); +EntitiesInText EntitiesFromMTP(const QVector &entities); +enum class ConvertOption { + WithLocal, + SkipLocal, +}; +MTPVector EntitiesToMTP(const EntitiesInText &links, ConvertOption option = ConvertOption::WithLocal); -// New entities are added to the ones that are already in inOutEntities. +// New entities are added to the ones that are already in result. // Changes text if (flags & TextParseMono). -void textParseEntities(QString &text, int32 flags, EntitiesInText *inOutEntities, bool rich = false); -QString textApplyEntities(const QString &text, const EntitiesInText &entities); +void ParseEntities(TextWithEntities &result, int32 flags, bool rich = false); +QString ApplyEntities(const TextWithEntities &text); -QString prepareTextWithEntities(QString result, int32 flags, EntitiesInText *inOutEntities); +void PrepareForSending(TextWithEntities &result, int32 flags); +void Trim(TextWithEntities &result); -inline QString prepareText(QString result, bool checkLinks = false) { - EntitiesInText entities; - auto prepareFlags = checkLinks ? (TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands) : 0; - return prepareTextWithEntities(result, prepareFlags, &entities); +enum class PrepareTextOption { + IgnoreLinks, + CheckLinks, +}; +inline QString PrepareForSending(const QString &text, PrepareTextOption option = PrepareTextOption::IgnoreLinks) { + auto result = TextWithEntities { text }; + auto prepareFlags = (option == PrepareTextOption::CheckLinks) ? (TextParseLinks | TextParseMentions | TextParseHashtags | TextParseBotCommands) : 0; + PrepareForSending(result, prepareFlags); + return result.text; } -// replace bad symbols with space and remove \r -void cleanTextWithEntities(QString &result, EntitiesInText *inOutEntities); -void trimTextWithEntities(QString &result, EntitiesInText *inOutEntities); +// Replace bad symbols with space and remove '\r'. +void ApplyServerCleaning(TextWithEntities &result); + +} // namespace TextUtilities namespace Lang { @@ -203,4 +239,4 @@ struct ReplaceTag { }; -} +} // namespace Lang diff --git a/Telegram/SourceFiles/ui/toast/toast_widget.cpp b/Telegram/SourceFiles/ui/toast/toast_widget.cpp index b1cad7acd..b3c50f913 100644 --- a/Telegram/SourceFiles/ui/toast/toast_widget.cpp +++ b/Telegram/SourceFiles/ui/toast/toast_widget.cpp @@ -39,7 +39,7 @@ Widget::Widget(QWidget *parent, const Config &config) : TWidget(parent) if (_multiline) { toastOptions.maxh *= kToastMaxLines; } - _text.setText(st::toastTextStyle, _multiline ? config.text : textOneLine(config.text), toastOptions); + _text.setText(st::toastTextStyle, _multiline ? config.text : TextUtilities::SingleLine(config.text), toastOptions); setAttribute(Qt::WA_TransparentForMouseEvents); diff --git a/Telegram/SourceFiles/ui/widgets/input_fields.cpp b/Telegram/SourceFiles/ui/widgets/input_fields.cpp index b17a72f27..c3f1297c8 100644 --- a/Telegram/SourceFiles/ui/widgets/input_fields.cpp +++ b/Telegram/SourceFiles/ui/widgets/input_fields.cpp @@ -840,25 +840,22 @@ void FlatTextarea::parseLinks() { // some code is duplicated in text.cpp! return; } - initLinkSets(); - - int32 len = text.size(); + auto len = text.size(); const QChar *start = text.unicode(), *end = start + text.size(); - for (int32 offset = 0, matchOffset = offset; offset < len;) { - QRegularExpressionMatch m = reDomain().match(text, matchOffset); + for (auto offset = 0, matchOffset = offset; offset < len;) { + auto m = TextUtilities::RegExpDomain().match(text, matchOffset); if (!m.hasMatch()) break; - int32 domainOffset = m.capturedStart(); + auto domainOffset = m.capturedStart(); - QString protocol = m.captured(1).toLower(); - QString topDomain = m.captured(3).toLower(); - - bool isProtocolValid = protocol.isEmpty() || validProtocols().contains(hashCrc32(protocol.constData(), protocol.size() * sizeof(QChar))); - bool isTopDomainValid = !protocol.isEmpty() || validTopDomains().contains(hashCrc32(topDomain.constData(), topDomain.size() * sizeof(QChar))); + auto protocol = m.captured(1).toLower(); + auto topDomain = m.captured(3).toLower(); + auto isProtocolValid = protocol.isEmpty() || TextUtilities::IsValidProtocol(protocol); + auto isTopDomainValid = !protocol.isEmpty() || TextUtilities::IsValidTopDomain(topDomain); if (protocol.isEmpty() && domainOffset > offset + 1 && *(start + domainOffset - 1) == QChar('@')) { - QString forMailName = text.mid(offset, domainOffset - offset - 1); - QRegularExpressionMatch mMailName = reMailName().match(forMailName); + auto forMailName = text.mid(offset, domainOffset - offset - 1); + auto mMailName = TextUtilities::RegExpMailNameAtEnd().match(forMailName); if (mMailName.hasMatch()) { offset = matchOffset = m.capturedEnd(); continue; diff --git a/Telegram/SourceFiles/window/themes/window_theme_editor_block.cpp b/Telegram/SourceFiles/window/themes/window_theme_editor_block.cpp index 45e76988d..aa097d8a5 100644 --- a/Telegram/SourceFiles/window/themes/window_theme_editor_block.cpp +++ b/Telegram/SourceFiles/window/themes/window_theme_editor_block.cpp @@ -164,7 +164,7 @@ void EditorBlock::Row::fillValueString() { void EditorBlock::Row::fillSearchIndex() { _searchWords.clear(); _searchStartChars.clear(); - auto toIndex = _name + ' ' + _copyOf + ' ' + textAccentFold(_description.originalText()) + ' ' + _valueString; + auto toIndex = _name + ' ' + _copyOf + ' ' + TextUtilities::RemoveAccents(_description.originalText()) + ' ' + _valueString; auto words = toIndex.toLower().split(SearchSplitter, QString::SkipEmptyParts); for_const (auto &word, words) { _searchWords.insert(word); @@ -346,11 +346,8 @@ void EditorBlock::scrollToSelected() { } void EditorBlock::searchByQuery(QString query) { - auto searchWords = QStringList(); - if (!query.isEmpty()) { - searchWords = textAccentFold(query.trimmed().toLower()).split(SearchSplitter, QString::SkipEmptyParts); - query = searchWords.join(' '); - } + auto words = TextUtilities::PrepareSearchWords(query, &SearchSplitter); + query = words.isEmpty() ? QString() : words.join(' '); if (_searchQuery != query) { setSelected(-1); setPressed(-1); @@ -359,7 +356,7 @@ void EditorBlock::searchByQuery(QString query) { _searchResults.clear(); auto toFilter = OrderedSet(); - for_const (auto &word, searchWords) { + for_const (auto &word, words) { if (word.isEmpty()) continue; auto testToFilter = _searchIndex.value(word[0]); @@ -371,8 +368,8 @@ void EditorBlock::searchByQuery(QString query) { } } if (!toFilter.isEmpty()) { - auto allWordsFound = [&searchWords](const Row &row) { - for_const (auto &word, searchWords) { + auto allWordsFound = [&words](const Row &row) { + for_const (auto &word, words) { if (!row.searchWordsContain(word)) { return false; }