QQmlCodeModel::addFileWatches: don't warn for already watched files

Don't emit a warning for files that are already watched in
QQmlCodeModel::addFileWatches, add a parameter to
findFilePathsFromFileNames and findFilePathFromFileName to filter out
unwanted filePaths from the result.

Change-Id: Ic71229723952852437ea01d0d7e5c2ae1c53ac1c
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
Sami Shalayel 2025-08-22 14:15:24 +02:00
parent e0e4b12250
commit 7232c76aeb
11 changed files with 118 additions and 29 deletions

View File

@ -347,7 +347,8 @@ return all the found file paths.
This is an overapproximation and might find unrelated files with the same name. This is an overapproximation and might find unrelated files with the same name.
*/ */
QStringList QQmlCodeModel::findFilePathsFromFileNames(const QStringList &_fileNamesToSearch) QStringList QQmlCodeModel::findFilePathsFromFileNames(const QStringList &_fileNamesToSearch,
const QSet<QString> &ignoredFilePaths)
{ {
QStringList fileNamesToSearch{ _fileNamesToSearch }; QStringList fileNamesToSearch{ _fileNamesToSearch };
@ -364,9 +365,8 @@ QStringList QQmlCodeModel::findFilePathsFromFileNames(const QStringList &_fileNa
const QString rootDir = QUrl(QString::fromUtf8(m_rootUrl)).toLocalFile(); const QString rootDir = QUrl(QString::fromUtf8(m_rootUrl)).toLocalFile();
qCDebug(codeModelLog) << "Searching for files to watch in workspace folder" << rootDir; qCDebug(codeModelLog) << "Searching for files to watch in workspace folder" << rootDir;
const QStringList result = const QStringList result =
QQmlLSUtils::findFilePathsFromFileNames(rootDir, fileNamesToSearch); QQmlLSUtils::findFilePathsFromFileNames(rootDir, fileNamesToSearch, ignoredFilePaths);
QMutexLocker guard(&m_mutex); QMutexLocker guard(&m_mutex);
for (const auto &fileName : fileNamesToSearch) { for (const auto &fileName : fileNamesToSearch) {
@ -423,7 +423,13 @@ are modified.
void QQmlCodeModel::addFileWatches(const DomItem &qmlFile) void QQmlCodeModel::addFileWatches(const DomItem &qmlFile)
{ {
const auto filesToWatch = fileNamesToWatch(qmlFile); const auto filesToWatch = fileNamesToWatch(qmlFile);
const QStringList filepathsToWatch = findFilePathsFromFileNames(filesToWatch);
// remove already watched files to avoid a warning later on
const QStringList alreadyWatchedFiles = m_cppFileWatcher.files();
const QSet<QString> alreadyWatchedFilesSet(alreadyWatchedFiles.begin(),
alreadyWatchedFiles.end());
QStringList filepathsToWatch = findFilePathsFromFileNames(filesToWatch, alreadyWatchedFilesSet);
if (filepathsToWatch.isEmpty()) if (filepathsToWatch.isEmpty())
return; return;

View File

@ -125,7 +125,8 @@ public:
QStringList importPaths() const; QStringList importPaths() const;
void setImportPaths(const QStringList &paths); void setImportPaths(const QStringList &paths);
QQmlToolingSharedSettings *settings() const { return m_settings; } QQmlToolingSharedSettings *settings() const { return m_settings; }
QStringList findFilePathsFromFileNames(const QStringList &fileNames); QStringList findFilePathsFromFileNames(const QStringList &fileNames,
const QSet<QString> &alreadyWatchedFiles);
static QStringList fileNamesToWatch(const QQmlJS::Dom::DomItem &qmlFile); static QStringList fileNamesToWatch(const QQmlJS::Dom::DomItem &qmlFile);
void disableCMakeCalls(); void disableCMakeCalls();

View File

@ -1994,7 +1994,7 @@ static std::optional<Location> createCppTypeLocation(const QQmlJSScope::ConstPtr
const QStringList &headerLocations, const QStringList &headerLocations,
const QQmlJS::SourceLocation &location) const QQmlJS::SourceLocation &location)
{ {
const QString filePath = findFilePathFromFileName(headerLocations, type->filePath()); const QString filePath = findFilePathFromFileName(headerLocations, type->filePath(), {});
if (filePath.isEmpty()) { if (filePath.isEmpty()) {
qCWarning(QQmlLSUtilsLog) << "Couldn't find the C++ file '%1'."_L1.arg(type->filePath()); qCWarning(QQmlLSUtilsLog) << "Couldn't find the C++ file '%1'."_L1.arg(type->filePath());
return {}; return {};
@ -2553,7 +2553,8 @@ RenameUsages::RenameUsages(const QList<Edit> &renamesInFile,
enum SearchOption { FindFirst, FindAll }; enum SearchOption { FindFirst, FindAll };
static QStringList findFilePathsFromFileNamesImpl(const QStringList &rootDirs, static QStringList findFilePathsFromFileNamesImpl(const QStringList &rootDirs,
const QStringList &fileNamesToSearch, const QStringList &fileNamesToSearch,
SearchOption option) SearchOption option,
const QSet<QString> &ignoredFilePaths)
{ {
if (fileNamesToSearch.isEmpty() || rootDirs.isEmpty()) if (fileNamesToSearch.isEmpty() || rootDirs.isEmpty())
return {}; return {};
@ -2594,25 +2595,31 @@ static QStringList findFilePathsFromFileNamesImpl(const QStringList &rootDirs,
if (!fileNamesToSearch.contains(entry.fileName())) if (!fileNamesToSearch.contains(entry.fileName()))
continue; continue;
result << entry.absoluteFilePath(); if (ignoredFilePaths.contains(entry.absoluteFilePath()))
continue;
if (option == FindFirst) if (option == FindFirst)
return result; return { entry.absoluteFilePath() };
result << entry.absoluteFilePath();
} }
} }
return result; return result;
} }
QStringList findFilePathsFromFileNames(const QString &rootDir, QStringList findFilePathsFromFileNames(const QString &rootDir, const QStringList &fileNamesToSearch,
const QStringList &fileNamesToSearch) const QSet<QString> &ignoredFilePaths)
{ {
return findFilePathsFromFileNamesImpl({ rootDir }, fileNamesToSearch, FindAll); return findFilePathsFromFileNamesImpl({ rootDir }, fileNamesToSearch, FindAll,
ignoredFilePaths);
} }
QString findFilePathFromFileName(const QStringList &rootDirs, const QString &fileNameToSearch) QString findFilePathFromFileName(const QStringList &rootDirs, const QString &fileNameToSearch,
const QSet<QString> &ignoredFilePaths)
{ {
const QStringList result = const QStringList result = findFilePathsFromFileNamesImpl(rootDirs, { fileNameToSearch },
findFilePathsFromFileNamesImpl(rootDirs, { fileNameToSearch }, FindFirst); FindFirst, ignoredFilePaths);
return result.isEmpty() ? QString{} : result.front(); return result.isEmpty() ? QString{} : result.front();
} }

View File

@ -296,9 +296,10 @@ QQmlJSScope::ConstPtr findDefiningScopeForEnumeration(const QQmlJSScope::ConstPt
const QString &nameToCheck); const QString &nameToCheck);
QQmlJSScope::ConstPtr findDefiningScopeForEnumerationKey(const QQmlJSScope::ConstPtr &referrerScope, QQmlJSScope::ConstPtr findDefiningScopeForEnumerationKey(const QQmlJSScope::ConstPtr &referrerScope,
const QString &nameToCheck); const QString &nameToCheck);
QStringList findFilePathsFromFileNames(const QString &rootDir, QStringList findFilePathsFromFileNames(const QString &rootDir, const QStringList &fileNamesToSearch,
const QStringList &fileNamesToSearch); const QSet<QString> &ignoredPaths);
QString findFilePathFromFileName(const QStringList &rootDirs, const QString &fileNameToSearch); QString findFilePathFromFileName(const QStringList &rootDirs, const QString &fileNameToSearch,
const QSet<QString> &ignoredPaths);
} // namespace QQmlLSUtils } // namespace QQmlLSUtils
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -75,26 +75,33 @@ void tst_qmlls_qqmlcodemodel::findFilePathsFromFileNames_data()
QTest::addColumn<QStringList>("fileNames"); QTest::addColumn<QStringList>("fileNames");
QTest::addColumn<QStringList>("expectedPaths"); QTest::addColumn<QStringList>("expectedPaths");
QTest::addColumn<QSet<QString>>("missingFiles"); QTest::addColumn<QSet<QString>>("missingFiles");
QTest::addColumn<QSet<QString>>("alreadyWatchedFiles");
const QString folder = testFile("sourceFolder"); const QString folder = testFile("sourceFolder");
const QString subfolder = testFile("sourceFolder/subSourceFolder/subsubSourceFolder"); const QString subfolder = testFile("sourceFolder/subSourceFolder/subsubSourceFolder");
const QSet<QString> noMissingFiles; const QSet<QString> noMissingFiles;
const QSet<QString> noAlreadyWatchedFiles;
QTest::addRow("notExistingFile") << QStringList{ u"notExistingFile.h"_s } << QStringList{} QTest::addRow("notExistingFile")
<< QSet<QString>{ u"notExistingFile.h"_s }; << QStringList{ u"notExistingFile.h"_s } << QStringList{}
<< QSet<QString>{ u"notExistingFile.h"_s } << noAlreadyWatchedFiles;
QTest::addRow("myqmlelement") << QStringList{ u"myqmlelement.h"_s } QTest::addRow("myqmlelement") << QStringList{ u"myqmlelement.h"_s }
<< QStringList{ folder + u"/myqmlelement.h"_s, << QStringList{ folder + u"/myqmlelement.h"_s,
subfolder + u"/myqmlelement.h"_s } subfolder + u"/myqmlelement.h"_s }
<< noMissingFiles; << noMissingFiles << noAlreadyWatchedFiles;
QTest::addRow("myqmlelement2") QTest::addRow("myqmlelementAlreadyWatched")
<< QStringList{ u"myqmlelement2.hpp"_s } << QStringList{ u"myqmlelement.h"_s } << QStringList{ folder + u"/myqmlelement.h"_s }
<< QStringList{ folder + u"/myqmlelement2.hpp"_s } << noMissingFiles; << noMissingFiles << QSet<QString>{ subfolder + u"/myqmlelement.h"_s };
QTest::addRow("anotherqmlelement") QTest::addRow("myqmlelement2") << QStringList{ u"myqmlelement2.hpp"_s }
<< QStringList{ u"anotherqmlelement.cpp"_s } << QStringList{ folder + u"/myqmlelement2.hpp"_s }
<< QStringList{ subfolder + u"/anotherqmlelement.cpp"_s } << noMissingFiles; << noMissingFiles << noAlreadyWatchedFiles;
QTest::addRow("anotherqmlelement") << QStringList{ u"anotherqmlelement.cpp"_s }
<< QStringList{ subfolder + u"/anotherqmlelement.cpp"_s }
<< noMissingFiles << noAlreadyWatchedFiles;
} }
void tst_qmlls_qqmlcodemodel::findFilePathsFromFileNames() void tst_qmlls_qqmlcodemodel::findFilePathsFromFileNames()
@ -102,10 +109,11 @@ void tst_qmlls_qqmlcodemodel::findFilePathsFromFileNames()
QFETCH(QStringList, fileNames); QFETCH(QStringList, fileNames);
QFETCH(QStringList, expectedPaths); QFETCH(QStringList, expectedPaths);
QFETCH(QSet<QString>, missingFiles); QFETCH(QSet<QString>, missingFiles);
QFETCH(QSet<QString>, alreadyWatchedFiles);
QmlLsp::QQmlCodeModel model(testFileUrl(u"sourceFolder"_s).toEncoded()); QmlLsp::QQmlCodeModel model(testFileUrl(u"sourceFolder"_s).toEncoded());
auto result = model.findFilePathsFromFileNames(fileNames); auto result = model.findFilePathsFromFileNames(fileNames, alreadyWatchedFiles);
// the order only is required for the QCOMPARE // the order only is required for the QCOMPARE
std::sort(result.begin(), result.end()); std::sort(result.begin(), result.end());

View File

@ -4629,7 +4629,68 @@ void tst_qmlls_utils::maxFilesToSearch()
"\" after reaching QMLLS_MAX_FILES_TO_SEARCH (currently set to 1111). Set " "\" after reaching QMLLS_MAX_FILES_TO_SEARCH (currently set to 1111). Set "
"the environment variable \"QMLLS_MAX_FILES_TO_SEARCH\" to a higher value " "the environment variable \"QMLLS_MAX_FILES_TO_SEARCH\" to a higher value "
"to spend more time on searching."); "to spend more time on searching.");
QQmlLSUtils::findFilePathsFromFileNames(QT_QMLLS_BIG_FOLDER ""_L1, { "qt"_L1, "qwer"_L1 }); QQmlLSUtils::findFilePathsFromFileNames(QT_QMLLS_BIG_FOLDER ""_L1, { "qt"_L1, "qwer"_L1 }, {});
}
void tst_qmlls_utils::findFilePathsFromFileNames_data()
{
QTest::addColumn<QSet<QString>>("ignored");
QTest::addColumn<QStringList>("expected");
QTest::addRow("all") << QSet<QString>()
<< QStringList{
testFile("findFilePathsFromFileNames/HelloWorld.txt"_L1),
testFile("findFilePathsFromFileNames/a/HelloWorld.txt"_L1),
testFile("findFilePathsFromFileNames/a/b/HelloWorld.txt"_L1),
testFile("findFilePathsFromFileNames/a/b/c/HelloWorld.txt"_L1),
};
QTest::addRow("ignore3")
<< QSet<QString> {
testFile("findFilePathsFromFileNames/a/HelloWorld.txt"_L1),
testFile("findFilePathsFromFileNames/a/b/HelloWorld.txt"_L1),
}
<< QStringList{
testFile("findFilePathsFromFileNames/HelloWorld.txt"_L1),
testFile("findFilePathsFromFileNames/a/b/c/HelloWorld.txt"_L1),
};
}
void tst_qmlls_utils::findFilePathsFromFileNames()
{
QFETCH(QSet<QString>, ignored);
QFETCH(QStringList, expected);
QStringList filePaths = QQmlLSUtils::findFilePathsFromFileNames(
testFile("findFilePathsFromFileNames"_L1), { "HelloWorld.txt"_L1 }, ignored);
std::sort(filePaths.begin(), filePaths.end());
std::sort(expected.begin(), expected.end());
QCOMPARE(filePaths, expected);
}
void tst_qmlls_utils::findFilePathFromFileName_data()
{
QTest::addColumn<QSet<QString>>("ignored");
QTest::addColumn<QString>("expected");
QTest::addRow("withoutIgnore")
<< QSet<QString>() << testFile("findFilePathsFromFileNames/HelloWorld.txt"_L1);
QTest::addRow("withIgnore") << QSet<QString>{
testFile("findFilePathsFromFileNames/HelloWorld.txt"_L1),
testFile("findFilePathsFromFileNames/a/HelloWorld.txt"_L1),
testFile("findFilePathsFromFileNames/a/b/HelloWorld.txt"_L1),
} << testFile("findFilePathsFromFileNames/a/b/c/HelloWorld.txt"_L1);
}
void tst_qmlls_utils::findFilePathFromFileName()
{
QFETCH(QSet<QString>, ignored);
QFETCH(QString, expected);
QString filePaths = QQmlLSUtils::findFilePathFromFileName(
{ testFile("findFilePathsFromFileNames"_L1) }, { "HelloWorld.txt"_L1 }, ignored);
QCOMPARE(filePaths, expected);
} }
QTEST_MAIN(tst_qmlls_utils) QTEST_MAIN(tst_qmlls_utils)

View File

@ -81,6 +81,11 @@ private slots:
void cmakeBuildCommand(); void cmakeBuildCommand();
void maxFilesToSearch(); void maxFilesToSearch();
void findFilePathsFromFileNames_data();
void findFilePathsFromFileNames();
void findFilePathFromFileName_data();
void findFilePathFromFileName();
private: private:
using EnvironmentAndFile = std::tuple<QQmlJS::Dom::DomItem, QQmlJS::Dom::DomItem>; using EnvironmentAndFile = std::tuple<QQmlJS::Dom::DomItem, QQmlJS::Dom::DomItem>;