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
qqmlcodemodel.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
4#include "qqmlcodemodel_p.h"
5#include "qqmllsplugin_p.h"
6#include "qtextdocument_p.h"
7#include "qqmllsutils_p.h"
8
9#include <QtCore/qfileinfo.h>
10#include <QtCore/qdir.h>
11#include <QtCore/qthreadpool.h>
12#include <QtCore/qlibraryinfo.h>
13#include <QtCore/qprocess.h>
14#include <QtCore/qdiriterator.h>
15#include <QtQmlDom/private/qqmldomtop_p.h>
16
17#include <memory>
18#include <algorithm>
19
21
22namespace QmlLsp {
23
24Q_LOGGING_CATEGORY(codeModelLog, "qt.languageserver.codemodel")
25
26using namespace QQmlJS::Dom;
27using namespace Qt::StringLiterals;
28
85 : QObject { parent },
86 m_importPaths(QLibraryInfo::path(QLibraryInfo::QmlImportsPath)),
87 m_currentEnv(std::make_shared<DomEnvironment>(
88 m_importPaths, DomEnvironment::Option::SingleThreaded,
89 DomCreationOptions{} | DomCreationOption::WithRecovery
92 m_validEnv(std::make_shared<DomEnvironment>(
93 m_importPaths, DomEnvironment::Option::SingleThreaded,
94 DomCreationOptions{} | DomCreationOption::WithRecovery
97 m_settings(settings),
98 m_pluginLoader(QmlLSPluginInterface_iid, u"/qmlls"_s)
99{
100}
101
107{
108 m_cmakeStatus = DoesNotHaveCMake;
109 m_cppFileWatcher.removePaths(m_cppFileWatcher.files());
110 QObject::disconnect(&m_cppFileWatcher, &QFileSystemWatcher::fileChanged, nullptr, nullptr);
111}
112
114{
115 QObject::disconnect(&m_cppFileWatcher, &QFileSystemWatcher::fileChanged, nullptr, nullptr);
116 while (true) {
117 bool shouldWait;
118 {
119 QMutexLocker l(&m_mutex);
120 m_state = State::Stopping;
121 m_openDocumentsToUpdate.clear();
122 shouldWait = m_nIndexInProgress != 0 || m_nUpdateInProgress != 0;
123 }
124 if (!shouldWait)
125 break;
127 }
128}
129
134
135int QQmlCodeModel::indexEvalProgress() const
136{
137 Q_ASSERT(!m_mutex.tryLock()); // should be called while locked
138 const int dirCost = 10;
139 int costToDo = 1;
140 for (const ToIndex &el : std::as_const(m_toIndex))
141 costToDo += dirCost * el.leftDepth;
142 costToDo += m_indexInProgressCost;
143 return m_indexDoneCost * 100 / (costToDo + m_indexDoneCost);
144}
145
146void QQmlCodeModel::indexStart()
147{
148 Q_ASSERT(!m_mutex.tryLock()); // should be called while locked
149 qCDebug(codeModelLog) << "indexStart";
150}
151
152void QQmlCodeModel::indexEnd()
153{
154 Q_ASSERT(!m_mutex.tryLock()); // should be called while locked
155 qCDebug(codeModelLog) << "indexEnd";
156 m_lastIndexProgress = 0;
157 m_nIndexInProgress = 0;
158 m_toIndex.clear();
159 m_indexInProgressCost = 0;
160 m_indexDoneCost = 0;
161}
162
163void QQmlCodeModel::indexSendProgress(int progress)
164{
165 if (progress <= m_lastIndexProgress)
166 return;
167 m_lastIndexProgress = progress;
168 // ### actually send progress
169}
170
171bool QQmlCodeModel::indexCancelled()
172{
173 QMutexLocker l(&m_mutex);
174 if (m_state == State::Stopping)
175 return true;
176 return false;
177}
178
179void QQmlCodeModel::indexDirectory(const QString &path, int depthLeft)
180{
181 if (indexCancelled())
182 return;
183 QDir dir(path);
184 if (depthLeft > 1) {
185 const QStringList dirs =
187 for (const QString &child : dirs)
188 addDirectory(dir.filePath(child), --depthLeft);
189 }
190 const QStringList qmljs =
191 dir.entryList(QStringList({ u"*.qml"_s, u"*.js"_s, u"*.mjs"_s }), QDir::Files);
192 int progress = 0;
193 {
194 QMutexLocker l(&m_mutex);
195 m_indexInProgressCost += qmljs.size();
196 progress = indexEvalProgress();
197 }
198 indexSendProgress(progress);
199 if (qmljs.isEmpty())
200 return;
201 DomItem newCurrent = m_currentEnv.makeCopy(DomItem::CopyOption::EnvConnected).item();
202 for (const QString &file : qmljs) {
203 if (indexCancelled())
204 return;
205 QString fPath = dir.filePath(file);
206 auto newCurrentPtr = newCurrent.ownerAs<DomEnvironment>();
207 FileToLoad fileToLoad = FileToLoad::fromFileSystem(newCurrentPtr, fPath);
208 if (!fileToLoad.canonicalPath().isEmpty()) {
209 newCurrentPtr->loadBuiltins();
210 newCurrentPtr->loadFile(fileToLoad, [](Path, const DomItem &, const DomItem &) {});
211 newCurrentPtr->loadPendingDependencies();
212 newCurrent.commitToBase(m_validEnv.ownerAs<DomEnvironment>());
213 }
214 {
215 QMutexLocker l(&m_mutex);
216 ++m_indexDoneCost;
217 --m_indexInProgressCost;
218 progress = indexEvalProgress();
219 }
220 indexSendProgress(progress);
221 }
222}
223
225{
227 // ### create progress, &scan in a separate instance
228 const int maxDepth = 5;
229 for (const auto &path : paths)
230 addDirectory(path, maxDepth);
232}
233
234void QQmlCodeModel::addDirectory(const QString &path, int depthLeft)
235{
236 if (depthLeft < 1)
237 return;
238 {
239 QMutexLocker l(&m_mutex);
240 for (auto it = m_toIndex.begin(); it != m_toIndex.end();) {
241 if (it->path.startsWith(path)) {
242 if (it->path.size() == path.size())
243 return;
244 if (it->path.at(path.size()) == u'/') {
245 it = m_toIndex.erase(it);
246 continue;
247 }
248 } else if (path.startsWith(it->path) && path.at(it->path.size()) == u'/')
249 return;
250 ++it;
251 }
252 m_toIndex.append({ path, depthLeft });
253 }
254}
255
257{
258 {
259 QMutexLocker l(&m_mutex);
260 auto toRemove = [path](const QString &p) {
261 return p.startsWith(path) && (p.size() == path.size() || p.at(path.size()) == u'/');
262 };
263 auto it = m_toIndex.begin();
264 auto end = m_toIndex.end();
265 while (it != end) {
266 if (toRemove(it->path))
267 it = m_toIndex.erase(it);
268 else
269 ++it;
270 }
271 }
272 if (auto validEnvPtr = m_validEnv.ownerAs<DomEnvironment>())
273 validEnvPtr->removePath(path);
274 if (auto currentEnvPtr = m_currentEnv.ownerAs<DomEnvironment>())
275 currentEnvPtr->removePath(path);
276}
277
279{
280 QString res;
281 {
282 QMutexLocker l(&m_mutex);
283 res = m_url2path.value(url);
284 }
285 if (!res.isEmpty() && options == UrlLookup::Caching)
286 return res;
288 QFileInfo f(qurl.toLocalFile());
289 QString cPath = f.canonicalFilePath();
290 if (cPath.isEmpty())
291 cPath = f.filePath();
292 {
293 QMutexLocker l(&m_mutex);
294 if (!res.isEmpty() && res != cPath)
295 m_path2url.remove(res);
296 m_url2path.insert(url, cPath);
297 m_path2url.insert(cPath, url);
298 }
299 return cPath;
300}
301
302void QQmlCodeModel::newOpenFile(const QByteArray &url, int version, const QString &docText)
303{
304 {
305 QMutexLocker l(&m_mutex);
306 auto &openDoc = m_openDocuments[url];
307 if (!openDoc.textDocument)
308 openDoc.textDocument = std::make_shared<Utils::TextDocument>();
309 QMutexLocker l2(openDoc.textDocument->mutex());
310 openDoc.textDocument->setVersion(version);
311 openDoc.textDocument->setPlainText(docText);
312 }
315}
316
318{
319 QMutexLocker l(&m_mutex);
320 return m_openDocuments.value(url);
321}
322
324{
325 QMutexLocker l(&m_mutex);
326 return m_tokens;
327}
328
330{
331 QMutexLocker l(&m_mutex);
332 return m_tokens;
333}
334
336{
337 const int maxIndexThreads = 1;
338 {
339 QMutexLocker l(&m_mutex);
340 if (m_toIndex.isEmpty() || m_nIndexInProgress >= maxIndexThreads)
341 return;
342 if (++m_nIndexInProgress == 1)
343 indexStart();
344 }
345 QThreadPool::globalInstance()->start([this]() {
346 while (indexSome()) { }
347 });
348}
349
350bool QQmlCodeModel::indexSome()
351{
352 qCDebug(codeModelLog) << "indexSome";
354 {
355 QMutexLocker l(&m_mutex);
356 if (m_toIndex.isEmpty()) {
357 if (--m_nIndexInProgress == 0)
358 indexEnd();
359 return false;
360 }
361 toIndex = m_toIndex.last();
362 m_toIndex.removeLast();
363 }
364 bool hasMore = false;
365 {
366 auto guard = qScopeGuard([this, &hasMore]() {
367 QMutexLocker l(&m_mutex);
368 if (m_toIndex.isEmpty()) {
369 if (--m_nIndexInProgress == 0)
370 indexEnd();
371 hasMore = false;
372 } else {
373 hasMore = true;
374 }
375 });
376 indexDirectory(toIndex.path, toIndex.leftDepth);
377 }
378 return hasMore;
379}
380
382{
383 qCDebug(codeModelLog) << "openNeedUpdate";
384 const int maxIndexThreads = 1;
385 {
386 QMutexLocker l(&m_mutex);
387 if (m_openDocumentsToUpdate.isEmpty() || m_nUpdateInProgress >= maxIndexThreads)
388 return;
389 if (++m_nUpdateInProgress == 1)
390 openUpdateStart();
391 }
392 QThreadPool::globalInstance()->start([this]() {
393 while (openUpdateSome()) { }
394 });
395}
396
397bool QQmlCodeModel::openUpdateSome()
398{
399 qCDebug(codeModelLog) << "openUpdateSome start";
400 QByteArray toUpdate;
401 {
402 QMutexLocker l(&m_mutex);
403 if (m_openDocumentsToUpdate.isEmpty()) {
404 if (--m_nUpdateInProgress == 0)
405 openUpdateEnd();
406 return false;
407 }
408 auto it = m_openDocumentsToUpdate.find(m_lastOpenDocumentUpdated);
409 auto end = m_openDocumentsToUpdate.end();
410 if (it == end)
411 it = m_openDocumentsToUpdate.begin();
412 else if (++it == end)
413 it = m_openDocumentsToUpdate.begin();
414 toUpdate = *it;
415 m_openDocumentsToUpdate.erase(it);
416 }
417 bool hasMore = false;
418 {
419 auto guard = qScopeGuard([this, &hasMore]() {
420 QMutexLocker l(&m_mutex);
421 if (m_openDocumentsToUpdate.isEmpty()) {
422 if (--m_nUpdateInProgress == 0)
423 openUpdateEnd();
424 hasMore = false;
425 } else {
426 hasMore = true;
427 }
428 });
429 openUpdate(toUpdate);
430 }
431 return hasMore;
432}
433
434void QQmlCodeModel::openUpdateStart()
435{
436 qCDebug(codeModelLog) << "openUpdateStart";
437}
438
439void QQmlCodeModel::openUpdateEnd()
440{
441 qCDebug(codeModelLog) << "openUpdateEnd";
442}
443
448void QQmlCodeModel::initializeCMakeStatus(const QString &pathForSettings)
449{
450 if (m_settings) {
451 const QString cmakeCalls = u"no-cmake-calls"_s;
452 m_settings->search(pathForSettings);
453 if (m_settings->isSet(cmakeCalls) && m_settings->value(cmakeCalls).toBool()) {
454 qWarning() << "Disabling CMake calls via .qmlls.ini setting.";
455 m_cmakeStatus = DoesNotHaveCMake;
456 return;
457 }
458 }
459
460 QProcess process;
461 process.setProgram(u"cmake"_s);
462 process.setArguments({ u"--version"_s });
463 process.start();
464 process.waitForFinished();
465 m_cmakeStatus = process.exitCode() == 0 ? HasCMake : DoesNotHaveCMake;
466
467 if (m_cmakeStatus == DoesNotHaveCMake) {
468 qWarning() << "Disabling CMake calls because CMake was not found.";
469 return;
470 }
471
472 QObject::connect(&m_cppFileWatcher, &QFileSystemWatcher::fileChanged, this,
473 &QQmlCodeModel::onCppFileChanged);
474}
475
488bool QQmlCodeModel::callCMakeBuild(const QStringList &buildPaths)
489{
490 bool success = true;
491 for (const auto &path : buildPaths) {
492 if (!QFileInfo::exists(path + u"/.cmake"_s))
493 continue;
494
495 QProcess process;
496 const auto command = QQmlLSUtils::cmakeBuildCommand(path);
497 process.setProgram(command.first);
498 process.setArguments(command.second);
499 qCDebug(codeModelLog) << "Running" << process.program() << process.arguments();
500 process.start();
501
502 // TODO: run process concurrently instead of blocking qmlls
503 success &= process.waitForFinished();
504 success &= (process.exitCode() == 0);
505 qCDebug(codeModelLog) << process.program() << process.arguments() << "terminated with"
506 << process.exitCode();
507 }
508 return success;
509}
510
519{
521 for (const auto &rootUrl : m_rootUrls) {
522 const QString rootDir = QUrl(QString::fromUtf8(rootUrl)).toLocalFile();
523
524 if (rootDir.isEmpty())
525 continue;
526
527 qCDebug(codeModelLog) << "Searching for files to watch in workspace folder" << rootDir;
529 while (it.hasNext()) {
530 QFileInfo info = it.nextFileInfo();
531 result << info.absoluteFilePath();
532 }
533 }
534 return result;
535}
536
543{
544 const QmlFile *file = qmlFile.as<QmlFile>();
545 if (!file)
546 return {};
547
548 auto resolver = file->typeResolver();
549 if (!resolver)
550 return {};
551
552 auto types = resolver->importedTypes();
553
555 for (const auto &type : types) {
556 if (!type.scope)
557 continue;
558 // note: the factory only loads composite types
559 const bool isComposite = type.scope.factory() || type.scope->isComposite();
560 if (isComposite)
561 continue;
562
563 const QString filePath = QFileInfo(type.scope->filePath()).fileName();
564 result << filePath;
565 }
566
567 return result;
568}
569
575void QQmlCodeModel::addFileWatches(const DomItem &qmlFile)
576{
577 const auto filesToWatch = fileNamesToWatch(qmlFile);
578 const QStringList filepathsToWatch = findFilePathsFromFileNames(filesToWatch);
579 const auto unwatchedPaths = m_cppFileWatcher.addPaths(filepathsToWatch);
580 if (!unwatchedPaths.isEmpty()) {
581 qCDebug(codeModelLog) << "Cannot watch paths" << unwatchedPaths << "from requested"
582 << filepathsToWatch;
583 }
584}
585
586void QQmlCodeModel::onCppFileChanged(const QString &)
587{
588 m_rebuildRequired = true;
589}
590
591void QQmlCodeModel::newDocForOpenFile(const QByteArray &url, int version, const QString &docText)
592{
593 qCDebug(codeModelLog) << "updating doc" << url << "to version" << version << "("
594 << docText.size() << "chars)";
595
597 if (m_cmakeStatus == RequiresInitialization)
598 initializeCMakeStatus(fPath);
599
600 DomItem newCurrent = m_currentEnv.makeCopy(DomItem::CopyOption::EnvConnected).item();
602
603 if (m_cmakeStatus == HasCMake && !loadPaths.isEmpty() && m_rebuildRequired) {
604 callCMakeBuild(loadPaths);
605 m_rebuildRequired = false;
606 }
607
608 loadPaths.append(m_importPaths);
609 if (std::shared_ptr<DomEnvironment> newCurrentPtr = newCurrent.ownerAs<DomEnvironment>()) {
610 newCurrentPtr->setLoadPaths(loadPaths);
611 }
612 Path p;
613 auto newCurrentPtr = newCurrent.ownerAs<DomEnvironment>();
614 newCurrentPtr->loadFile(FileToLoad::fromMemory(newCurrentPtr, fPath, docText),
615 [&p, this](Path, const DomItem &, const DomItem &newValue) {
616 const DomItem file = newValue.fileObject();
617 p = file.canonicalPath();
618 if (m_cmakeStatus == HasCMake)
619 addFileWatches(file);
620 });
621 newCurrentPtr->loadPendingDependencies();
622 if (p) {
623 newCurrent.commitToBase(m_validEnv.ownerAs<DomEnvironment>());
624 DomItem item = m_currentEnv.path(p);
625 {
626 QMutexLocker l(&m_mutex);
627 OpenDocument &doc = m_openDocuments[url];
628 if (!doc.textDocument) {
629 qCWarning(lspServerLog)
630 << "ignoring update to closed document" << QString::fromUtf8(url);
631 return;
632 } else {
633 QMutexLocker l(doc.textDocument->mutex());
634 if (doc.textDocument->version() && *doc.textDocument->version() > version) {
635 qCWarning(lspServerLog)
636 << "docUpdate: version" << version << "of document"
637 << QString::fromUtf8(url) << "is not the latest anymore";
638 return;
639 }
640 }
641 if (!doc.snapshot.docVersion || *doc.snapshot.docVersion < version) {
642 doc.snapshot.docVersion = version;
643 doc.snapshot.doc = item;
644 } else {
645 qCWarning(lspServerLog) << "skipping update of current doc to obsolete version"
646 << version << "of document" << QString::fromUtf8(url);
647 }
648 if (item.field(Fields::isValid).value().toBool(false)) {
649 if (!doc.snapshot.validDocVersion || *doc.snapshot.validDocVersion < version) {
650 DomItem vDoc = m_validEnv.path(p);
651 doc.snapshot.validDocVersion = version;
652 doc.snapshot.validDoc = vDoc;
653 } else {
654 qCWarning(lspServerLog) << "skippig update of valid doc to obsolete version"
655 << version << "of document" << QString::fromUtf8(url);
656 }
657 } else {
658 qCWarning(lspServerLog)
659 << "avoid update of validDoc to " << version << "of document"
660 << QString::fromUtf8(url) << "as it is invalid";
661 }
662 }
663 }
664 if (codeModelLog().isDebugEnabled()) {
665 qCDebug(codeModelLog) << "finished update doc of " << url << "to version" << version;
666 snapshotByUrl(url).dump(qDebug() << "postSnapshot",
668 }
669 // we should update the scope in the future thus call addOpen(url)
671}
672
674{
675 QMutexLocker l(&m_mutex);
676 m_openDocuments.remove(url);
677}
678
679void QQmlCodeModel::setRootUrls(const QList<QByteArray> &urls)
680{
681 QMutexLocker l(&m_mutex);
682 m_rootUrls = urls;
683}
684
685void QQmlCodeModel::addRootUrls(const QList<QByteArray> &urls)
686{
687 QMutexLocker l(&m_mutex);
688 for (const QByteArray &url : urls) {
689 if (!m_rootUrls.contains(url))
690 m_rootUrls.append(url);
691 }
692}
693
694void QQmlCodeModel::removeRootUrls(const QList<QByteArray> &urls)
695{
696 QMutexLocker l(&m_mutex);
697 for (const QByteArray &url : urls)
698 m_rootUrls.removeOne(url);
699}
700
701QList<QByteArray> QQmlCodeModel::rootUrls() const
702{
703 QMutexLocker l(&m_mutex);
704 return m_rootUrls;
705}
706
708{
709 QMutexLocker l(&m_mutex);
710 return m_buildPathsForRootUrl.value(url);
711}
712
713static bool isNotSeparator(char c)
714{
715 return c != '/';
716}
717
719{
720 QList<QByteArray> roots;
721 {
722 QMutexLocker l(&m_mutex);
723 roots = m_buildPathsForRootUrl.keys();
724 }
725 // we want to longest match to be first, as it should override shorter matches
726 std::sort(roots.begin(), roots.end(), [](const QByteArray &el1, const QByteArray &el2) {
727 if (el1.size() > el2.size())
728 return true;
729 if (el1.size() < el2.size())
730 return false;
731 return el1 < el2;
732 });
733 QStringList buildPaths;
734 QStringList defaultValues;
735 if (!roots.isEmpty() && roots.last().isEmpty())
736 roots.removeLast();
737 QByteArray urlSlash(url);
738 if (!urlSlash.isEmpty() && isNotSeparator(urlSlash.at(urlSlash.size() - 1)))
739 urlSlash.append('/');
740 // look if the file has a know prefix path
741 for (const QByteArray &root : roots) {
742 if (urlSlash.startsWith(root)) {
743 buildPaths += buildPathsForRootUrl(root);
744 break;
745 }
746 }
748
749 // fallback to the empty root, if is has an entry.
750 // This is the buildPath that is passed to qmlls via --build-dir.
751 if (buildPaths.isEmpty()) {
752 buildPaths += buildPathsForRootUrl(QByteArray());
753 }
754
755 // look in the QMLLS_BUILD_DIRS environment variable
756 if (buildPaths.isEmpty()) {
757 QStringList envPaths = qEnvironmentVariable("QMLLS_BUILD_DIRS")
759 buildPaths += envPaths;
760 }
761
762 // look in the settings.
763 // This is the one that is passed via the .qmlls.ini file.
764 if (buildPaths.isEmpty() && m_settings) {
765 m_settings->search(path);
766 QString buildDir = QStringLiteral(u"buildDir");
767 if (m_settings->isSet(buildDir))
768 buildPaths += m_settings->value(buildDir).toString().split(QDir::listSeparator(),
770 }
771
772 // heuristic to find build directory
773 if (buildPaths.isEmpty()) {
774 QDir d(path);
775 d.setNameFilters(QStringList({ u"build*"_s }));
776 const int maxDirDepth = 8;
777 int iDir = maxDirDepth;
778 QString dirName = d.dirName();
779 QDateTime lastModified;
780 while (d.cdUp() && --iDir > 0) {
781 for (const QFileInfo &fInfo : d.entryInfoList(QDir::Dirs)) {
782 if (fInfo.completeBaseName() == u"build"
783 || fInfo.completeBaseName().startsWith(u"build-%1"_s.arg(dirName))) {
784 if (iDir > 1)
785 iDir = 1;
786 if (!lastModified.isValid() || lastModified < fInfo.lastModified()) {
787 buildPaths.clear();
788 buildPaths.append(fInfo.absoluteFilePath());
789 }
790 }
791 }
792 }
793 }
794 // add dependent build directories
796 std::reverse(buildPaths.begin(), buildPaths.end());
797 const int maxDeps = 4;
798 while (!buildPaths.isEmpty()) {
799 QString bPath = buildPaths.last();
800 buildPaths.removeLast();
801 res += bPath;
802 if (QFile::exists(bPath + u"/_deps") && bPath.split(u"/_deps/"_s).size() < maxDeps) {
803 QDir d(bPath + u"/_deps");
804 for (const QFileInfo &fInfo : d.entryInfoList(QDir::Dirs))
805 buildPaths.append(fInfo.absoluteFilePath());
806 }
807 }
808 return res;
809}
810
812{
813 QMutexLocker l(&m_mutex);
814 if (!url.isEmpty() && isNotSeparator(url.at(url.size() - 1)))
815 url.append('/');
816 if (paths.isEmpty())
817 m_buildPathsForRootUrl.remove(url);
818 else
819 m_buildPathsForRootUrl.insert(url, paths);
820}
821
822void QQmlCodeModel::openUpdate(const QByteArray &url)
823{
824 bool updateDoc = false;
825 bool updateScope = false;
826 std::optional<int> rNow = 0;
827 QString docText;
828 DomItem validDoc;
829 std::shared_ptr<Utils::TextDocument> document;
830 {
831 QMutexLocker l(&m_mutex);
832 OpenDocument &doc = m_openDocuments[url];
833 document = doc.textDocument;
834 if (!document)
835 return;
836 {
837 QMutexLocker l2(document->mutex());
838 rNow = document->version();
839 }
840 if (rNow && (!doc.snapshot.docVersion || *doc.snapshot.docVersion != *rNow))
841 updateDoc = true;
842 else if (doc.snapshot.validDocVersion
843 && (!doc.snapshot.scopeVersion
845 updateScope = true;
846 else
847 return;
848 if (updateDoc) {
849 QMutexLocker l2(doc.textDocument->mutex());
850 rNow = doc.textDocument->version();
851 docText = doc.textDocument->toPlainText();
852 } else {
853 validDoc = doc.snapshot.validDoc;
854 rNow = doc.snapshot.validDocVersion;
855 }
856 }
857 if (updateDoc) {
858 newDocForOpenFile(url, *rNow, docText);
859 }
860 if (updateScope) {
861 // to do
862 }
863}
864
866{
867 QMutexLocker l(&m_mutex);
868 m_openDocumentsToUpdate.insert(url);
869}
870
871QDebug OpenDocumentSnapshot::dump(QDebug dbg, DumpOptions options)
872{
873 dbg.noquote().nospace() << "{";
874 dbg << " url:" << QString::fromUtf8(url) << "\n";
875 dbg << " docVersion:" << (docVersion ? QString::number(*docVersion) : u"*none*"_s) << "\n";
876 if (options & DumpOption::LatestCode) {
877 dbg << " doc: ------------\n"
878 << doc.field(Fields::code).value().toString() << "\n==========\n";
879 } else {
880 dbg << u" doc:"
881 << (doc ? u"%1chars"_s.arg(doc.field(Fields::code).value().toString().size())
882 : u"*none*"_s)
883 << "\n";
884 }
885 dbg << " validDocVersion:"
886 << (validDocVersion ? QString::number(*validDocVersion) : u"*none*"_s) << "\n";
887 if (options & DumpOption::ValidCode) {
888 dbg << " validDoc: ------------\n"
889 << validDoc.field(Fields::code).value().toString() << "\n==========\n";
890 } else {
891 dbg << u" validDoc:"
892 << (validDoc ? u"%1chars"_s.arg(validDoc.field(Fields::code).value().toString().size())
893 : u"*none*"_s)
894 << "\n";
895 }
896 dbg << " scopeVersion:" << (scopeVersion ? QString::number(*scopeVersion) : u"*none*"_s)
897 << "\n";
898 dbg << " scopeDependenciesLoadTime:" << scopeDependenciesLoadTime << "\n";
899 dbg << " scopeDependenciesChanged" << scopeDependenciesChanged << "\n";
900 dbg << "}";
901 return dbg;
902}
903
904} // namespace QmlLsp
905
\inmodule QtCore
Definition qbytearray.h:57
QString toString(const QString &defaultValue={}) const
Returns the string value stored in this QCborValue, if it is of the string type.
\inmodule QtCore\reentrant
Definition qdatetime.h:283
bool isValid() const
Returns true if this datetime represents a definite moment, otherwise false.
\inmodule QtCore
The QDirIterator class provides an iterator for directory entrylists.
\inmodule QtCore
Definition qdir.h:20
@ Files
Definition qdir.h:23
@ NoSymLinks
Definition qdir.h:25
@ NoDotAndDotDot
Definition qdir.h:44
@ Dirs
Definition qdir.h:22
static constexpr QChar listSeparator() noexcept
Definition qdir.h:200
QString fileName() const
bool exists() const
Returns true if the file system entry this QFileInfo refers to exists; otherwise returns false.
void fileChanged(const QString &path, QPrivateSignal)
This signal is emitted when the file at the specified path is modified, renamed or removed from disk.
QStringList files() const
Returns a list of paths to files that are being watched.
QStringList removePaths(const QStringList &files)
Removes the specified paths from the file system watcher.
QStringList addPaths(const QStringList &files)
Adds each path in paths to the file system watcher.
bool exists() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qfile.cpp:351
bool remove(const Key &key)
Removes the item that has the key from the hash.
Definition qhash.h:958
QList< Key > keys() const
Returns a list containing all the keys in the hash, in an arbitrary order.
Definition qhash.h:1086
T value(const Key &key) const noexcept
Definition qhash.h:1054
iterator insert(const Key &key, const T &value)
Inserts a new item with the key and a value of value.
Definition qhash.h:1303
Implements a server for the language server protocol.
\inmodule QtCore
bool removeOne(const AT &t)
Definition qlist.h:598
void append(parameter_type t)
Definition qlist.h:458
\inmodule QtCore
Definition qmutex.h:313
bool tryLock(int timeout=0) noexcept
Attempts to lock the mutex.
Definition qmutex.h:287
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3236
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)
T const * as() const
std::shared_ptr< T > ownerAs() const
DomItem field(QStringView name) const
DomItem path(const Path &p, const ErrorHandler &h=&defaultErrorHandler) const
MutableDomItem makeCopy(CopyOption option=CopyOption::EnvConnected) const
QCborValue value() const
static FileToLoad fromFileSystem(const std::weak_ptr< DomEnvironment > &environment, const QString &canonicalPath)
static FileToLoad fromMemory(const std::weak_ptr< DomEnvironment > &environment, const QString &path, const QString &data)
A QmlFile, when loaded in a DomEnvironment that has the DomCreationOption::WithSemanticAnalysis,...
static QPair< QString, QStringList > cmakeBuildCommand(const QString &path)
bool search(const QString &path)
bool isSet(QString name) const
QVariant value(QString name) const
qsizetype size() const
Definition qset.h:50
iterator begin()
Definition qset.h:136
iterator end()
Definition qset.h:140
bool isEmpty() const
Definition qset.h:52
void clear()
Definition qset.h:61
iterator erase(const_iterator i)
Definition qset.h:145
iterator find(const T &value)
Definition qset.h:159
iterator insert(const T &value)
Definition qset.h:155
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QStringList split(const QString &sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the string into substrings wherever sep occurs, and returns the list of those strings.
Definition qstring.cpp:8218
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
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
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 & removeLast()
Definition qstring.h:558
QString last(qsizetype n) const &
Definition qstring.h:392
static QThreadPool * globalInstance()
Returns the global QThreadPool instance.
static void yieldCurrentThread()
Definition qthread.cpp:1054
\inmodule QtCore
Definition qurl.h:94
bool isEmpty() const
Returns true if the URL has no data; otherwise returns false.
Definition qurl.cpp:1896
QString toLocalFile() const
Returns the path of this URL formatted as a local file path.
Definition qurl.cpp:3425
QString toString() const
Returns the variant as a QString if the variant has a userType() including, but not limited to:
bool toBool() const
Returns the variant as a bool if the variant has userType() Bool.
std::optional< int > scopeVersion
std::optional< int > docVersion
std::optional< int > validDocVersion
QDebug dump(QDebug dbg, DumpOptions dump=DumpOption::NoCode)
QQmlJS::Dom::DomItem validDoc
std::shared_ptr< Utils::TextDocument > textDocument
OpenDocumentSnapshot snapshot
void newOpenFile(const QByteArray &url, int version, const QString &docText)
void addRootUrls(const QList< QByteArray > &urls)
QList< QByteArray > rootUrls() const
QQmlCodeModel(QObject *parent=nullptr, QQmlToolingSettings *settings=nullptr)
void setBuildPathsForRootUrl(QByteArray url, const QStringList &paths)
OpenDocumentSnapshot snapshotByUrl(const QByteArray &url)
static QStringList fileNamesToWatch(const QQmlJS::Dom::DomItem &qmlFile)
void addOpenToUpdate(const QByteArray &)
QString url2Path(const QByteArray &url, UrlLookup options=UrlLookup::Caching)
OpenDocument openDocumentByUrl(const QByteArray &url)
QStringList findFilePathsFromFileNames(const QStringList &fileNames) const
QStringList buildPathsForFileUrl(const QByteArray &url)
void setRootUrls(const QList< QByteArray > &urls)
void removeDirectory(const QString &path)
void removeRootUrls(const QList< QByteArray > &urls)
void addDirectoriesToIndex(const QStringList &paths, QLanguageServer *server)
void newDocForOpenFile(const QByteArray &url, int version, const QString &docText)
void updatedSnapshot(const QByteArray &url)
QStringList buildPathsForRootUrl(const QByteArray &url)
void closeOpenFile(const QByteArray &url)
RegisteredSemanticTokens & registeredTokens()
QSet< QString >::iterator it
Combined button and popup list for selecting options.
static bool isNotSeparator(char c)
@ SkipEmptyParts
Definition qnamespace.h:128
QList< QString > QStringList
Constructs a string list that contains the given string, str.
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage return DBusPendingCall DBusPendingCall return DBusPendingCall return dbus_int32_t return DBusServer * server
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
GLuint GLuint end
GLsizei GLenum GLenum * types
GLfloat GLfloat f
GLenum type
GLsizei const GLuint * paths
GLuint res
const GLubyte * c
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
static bool isComposite(const QQmlJSScope::ConstPtr &scope)
#define QmlLSPluginInterface_iid
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
#define QStringLiteral(str)
QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
#define emit
#define Q_UNUSED(x)
static uint toIndex(ExecutionEngine *e, const Value &v)
QFile file
[0]
QSettings settings("MySoft", "Star Runner")
[0]
QUrl url("example.com")
[constructor-url-reference]
QString dir
[11]
QStringList fileNames
[4]
QGraphicsItem * item
QLayoutItem * child
[0]
QHostInfo info
[0]
QStringView el
bool contains(const AT &t) const noexcept
Definition qlist.h:45