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;
87 if (position >= code.size())
91 std::find_if(
std::next(code.cbegin(), position), code.cend(),
92 [](
const QChar &c) {
return c == u'\n' || c == u'\r' || !c.isSpace(); });
94 return newline == code.cend() || newline->isSpace();
108DomItem CompletionRequest::patchInvalidFileForParser(
const DomItem &file, qsizetype position)
const
111 if (position > 0 && code[position - 1] == u'.' && positionIsFollowedBySpaces(position, code)) {
112 qCWarning(QQmlLSCompletionLog)
113 <<
"Patching invalid document: adding a semicolon after '.' for "
114 << QString::fromUtf8(m_parameters.textDocument.uri);
116 const QString patchedCode =
117 code.first(position).append(u"_dummyIdentifier;").append(code.sliced(position));
122 DomItem newCurrent = file.environment().makeCopy(DomItem::CopyOption::EnvConnected).item();
125 auto newCurrentPtr = newCurrent.ownerAs<DomEnvironment>();
126 newCurrentPtr->loadFile(
127 FileToLoad::fromMemory(newCurrentPtr, file.canonicalFilePath(), patchedCode),
128 [&result](Path,
const DomItem &,
const DomItem &newValue) {
129 result = newValue.fileObject();
131 newCurrentPtr->loadPendingDependencies();
135 qCWarning(QQmlLSCompletionLog) <<
"No valid document for completions for "
136 << QString::fromUtf8(m_parameters.textDocument.uri);
141QList<CompletionItem> CompletionRequest::completions(QmlLsp::OpenDocumentSnapshot &doc,
142 const QQmlLSCompletion &completionEngine)
const
144 QList<CompletionItem> res;
147 const qsizetype pos = QQmlLSUtils::textOffsetFrom(code, m_parameters.position.line,
148 m_parameters.position.character);
150 const bool useValidDoc =
151 doc.validDoc && doc.validDocVersion && *doc.validDocVersion >= m_minVersion;
153 const DomItem file = useValidDoc
154 ? doc.validDoc.fileObject(QQmlJS::Dom::GoTo::MostLikely)
155 : patchInvalidFileForParser(doc.doc.fileObject(QQmlJS::Dom::GoTo::MostLikely), pos);
158 if (std::shared_ptr<DomEnvironment> envPtr = file.environment().ownerAs<DomEnvironment>())
159 envPtr->clearReferenceCache();
162 CompletionContextStrings ctx(code, pos);
163 auto itemsFound = QQmlLSUtils::itemsFromTextLocation(file, m_parameters.position.line,
164 m_parameters.position.character
165 - ctx.filterChars().size());
166 if (itemsFound.isEmpty()) {
167 qCDebug(QQmlLSCompletionLog) <<
"No items found for completions at" << urlAndPos();
171 if (itemsFound.size() > 1) {
173 for (
auto &it : itemsFound)
174 paths.append(it.domItem.canonicalPath().toString());
175 qCWarning(QQmlLSCompletionLog) <<
"Multiple elements of " << urlAndPos()
176 <<
" at the same depth:" << paths <<
"(using first)";
178 const DomItem currentItem = itemsFound.first().domItem;
179 qCDebug(QQmlLSCompletionLog) <<
"Completion at " << urlAndPos() <<
" "
180 << m_parameters.position.line <<
":"
181 << m_parameters.position.character <<
"offset:" << pos
182 <<
"base:" << ctx.base() <<
"filter:" << ctx.filterChars()
183 <<
"lastVersion:" << (doc.docVersion ? (*doc.docVersion) : -1)
185 << (doc.validDocVersion ? (*doc.validDocVersion) : -1) <<
"in"
186 << currentItem.internalKindStr() << currentItem.canonicalPath();
187 auto result = completionEngine.completions(currentItem, ctx);