From 6c10d36f8776900f75f16ae26a127e4771eda1e6 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Thu, 25 Jan 2018 15:51:36 +0100 Subject: Try harder to get context help in presence of diagnostics - even if there are diagnostics still try to retrieve symbol info and help - fall back to text based keyword extraction in case code model info fails - if both a code model tool tip (e.g. function signature or type) and help are available, show both Task-number: QTCREATORBUG-15959 Change-Id: Id85a223c24849ead1b25d63776d64a7da1cc73ef Reviewed-by: Nikolai Kosjar --- src/plugins/clangcodemodel/clanghoverhandler.cpp | 115 ++++++++++++++++++----- 1 file changed, 90 insertions(+), 25 deletions(-) (limited to 'src/plugins/clangcodemodel/clanghoverhandler.cpp') diff --git a/src/plugins/clangcodemodel/clanghoverhandler.cpp b/src/plugins/clangcodemodel/clanghoverhandler.cpp index be59805170..1dc294cab5 100644 --- a/src/plugins/clangcodemodel/clanghoverhandler.cpp +++ b/src/plugins/clangcodemodel/clanghoverhandler.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include @@ -37,6 +38,7 @@ #include #include +#include #include #include @@ -105,6 +107,79 @@ ClangHoverHandler::~ClangHoverHandler() abort(); } +static int skipChars(QTextCursor *tc, + QTextCursor::MoveOperation op, + int offset, + std::function skip) +{ + const QTextDocument *doc = tc->document(); + QChar ch = doc->characterAt(tc->position() + offset); + if (ch.isNull()) + return 0; + int count = 0; + while (skip(ch)) { + if (tc->movePosition(op)) + ++count; + else + break; + ch = doc->characterAt(tc->position() + offset); + } + return count; +} + +static int skipCharsForward(QTextCursor *tc, std::function skip) +{ + return skipChars(tc, QTextCursor::NextCharacter, 0, skip); +} + +static int skipCharsBackward(QTextCursor *tc, std::function skip) +{ + return skipChars(tc, QTextCursor::PreviousCharacter, -1, skip); +} + +static QStringList fallbackWords(QTextDocument *document, int pos) +{ + const auto isSpace = [](const QChar &c) { return c.isSpace(); }; + const auto isColon = [](const QChar &c) { return c == ':'; }; + const auto isValidIdentifierChar = [document](const QTextCursor &tc) { + return CppTools::isValidIdentifierChar(document->characterAt(tc.position())); + }; + // move to the end + QTextCursor endCursor(document); + endCursor.setPosition(pos); + do { + CppTools::moveCursorToEndOfIdentifier(&endCursor); + // possibly skip :: + QTextCursor temp(endCursor); + skipCharsForward(&temp, isSpace); + const int colons = skipCharsForward(&temp, isColon); + skipCharsForward(&temp, isSpace); + if (colons == 2 && isValidIdentifierChar(temp)) + endCursor = temp; + } while (isValidIdentifierChar(endCursor)); + + QStringList results; + QTextCursor startCursor(endCursor); + do { + CppTools::moveCursorToStartOfIdentifier(&startCursor); + if (startCursor.position() == endCursor.position()) + break; + QTextCursor temp(endCursor); + temp.setPosition(startCursor.position(), QTextCursor::KeepAnchor); + results.append(temp.selectedText().remove(QRegularExpression("\\s"))); + // possibly skip :: + temp = startCursor; + skipCharsBackward(&temp, isSpace); + const int colons = skipCharsBackward(&temp, isColon); + skipCharsBackward(&temp, isSpace); + if (colons == 2 + && CppTools::isValidIdentifierChar(document->characterAt(temp.position() - 1))) { + startCursor = temp; + } + } while (!isValidIdentifierChar(startCursor)); + return results; +} + void ClangHoverHandler::identifyMatch(TextEditorWidget *editorWidget, int pos, BaseHoverHandler::ReportPriority report) @@ -118,8 +193,6 @@ void ClangHoverHandler::identifyMatch(TextEditorWidget *editorWidget, qCDebug(hoverLog) << "Checking for diagnostic at" << pos; setPriority(Priority_Diagnostic); m_cursorPosition = pos; - report(priority()); - return; } // Check for tooltips (async) @@ -128,17 +201,25 @@ void ClangHoverHandler::identifyMatch(TextEditorWidget *editorWidget, qCDebug(hoverLog) << "Requesting tooltip info at" << pos; m_reportPriority = report; m_futureWatcher.reset(new QFutureWatcher()); - QObject::connect(m_futureWatcher.data(), &QFutureWatcherBase::finished, [this]() { - if (m_futureWatcher->isCanceled()) - m_reportPriority(Priority_None); - else - processToolTipInfo(m_futureWatcher->result()); - }); + const QStringList fallback = fallbackWords(editorWidget->document(), pos); + QObject::connect(m_futureWatcher.data(), + &QFutureWatcherBase::finished, + [this, fallback]() { + if (m_futureWatcher->isCanceled()) { + m_reportPriority(Priority_None); + } else { + CppTools::ToolTipInfo info = m_futureWatcher->result(); + qCDebug(hoverLog) + << "Appending word-based fallback lookup" << fallback; + info.qDocIdCandidates += fallback; + processToolTipInfo(info); + } + }); m_futureWatcher->setFuture(future); return; } - report(Priority_None); // Ops, something went wrong. + report(priority()); // Ops, something went wrong. } void ClangHoverHandler::abort() @@ -198,22 +279,6 @@ void ClangHoverHandler::processToolTipInfo(const CppTools::ToolTipInfo &info) m_reportPriority(priority()); } -void ClangHoverHandler::decorateToolTip() -{ - if (priority() == Priority_Diagnostic) - return; - - if (Qt::mightBeRichText(toolTip())) - setToolTip(toolTip().toHtmlEscaped()); - - const HelpItem &help = lastHelpItemIdentified(); - if (help.isValid()) { - const QString text = CppTools::CppHoverHandler::tooltipTextForHelpItem(help); - if (!text.isEmpty()) - setToolTip(text); - } -} - void ClangHoverHandler::operateTooltip(TextEditor::TextEditorWidget *editorWidget, const QPoint &point) { -- cgit v1.2.1