diff --git a/src/plugins/texteditor/snippets/snippet.cpp b/src/plugins/texteditor/snippets/snippet.cpp index 4d2c1a54ef0..ea4671596cf 100644 --- a/src/plugins/texteditor/snippets/snippet.cpp +++ b/src/plugins/texteditor/snippets/snippet.cpp @@ -70,6 +70,22 @@ public: } }; +QString SnippetParseError::htmlMessage() const +{ + QString message = errorMessage; + if (pos < 0 || pos > 50) + return message; + QString detail = text.left(50); + if (detail != text) + detail.append("..."); + detail.replace(QChar::Space, " "); + message.append("
" + detail + "
"); + for (int i = 0; i < pos; ++i) + message.append(" "); + message.append("^
"); + return message; +} + // -------------------------------------------------------------------- // Snippet: // -------------------------------------------------------------------- @@ -189,7 +205,12 @@ QString Snippet::generateTip() const {'<', "<"}, {'>', ">"}}; - ParsedSnippet parsedSnippet = Snippet::parse(m_content); + SnippetParseResult result = Snippet::parse(m_content); + + if (Utils::holds_alternative(result)) + return Utils::get(result).htmlMessage(); + QTC_ASSERT(Utils::holds_alternative(result), return {}); + auto parsedSnippet = Utils::get(result); QString tip(""); int pos = 0; @@ -215,7 +236,7 @@ QString Snippet::generateTip() const return tip; } -ParsedSnippet Snippet::parse(const QString &snippet) +SnippetParseResult Snippet::parse(const QString &snippet) { static UppercaseMangler ucMangler; static LowercaseMangler lcMangler; @@ -228,15 +249,10 @@ ParsedSnippet Snippet::parse(const QString &snippet) = Utils::TemplateEngine::processText(Utils::globalMacroExpander(), snippet, &errorMessage); - result.success = errorMessage.isEmpty(); - if (!result.success) { - result.text = snippet; - result.errorMessage = errorMessage; - return result; - } + if (!errorMessage.isEmpty()) + return {SnippetParseError{errorMessage, {}, -1}}; const int count = preprocessedSnippet.count(); - bool success = true; int start = -1; NameMangler *mangler = nullptr; @@ -260,8 +276,9 @@ ParsedSnippet Snippet::parse(const QString &snippet) } if (mangler) { - success = false; - break; + return SnippetParseResult{SnippetParseError{tr("Expected delimiter after mangler id"), + preprocessedSnippet, + i}}; } if (current == QLatin1Char(':') && start >= 0) { @@ -272,8 +289,11 @@ ParsedSnippet Snippet::parse(const QString &snippet) } else if (next == QLatin1Char('c')) { mangler = &tcMangler; } else { - success = false; - break; + return SnippetParseResult{ + SnippetParseError{tr("Expected mangler id 'l'(lowercase), 'u'(uppercase), " + "or 'c'(titlecase) after colon"), + preprocessedSnippet, + i}}; } ++i; continue; @@ -288,17 +308,12 @@ ParsedSnippet Snippet::parse(const QString &snippet) result.text.append(current); } - if (start >= 0) - success = false; - - result.success = success; - - if (!success) { - result.ranges.clear(); - result.text = preprocessedSnippet; + if (start >= 0) { + return SnippetParseResult{ + SnippetParseError{tr("Missing closing variable delimiter"), result.text, start}}; } - return result; + return SnippetParseResult(result); } #ifdef WITH_TESTS @@ -418,17 +433,19 @@ void Internal::TextEditorPlugin::testSnippetParsing() Q_ASSERT(ranges_start.count() == ranges_length.count()); // sanity check for the test data Q_ASSERT(ranges_start.count() == ranges_mangler.count()); // sanity check for the test data - ParsedSnippet result = Snippet::parse(input); + SnippetParseResult result = Snippet::parse(input); + QCOMPARE(Utils::holds_alternative(result), success); - QCOMPARE(result.text, text); - QCOMPARE(result.success, success); - QCOMPARE(result.ranges.count(), ranges_start.count()); + ParsedSnippet snippet = Utils::get(result); + + QCOMPARE(snippet.text, text); + QCOMPARE(snippet.ranges.count(), ranges_start.count()); for (int i = 0; i < ranges_start.count(); ++i) { - QCOMPARE(result.ranges.at(i).start, ranges_start.at(i)); - QCOMPARE(result.ranges.at(i).length, ranges_length.at(i)); + QCOMPARE(snippet.ranges.at(i).start, ranges_start.at(i)); + QCOMPARE(snippet.ranges.at(i).length, ranges_length.at(i)); Utils::Id id = NOMANGLER_ID; - if (result.ranges.at(i).mangler) - id = result.ranges.at(i).mangler->id(); + if (snippet.ranges.at(i).mangler) + id = snippet.ranges.at(i).mangler->id(); QCOMPARE(id, ranges_mangler.at(i)); } } diff --git a/src/plugins/texteditor/snippets/snippet.h b/src/plugins/texteditor/snippets/snippet.h index 40c152c4831..34e8ac51bc2 100644 --- a/src/plugins/texteditor/snippets/snippet.h +++ b/src/plugins/texteditor/snippets/snippet.h @@ -28,6 +28,7 @@ #include #include +#include #include #include @@ -48,8 +49,6 @@ class TEXTEDITOR_EXPORT ParsedSnippet { public: QString text; - QString errorMessage; - bool success; struct Range { Range(int s, int l, NameMangler *m) : start(s), length(l), mangler(m) { } int start; @@ -59,8 +58,21 @@ public: QList ranges; }; +class TEXTEDITOR_EXPORT SnippetParseError +{ +public: + QString errorMessage; + QString text; + int pos; + + QString htmlMessage() const; +}; + +using SnippetParseResult = Utils::variant; + class TEXTEDITOR_EXPORT Snippet { + Q_DECLARE_TR_FUNCTIONS(Snippet) public: explicit Snippet(const QString &groupId = QString(), const QString &id = QString()); ~Snippet(); @@ -91,7 +103,7 @@ public: static const QChar kVariableDelimiter; static const QChar kEscapeChar; - static ParsedSnippet parse(const QString &snippet); + static SnippetParseResult parse(const QString &snippet); private: bool m_isRemoved = false; diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 4a0862d69d5..81459f90cd2 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -2700,15 +2700,14 @@ void TextEditorWidget::keyPressEvent(QKeyEvent *e) void TextEditorWidget::insertCodeSnippet(const QTextCursor &cursor_arg, const QString &snippet) { - ParsedSnippet data = Snippet::parse(snippet); - - if (!data.success) { - QString message = QString::fromLatin1("Cannot parse snippet \"%1\".").arg(snippet); - if (!data.errorMessage.isEmpty()) - message += QLatin1String("\nParse error: ") + data.errorMessage; - QMessageBox::warning(this, QLatin1String("Snippet Parse Error"), message); + SnippetParseResult result = Snippet::parse(snippet); + if (Utils::holds_alternative(result)) { + const auto &error = Utils::get(result); + QMessageBox::warning(this, QLatin1String("Snippet Parse Error"), error.htmlMessage()); return; } + QTC_ASSERT(Utils::holds_alternative(result), return); + ParsedSnippet data = Utils::get(result); QTextCursor cursor = cursor_arg; cursor.beginEditBlock();