forked from qt-creator/qt-creator
FileInProjectFinder: Report length of matched postfix
We want to know how exact the match is, so that we can reject results that don't fit our needs. We cannot pass an extra fuzzy/exact parameter to findFileOrDirectory, because we cache the results, and we need to be able to reuse results obtained from different searches. Task-number: QTCREATORBUG-21107 Change-Id: I870100f4c0acd0e9b007f076543340b34a25a447 Reviewed-by: hjk <hjk@qt.io> Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
@@ -42,15 +42,16 @@ Q_LOGGING_CATEGORY(finderLog, "qtc.utils.fileinprojectfinder");
|
|||||||
|
|
||||||
namespace Utils {
|
namespace Utils {
|
||||||
|
|
||||||
static bool checkPath(const QString &candidate, FileInProjectFinder::FileHandler fileHandler,
|
static bool checkPath(const QString &candidate, int matchLength,
|
||||||
|
FileInProjectFinder::FileHandler fileHandler,
|
||||||
FileInProjectFinder::DirectoryHandler directoryHandler)
|
FileInProjectFinder::DirectoryHandler directoryHandler)
|
||||||
{
|
{
|
||||||
const QFileInfo candidateInfo(candidate);
|
const QFileInfo candidateInfo(candidate);
|
||||||
if (fileHandler && candidateInfo.isFile()) {
|
if (fileHandler && candidateInfo.isFile()) {
|
||||||
fileHandler(candidate);
|
fileHandler(candidate, matchLength);
|
||||||
return true;
|
return true;
|
||||||
} else if (directoryHandler && candidateInfo.isDir()) {
|
} else if (directoryHandler && candidateInfo.isDir()) {
|
||||||
directoryHandler(QDir(candidate).entryList());
|
directoryHandler(QDir(candidate).entryList(), matchLength);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -144,7 +145,7 @@ QString FileInProjectFinder::findFile(const QUrl &fileUrl, bool *success) const
|
|||||||
originalPath = fileUrl.path();
|
originalPath = fileUrl.path();
|
||||||
|
|
||||||
QString result;
|
QString result;
|
||||||
bool found = findFileOrDirectory(originalPath, [&](const QString &fileName) {
|
bool found = findFileOrDirectory(originalPath, [&](const QString &fileName, int) {
|
||||||
result = fileName;
|
result = fileName;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -155,10 +156,13 @@ QString FileInProjectFinder::findFile(const QUrl &fileUrl, bool *success) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool FileInProjectFinder::handleSuccess(const QString &originalPath, const QString &found,
|
bool FileInProjectFinder::handleSuccess(const QString &originalPath, const QString &found,
|
||||||
const char *where) const
|
int matchLength, const char *where) const
|
||||||
{
|
{
|
||||||
qCDebug(finderLog) << "FileInProjectFinder: found" << found << where;
|
qCDebug(finderLog) << "FileInProjectFinder: found" << found << where;
|
||||||
m_cache.insert(originalPath, found);
|
CacheEntry entry;
|
||||||
|
entry.path = found;
|
||||||
|
entry.matchLength = matchLength;
|
||||||
|
m_cache.insert(originalPath, entry);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,15 +185,16 @@ bool FileInProjectFinder::findFileOrDirectory(const QString &originalPath, FileH
|
|||||||
node = *it;
|
node = *it;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const int origLength = originalPath.length();
|
||||||
if (node) {
|
if (node) {
|
||||||
if (!node->localPath.isEmpty()) {
|
if (!node->localPath.isEmpty()) {
|
||||||
const QString localPath = node->localPath.toString();
|
const QString localPath = node->localPath.toString();
|
||||||
if (checkPath(localPath, fileHandler, directoryHandler))
|
if (checkPath(localPath, origLength, fileHandler, directoryHandler))
|
||||||
return handleSuccess(originalPath, localPath, "in deployment data");
|
return handleSuccess(originalPath, localPath, origLength, "in mapped paths");
|
||||||
} else if (directoryHandler) {
|
} else if (directoryHandler) {
|
||||||
directoryHandler(node->children.keys());
|
directoryHandler(node->children.keys(), origLength);
|
||||||
qCDebug(finderLog) << "FileInProjectFinder: found virtual directory" << originalPath
|
qCDebug(finderLog) << "FileInProjectFinder: found virtual directory" << originalPath
|
||||||
<< "in deployment data";
|
<< "in mapped paths";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -198,9 +203,9 @@ bool FileInProjectFinder::findFileOrDirectory(const QString &originalPath, FileH
|
|||||||
if (it != m_cache.end()) {
|
if (it != m_cache.end()) {
|
||||||
qCDebug(finderLog) << "FileInProjectFinder: checking cache ...";
|
qCDebug(finderLog) << "FileInProjectFinder: checking cache ...";
|
||||||
// check if cached path is still there
|
// check if cached path is still there
|
||||||
const QString &candidate = it.value();
|
const CacheEntry &candidate = it.value();
|
||||||
if (checkPath(candidate, fileHandler, directoryHandler)) {
|
if (checkPath(candidate.path, candidate.matchLength, fileHandler, directoryHandler)) {
|
||||||
qCDebug(finderLog) << "FileInProjectFinder: found" << candidate << "in the cache";
|
qCDebug(finderLog) << "FileInProjectFinder: found" << candidate.path << "in the cache";
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
m_cache.erase(it);
|
m_cache.erase(it);
|
||||||
@@ -223,8 +228,12 @@ bool FileInProjectFinder::findFileOrDirectory(const QString &originalPath, FileH
|
|||||||
prefixToIgnore = originalPath.indexOf(appResourcePath) + appResourcePath.length();
|
prefixToIgnore = originalPath.indexOf(appResourcePath) + appResourcePath.length();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (prefixToIgnore == -1 && checkPath(originalPath, fileHandler, directoryHandler))
|
|
||||||
return handleSuccess(originalPath, originalPath, "in project directory");
|
if (prefixToIgnore == -1
|
||||||
|
&& checkPath(originalPath, origLength, fileHandler, directoryHandler)) {
|
||||||
|
return handleSuccess(originalPath, originalPath, origLength,
|
||||||
|
"in project directory");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qCDebug(finderLog) << "FileInProjectFinder:"
|
qCDebug(finderLog) << "FileInProjectFinder:"
|
||||||
@@ -244,8 +253,9 @@ bool FileInProjectFinder::findFileOrDirectory(const QString &originalPath, FileH
|
|||||||
QString candidate = originalPath;
|
QString candidate = originalPath;
|
||||||
candidate.remove(0, prefixToIgnore);
|
candidate.remove(0, prefixToIgnore);
|
||||||
candidate.prepend(m_projectDir.toString());
|
candidate.prepend(m_projectDir.toString());
|
||||||
if (checkPath(candidate, fileHandler, directoryHandler))
|
const int matchLength = origLength - prefixToIgnore;
|
||||||
return handleSuccess(originalPath, candidate, "in project directory");
|
if (checkPath(candidate, matchLength, fileHandler, directoryHandler))
|
||||||
|
return handleSuccess(originalPath, candidate, matchLength, "in project directory");
|
||||||
prefixToIgnore = originalPath.indexOf(separator, prefixToIgnore + 1);
|
prefixToIgnore = originalPath.indexOf(separator, prefixToIgnore + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -260,12 +270,16 @@ bool FileInProjectFinder::findFileOrDirectory(const QString &originalPath, FileH
|
|||||||
if (directoryHandler)
|
if (directoryHandler)
|
||||||
matches.append(pathSegmentsWithSameName(lastSegment));
|
matches.append(pathSegmentsWithSameName(lastSegment));
|
||||||
const QString matchedFilePath = bestMatch(matches, originalPath);
|
const QString matchedFilePath = bestMatch(matches, originalPath);
|
||||||
if (!matchedFilePath.isEmpty() && checkPath(matchedFilePath, fileHandler, directoryHandler))
|
const int matchLength = commonPostFixLength(matchedFilePath, originalPath);
|
||||||
return handleSuccess(originalPath, matchedFilePath, "when matching project files");
|
if (!matchedFilePath.isEmpty()
|
||||||
|
&& checkPath(matchedFilePath, matchLength, fileHandler, directoryHandler)) {
|
||||||
|
return handleSuccess(originalPath, matchedFilePath, matchLength,
|
||||||
|
"when matching project files");
|
||||||
|
}
|
||||||
|
|
||||||
QString foundPath = findInSearchPaths(originalPath, fileHandler, directoryHandler);
|
CacheEntry foundPath = findInSearchPaths(originalPath, fileHandler, directoryHandler);
|
||||||
if (!foundPath.isEmpty())
|
if (!foundPath.path.isEmpty())
|
||||||
return handleSuccess(originalPath, foundPath, "in search path");
|
return handleSuccess(originalPath, foundPath.path, foundPath.matchLength, "in search path");
|
||||||
|
|
||||||
qCDebug(finderLog) << "FileInProjectFinder: checking absolute path in sysroot ...";
|
qCDebug(finderLog) << "FileInProjectFinder: checking absolute path in sysroot ...";
|
||||||
|
|
||||||
@@ -273,8 +287,8 @@ bool FileInProjectFinder::findFileOrDirectory(const QString &originalPath, FileH
|
|||||||
if (!m_sysroot.isEmpty()) {
|
if (!m_sysroot.isEmpty()) {
|
||||||
FileName sysrootPath = m_sysroot;
|
FileName sysrootPath = m_sysroot;
|
||||||
sysrootPath.appendPath(originalPath);
|
sysrootPath.appendPath(originalPath);
|
||||||
if (checkPath(sysrootPath.toString(), fileHandler, directoryHandler))
|
if (checkPath(sysrootPath.toString(), origLength, fileHandler, directoryHandler))
|
||||||
return handleSuccess(originalPath, sysrootPath.toString(), "in sysroot");
|
return handleSuccess(originalPath, sysrootPath.toString(), origLength, "in sysroot");
|
||||||
}
|
}
|
||||||
|
|
||||||
qCDebug(finderLog) << "FileInProjectFinder: couldn't find file!";
|
qCDebug(finderLog) << "FileInProjectFinder: couldn't find file!";
|
||||||
@@ -282,16 +296,17 @@ bool FileInProjectFinder::findFileOrDirectory(const QString &originalPath, FileH
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString FileInProjectFinder::findInSearchPaths(const QString &filePath, FileHandler fileHandler,
|
FileInProjectFinder::CacheEntry FileInProjectFinder::findInSearchPaths(
|
||||||
DirectoryHandler directoryHandler) const
|
const QString &filePath, FileHandler fileHandler, DirectoryHandler directoryHandler) const
|
||||||
{
|
{
|
||||||
for (const FileName &dirPath : m_searchDirectories) {
|
for (const FileName &dirPath : m_searchDirectories) {
|
||||||
const QString found = findInSearchPath(dirPath.toString(), filePath, fileHandler,
|
const CacheEntry found = findInSearchPath(dirPath.toString(), filePath,
|
||||||
directoryHandler);
|
fileHandler, directoryHandler);
|
||||||
if (!found.isEmpty())
|
if (!found.path.isEmpty())
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
return QString();
|
|
||||||
|
return CacheEntry();
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString chopFirstDir(const QString &dirPath)
|
static QString chopFirstDir(const QString &dirPath)
|
||||||
@@ -303,32 +318,35 @@ static QString chopFirstDir(const QString &dirPath)
|
|||||||
return dirPath.mid(i + 1);
|
return dirPath.mid(i + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString FileInProjectFinder::findInSearchPath(const QString &searchPath, const QString &filePath,
|
FileInProjectFinder::CacheEntry FileInProjectFinder::findInSearchPath(
|
||||||
FileHandler fileHandler,
|
const QString &searchPath, const QString &filePath,
|
||||||
DirectoryHandler directoryHandler)
|
FileHandler fileHandler, DirectoryHandler directoryHandler)
|
||||||
{
|
{
|
||||||
qCDebug(finderLog) << "FileInProjectFinder: checking search path" << searchPath;
|
qCDebug(finderLog) << "FileInProjectFinder: checking search path" << searchPath;
|
||||||
|
|
||||||
QString s = filePath;
|
QString s = filePath;
|
||||||
while (!s.isEmpty()) {
|
while (!s.isEmpty()) {
|
||||||
const QString candidate = searchPath + QLatin1Char('/') + s;
|
CacheEntry result;
|
||||||
qCDebug(finderLog) << "FileInProjectFinder: trying" << candidate;
|
result.path = searchPath + QLatin1Char('/') + s;
|
||||||
|
result.matchLength = s.length() + 1;
|
||||||
|
qCDebug(finderLog) << "FileInProjectFinder: trying" << result.path;
|
||||||
|
|
||||||
if (checkPath(candidate, fileHandler, directoryHandler))
|
if (checkPath(result.path, result.matchLength, fileHandler, directoryHandler))
|
||||||
return candidate;
|
return result;
|
||||||
|
|
||||||
QString next = chopFirstDir(s);
|
QString next = chopFirstDir(s);
|
||||||
if (next.isEmpty()) {
|
if (next.isEmpty()) {
|
||||||
if (directoryHandler && QFileInfo(searchPath).fileName() == s) {
|
if (directoryHandler && QFileInfo(searchPath).fileName() == s) {
|
||||||
directoryHandler(QDir(searchPath).entryList());
|
result.path = searchPath;
|
||||||
return searchPath;
|
directoryHandler(QDir(searchPath).entryList(), result.matchLength);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
s = next;
|
s = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
return QString();
|
return CacheEntry();
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList FileInProjectFinder::filesWithSameFileName(const QString &fileName) const
|
QStringList FileInProjectFinder::filesWithSameFileName(const QString &fileName) const
|
||||||
@@ -358,7 +376,8 @@ QStringList FileInProjectFinder::pathSegmentsWithSameName(const QString &pathSeg
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
int FileInProjectFinder::rankFilePath(const QString &candidatePath, const QString &filePathToFind)
|
int FileInProjectFinder::commonPostFixLength(const QString &candidatePath,
|
||||||
|
const QString &filePathToFind)
|
||||||
{
|
{
|
||||||
int rank = 0;
|
int rank = 0;
|
||||||
for (int a = candidatePath.length(), b = filePathToFind.length();
|
for (int a = candidatePath.length(), b = filePathToFind.length();
|
||||||
@@ -378,7 +397,7 @@ QString FileInProjectFinder::bestMatch(const QStringList &filePaths, const QStri
|
|||||||
}
|
}
|
||||||
auto it = std::max_element(filePaths.constBegin(), filePaths.constEnd(),
|
auto it = std::max_element(filePaths.constBegin(), filePaths.constEnd(),
|
||||||
[&filePathToFind] (const QString &a, const QString &b) -> bool {
|
[&filePathToFind] (const QString &a, const QString &b) -> bool {
|
||||||
return rankFilePath(a, filePathToFind) < rankFilePath(b, filePathToFind);
|
return commonPostFixLength(a, filePathToFind) < commonPostFixLength(b, filePathToFind);
|
||||||
});
|
});
|
||||||
if (it != filePaths.cend()) {
|
if (it != filePaths.cend()) {
|
||||||
qCDebug(finderLog) << "FileInProjectFinder: found best match" << *it << "in project files";
|
qCDebug(finderLog) << "FileInProjectFinder: found best match" << *it << "in project files";
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ class QTCREATOR_UTILS_EXPORT FileInProjectFinder
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
using FileHandler = std::function<void(const QString &)>;
|
using FileHandler = std::function<void(const QString &, int)>;
|
||||||
using DirectoryHandler = std::function<void(const QStringList &)>;
|
using DirectoryHandler = std::function<void(const QStringList &, int)>;
|
||||||
|
|
||||||
FileInProjectFinder();
|
FileInProjectFinder();
|
||||||
~FileInProjectFinder();
|
~FileInProjectFinder();
|
||||||
@@ -68,16 +68,22 @@ private:
|
|||||||
QHash<QString, PathMappingNode *> children;
|
QHash<QString, PathMappingNode *> children;
|
||||||
};
|
};
|
||||||
|
|
||||||
QString findInSearchPaths(const QString &filePath, FileHandler fileHandler,
|
struct CacheEntry {
|
||||||
|
QString path;
|
||||||
|
int matchLength = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
CacheEntry findInSearchPaths(const QString &filePath, FileHandler fileHandler,
|
||||||
DirectoryHandler directoryHandler) const;
|
DirectoryHandler directoryHandler) const;
|
||||||
static QString findInSearchPath(const QString &searchPath, const QString &filePath,
|
static CacheEntry findInSearchPath(const QString &searchPath, const QString &filePath,
|
||||||
FileHandler fileHandler, DirectoryHandler directoryHandler);
|
FileHandler fileHandler, DirectoryHandler directoryHandler);
|
||||||
QStringList filesWithSameFileName(const QString &fileName) const;
|
QStringList filesWithSameFileName(const QString &fileName) const;
|
||||||
QStringList pathSegmentsWithSameName(const QString &path) const;
|
QStringList pathSegmentsWithSameName(const QString &path) const;
|
||||||
|
|
||||||
bool handleSuccess(const QString &originalPath, const QString &found, const char *where) const;
|
bool handleSuccess(const QString &originalPath, const QString &found, int confidence,
|
||||||
|
const char *where) const;
|
||||||
|
|
||||||
static int rankFilePath(const QString &candidatePath, const QString &filePathToFind);
|
static int commonPostFixLength(const QString &candidatePath, const QString &filePathToFind);
|
||||||
static QString bestMatch(const QStringList &filePaths, const QString &filePathToFind);
|
static QString bestMatch(const QStringList &filePaths, const QString &filePathToFind);
|
||||||
|
|
||||||
FileName m_projectDir;
|
FileName m_projectDir;
|
||||||
@@ -86,7 +92,7 @@ private:
|
|||||||
FileNameList m_searchDirectories;
|
FileNameList m_searchDirectories;
|
||||||
PathMappingNode m_pathMapRoot;
|
PathMappingNode m_pathMapRoot;
|
||||||
|
|
||||||
mutable QHash<QString,QString> m_cache;
|
mutable QHash<QString, CacheEntry> m_cache;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Utils
|
} // namespace Utils
|
||||||
|
|||||||
Reference in New Issue
Block a user