#include "message_links.h" #include #include /* Отделить адрес от текста, сохраняя сбалансированные скобки внутри пути/query. */ QVector messageLinks(const QString& text) { static const QRegularExpression pattern(QStringLiteral( "utun://[A-Za-z0-9+/]+={0,2}|(?i:https?://)[^\\s\\p{Z}\\p{Cc}<>\"'`«»“”‘’]+")); QVector links; auto matches = pattern.globalMatch(text); while (matches.hasNext()) { const auto match = matches.next(); QString url = match.captured(); if (!url.startsWith(QStringLiteral("utun://"))) { // Один подсчёт и один проход по хвосту: повторный count() на каждой скобке даёт O(n²). qsizetype balance[3] = {}; for (const QChar ch : url) { const int opening = QStringLiteral("([{").indexOf(ch); const int closing = QStringLiteral(")]}").indexOf(ch); if (opening >= 0) balance[opening]--; if (closing >= 0) balance[closing]++; } auto end = url.size(); while (end > 0) { const QChar last = url.at(end - 1); const int closing = QStringLiteral(")]}").indexOf(last); const bool unmatched = closing >= 0 && balance[closing] > 0; if (!QStringLiteral(".,;:!?").contains(last) && !unmatched) break; if (closing >= 0) balance[closing]--; end--; } url.truncate(end); const QUrl parsed(url, QUrl::StrictMode); if (!parsed.isValid() || parsed.host().isEmpty()) continue; } links.append({(int)match.capturedStart(), (int)url.length(), url}); } return links; }