Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qqmllshelputils.cpp
Go to the documentation of this file.
1// Copyright (C) 2024 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qqmllshelputils_p.h"
5
6#include <QtQmlLS/private/qqmllsutils_p.h>
7#include <QtCore/private/qfactoryloader_p.h>
8#include <QtCore/qlibraryinfo.h>
9#include <QtCore/qdiriterator.h>
10#include <QtCore/qdir.h>
11#include <QtQmlCompiler/private/qqmljstyperesolver_p.h>
12#include <optional>
13
15
16Q_STATIC_LOGGING_CATEGORY(QQmlLSHelpUtilsLog, "qt.languageserver.helpUtils")
17
18using namespace QQmlJS::Dom;
19
20static QStringList documentationFiles(const QString &qtInstallationPath)
21{
23 QDirIterator dirIterator(qtInstallationPath, QStringList{ "*.qch"_L1 }, QDir::Files);
24 while (dirIterator.hasNext()) {
25 const auto fileInfo = dirIterator.nextFileInfo();
26 result << fileInfo.absoluteFilePath();
27 }
28 return result;
29}
30
32{
33 const QFactoryLoader pluginLoader(QQmlLSHelpPluginInterface_iid, u"/help"_s);
34 const auto keys = pluginLoader.metaDataKeys();
35 for (qsizetype i = 0; i < keys.size(); ++i) {
36 auto instance = qobject_cast<QQmlLSHelpPluginInterface *>(pluginLoader.instance(i));
37 if (instance) {
38 m_helpPlugin =
39 instance->initialize(QDir::tempPath() + "/collectionFile.qhc"_L1, nullptr);
40 break;
41 }
42 }
43}
44
46{
47 if (m_docRootPath == path)
48 return;
49 m_docRootPath = path;
50
51 const auto foundQchFiles = documentationFiles(path);
52 if (foundQchFiles.isEmpty()) {
53 qCWarning(QQmlLSHelpUtilsLog)
54 << "No documentation files found in the Qt doc installation path: " << path;
55 return;
56 }
57
58 return registerDocumentations(foundQchFiles);
59}
60
62{
63 return m_docRootPath;
64}
65
66void HelpManager::registerDocumentations(const QStringList &docs) const
67{
68 if (!m_helpPlugin)
69 return;
70 std::for_each(docs.cbegin(), docs.cend(),
71 [this](const auto &file) { m_helpPlugin->registerDocumentation(file); });
72}
73
74std::optional<QByteArray> HelpManager::extractDocumentation(const DomItem &item) const
75{
76 if (item.internalKind() == DomType::ScriptIdentifierExpression) {
77 const auto resolvedType =
79 if (!resolvedType)
80 return std::nullopt;
81
82 return extractDocumentationForIdentifiers(item, resolvedType.value());
83 } else {
84 return extractDocumentationForDomElements(item);
85 }
86
87 Q_UNREACHABLE_RETURN(std::nullopt);
88}
89
90std::optional<QByteArray>
91HelpManager::extractDocumentationForIdentifiers(const DomItem &item,
93{
94 const auto qmlFile = item.containingFile().as<QmlFile>();
95 if (!qmlFile)
96 return std::nullopt;
97 const auto links = collectDocumentationLinks(expr.semanticScope, qmlFile->typeResolver(),
98 expr.name.value());
99 switch (expr.type) {
104 ExtractDocumentation extractor(DomType::PropertyDefinition);
105 return tryExtract(extractor, links, expr.name.value());
106 }
112 ExtractDocumentation extractor(DomType::MethodInfo);
113 return tryExtract(extractor, links, expr.name.value());
114 }
118 ExtractDocumentation extractor(DomType::QmlObject);
119 return tryExtract(extractor, links, expr.name.value());
120 }
121
122 // Not implemented yet
125 default:
126 qCDebug(QQmlLSHelpUtilsLog)
127 << "Documentation extraction for" << expr.name.value() << "was not implemented";
128 return std::nullopt;
129 }
130 Q_UNREACHABLE_RETURN(std::nullopt);
131}
132
133std::optional<QByteArray> HelpManager::extractDocumentationForDomElements(const DomItem &item) const
134{
135 const auto qmlFile = item.containingFile().as<QmlFile>();
136 if (!qmlFile)
137 return std::nullopt;
138
139 const auto name = item.field(Fields::name).value().toString();
140 std::vector<QQmlLSHelpProviderBase::DocumentLink> links;
141 switch (item.internalKind()) {
142 case DomType::QmlObject: {
143 links = collectDocumentationLinks(item.nearestSemanticScope(), qmlFile->typeResolver(),
144 name);
145 break;
146 }
147 case DomType::PropertyDefinition: {
148 const auto scope =
149 QQmlLSUtils::findDefiningScopeForProperty(item.nearestSemanticScope(), name);
150 links = collectDocumentationLinks(scope, qmlFile->typeResolver(), name);
151 break;
152 }
153 case DomType::Binding: {
154 const auto scope =
155 QQmlLSUtils::findDefiningScopeForBinding(item.nearestSemanticScope(), name);
156 links = collectDocumentationLinks(scope, qmlFile->typeResolver(), name);
157 break;
158 }
159 case DomType::MethodInfo: {
160 const auto scope =
161 QQmlLSUtils::findDefiningScopeForMethod(item.nearestSemanticScope(), name);
162 links = collectDocumentationLinks(scope, qmlFile->typeResolver(), name);
163 break;
164 }
165 default:
166 qCDebug(QQmlLSHelpUtilsLog)
167 << item.internalKindStr() << "was not implemented for documentation extraction";
168 return std::nullopt;
169 }
170
171 ExtractDocumentation extractor(item.internalKind());
172 return tryExtract(extractor, links, name);
173}
174
175std::optional<QByteArray>
176HelpManager::tryExtract(ExtractDocumentation &extractor,
177 const std::vector<QQmlLSHelpProviderBase::DocumentLink> &links,
178 const QString &name) const
179{
180 if (!m_helpPlugin)
181 return std::nullopt;
182
183 for (auto &&link : links) {
184 const auto fileData = m_helpPlugin->fileData(link.url);
185 if (fileData.isEmpty()) {
186 qCDebug(QQmlLSHelpUtilsLog) << "No documentation found for" << link.url;
187 continue;
188 }
189 const auto &documentation = extractor.execute(QString::fromUtf8(fileData), name,
191 if (documentation.isEmpty())
192 continue;
193 return documentation.toUtf8();
194 }
195
196 return std::nullopt;
197}
198
199std::optional<QByteArray>
200HelpManager::documentationForItem(const DomItem &file, QLspSpecification::Position position) const
201{
202 if (!m_helpPlugin)
203 return std::nullopt;
204
205 if (m_helpPlugin->registeredNamespaces().empty())
206 return std::nullopt;
207
208 std::optional<QByteArray> result;
209 const auto [line, character] = position;
210 const auto itemLocations = QQmlLSUtils::itemsFromTextLocation(file, line, character);
211
212 // Process found item's internalKind and fetch its documentation.
213 for (const auto &entry : itemLocations) {
214 result = extractDocumentation(entry.domItem);
215 if (result.has_value())
216 break;
217 }
218
219 return result;
220}
221
222/*
223 * Returns the list of potential documentation links for the given item.
224 * A keyword is not necessarily a unique name, so we need to find the scope where
225 * the keyword is defined. If the item is a property, method or binding, it will
226 * search for the defining scope and return the documentation links by looking at
227 * the imported names. If the item is a QmlObject, it will return the documentation
228 * links for qmlobject name.
229 */
230std::vector<QQmlLSHelpProviderBase::DocumentLink>
231HelpManager::collectDocumentationLinks(
232 const QQmlJSScope::ConstPtr &scope, const std::shared_ptr<QQmlJSTypeResolver> &typeResolver,
233 const QString &name) const
234{
235 if (!m_helpPlugin)
236 return {};
237 const auto potentialDocumentationLinks =
238 [this](const QQmlJSScope::ConstPtr &scope,
239 const std::shared_ptr<QQmlJSTypeResolver> &typeResolver)
240 -> std::vector<QQmlLSHelpProviderBase::DocumentLink> {
241 if (!scope || !typeResolver)
242 return {};
243
244 std::vector<QQmlLSHelpProviderBase::DocumentLink> links;
245 const auto docLinks = m_helpPlugin->documentsForKeyword(typeResolver->nameForType(scope));
246 std::copy(docLinks.cbegin(), docLinks.cend(), std::back_inserter(links));
247 return links;
248 };
249
250 // If the scope is not found for the defined scope, return all the links related to this name.
251 const auto result = potentialDocumentationLinks(scope, typeResolver);
252 return result.empty() ? m_helpPlugin->documentsForKeyword(name) : result;
253}
254
std::optional< QByteArray > documentationForItem(const QQmlJS::Dom::DomItem &file, QLspSpecification::Position position) const
QString documentationRootPath() const
void setDocumentationRootPath(const QString &path)
The QDirIterator class provides an iterator for directory entrylists.
QFileInfo nextFileInfo()
static QString tempPath()
Returns the absolute canonical path of the system's temporary directory.
Definition qdir.cpp:2133
@ Files
Definition qdir.h:23
QString absoluteFilePath() const
A QmlFile, when loaded in a DomEnvironment that has the DomCreationOption::WithSemanticAnalysis,...
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6025
QQmlJSScope::ConstPtr findDefiningScopeForProperty(const QQmlJSScope::ConstPtr &referrerScope, const QString &nameToCheck)
Finds the scope where a property is first defined.
QQmlJSScope::ConstPtr findDefiningScopeForMethod(const QQmlJSScope::ConstPtr &referrerScope, const QString &nameToCheck)
QQmlJSScope::ConstPtr findDefiningScopeForBinding(const QQmlJSScope::ConstPtr &referrerScope, const QString &nameToCheck)
std::optional< ExpressionType > resolveExpressionType(const QQmlJS::Dom::DomItem &item, ResolveOptions options)
QList< ItemLocation > itemsFromTextLocation(const DomItem &file, int line, int character)
Find the DomItem representing the object situated in file at given line and character/column.
@ EnumeratorValueIdentifier
@ GroupedPropertyIdentifier
@ PropertyChangedHandlerIdentifier
@ PropertyChangedSignalIdentifier
@ SignalHandlerIdentifier
Combined button and popup list for selecting options.
#define qCWarning(category,...)
#define qCDebug(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)
GLuint name
GLuint entry
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
#define QQmlLSHelpPluginInterface_iid
static QStringList documentationFiles(const QString &qtInstallationPath)
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
ptrdiff_t qsizetype
Definition qtypes.h:165
QFile file
[0]
QStringList keys
QGraphicsItem * item
QQmlJSScope::ConstPtr semanticScope
std::optional< QString > name