aboutsummaryrefslogtreecommitdiffstats
path: root/src/libs/utils
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/utils')
-rw-r--r--src/libs/utils/fileinprojectfinder.cpp137
-rw-r--r--src/libs/utils/fileinprojectfinder.h14
2 files changed, 95 insertions, 56 deletions
diff --git a/src/libs/utils/fileinprojectfinder.cpp b/src/libs/utils/fileinprojectfinder.cpp
index 04495893920..054e5d9c3b4 100644
--- a/src/libs/utils/fileinprojectfinder.cpp
+++ b/src/libs/utils/fileinprojectfinder.cpp
@@ -31,11 +31,13 @@
#include "qrcparser.h"
#include "qtcassert.h"
+#include <QCursor>
#include <QDebug>
+#include <QDir>
#include <QFileInfo>
#include <QLoggingCategory>
+#include <QMenu>
#include <QUrl>
-#include <QDir>
#include <algorithm>
@@ -140,12 +142,12 @@ void FileInProjectFinder::addMappedPath(const FileName &localFilePath, const QSt
folder specified. Third, we walk the list of project files, and search for a file name match
there. If all fails, it returns the original path from the file URL.
*/
-QString FileInProjectFinder::findFile(const QUrl &fileUrl, bool *success) const
+FileNameList FileInProjectFinder::findFile(const QUrl &fileUrl, bool *success) const
{
qCDebug(finderLog) << "FileInProjectFinder: trying to find file" << fileUrl.toString() << "...";
if (fileUrl.scheme() == "qrc" || fileUrl.toString().startsWith(':')) {
- const QString result = m_qrcUrlFinder.find(fileUrl);
+ const FileNameList result = m_qrcUrlFinder.find(fileUrl);
if (!result.isEmpty()) {
if (success)
*success = true;
@@ -157,10 +159,12 @@ QString FileInProjectFinder::findFile(const QUrl &fileUrl, bool *success) const
if (originalPath.isEmpty()) // e.g. qrc://
originalPath = fileUrl.path();
- QString result = originalPath;
+ FileNameList result;
bool found = findFileOrDirectory(originalPath, [&](const QString &fileName, int) {
- result = fileName;
+ result << FileName::fromString(fileName);
});
+ if (!found)
+ result << FileName::fromString(originalPath);
if (success)
*success = found;
@@ -168,12 +172,12 @@ QString FileInProjectFinder::findFile(const QUrl &fileUrl, bool *success) const
return result;
}
-bool FileInProjectFinder::handleSuccess(const QString &originalPath, const QString &found,
+bool FileInProjectFinder::handleSuccess(const QString &originalPath, const QStringList &found,
int matchLength, const char *where) const
{
qCDebug(finderLog) << "FileInProjectFinder: found" << found << where;
CacheEntry entry;
- entry.path = found;
+ entry.paths = found;
entry.matchLength = matchLength;
m_cache.insert(originalPath, entry);
return true;
@@ -202,8 +206,10 @@ bool FileInProjectFinder::findFileOrDirectory(const QString &originalPath, FileH
if (node) {
if (!node->localPath.isEmpty()) {
const QString localPath = node->localPath.toString();
- if (checkPath(localPath, origLength, fileHandler, directoryHandler))
- return handleSuccess(originalPath, localPath, origLength, "in mapped paths");
+ if (checkPath(localPath, origLength, fileHandler, directoryHandler)) {
+ return handleSuccess(originalPath, QStringList(localPath), origLength,
+ "in mapped paths");
+ }
} else if (directoryHandler) {
directoryHandler(node->children.keys(), origLength);
qCDebug(finderLog) << "FileInProjectFinder: found virtual directory" << originalPath
@@ -216,13 +222,18 @@ bool FileInProjectFinder::findFileOrDirectory(const QString &originalPath, FileH
if (it != m_cache.end()) {
qCDebug(finderLog) << "FileInProjectFinder: checking cache ...";
// check if cached path is still there
- const CacheEntry &candidate = it.value();
- if (checkPath(candidate.path, candidate.matchLength, fileHandler, directoryHandler)) {
- qCDebug(finderLog) << "FileInProjectFinder: found" << candidate.path << "in the cache";
- return true;
- } else {
- m_cache.erase(it);
+ CacheEntry &candidate = it.value();
+ for (auto pathIt = candidate.paths.begin(); pathIt != candidate.paths.end();) {
+ if (checkPath(*pathIt, candidate.matchLength, fileHandler, directoryHandler)) {
+ qCDebug(finderLog) << "FileInProjectFinder: found" << *pathIt << "in the cache";
+ ++pathIt;
+ } else {
+ pathIt = candidate.paths.erase(pathIt);
+ }
}
+ if (!candidate.paths.empty())
+ return true;
+ m_cache.erase(it);
}
if (!m_projectDir.isEmpty()) {
@@ -244,7 +255,7 @@ bool FileInProjectFinder::findFileOrDirectory(const QString &originalPath, FileH
if (prefixToIgnore == -1
&& checkPath(originalPath, origLength, fileHandler, directoryHandler)) {
- return handleSuccess(originalPath, originalPath, origLength,
+ return handleSuccess(originalPath, QStringList(originalPath), origLength,
"in project directory");
}
}
@@ -267,8 +278,11 @@ bool FileInProjectFinder::findFileOrDirectory(const QString &originalPath, FileH
candidate.remove(0, prefixToIgnore);
candidate.prepend(m_projectDir.toString());
const int matchLength = origLength - prefixToIgnore;
- if (checkPath(candidate, matchLength, fileHandler, directoryHandler))
- return handleSuccess(originalPath, candidate, matchLength, "in project directory");
+ // FIXME: This might be a worse match than what we find later.
+ if (checkPath(candidate, matchLength, fileHandler, directoryHandler)) {
+ return handleSuccess(originalPath, QStringList(candidate), matchLength,
+ "in project directory");
+ }
prefixToIgnore = originalPath.indexOf(separator, prefixToIgnore + 1);
}
}
@@ -282,17 +296,23 @@ bool FileInProjectFinder::findFileOrDirectory(const QString &originalPath, FileH
matches.append(filesWithSameFileName(lastSegment));
if (directoryHandler)
matches.append(pathSegmentsWithSameName(lastSegment));
- const QString matchedFilePath = bestMatch(matches, originalPath);
- const int matchLength = commonPostFixLength(matchedFilePath, originalPath);
- if (!matchedFilePath.isEmpty()
- && checkPath(matchedFilePath, matchLength, fileHandler, directoryHandler)) {
- return handleSuccess(originalPath, matchedFilePath, matchLength,
- "when matching project files");
+ const QStringList matchedFilePaths = bestMatches(matches, originalPath);
+ if (!matchedFilePaths.empty()) {
+ const int matchLength = commonPostFixLength(matchedFilePaths.first(), originalPath);
+ QStringList hits;
+ for (const QString &matchedFilePath : matchedFilePaths) {
+ if (checkPath(matchedFilePath, matchLength, fileHandler, directoryHandler))
+ hits << matchedFilePath;
+ }
+ if (!hits.empty())
+ return handleSuccess(originalPath, hits, matchLength, "when matching project files");
}
CacheEntry foundPath = findInSearchPaths(originalPath, fileHandler, directoryHandler);
- if (!foundPath.path.isEmpty())
- return handleSuccess(originalPath, foundPath.path, foundPath.matchLength, "in search path");
+ if (!foundPath.paths.isEmpty()) {
+ return handleSuccess(originalPath, foundPath.paths, foundPath.matchLength,
+ "in search path");
+ }
qCDebug(finderLog) << "FileInProjectFinder: checking absolute path in sysroot ...";
@@ -300,8 +320,10 @@ bool FileInProjectFinder::findFileOrDirectory(const QString &originalPath, FileH
if (!m_sysroot.isEmpty()) {
FileName sysrootPath = m_sysroot;
sysrootPath.appendPath(originalPath);
- if (checkPath(sysrootPath.toString(), origLength, fileHandler, directoryHandler))
- return handleSuccess(originalPath, sysrootPath.toString(), origLength, "in sysroot");
+ if (checkPath(sysrootPath.toString(), origLength, fileHandler, directoryHandler)) {
+ return handleSuccess(originalPath, QStringList(sysrootPath.toString()), origLength,
+ "in sysroot");
+ }
}
qCDebug(finderLog) << "FileInProjectFinder: couldn't find file!";
@@ -315,7 +337,7 @@ FileInProjectFinder::CacheEntry FileInProjectFinder::findInSearchPaths(
for (const FileName &dirPath : m_searchDirectories) {
const CacheEntry found = findInSearchPath(dirPath.toString(), filePath,
fileHandler, directoryHandler);
- if (!found.path.isEmpty())
+ if (!found.paths.isEmpty())
return found;
}
@@ -340,17 +362,17 @@ FileInProjectFinder::CacheEntry FileInProjectFinder::findInSearchPath(
QString s = filePath;
while (!s.isEmpty()) {
CacheEntry result;
- result.path = searchPath + QLatin1Char('/') + s;
+ result.paths << searchPath + '/' + s;
result.matchLength = s.length() + 1;
- qCDebug(finderLog) << "FileInProjectFinder: trying" << result.path;
+ qCDebug(finderLog) << "FileInProjectFinder: trying" << result.paths.first();
- if (checkPath(result.path, result.matchLength, fileHandler, directoryHandler))
+ if (checkPath(result.paths.first(), result.matchLength, fileHandler, directoryHandler))
return result;
QString next = chopFirstDir(s);
if (next.isEmpty()) {
if (directoryHandler && QFileInfo(searchPath).fileName() == s) {
- result.path = searchPath;
+ result.paths = QStringList{searchPath};
directoryHandler(QDir(searchPath).entryList(), result.matchLength);
return result;
}
@@ -399,24 +421,30 @@ int FileInProjectFinder::commonPostFixLength(const QString &candidatePath,
return rank;
}
-QString FileInProjectFinder::bestMatch(const QStringList &filePaths, const QString &filePathToFind)
+QStringList FileInProjectFinder::bestMatches(const QStringList &filePaths,
+ const QString &filePathToFind)
{
if (filePaths.isEmpty())
- return QString();
+ return {};
if (filePaths.length() == 1) {
qCDebug(finderLog) << "FileInProjectFinder: found" << filePaths.first()
<< "in project files";
- return filePaths.first();
+ return filePaths;
}
- auto it = std::max_element(filePaths.constBegin(), filePaths.constEnd(),
- [&filePathToFind] (const QString &a, const QString &b) -> bool {
- return commonPostFixLength(a, filePathToFind) < commonPostFixLength(b, filePathToFind);
- });
- if (it != filePaths.cend()) {
- qCDebug(finderLog) << "FileInProjectFinder: found best match" << *it << "in project files";
- return *it;
+ int bestRank = -1;
+ QStringList bestFilePaths;
+ for (const QString &fp : filePaths) {
+ const int currentRank = commonPostFixLength(fp, filePathToFind);
+ if (currentRank < bestRank)
+ continue;
+ if (currentRank > bestRank) {
+ bestRank = currentRank;
+ bestFilePaths.clear();
+ }
+ bestFilePaths << fp;
}
- return QString();
+ QTC_CHECK(!bestFilePaths.empty());
+ return bestFilePaths;
}
FileNameList FileInProjectFinder::searchDirectories() const
@@ -434,9 +462,9 @@ FileInProjectFinder::PathMappingNode::~PathMappingNode()
qDeleteAll(children);
}
-QString FileInProjectFinder::QrcUrlFinder::find(const QUrl &fileUrl) const
+FileNameList FileInProjectFinder::QrcUrlFinder::find(const QUrl &fileUrl) const
{
- QString result;
+ FileNameList result;
const auto fileIt = m_fileCache.constFind(fileUrl);
if (fileIt != m_fileCache.cend())
return fileIt.value();
@@ -448,10 +476,7 @@ QString FileInProjectFinder::QrcUrlFinder::find(const QUrl &fileUrl) const
continue;
QStringList hits;
qrcParser->collectFilesAtPath(QrcParser::normalizedQrcFilePath(fileUrl.toString()), &hits);
- if (!hits.empty()) {
- result = hits.first();
- break;
- }
+ result = transform(hits, [](const QString &fp) { return FileName::fromString(fp); });
}
m_fileCache.insert(fileUrl, result);
return result;
@@ -464,4 +489,16 @@ void FileInProjectFinder::QrcUrlFinder::setProjectFiles(const FileNameList &proj
m_parserCache.clear();
}
+FileName chooseFileFromList(const FileNameList &candidates)
+{
+ if (candidates.length() == 1)
+ return candidates.first();
+ QMenu filesMenu;
+ for (const FileName &candidate : candidates)
+ filesMenu.addAction(candidate.toUserOutput());
+ if (const QAction * const action = filesMenu.exec(QCursor::pos()))
+ return FileName::fromUserInput(action->text());
+ return FileName();
+}
+
} // namespace Utils
diff --git a/src/libs/utils/fileinprojectfinder.h b/src/libs/utils/fileinprojectfinder.h
index 2b1dc940902..ac8cd0a588c 100644
--- a/src/libs/utils/fileinprojectfinder.h
+++ b/src/libs/utils/fileinprojectfinder.h
@@ -55,7 +55,7 @@ public:
void addMappedPath(const FileName &localFilePath, const QString &remoteFilePath);
- QString findFile(const QUrl &fileUrl, bool *success = nullptr) const;
+ FileNameList findFile(const QUrl &fileUrl, bool *success = nullptr) const;
bool findFileOrDirectory(const QString &originalPath, FileHandler fileHandler = nullptr,
DirectoryHandler directoryHandler = nullptr) const;
@@ -71,17 +71,17 @@ private:
};
struct CacheEntry {
- QString path;
+ QStringList paths;
int matchLength = 0;
};
class QrcUrlFinder {
public:
- QString find(const QUrl &fileUrl) const;
+ FileNameList find(const QUrl &fileUrl) const;
void setProjectFiles(const FileNameList &projectFiles);
private:
FileNameList m_allQrcFiles;
- mutable QHash<QUrl, QString> m_fileCache;
+ mutable QHash<QUrl, FileNameList> m_fileCache;
mutable QHash<FileName, QSharedPointer<QrcParser>> m_parserCache;
};
@@ -92,11 +92,11 @@ private:
QStringList filesWithSameFileName(const QString &fileName) const;
QStringList pathSegmentsWithSameName(const QString &path) const;
- bool handleSuccess(const QString &originalPath, const QString &found, int confidence,
+ bool handleSuccess(const QString &originalPath, const QStringList &found, int confidence,
const char *where) const;
static int commonPostFixLength(const QString &candidatePath, const QString &filePathToFind);
- static QString bestMatch(const QStringList &filePaths, const QString &filePathToFind);
+ static QStringList bestMatches(const QStringList &filePaths, const QString &filePathToFind);
FileName m_projectDir;
FileName m_sysroot;
@@ -108,4 +108,6 @@ private:
QrcUrlFinder m_qrcUrlFinder;
};
+QTCREATOR_UTILS_EXPORT FileName chooseFileFromList(const FileNameList &candidates);
+
} // namespace Utils