21bool CompletionRequest::fillFrom(QmlLsp::OpenDocument doc,
const Parameters ¶ms,
26 m_parameters = params;
27 m_response = std::move(response);
29 if (!doc.textDocument)
32 std::optional<
int> targetVersion;
34 QMutexLocker l(doc.textDocument->mutex());
35 targetVersion = doc.textDocument->version();
36 code = doc.textDocument->toPlainText();
38 m_minVersion = (targetVersion ? *targetVersion : 0);
58 QLspSpecification::CompletionOptions cOptions;
59 if (caps.completionProvider)
60 cOptions = *caps.completionProvider;
61 cOptions.resolveProvider =
false;
62 cOptions.triggerCharacters = QList<QByteArray>({ QByteArray(
".") });
63 caps.completionProvider = cOptions;
85 if (position >= code.size())
89 std::find_if(
std::next(code.cbegin(), position), code.cend(),
90 [](
const QChar &c) {
return c == u'\n' || c == u'\r' || !c.isSpace(); });
92 return newline == code.cend() || newline->isSpace();
107CompletionRequest::patchInvalidFileForParser(
const DomItem &file, qsizetype position,
108 QmlLsp::QQmlCodeModelManager *codeModelManager)
const
110 const QByteArray &uri = m_parameters.textDocument.uri;
113 if (position > 0 && code[position - 1] == u'.' && positionIsFollowedBySpaces(position, code)) {
114 qCWarning(QQmlLSCompletionLog)
115 <<
"Patching invalid document: adding a semicolon after '.' for "
116 << QString::fromUtf8(uri);
118 const QString patchedCode =
119 code.first(position).append(u"_dummyIdentifier;").append(code.sliced(position));
124 DomItem newCurrent = file.environment().makeCopy(DomItem::CopyOption::EnvConnected).item();
127 auto newCurrentPtr = newCurrent.ownerAs<DomEnvironment>();
128 newCurrentPtr->setLoadPaths(codeModelManager->importPathsForUrl(uri));
129 newCurrentPtr->setResourceFiles(codeModelManager->resourceFilesForFileUrl(uri));
130 newCurrentPtr->loadFile(
131 FileToLoad::fromMemory(newCurrentPtr, file.canonicalFilePath(), patchedCode),
132 [&result](Path,
const DomItem &,
const DomItem &newValue) {
133 result = newValue.fileObject();
135 newCurrentPtr->loadPendingDependencies();
139 qCWarning(QQmlLSCompletionLog)
140 <<
"No valid document for completions for " << QString::fromUtf8(uri);
145QList<CompletionItem> CompletionRequest::completions(QmlLsp::QQmlCodeModelManager *codeModelManager,
146 const QQmlLSCompletion &completionEngine)
const
148 QList<CompletionItem> res;
150 QmlLsp::OpenDocumentSnapshot doc =
151 codeModelManager->snapshotByUrl(m_parameters.textDocument.uri);
152 const qsizetype pos = QQmlLSUtils::textOffsetFrom(code, m_parameters.position.line,
153 m_parameters.position.character);
155 const bool useValidDoc =
156 doc.validDoc && doc.validDocVersion && *doc.validDocVersion >= m_minVersion;
158 const DomItem file = useValidDoc
159 ? doc.validDoc.fileObject(QQmlJS::Dom::GoTo::MostLikely)
160 : patchInvalidFileForParser(doc.doc.fileObject(QQmlJS::Dom::GoTo::MostLikely), pos,
164 if (std::shared_ptr<DomEnvironment> envPtr = file.environment().ownerAs<DomEnvironment>())
165 envPtr->clearReferenceCache();
168 CompletionContextStrings ctx(code, pos);
169 auto itemsFound = QQmlLSUtils::itemsFromTextLocation(file, m_parameters.position.line,
170 m_parameters.position.character
171 - ctx.filterChars().size());
172 if (itemsFound.isEmpty()) {
173 qCDebug(QQmlLSCompletionLog) <<
"No items found for completions at" << urlAndPos();
177 if (itemsFound.size() > 1) {
179 for (
auto &it : itemsFound)
180 paths.append(it.domItem.canonicalPath().toString());
181 qCWarning(QQmlLSCompletionLog) <<
"Multiple elements of " << urlAndPos()
182 <<
" at the same depth:" << paths <<
"(using first)";
184 const DomItem currentItem = itemsFound.first().domItem;
185 qCDebug(QQmlLSCompletionLog) <<
"Completion at " << urlAndPos() <<
" "
186 << m_parameters.position.line <<
":"
187 << m_parameters.position.character <<
"offset:" << pos
188 <<
"base:" << ctx.base() <<
"filter:" << ctx.filterChars()
189 <<
"lastVersion:" << (doc.docVersion ? (*doc.docVersion) : -1)
191 << (doc.validDocVersion ? (*doc.validDocVersion) : -1) <<
"in"
192 << currentItem.internalKindStr() << currentItem.canonicalPath();
193 auto result = completionEngine.completions(currentItem, ctx);