forked from qt-creator/qt-creator
Prefer project's Qt version for context help
To avoid additional dependencies, let the QtSupport plugin register a function for filtering help URLs, and add information about the originating file path to the help items created by the context help handlers. The filter hook then finds out the corresponding project, active target, and corresponding Qt version, and filters the help URLs accordingly. With the default setting in "Kits > Qt Versions > Register documentation", only the highest versioned documentation set within a major Qt version is registered, so context help now shows the highest versioned help from Qt 5 or Qt 6 depending on active target. If that is changed to "All", context help now shows documentation from exactly the version that is currently used by the project. Fixes: QTCREATORBUG-10331 Change-Id: I7d87793737cc5ab7d228ee4bfe568d7e8343ee7d Reviewed-by: Christian Kandeler <christian.kandeler@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
@@ -261,7 +261,9 @@ public:
|
|||||||
QString searchTermFromCursor(const QTextCursor &cursor) const;
|
QString searchTermFromCursor(const QTextCursor &cursor) const;
|
||||||
QTextCursor adjustedCursor(const QTextCursor &cursor, const TextDocument *doc);
|
QTextCursor adjustedCursor(const QTextCursor &cursor, const TextDocument *doc);
|
||||||
|
|
||||||
void setHelpItemForTooltip(const MessageId &token, const QString &fqn = {},
|
void setHelpItemForTooltip(const MessageId &token,
|
||||||
|
const DocumentUri &uri,
|
||||||
|
const QString &fqn = {},
|
||||||
HelpItem::Category category = HelpItem::Unknown,
|
HelpItem::Category category = HelpItem::Unknown,
|
||||||
const QString &type = {});
|
const QString &type = {});
|
||||||
|
|
||||||
@@ -928,7 +930,10 @@ void ClangdClient::gatherHelpItemForTooltip(const HoverRequest::Response &hoverR
|
|||||||
if (closingQuoteIndex != -1) {
|
if (closingQuoteIndex != -1) {
|
||||||
const QString macroName = markupString.mid(nameStart,
|
const QString macroName = markupString.mid(nameStart,
|
||||||
closingQuoteIndex - nameStart);
|
closingQuoteIndex - nameStart);
|
||||||
d->setHelpItemForTooltip(hoverResponse.id(), macroName, HelpItem::Macro);
|
d->setHelpItemForTooltip(hoverResponse.id(),
|
||||||
|
uri,
|
||||||
|
macroName,
|
||||||
|
HelpItem::Macro);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -941,6 +946,7 @@ void ClangdClient::gatherHelpItemForTooltip(const HoverRequest::Response &hoverR
|
|||||||
const auto filePath = Utils::FilePath::fromUserInput(lines.last().simplified());
|
const auto filePath = Utils::FilePath::fromUserInput(lines.last().simplified());
|
||||||
if (filePath.exists()) {
|
if (filePath.exists()) {
|
||||||
d->setHelpItemForTooltip(hoverResponse.id(),
|
d->setHelpItemForTooltip(hoverResponse.id(),
|
||||||
|
uri,
|
||||||
filePath.fileName(),
|
filePath.fileName(),
|
||||||
HelpItem::Brief);
|
HelpItem::Brief);
|
||||||
return;
|
return;
|
||||||
@@ -961,7 +967,7 @@ void ClangdClient::gatherHelpItemForTooltip(const HoverRequest::Response &hoverR
|
|||||||
}
|
}
|
||||||
const ClangdAstPath path = getAstPath(ast, range);
|
const ClangdAstPath path = getAstPath(ast, range);
|
||||||
if (path.isEmpty()) {
|
if (path.isEmpty()) {
|
||||||
d->setHelpItemForTooltip(id);
|
d->setHelpItemForTooltip(id, uri);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ClangdAstNode node = path.last();
|
ClangdAstNode node = path.last();
|
||||||
@@ -985,7 +991,7 @@ void ClangdClient::gatherHelpItemForTooltip(const HoverRequest::Response &hoverR
|
|||||||
type = type.left(angleBracketIndex);
|
type = type.left(angleBracketIndex);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (gatherMemberFunctionOverrideHelpItemForTooltip(id, path))
|
if (gatherMemberFunctionOverrideHelpItemForTooltip(id, uri, path))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const bool isMemberFunction = node.role() == "expression" && node.kind() == "Member"
|
const bool isMemberFunction = node.role() == "expression" && node.kind() == "Member"
|
||||||
@@ -993,8 +999,9 @@ void ClangdClient::gatherHelpItemForTooltip(const HoverRequest::Response &hoverR
|
|||||||
const bool isFunction = node.role() == "expression" && node.kind() == "DeclRef"
|
const bool isFunction = node.role() == "expression" && node.kind() == "DeclRef"
|
||||||
&& type.contains('(');
|
&& type.contains('(');
|
||||||
if (isMemberFunction || isFunction) {
|
if (isMemberFunction || isFunction) {
|
||||||
const auto symbolInfoHandler = [this, id, type, isFunction]
|
const auto symbolInfoHandler = [this, id, uri, type, isFunction](const QString &name,
|
||||||
(const QString &name, const QString &prefix, const MessageId &) {
|
const QString &prefix,
|
||||||
|
const MessageId &) {
|
||||||
qCDebug(clangdLog) << "handling symbol info reply";
|
qCDebug(clangdLog) << "handling symbol info reply";
|
||||||
const QString fqn = prefix + name;
|
const QString fqn = prefix + name;
|
||||||
|
|
||||||
@@ -1003,7 +1010,11 @@ void ClangdClient::gatherHelpItemForTooltip(const HoverRequest::Response &hoverR
|
|||||||
// But since HtmlDocExtractor::getFunctionDescription() is always called
|
// But since HtmlDocExtractor::getFunctionDescription() is always called
|
||||||
// with mainOverload = true, such information would get ignored anyway.
|
// with mainOverload = true, such information would get ignored anyway.
|
||||||
if (!fqn.isEmpty())
|
if (!fqn.isEmpty())
|
||||||
d->setHelpItemForTooltip(id, fqn, HelpItem::Function, isFunction ? type : "()");
|
d->setHelpItemForTooltip(id,
|
||||||
|
uri,
|
||||||
|
fqn,
|
||||||
|
HelpItem::Function,
|
||||||
|
isFunction ? type : "()");
|
||||||
};
|
};
|
||||||
requestSymbolInfo(uri.toFilePath(), range.start(), symbolInfoHandler);
|
requestSymbolInfo(uri.toFilePath(), range.start(), symbolInfoHandler);
|
||||||
return;
|
return;
|
||||||
@@ -1013,8 +1024,11 @@ void ClangdClient::gatherHelpItemForTooltip(const HoverRequest::Response &hoverR
|
|||||||
&& (node.kind() == "Var" || node.kind() == "ParmVar"
|
&& (node.kind() == "Var" || node.kind() == "ParmVar"
|
||||||
|| node.kind() == "Field"))) {
|
|| node.kind() == "Field"))) {
|
||||||
if (node.arcanaContains("EnumConstant")) {
|
if (node.arcanaContains("EnumConstant")) {
|
||||||
d->setHelpItemForTooltip(id, node.detail().value_or(QString()),
|
d->setHelpItemForTooltip(id,
|
||||||
HelpItem::Enum, type);
|
uri,
|
||||||
|
node.detail().value_or(QString()),
|
||||||
|
HelpItem::Enum,
|
||||||
|
type);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
stripTemplatePartOffType();
|
stripTemplatePartOffType();
|
||||||
@@ -1025,10 +1039,13 @@ void ClangdClient::gatherHelpItemForTooltip(const HoverRequest::Response &hoverR
|
|||||||
&& type != "char" && !type.contains(" char")
|
&& type != "char" && !type.contains(" char")
|
||||||
&& type != "double" && !type.contains(" double")
|
&& type != "double" && !type.contains(" double")
|
||||||
&& type != "float" && type != "bool") {
|
&& type != "float" && type != "bool") {
|
||||||
d->setHelpItemForTooltip(id, type, node.qdocCategoryForDeclaration(
|
d->setHelpItemForTooltip(id,
|
||||||
|
uri,
|
||||||
|
type,
|
||||||
|
node.qdocCategoryForDeclaration(
|
||||||
HelpItem::ClassOrNamespace));
|
HelpItem::ClassOrNamespace));
|
||||||
} else {
|
} else {
|
||||||
d->setHelpItemForTooltip(id);
|
d->setHelpItemForTooltip(id, uri);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1041,19 +1058,19 @@ void ClangdClient::gatherHelpItemForTooltip(const HoverRequest::Response &hoverR
|
|||||||
ns.prepend("::").prepend(name);
|
ns.prepend("::").prepend(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
d->setHelpItemForTooltip(hoverResponse.id(), ns, HelpItem::ClassOrNamespace);
|
d->setHelpItemForTooltip(id, uri, ns, HelpItem::ClassOrNamespace);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (node.role() == "type") {
|
if (node.role() == "type") {
|
||||||
if (node.kind() == "Enum") {
|
if (node.kind() == "Enum") {
|
||||||
d->setHelpItemForTooltip(id, node.detail().value_or(QString()), HelpItem::Enum);
|
d->setHelpItemForTooltip(id, uri, node.detail().value_or(QString()), HelpItem::Enum);
|
||||||
} else if (node.kind() == "Record" || node.kind() == "TemplateSpecialization") {
|
} else if (node.kind() == "Record" || node.kind() == "TemplateSpecialization") {
|
||||||
stripTemplatePartOffType();
|
stripTemplatePartOffType();
|
||||||
d->setHelpItemForTooltip(id, type, HelpItem::ClassOrNamespace);
|
d->setHelpItemForTooltip(id, uri, type, HelpItem::ClassOrNamespace);
|
||||||
} else if (node.kind() == "Typedef") {
|
} else if (node.kind() == "Typedef") {
|
||||||
d->setHelpItemForTooltip(id, type, HelpItem::Typedef);
|
d->setHelpItemForTooltip(id, uri, type, HelpItem::Typedef);
|
||||||
} else {
|
} else {
|
||||||
d->setHelpItemForTooltip(id);
|
d->setHelpItemForTooltip(id, uri);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1061,20 +1078,23 @@ void ClangdClient::gatherHelpItemForTooltip(const HoverRequest::Response &hoverR
|
|||||||
const QString name = node.detail().value_or(QString());
|
const QString name = node.detail().value_or(QString());
|
||||||
if (!name.isEmpty())
|
if (!name.isEmpty())
|
||||||
type = name;
|
type = name;
|
||||||
d->setHelpItemForTooltip(id, type, HelpItem::ClassOrNamespace);
|
d->setHelpItemForTooltip(id, uri, type, HelpItem::ClassOrNamespace);
|
||||||
}
|
}
|
||||||
if (node.role() == "specifier" && node.kind() == "NamespaceAlias") {
|
if (node.role() == "specifier" && node.kind() == "NamespaceAlias") {
|
||||||
d->setHelpItemForTooltip(id, node.detail().value_or(QString()).chopped(2),
|
d->setHelpItemForTooltip(id,
|
||||||
|
uri,
|
||||||
|
node.detail().value_or(QString()).chopped(2),
|
||||||
HelpItem::ClassOrNamespace);
|
HelpItem::ClassOrNamespace);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
d->setHelpItemForTooltip(id);
|
d->setHelpItemForTooltip(id, uri);
|
||||||
};
|
};
|
||||||
d->getAndHandleAst(doc, astHandler, AstCallbackMode::SyncIfPossible);
|
d->getAndHandleAst(doc, astHandler, AstCallbackMode::SyncIfPossible);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ClangdClient::gatherMemberFunctionOverrideHelpItemForTooltip(
|
bool ClangdClient::gatherMemberFunctionOverrideHelpItemForTooltip(
|
||||||
const LanguageServerProtocol::MessageId &token,
|
const LanguageServerProtocol::MessageId &token,
|
||||||
|
const DocumentUri &uri,
|
||||||
const QList<ClangdAstNode> &path)
|
const QList<ClangdAstNode> &path)
|
||||||
{
|
{
|
||||||
// Heuristic: If we encounter a member function re-declaration, continue under the
|
// Heuristic: If we encounter a member function re-declaration, continue under the
|
||||||
@@ -1112,8 +1132,11 @@ bool ClangdClient::gatherMemberFunctionOverrideHelpItemForTooltip(
|
|||||||
const ClangdAstNode baseClassNode = baseTypeNodeChildren->first();
|
const ClangdAstNode baseClassNode = baseTypeNodeChildren->first();
|
||||||
if (!baseClassNode.detail())
|
if (!baseClassNode.detail())
|
||||||
return false;
|
return false;
|
||||||
d->setHelpItemForTooltip(token, *baseClassNode.detail() + "::" + *methodNode.detail(),
|
d->setHelpItemForTooltip(token,
|
||||||
HelpItem::Function, "()");
|
uri,
|
||||||
|
*baseClassNode.detail() + "::" + *methodNode.detail(),
|
||||||
|
HelpItem::Function,
|
||||||
|
"()");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1233,7 +1256,9 @@ QTextCursor ClangdClient::Private::adjustedCursor(const QTextCursor &cursor,
|
|||||||
return cursor;
|
return cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangdClient::Private::setHelpItemForTooltip(const MessageId &token, const QString &fqn,
|
void ClangdClient::Private::setHelpItemForTooltip(const MessageId &token,
|
||||||
|
const DocumentUri &uri,
|
||||||
|
const QString &fqn,
|
||||||
HelpItem::Category category,
|
HelpItem::Category category,
|
||||||
const QString &type)
|
const QString &type)
|
||||||
{
|
{
|
||||||
@@ -1256,7 +1281,7 @@ void ClangdClient::Private::setHelpItemForTooltip(const MessageId &token, const
|
|||||||
if (category == HelpItem::Enum && !type.isEmpty())
|
if (category == HelpItem::Enum && !type.isEmpty())
|
||||||
mark = type;
|
mark = type;
|
||||||
|
|
||||||
HelpItem helpItem(helpIds, mark, category);
|
const HelpItem helpItem(helpIds, uri.toFilePath(), mark, category);
|
||||||
if (isTesting)
|
if (isTesting)
|
||||||
emit q->helpItemGathered(helpItem);
|
emit q->helpItemGathered(helpItem);
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -71,6 +71,7 @@ public:
|
|||||||
const LanguageServerProtocol::DocumentUri &uri);
|
const LanguageServerProtocol::DocumentUri &uri);
|
||||||
bool gatherMemberFunctionOverrideHelpItemForTooltip(
|
bool gatherMemberFunctionOverrideHelpItemForTooltip(
|
||||||
const LanguageServerProtocol::MessageId &token,
|
const LanguageServerProtocol::MessageId &token,
|
||||||
|
const LanguageServerProtocol::DocumentUri &uri,
|
||||||
const QList<ClangdAstNode> &path);
|
const QList<ClangdAstNode> &path);
|
||||||
|
|
||||||
void setVirtualRanges(const Utils::FilePath &filePath,
|
void setVirtualRanges(const Utils::FilePath &filePath,
|
||||||
|
|||||||
@@ -70,8 +70,10 @@ void CMakeEditor::contextHelp(const HelpCallback &callback) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
const QString id = "command/" + textAt(begin, end - begin).toLower();
|
const QString id = "command/" + textAt(begin, end - begin).toLower();
|
||||||
callback(
|
callback({{id, Utils::Text::wordUnderCursor(editorWidget()->textCursor())},
|
||||||
{{id, Utils::Text::wordUnderCursor(editorWidget()->textCursor())}, {}, HelpItem::Unknown});
|
{},
|
||||||
|
{},
|
||||||
|
HelpItem::Unknown});
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -11,14 +11,16 @@
|
|||||||
|
|
||||||
using namespace Core;
|
using namespace Core;
|
||||||
|
|
||||||
|
Q_GLOBAL_STATIC(HelpItem::LinkNarrower, m_linkNarrower);
|
||||||
|
|
||||||
HelpItem::HelpItem() = default;
|
HelpItem::HelpItem() = default;
|
||||||
|
|
||||||
HelpItem::HelpItem(const char *helpId)
|
HelpItem::HelpItem(const char *helpId)
|
||||||
: HelpItem(QStringList(QString::fromUtf8(helpId)), {}, Unknown)
|
: HelpItem(QStringList(QString::fromUtf8(helpId)), {}, {}, Unknown)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
HelpItem::HelpItem(const QString &helpId)
|
HelpItem::HelpItem(const QString &helpId)
|
||||||
: HelpItem(QStringList(helpId), {}, Unknown)
|
: HelpItem(QStringList(helpId), {}, {}, Unknown)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
HelpItem::HelpItem(const QUrl &url)
|
HelpItem::HelpItem(const QUrl &url)
|
||||||
@@ -31,13 +33,20 @@ HelpItem::HelpItem(const QUrl &url, const QString &docMark, HelpItem::Category c
|
|||||||
, m_category(category)
|
, m_category(category)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
HelpItem::HelpItem(const QString &helpId, const QString &docMark, Category category)
|
HelpItem::HelpItem(const QString &helpId,
|
||||||
: HelpItem(QStringList(helpId), docMark, category)
|
const Utils::FilePath &filePath,
|
||||||
|
const QString &docMark,
|
||||||
|
Category category)
|
||||||
|
: HelpItem(QStringList(helpId), filePath, docMark, category)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
HelpItem::HelpItem(const QStringList &helpIds, const QString &docMark, Category category)
|
HelpItem::HelpItem(const QStringList &helpIds,
|
||||||
|
const Utils::FilePath &filePath,
|
||||||
|
const QString &docMark,
|
||||||
|
Category category)
|
||||||
: m_docMark(docMark)
|
: m_docMark(docMark)
|
||||||
, m_category(category)
|
, m_category(category)
|
||||||
|
, m_filePath(filePath)
|
||||||
{
|
{
|
||||||
setHelpIds(helpIds);
|
setHelpIds(helpIds);
|
||||||
}
|
}
|
||||||
@@ -75,6 +84,24 @@ void HelpItem::setCategory(Category cat)
|
|||||||
HelpItem::Category HelpItem::category() const
|
HelpItem::Category HelpItem::category() const
|
||||||
{ return m_category; }
|
{ return m_category; }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Sets the \a filePath that this help item originates from, for example
|
||||||
|
in case of context help.
|
||||||
|
*/
|
||||||
|
void HelpItem::setFilePath(const Utils::FilePath &filePath)
|
||||||
|
{
|
||||||
|
m_filePath = filePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns the filePath that this help item originates from, for example
|
||||||
|
in case of context help.
|
||||||
|
*/
|
||||||
|
Utils::FilePath HelpItem::filePath() const
|
||||||
|
{
|
||||||
|
return m_filePath;
|
||||||
|
}
|
||||||
|
|
||||||
bool HelpItem::isEmpty() const
|
bool HelpItem::isEmpty() const
|
||||||
{
|
{
|
||||||
return m_helpUrl.isEmpty() && m_helpIds.isEmpty();
|
return m_helpUrl.isEmpty() && m_helpIds.isEmpty();
|
||||||
@@ -184,7 +211,7 @@ static QVersionNumber qtVersionHeuristic(const QString &digits)
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::pair<QUrl, QVersionNumber> extractVersion(const QUrl &url)
|
std::pair<QUrl, QVersionNumber> HelpItem::extractQtVersionNumber(const QUrl &url)
|
||||||
{
|
{
|
||||||
const QString host = url.host();
|
const QString host = url.host();
|
||||||
const QStringList hostParts = host.split('.');
|
const QStringList hostParts = host.split('.');
|
||||||
@@ -203,8 +230,8 @@ static std::pair<QUrl, QVersionNumber> extractVersion(const QUrl &url)
|
|||||||
// sort primary by "url without version" and seconday by "version"
|
// sort primary by "url without version" and seconday by "version"
|
||||||
static bool helpUrlLessThan(const QUrl &a, const QUrl &b)
|
static bool helpUrlLessThan(const QUrl &a, const QUrl &b)
|
||||||
{
|
{
|
||||||
const std::pair<QUrl, QVersionNumber> va = extractVersion(a);
|
const std::pair<QUrl, QVersionNumber> va = HelpItem::extractQtVersionNumber(a);
|
||||||
const std::pair<QUrl, QVersionNumber> vb = extractVersion(b);
|
const std::pair<QUrl, QVersionNumber> vb = HelpItem::extractQtVersionNumber(b);
|
||||||
const QString sa = va.first.toString();
|
const QString sa = va.first.toString();
|
||||||
const QString sb = vb.first.toString();
|
const QString sb = vb.first.toString();
|
||||||
if (sa == sb)
|
if (sa == sb)
|
||||||
@@ -258,7 +285,7 @@ static const HelpItem::Links getBestLinks(const HelpItem::Links &links)
|
|||||||
HelpItem::Links bestLinks;
|
HelpItem::Links bestLinks;
|
||||||
QUrl currentUnversionedUrl;
|
QUrl currentUnversionedUrl;
|
||||||
for (const HelpItem::Link &link : links) {
|
for (const HelpItem::Link &link : links) {
|
||||||
const QUrl unversionedUrl = extractVersion(link.second).first;
|
const QUrl unversionedUrl = HelpItem::extractQtVersionNumber(link.second).first;
|
||||||
if (unversionedUrl != currentUnversionedUrl) {
|
if (unversionedUrl != currentUnversionedUrl) {
|
||||||
currentUnversionedUrl = unversionedUrl;
|
currentUnversionedUrl = unversionedUrl;
|
||||||
bestLinks.push_back(link);
|
bestLinks.push_back(link);
|
||||||
@@ -279,7 +306,7 @@ static const HelpItem::Links getBestLink(const HelpItem::Links &links)
|
|||||||
// Default to first link if version extraction failed, possibly because it is not a Qt doc link
|
// Default to first link if version extraction failed, possibly because it is not a Qt doc link
|
||||||
HelpItem::Link bestLink = links.front();
|
HelpItem::Link bestLink = links.front();
|
||||||
for (const HelpItem::Link &link : links) {
|
for (const HelpItem::Link &link : links) {
|
||||||
const QVersionNumber version = extractVersion(link.second).second;
|
const QVersionNumber version = HelpItem::extractQtVersionNumber(link.second).second;
|
||||||
if (version > highestVersion) {
|
if (version > highestVersion) {
|
||||||
highestVersion = version;
|
highestVersion = version;
|
||||||
bestLink = link;
|
bestLink = link;
|
||||||
@@ -292,7 +319,8 @@ const HelpItem::Links HelpItem::bestLinks() const
|
|||||||
{
|
{
|
||||||
if (isFuzzyMatch())
|
if (isFuzzyMatch())
|
||||||
return getBestLinks(links());
|
return getBestLinks(links());
|
||||||
return getBestLink(links());
|
const Links filteredLinks = *m_linkNarrower ? (*m_linkNarrower)(*this, links()) : links();
|
||||||
|
return getBestLink(filteredLinks);
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString HelpItem::keyword() const
|
const QString HelpItem::keyword() const
|
||||||
@@ -306,3 +334,8 @@ bool HelpItem::isFuzzyMatch() const
|
|||||||
links();
|
links();
|
||||||
return m_isFuzzyMatch;
|
return m_isFuzzyMatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void HelpItem::setLinkNarrower(const LinkNarrower &narrower)
|
||||||
|
{
|
||||||
|
*m_linkNarrower = narrower;
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
#include "core_global.h"
|
#include "core_global.h"
|
||||||
|
|
||||||
|
#include <utils/filepath.h>
|
||||||
|
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
@@ -12,6 +14,10 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QVersionNumber;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
|
|
||||||
class CORE_EXPORT HelpItem
|
class CORE_EXPORT HelpItem
|
||||||
@@ -19,6 +25,7 @@ class CORE_EXPORT HelpItem
|
|||||||
public:
|
public:
|
||||||
using Link = std::pair<QString, QUrl>;
|
using Link = std::pair<QString, QUrl>;
|
||||||
using Links = std::vector<Link>;
|
using Links = std::vector<Link>;
|
||||||
|
using LinkNarrower = std::function<Links(const HelpItem &, const Links &)>;
|
||||||
|
|
||||||
enum Category {
|
enum Category {
|
||||||
ClassOrNamespace,
|
ClassOrNamespace,
|
||||||
@@ -36,8 +43,14 @@ public:
|
|||||||
HelpItem();
|
HelpItem();
|
||||||
HelpItem(const char *helpId);
|
HelpItem(const char *helpId);
|
||||||
HelpItem(const QString &helpId);
|
HelpItem(const QString &helpId);
|
||||||
HelpItem(const QString &helpId, const QString &docMark, Category category);
|
HelpItem(const QString &helpId,
|
||||||
HelpItem(const QStringList &helpIds, const QString &docMark, Category category);
|
const Utils::FilePath &filePath,
|
||||||
|
const QString &docMark,
|
||||||
|
Category category);
|
||||||
|
HelpItem(const QStringList &helpIds,
|
||||||
|
const Utils::FilePath &filePath,
|
||||||
|
const QString &docMark,
|
||||||
|
Category category);
|
||||||
explicit HelpItem(const QUrl &url);
|
explicit HelpItem(const QUrl &url);
|
||||||
HelpItem(const QUrl &url, const QString &docMark, Category category);
|
HelpItem(const QUrl &url, const QString &docMark, Category category);
|
||||||
|
|
||||||
@@ -53,6 +66,9 @@ public:
|
|||||||
void setCategory(Category cat);
|
void setCategory(Category cat);
|
||||||
Category category() const;
|
Category category() const;
|
||||||
|
|
||||||
|
void setFilePath(const Utils::FilePath &filePath);
|
||||||
|
Utils::FilePath filePath() const;
|
||||||
|
|
||||||
bool isEmpty() const;
|
bool isEmpty() const;
|
||||||
bool isValid() const;
|
bool isValid() const;
|
||||||
|
|
||||||
@@ -62,6 +78,10 @@ public:
|
|||||||
const QString keyword() const;
|
const QString keyword() const;
|
||||||
bool isFuzzyMatch() const;
|
bool isFuzzyMatch() const;
|
||||||
|
|
||||||
|
// used by QtSupport to narrow to "best" Qt version
|
||||||
|
static void setLinkNarrower(const LinkNarrower &narrower);
|
||||||
|
static std::pair<QUrl, QVersionNumber> extractQtVersionNumber(const QUrl &url);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString extractContent(bool extended) const;
|
QString extractContent(bool extended) const;
|
||||||
|
|
||||||
@@ -69,6 +89,7 @@ private:
|
|||||||
QStringList m_helpIds;
|
QStringList m_helpIds;
|
||||||
QString m_docMark;
|
QString m_docMark;
|
||||||
Category m_category = Unknown;
|
Category m_category = Unknown;
|
||||||
|
Utils::FilePath m_filePath;
|
||||||
mutable std::optional<Links> m_helpLinks; // cached help links
|
mutable std::optional<Links> m_helpLinks; // cached help links
|
||||||
mutable std::optional<QString> m_firstParagraph;
|
mutable std::optional<QString> m_firstParagraph;
|
||||||
mutable QString m_keyword;
|
mutable QString m_keyword;
|
||||||
|
|||||||
@@ -50,16 +50,20 @@ private:
|
|||||||
tip += evaluator.diagnosis();
|
tip += evaluator.diagnosis();
|
||||||
setPriority(Priority_Diagnostic);
|
setPriority(Priority_Diagnostic);
|
||||||
}
|
}
|
||||||
|
const Utils::FilePath filePath = editorWidget->textDocument()->filePath();
|
||||||
const QStringList fallback = identifierWordsUnderCursor(tc);
|
const QStringList fallback = identifierWordsUnderCursor(tc);
|
||||||
if (evaluator.identifiedCppElement()) {
|
if (evaluator.identifiedCppElement()) {
|
||||||
const QSharedPointer<CppElement> &cppElement = evaluator.cppElement();
|
const QSharedPointer<CppElement> &cppElement = evaluator.cppElement();
|
||||||
const QStringList candidates = cppElement->helpIdCandidates;
|
const QStringList candidates = cppElement->helpIdCandidates;
|
||||||
const HelpItem helpItem(candidates + fallback, cppElement->helpMark, cppElement->helpCategory);
|
const HelpItem helpItem(candidates + fallback,
|
||||||
|
filePath,
|
||||||
|
cppElement->helpMark,
|
||||||
|
cppElement->helpCategory);
|
||||||
setLastHelpItemIdentified(helpItem);
|
setLastHelpItemIdentified(helpItem);
|
||||||
if (!helpItem.isValid())
|
if (!helpItem.isValid())
|
||||||
tip += cppElement->tooltip;
|
tip += cppElement->tooltip;
|
||||||
} else {
|
} else {
|
||||||
setLastHelpItemIdentified({fallback, {}, HelpItem::Unknown});
|
setLastHelpItemIdentified({fallback, filePath, {}, HelpItem::Unknown});
|
||||||
}
|
}
|
||||||
setToolTip(tip);
|
setToolTip(tip);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -143,7 +143,10 @@ bool QmlJSHoverHandler::setQmlTypeHelp(const ScopeChain &scopeChain, const Docum
|
|||||||
helpIdPieces.removeAt(1);
|
helpIdPieces.removeAt(1);
|
||||||
helpIdCandidates += helpIdPieces.join('.');
|
helpIdCandidates += helpIdPieces.join('.');
|
||||||
|
|
||||||
const HelpItem helpItem(helpIdCandidates, qName.join('.'), HelpItem::QmlComponent);
|
const HelpItem helpItem(helpIdCandidates,
|
||||||
|
qmlDocument->fileName(),
|
||||||
|
qName.join('.'),
|
||||||
|
HelpItem::QmlComponent);
|
||||||
const HelpItem::Links links = helpItem.links();
|
const HelpItem::Links links = helpItem.links();
|
||||||
|
|
||||||
// Check if the module name contains a major version.
|
// Check if the module name contains a major version.
|
||||||
@@ -474,7 +477,10 @@ bool QmlJSHoverHandler::setQmlHelpItem(const ScopeChain &scopeChain,
|
|||||||
+ "::" + name,
|
+ "::" + name,
|
||||||
"QML." + className + "::" + name,
|
"QML." + className + "::" + name,
|
||||||
className + "::" + name};
|
className + "::" + name};
|
||||||
const HelpItem helpItem(helpIdCandidates, name, HelpItem::QmlProperty);
|
const HelpItem helpItem(helpIdCandidates,
|
||||||
|
qmlDocument->fileName(),
|
||||||
|
name,
|
||||||
|
HelpItem::QmlProperty);
|
||||||
if (helpItem.isValid()) {
|
if (helpItem.isValid()) {
|
||||||
setLastHelpItemIdentified(helpItem);
|
setLastHelpItemIdentified(helpItem);
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -28,10 +28,12 @@
|
|||||||
#include <projectexplorer/session.h>
|
#include <projectexplorer/session.h>
|
||||||
#include <projectexplorer/target.h>
|
#include <projectexplorer/target.h>
|
||||||
|
|
||||||
|
#include <utils/filepath.h>
|
||||||
#include <utils/infobar.h>
|
#include <utils/infobar.h>
|
||||||
#include <utils/macroexpander.h>
|
#include <utils/macroexpander.h>
|
||||||
|
|
||||||
using namespace Core;
|
using namespace Core;
|
||||||
|
using namespace Utils;
|
||||||
using namespace ProjectExplorer;
|
using namespace ProjectExplorer;
|
||||||
|
|
||||||
namespace QtSupport {
|
namespace QtSupport {
|
||||||
@@ -180,6 +182,53 @@ void QtSupportPlugin::extensionsInitialized()
|
|||||||
return qt ? qt->hostLibexecPath().toUserOutput() : QString();
|
return qt ? qt->hostLibexecPath().toUserOutput() : QString();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
HelpItem::setLinkNarrower([](const HelpItem &item, const HelpItem::Links &links) {
|
||||||
|
const FilePath filePath = item.filePath();
|
||||||
|
if (filePath.isEmpty())
|
||||||
|
return links;
|
||||||
|
const Project *project = SessionManager::projectForFile(filePath);
|
||||||
|
Target *target = project ? project->activeTarget() : nullptr;
|
||||||
|
QtVersion *qt = target ? QtKitAspect::qtVersion(target->kit()) : nullptr;
|
||||||
|
if (!qt)
|
||||||
|
return links;
|
||||||
|
|
||||||
|
// Find best-suited documentation version, so
|
||||||
|
// sort into buckets of links with exact, same minor, and same major, and return the first
|
||||||
|
// that has entries.
|
||||||
|
const QVersionNumber qtVersion = qt->qtVersion();
|
||||||
|
HelpItem::Links exactVersion;
|
||||||
|
HelpItem::Links sameMinor;
|
||||||
|
HelpItem::Links sameMajor;
|
||||||
|
bool hasExact = false;
|
||||||
|
bool hasSameMinor = false;
|
||||||
|
bool hasSameMajor = false;
|
||||||
|
for (const HelpItem::Link &link : links) {
|
||||||
|
const QUrl url = link.second;
|
||||||
|
const QVersionNumber version = HelpItem::extractQtVersionNumber(url).second;
|
||||||
|
// version.isNull() means it's not a Qt documentation URL, so include regardless
|
||||||
|
if (version.isNull() || version.majorVersion() == qtVersion.majorVersion()) {
|
||||||
|
sameMajor.push_back(link);
|
||||||
|
hasSameMajor = true;
|
||||||
|
if (version.isNull() || version.minorVersion() == qtVersion.minorVersion()) {
|
||||||
|
sameMinor.push_back(link);
|
||||||
|
hasSameMinor = true;
|
||||||
|
if (version.isNull() || version.microVersion() == qtVersion.microVersion()) {
|
||||||
|
exactVersion.push_back(link);
|
||||||
|
hasExact = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// HelpItem itself finds the highest version within sameMinor/Major/etc itself
|
||||||
|
if (hasExact)
|
||||||
|
return exactVersion;
|
||||||
|
if (hasSameMinor)
|
||||||
|
return sameMinor;
|
||||||
|
if (hasSameMajor)
|
||||||
|
return sameMajor;
|
||||||
|
return links;
|
||||||
|
});
|
||||||
|
|
||||||
askAboutQtInstallation();
|
askAboutQtInstallation();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user