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
qqmlcompletionsupport.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 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
5#include "qqmllsutils_p.h"
6
7#include <QtLanguageServer/private/qlanguageserverspectypes_p.h>
8#include <QtCore/qthreadpool.h>
9#include <QtCore/private/qduplicatetracker_p.h>
10#include <QtCore/QRegularExpression>
11#include <QtQmlDom/private/qqmldomexternalitems_p.h>
12#include <QtQmlDom/private/qqmldomtop_p.h>
13#include <QtQml/private/qqmlsignalnames_p.h>
14
16using namespace QLspSpecification;
17using namespace QQmlJS::Dom;
18using namespace Qt::StringLiterals;
19
21 Response &&response)
22{
23 // do not call BaseRequest::fillFrom() to avoid taking the Mutex twice and getting an
24 // inconsistent state.
26 m_response = std::move(response);
27
28 if (!doc.textDocument)
29 return false;
30
31 std::optional<int> targetVersion;
32 {
33 QMutexLocker l(doc.textDocument->mutex());
34 targetVersion = doc.textDocument->version();
35 code = doc.textDocument->toPlainText();
36 }
37 m_minVersion = (targetVersion ? *targetVersion : 0);
38
39 return true;
40}
41
43 : BaseT(codeModel), m_completionEngine(codeModel->pluginLoader())
44{
45}
46
47void QmlCompletionSupport::registerHandlers(QLanguageServer *, QLanguageServerProtocol *protocol)
48{
49 protocol->registerCompletionRequestHandler(getRequestHandler());
50 protocol->registerCompletionItemResolveRequestHandler(
51 [](const QByteArray &, const CompletionItem &cParams,
52 LSPResponse<CompletionItem> &&response) { response.sendResponse(cParams); });
53}
54
56{
57 return u"QmlCompletionSupport"_s;
58}
59
61 const QLspSpecification::InitializeParams &,
62 QLspSpecification::InitializeResult &serverCapabilities)
63{
64 QLspSpecification::CompletionOptions cOptions;
65 if (serverCapabilities.capabilities.completionProvider)
66 cOptions = *serverCapabilities.capabilities.completionProvider;
67 cOptions.resolveProvider = false;
68 cOptions.triggerCharacters = QList<QByteArray>({ QByteArray(".") });
69 serverCapabilities.capabilities.completionProvider = cOptions;
70}
71
72void QmlCompletionSupport::process(RequestPointerArgument req)
73{
75 m_codeModel->snapshotByUrl(req->m_parameters.textDocument.uri);
76 req->sendCompletions(req->completions(doc, m_completionEngine));
77}
78
80{
81 return QString::fromUtf8(m_parameters.textDocument.uri) + u":"
82 + QString::number(m_parameters.position.line) + u":"
83 + QString::number(m_parameters.position.character);
84}
85
86void CompletionRequest::sendCompletions(const QList<CompletionItem> &completions)
87{
88 m_response.sendResponse(completions);
89}
90
92{
93 if (position >= code.size())
94 return false;
95
96 auto newline =
97 std::find_if(std::next(code.cbegin(), position), code.cend(),
98 [](const QChar &c) { return c == u'\n' || c == u'\r' || !c.isSpace(); });
99
100 return newline == code.cend() || newline->isSpace();
101}
102
115{
116 // automatic semicolon insertion after dots, if there is nothing behind the dot!
117 if (position > 0 && code[position - 1] == u'.' && positionIsFollowedBySpaces(position, code)) {
118 qCWarning(QQmlLSCompletionLog)
119 << "Patching invalid document: adding a semicolon after '.' for "
120 << QString::fromUtf8(m_parameters.textDocument.uri);
121
122 const QString patchedCode =
123 code.first(position).append(u"_dummyIdentifier;").append(code.sliced(position));
124
125 // create a new (local) Dom only for the completions.
126 // This avoids weird behaviors, like the linting module complaining about the inserted
127 // semicolon that the user cannot see, for example.
128 DomItem newCurrent = file.environment().makeCopy(DomItem::CopyOption::EnvConnected).item();
129
131 auto newCurrentPtr = newCurrent.ownerAs<DomEnvironment>();
132 newCurrentPtr->loadFile(
133 FileToLoad::fromMemory(newCurrentPtr, file.canonicalFilePath(), patchedCode),
134 [&result](Path, const DomItem &, const DomItem &newValue) {
135 result = newValue.fileObject();
136 });
137 newCurrentPtr->loadPendingDependencies();
138 return result;
139 }
140
141 qCWarning(QQmlLSCompletionLog) << "No valid document for completions for "
142 << QString::fromUtf8(m_parameters.textDocument.uri);
143
144 return file;
145}
146
148 const QQmlLSCompletion &completionEngine) const
149{
150 QList<CompletionItem> res;
151
152
154 m_parameters.position.character);
155
156 const bool useValidDoc =
158
159 const DomItem file = useValidDoc
162
163 // clear reference cache to resolve latest versions (use a local env instead?)
164 if (std::shared_ptr<DomEnvironment> envPtr = file.environment().ownerAs<DomEnvironment>())
165 envPtr->clearReferenceCache();
166
167
169 auto itemsFound = QQmlLSUtils::itemsFromTextLocation(file, m_parameters.position.line,
170 m_parameters.position.character
171 - ctx.filterChars().size());
172 if (itemsFound.size() > 1) {
174 for (auto &it : itemsFound)
175 paths.append(it.domItem.canonicalPath().toString());
176 qCWarning(QQmlLSCompletionLog) << "Multiple elements of " << urlAndPos()
177 << " at the same depth:" << paths << "(using first)";
178 }
179 DomItem currentItem;
180 if (!itemsFound.isEmpty())
181 currentItem = itemsFound.first().domItem;
182 else
183 qCDebug(QQmlLSCompletionLog) << "No items found for completions at" << urlAndPos();
184 qCDebug(QQmlLSCompletionLog) << "Completion at " << urlAndPos() << " "
185 << m_parameters.position.line << ":"
186 << m_parameters.position.character << "offset:" << pos
187 << "base:" << ctx.base() << "filter:" << ctx.filterChars()
188 << "lastVersion:" << (doc.docVersion ? (*doc.docVersion) : -1)
189 << "validVersion:"
190 << (doc.validDocVersion ? (*doc.validDocVersion) : -1) << "in"
191 << currentItem.internalKindStr() << currentItem.canonicalPath();
192 auto result = completionEngine.completions(currentItem, ctx);
193 return result;
194}
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore
qint64 size() const override
\reimp
Definition qfile.cpp:1179
Implements a server for the language server protocol.
\inmodule QtCore
Definition qmutex.h:313
Represents a consistent set of types organized in modules, it is the top level of the DOM.
void loadFile(const FileToLoad &file, const Callback &callback, std::optional< DomType > fileType=std::optional< DomType >(), const ErrorHandler &h=nullptr)
QString internalKindStr() const
DomItem fileObject(GoTo option=GoTo::Strict) const
Path canonicalPath() const
static FileToLoad fromMemory(const std::weak_ptr< DomEnvironment > &environment, const QString &path, const QString &data)
QQmlLSCompletion provides completions for all kinds of QML and JS constructs.
static QList< QQmlLSUtilsItemLocation > itemsFromTextLocation(const DomItem &file, int line, int character)
Find the DomItem representing the object situated in file at given line and character/column.
static qsizetype textOffsetFrom(const QString &code, int row, int character)
Convert a text position from (line, column) into an offset.
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString sliced(qsizetype pos) const &
Definition qstring.h:394
const_iterator cbegin() const
Definition qstring.h:1353
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
const_iterator cend() const
Definition qstring.h:1361
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
QString first(qsizetype n) const &
Definition qstring.h:390
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:8084
QString & append(QChar c)
Definition qstring.cpp:3252
QmlCompletionSupport(QmlLsp::QQmlCodeModel *codeModel)
QString name() const override
void registerHandlers(QLanguageServer *server, QLanguageServerProtocol *protocol) override
QQmlLSCompletion m_completionEngine
void setupCapabilities(const QLspSpecification::InitializeParams &clientInfo, QLspSpecification::InitializeResult &) override
void process(RequestPointerArgument req) override
std::optional< int > docVersion
std::optional< int > validDocVersion
QQmlJS::Dom::DomItem validDoc
std::shared_ptr< Utils::TextDocument > textDocument
OpenDocumentSnapshot snapshotByUrl(const QByteArray &url)
EGLContext ctx
QSet< QString >::iterator it
Combined button and popup list for selecting options.
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
#define qCWarning(category,...)
#define qCDebug(category,...)
GLsizei const GLuint * paths
void ** params
GLuint res
const GLubyte * c
GLuint64EXT * result
[6]
static bool positionIsFollowedBySpaces(qsizetype position, const QString &code)
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
ptrdiff_t qsizetype
Definition qtypes.h:165
QFile file
[0]
void sendCompletions(const QList< QLspSpecification::CompletionItem > &completions)
DomItem patchInvalidFileForParser(const DomItem &file, qsizetype position) const
QList< QLspSpecification::CompletionItem > completions(QmlLsp::OpenDocumentSnapshot &doc, const QQmlLSCompletion &completionEngine) const
bool fillFrom(QmlLsp::OpenDocument doc, const Parameters &params, Response &&response)