forked from qt-creator/qt-creator
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 <nikolai.kosjar@qt.io>
This commit is contained in:
@@ -28,6 +28,7 @@
|
|||||||
#include <coreplugin/helpmanager.h>
|
#include <coreplugin/helpmanager.h>
|
||||||
#include <cpptools/baseeditordocumentprocessor.h>
|
#include <cpptools/baseeditordocumentprocessor.h>
|
||||||
#include <cpptools/cppmodelmanager.h>
|
#include <cpptools/cppmodelmanager.h>
|
||||||
|
#include <cpptools/cpptoolsreuse.h>
|
||||||
#include <cpptools/editordocumenthandle.h>
|
#include <cpptools/editordocumenthandle.h>
|
||||||
#include <texteditor/texteditor.h>
|
#include <texteditor/texteditor.h>
|
||||||
|
|
||||||
@@ -37,6 +38,7 @@
|
|||||||
|
|
||||||
#include <QFutureWatcher>
|
#include <QFutureWatcher>
|
||||||
#include <QLoggingCategory>
|
#include <QLoggingCategory>
|
||||||
|
#include <QRegularExpression>
|
||||||
#include <QTextCodec>
|
#include <QTextCodec>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
@@ -105,6 +107,79 @@ ClangHoverHandler::~ClangHoverHandler()
|
|||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int skipChars(QTextCursor *tc,
|
||||||
|
QTextCursor::MoveOperation op,
|
||||||
|
int offset,
|
||||||
|
std::function<bool(const QChar &)> 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<bool(const QChar &)> skip)
|
||||||
|
{
|
||||||
|
return skipChars(tc, QTextCursor::NextCharacter, 0, skip);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int skipCharsBackward(QTextCursor *tc, std::function<bool(const QChar &)> 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,
|
void ClangHoverHandler::identifyMatch(TextEditorWidget *editorWidget,
|
||||||
int pos,
|
int pos,
|
||||||
BaseHoverHandler::ReportPriority report)
|
BaseHoverHandler::ReportPriority report)
|
||||||
@@ -118,8 +193,6 @@ void ClangHoverHandler::identifyMatch(TextEditorWidget *editorWidget,
|
|||||||
qCDebug(hoverLog) << "Checking for diagnostic at" << pos;
|
qCDebug(hoverLog) << "Checking for diagnostic at" << pos;
|
||||||
setPriority(Priority_Diagnostic);
|
setPriority(Priority_Diagnostic);
|
||||||
m_cursorPosition = pos;
|
m_cursorPosition = pos;
|
||||||
report(priority());
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for tooltips (async)
|
// Check for tooltips (async)
|
||||||
@@ -128,17 +201,25 @@ void ClangHoverHandler::identifyMatch(TextEditorWidget *editorWidget,
|
|||||||
qCDebug(hoverLog) << "Requesting tooltip info at" << pos;
|
qCDebug(hoverLog) << "Requesting tooltip info at" << pos;
|
||||||
m_reportPriority = report;
|
m_reportPriority = report;
|
||||||
m_futureWatcher.reset(new QFutureWatcher<CppTools::ToolTipInfo>());
|
m_futureWatcher.reset(new QFutureWatcher<CppTools::ToolTipInfo>());
|
||||||
QObject::connect(m_futureWatcher.data(), &QFutureWatcherBase::finished, [this]() {
|
const QStringList fallback = fallbackWords(editorWidget->document(), pos);
|
||||||
if (m_futureWatcher->isCanceled())
|
QObject::connect(m_futureWatcher.data(),
|
||||||
|
&QFutureWatcherBase::finished,
|
||||||
|
[this, fallback]() {
|
||||||
|
if (m_futureWatcher->isCanceled()) {
|
||||||
m_reportPriority(Priority_None);
|
m_reportPriority(Priority_None);
|
||||||
else
|
} else {
|
||||||
processToolTipInfo(m_futureWatcher->result());
|
CppTools::ToolTipInfo info = m_futureWatcher->result();
|
||||||
|
qCDebug(hoverLog)
|
||||||
|
<< "Appending word-based fallback lookup" << fallback;
|
||||||
|
info.qDocIdCandidates += fallback;
|
||||||
|
processToolTipInfo(info);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
m_futureWatcher->setFuture(future);
|
m_futureWatcher->setFuture(future);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
report(Priority_None); // Ops, something went wrong.
|
report(priority()); // Ops, something went wrong.
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangHoverHandler::abort()
|
void ClangHoverHandler::abort()
|
||||||
@@ -198,22 +279,6 @@ void ClangHoverHandler::processToolTipInfo(const CppTools::ToolTipInfo &info)
|
|||||||
m_reportPriority(priority());
|
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,
|
void ClangHoverHandler::operateTooltip(TextEditor::TextEditorWidget *editorWidget,
|
||||||
const QPoint &point)
|
const QPoint &point)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ public:
|
|||||||
void identifyMatch(TextEditor::TextEditorWidget *editorWidget,
|
void identifyMatch(TextEditor::TextEditorWidget *editorWidget,
|
||||||
int pos,
|
int pos,
|
||||||
ReportPriority report) override;
|
ReportPriority report) override;
|
||||||
void decorateToolTip() override;
|
|
||||||
void operateTooltip(TextEditor::TextEditorWidget *editorWidget, const QPoint &point) override;
|
void operateTooltip(TextEditor::TextEditorWidget *editorWidget, const QPoint &point) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include <coreplugin/helpmanager.h>
|
#include <coreplugin/helpmanager.h>
|
||||||
#include <texteditor/texteditor.h>
|
#include <texteditor/texteditor.h>
|
||||||
|
|
||||||
|
#include <utils/optional.h>
|
||||||
#include <utils/textutils.h>
|
#include <utils/textutils.h>
|
||||||
#include <utils/executeondestruction.h>
|
#include <utils/executeondestruction.h>
|
||||||
|
|
||||||
@@ -78,47 +79,31 @@ void CppHoverHandler::identifyMatch(TextEditorWidget *editorWidget, int pos, Rep
|
|||||||
CppElementEvaluator evaluator(editorWidget);
|
CppElementEvaluator evaluator(editorWidget);
|
||||||
evaluator.setTextCursor(tc);
|
evaluator.setTextCursor(tc);
|
||||||
evaluator.execute();
|
evaluator.execute();
|
||||||
|
QString tip;
|
||||||
if (evaluator.hasDiagnosis()) {
|
if (evaluator.hasDiagnosis()) {
|
||||||
setToolTip(evaluator.diagnosis());
|
tip += evaluator.diagnosis();
|
||||||
setPriority(Priority_Diagnostic);
|
setPriority(Priority_Diagnostic);
|
||||||
} else if (evaluator.identifiedCppElement()) {
|
|
||||||
const QSharedPointer<CppElement> &cppElement = evaluator.cppElement();
|
|
||||||
if (priority() != Priority_Diagnostic) {
|
|
||||||
setToolTip(cppElement->tooltip);
|
|
||||||
setPriority(cppElement->tooltip.isEmpty() ? Priority_None : Priority_Tooltip);
|
|
||||||
}
|
}
|
||||||
|
if (evaluator.identifiedCppElement()) {
|
||||||
|
const QSharedPointer<CppElement> &cppElement = evaluator.cppElement();
|
||||||
QStringList candidates = cppElement->helpIdCandidates;
|
QStringList candidates = cppElement->helpIdCandidates;
|
||||||
candidates.removeDuplicates();
|
candidates.removeDuplicates();
|
||||||
|
Utils::optional<HelpItem> helpItem;
|
||||||
foreach (const QString &helpId, candidates) {
|
foreach (const QString &helpId, candidates) {
|
||||||
if (helpId.isEmpty())
|
if (helpId.isEmpty())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const QMap<QString, QUrl> helpLinks = HelpManager::linksForIdentifier(helpId);
|
const QMap<QString, QUrl> helpLinks = HelpManager::linksForIdentifier(helpId);
|
||||||
if (!helpLinks.isEmpty()) {
|
if (!helpLinks.isEmpty()) {
|
||||||
setLastHelpItemIdentified(HelpItem(helpId,
|
helpItem.emplace(helpId, cppElement->helpMark, cppElement->helpCategory, helpLinks);
|
||||||
cppElement->helpMark,
|
|
||||||
cppElement->helpCategory,
|
|
||||||
helpLinks));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (helpItem)
|
||||||
|
setLastHelpItemIdentified(helpItem.value()); // tool tip appended by decorateToolTip
|
||||||
|
else
|
||||||
|
tip += cppElement->tooltip;
|
||||||
}
|
}
|
||||||
}
|
setToolTip(tip);
|
||||||
|
|
||||||
void CppHoverHandler::decorateToolTip()
|
|
||||||
{
|
|
||||||
if (Qt::mightBeRichText(toolTip()))
|
|
||||||
setToolTip(toolTip().toHtmlEscaped());
|
|
||||||
|
|
||||||
if (priority() == Priority_Diagnostic)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const HelpItem &help = lastHelpItemIdentified();
|
|
||||||
if (help.isValid()) {
|
|
||||||
const QString text = tooltipTextForHelpItem(help);
|
|
||||||
if (!text.isEmpty())
|
|
||||||
setToolTip(text);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace CppTools
|
} // namespace CppTools
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ private:
|
|||||||
void identifyMatch(TextEditor::TextEditorWidget *editorWidget,
|
void identifyMatch(TextEditor::TextEditorWidget *editorWidget,
|
||||||
int pos,
|
int pos,
|
||||||
ReportPriority report) override;
|
ReportPriority report) override;
|
||||||
void decorateToolTip() override;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace CppTools
|
} // namespace CppTools
|
||||||
|
|||||||
@@ -148,11 +148,11 @@ void BaseHoverHandler::decorateToolTip()
|
|||||||
if (Qt::mightBeRichText(toolTip()))
|
if (Qt::mightBeRichText(toolTip()))
|
||||||
setToolTip(toolTip().toHtmlEscaped());
|
setToolTip(toolTip().toHtmlEscaped());
|
||||||
|
|
||||||
if (priority() != Priority_Diagnostic && lastHelpItemIdentified().isValid()) {
|
if (lastHelpItemIdentified().isValid()) {
|
||||||
const QString &contents = lastHelpItemIdentified().extractContent(false);
|
const QString &helpContents = lastHelpItemIdentified().extractContent(false);
|
||||||
if (!contents.isEmpty()) {
|
if (!helpContents.isEmpty()) {
|
||||||
m_toolTip = toolTip().toHtmlEscaped();
|
m_toolTip = toolTip().toHtmlEscaped();
|
||||||
m_toolTip.append(contents);
|
m_toolTip = m_toolTip.isEmpty() ? helpContents : ("<p>" + m_toolTip + "</p><hr/><p>" + helpContents + "</p>");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user