forked from qt-creator/qt-creator
Utils: improve readability of FileInProjectFinder
Preserve existing functionality, but split the large findFileOrDirectory logic into check* helper methods Change-Id: Ib7f1d1cebc001d014261616e15e4a87f6d01e8ac Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -15,8 +15,6 @@
|
|||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
static Q_LOGGING_CATEGORY(finderLog, "qtc.utils.fileinprojectfinder", QtWarningMsg);
|
static Q_LOGGING_CATEGORY(finderLog, "qtc.utils.fileinprojectfinder", QtWarningMsg);
|
||||||
}
|
}
|
||||||
@@ -161,24 +159,32 @@ bool FileInProjectFinder::handleSuccess(const FilePath &originalPath, const File
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FileInProjectFinder::findFileOrDirectory(const FilePath &originalPath, FileHandler fileHandler,
|
bool FileInProjectFinder::checkRootDirectory(const FilePath &originalPath,
|
||||||
DirectoryHandler directoryHandler) const
|
DirectoryHandler directoryHandler) const
|
||||||
{
|
{
|
||||||
if (originalPath.isEmpty()) {
|
if (!directoryHandler)
|
||||||
qCDebug(finderLog) << "FileInProjectFinder: malformed original path, returning";
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
if (directoryHandler && originalPath == m_projectDir && originalPath.isDir()) {
|
if (originalPath != m_projectDir || !originalPath.isDir())
|
||||||
const QStringList realEntries = QDir(originalPath.toFSPathString())
|
return false;
|
||||||
|
|
||||||
|
const QString originalFSPath = originalPath.toFSPathString();
|
||||||
|
const QStringList realEntries = QDir(originalFSPath)
|
||||||
.entryList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs);
|
.entryList(QDir::NoDotAndDotDot | QDir::Files | QDir::Dirs);
|
||||||
directoryHandler(realEntries, originalPath.toFSPathString().size());
|
|
||||||
|
directoryHandler(realEntries, originalFSPath.size());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto segments = originalPath.toFSPathString().split('/', Qt::SkipEmptyParts);
|
bool FileInProjectFinder::checkMappedPath(const FilePath &originalPath,
|
||||||
|
FileHandler fileHandler,
|
||||||
|
DirectoryHandler directoryHandler) const
|
||||||
|
{
|
||||||
|
const QString origFsPath = originalPath.toFSPathString();
|
||||||
|
const auto segments = origFsPath.split('/', Qt::SkipEmptyParts);
|
||||||
|
|
||||||
const PathMappingNode *node = &m_pathMapRoot;
|
const PathMappingNode *node = &m_pathMapRoot;
|
||||||
for (const auto &segment : segments) {
|
for (const QString &segment : segments) {
|
||||||
auto it = node->children.find(segment);
|
auto it = node->children.find(segment);
|
||||||
if (it == node->children.end()) {
|
if (it == node->children.end()) {
|
||||||
node = nullptr;
|
node = nullptr;
|
||||||
@@ -186,13 +192,14 @@ bool FileInProjectFinder::findFileOrDirectory(const FilePath &originalPath, File
|
|||||||
}
|
}
|
||||||
node = *it;
|
node = *it;
|
||||||
}
|
}
|
||||||
|
if (!node)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const int origLength = origFsPath.size();
|
||||||
|
|
||||||
const int origLength = originalPath.toFSPathString().length();
|
|
||||||
if (node) {
|
|
||||||
if (!node->localPath.isEmpty()) {
|
if (!node->localPath.isEmpty()) {
|
||||||
if (checkPath(node->localPath, origLength, fileHandler, directoryHandler)) {
|
if (checkPath(node->localPath, origLength, fileHandler, directoryHandler)) {
|
||||||
return handleSuccess(originalPath, {node->localPath}, origLength,
|
return handleSuccess(originalPath, {node->localPath}, origLength, "in mapped paths");
|
||||||
"in mapped paths");
|
|
||||||
}
|
}
|
||||||
} else if (directoryHandler) {
|
} else if (directoryHandler) {
|
||||||
directoryHandler(node->children.keys(), origLength);
|
directoryHandler(node->children.keys(), origLength);
|
||||||
@@ -200,12 +207,18 @@ bool FileInProjectFinder::findFileOrDirectory(const FilePath &originalPath, File
|
|||||||
<< "in mapped paths";
|
<< "in mapped paths";
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool FileInProjectFinder::checkCache(const FilePath &originalPath,
|
||||||
|
FileHandler fileHandler,
|
||||||
|
DirectoryHandler directoryHandler) const
|
||||||
|
{
|
||||||
auto it = m_cache.find(originalPath);
|
auto it = m_cache.find(originalPath);
|
||||||
if (it != m_cache.end()) {
|
if (it == m_cache.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
qCDebug(finderLog) << "FileInProjectFinder: checking cache ...";
|
qCDebug(finderLog) << "FileInProjectFinder: checking cache ...";
|
||||||
// check if cached path is still there
|
|
||||||
CacheEntry &candidate = it.value();
|
CacheEntry &candidate = it.value();
|
||||||
for (auto pathIt = candidate.paths.begin(); pathIt != candidate.paths.end();) {
|
for (auto pathIt = candidate.paths.begin(); pathIt != candidate.paths.end();) {
|
||||||
if (checkPath(*pathIt, candidate.matchLength, fileHandler, directoryHandler)) {
|
if (checkPath(*pathIt, candidate.matchLength, fileHandler, directoryHandler)) {
|
||||||
@@ -215,16 +228,28 @@ bool FileInProjectFinder::findFileOrDirectory(const FilePath &originalPath, File
|
|||||||
pathIt = candidate.paths.erase(pathIt);
|
pathIt = candidate.paths.erase(pathIt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!candidate.paths.empty())
|
if (!candidate.paths.empty())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
m_cache.erase(it);
|
m_cache.erase(it);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m_projectDir.isEmpty()) {
|
bool FileInProjectFinder::checkProjectDirectory(const FilePath &originalPath,
|
||||||
|
FileHandler fileHandler,
|
||||||
|
DirectoryHandler directoryHandler) const
|
||||||
|
{
|
||||||
|
if (m_projectDir.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
qCDebug(finderLog) << "FileInProjectFinder: checking project directory ...";
|
qCDebug(finderLog) << "FileInProjectFinder: checking project directory ...";
|
||||||
|
|
||||||
int prefixToIgnore = -1;
|
const QString origFsPath = originalPath.toFSPathString();
|
||||||
|
const int origLength = origFsPath.size();
|
||||||
const QChar separator = QLatin1Char('/');
|
const QChar separator = QLatin1Char('/');
|
||||||
|
|
||||||
|
int prefixToIgnore = -1;
|
||||||
if (originalPath.startsWith(m_projectDir.toFSPathString() + separator)) {
|
if (originalPath.startsWith(m_projectDir.toFSPathString() + separator)) {
|
||||||
if (originalPath.osType() == OsTypeMac) {
|
if (originalPath.osType() == OsTypeMac) {
|
||||||
// starting with the project path is not sufficient if the file was
|
// starting with the project path is not sufficient if the file was
|
||||||
@@ -233,99 +258,138 @@ bool FileInProjectFinder::findFileOrDirectory(const FilePath &originalPath, File
|
|||||||
if (originalPath.contains(appResourcePath)) {
|
if (originalPath.contains(appResourcePath)) {
|
||||||
// the path is inside the project, but most probably as a resource of an insource build
|
// the path is inside the project, but most probably as a resource of an insource build
|
||||||
// so ignore that path
|
// so ignore that path
|
||||||
prefixToIgnore = originalPath.toFSPathString().indexOf(appResourcePath) + appResourcePath.length();
|
prefixToIgnore = origFsPath.indexOf(appResourcePath) + appResourcePath.length();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prefixToIgnore == -1
|
if (prefixToIgnore == -1
|
||||||
&& checkPath(originalPath, origLength, fileHandler, directoryHandler)) {
|
&& checkPath(originalPath, origLength, fileHandler, directoryHandler)) {
|
||||||
return handleSuccess(originalPath, {originalPath}, origLength,
|
return handleSuccess(originalPath, {originalPath}, origLength, "in project directory");
|
||||||
"in project directory");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qCDebug(finderLog) << "FileInProjectFinder:"
|
qCDebug(finderLog) << "FileInProjectFinder: checking stripped paths in project directory ...";
|
||||||
<< "checking stripped paths in project directory ...";
|
|
||||||
|
|
||||||
// Strip directories one by one from the beginning of the path,
|
// Strip directories one by one from the beginning of the path,
|
||||||
// and see if the new relative path exists in the build directory.
|
// and see if the new relative path exists in the build directory.
|
||||||
if (prefixToIgnore < 0) {
|
if (prefixToIgnore < 0) {
|
||||||
if (!originalPath.isAbsolutePath()
|
if (!originalPath.isAbsolutePath() && !originalPath.startsWith(separator)) {
|
||||||
&& !originalPath.startsWith(separator)) {
|
|
||||||
prefixToIgnore = 0;
|
prefixToIgnore = 0;
|
||||||
} else {
|
} else {
|
||||||
prefixToIgnore = originalPath.toFSPathString().indexOf(separator);
|
prefixToIgnore = origFsPath.indexOf(separator);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Repeatedly strip directories from the front
|
||||||
while (prefixToIgnore != -1) {
|
while (prefixToIgnore != -1) {
|
||||||
QString candidateString = originalPath.toFSPathString();
|
QString candidateString = origFsPath;
|
||||||
candidateString.remove(0, prefixToIgnore);
|
candidateString.remove(0, prefixToIgnore);
|
||||||
candidateString.prepend(m_projectDir.toUrlishString());
|
candidateString.prepend(m_projectDir.toUrlishString());
|
||||||
const FilePath candidate = FilePath::fromString(candidateString);
|
const FilePath candidate = FilePath::fromString(candidateString);
|
||||||
const int matchLength = origLength - prefixToIgnore;
|
const int matchLength = origLength - prefixToIgnore;
|
||||||
// FIXME: This might be a worse match than what we find later.
|
// FIXME: This might be a worse match than what we find later.
|
||||||
if (checkPath(candidate, matchLength, fileHandler, directoryHandler)) {
|
if (checkPath(candidate, matchLength, fileHandler, directoryHandler)) {
|
||||||
return handleSuccess(originalPath, {candidate}, matchLength,
|
return handleSuccess(originalPath, {candidate}, matchLength, "in project directory");
|
||||||
"in project directory");
|
|
||||||
}
|
}
|
||||||
prefixToIgnore = originalPath.toUrlishString().indexOf(separator, prefixToIgnore + 1);
|
prefixToIgnore = originalPath.toUrlishString().indexOf(separator, prefixToIgnore + 1);
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// find best matching file path in project files
|
bool FileInProjectFinder::checkProjectFiles(const FilePath &originalPath,
|
||||||
|
FileHandler fileHandler,
|
||||||
|
DirectoryHandler directoryHandler) const
|
||||||
|
{
|
||||||
qCDebug(finderLog) << "FileInProjectFinder: checking project files ...";
|
qCDebug(finderLog) << "FileInProjectFinder: checking project files ...";
|
||||||
|
|
||||||
QStringList matches;
|
|
||||||
const QString lastSegment = originalPath.fileName();
|
const QString lastSegment = originalPath.fileName();
|
||||||
|
QStringList matches;
|
||||||
if (fileHandler)
|
if (fileHandler)
|
||||||
matches.append(filesWithSameFileName(lastSegment));
|
matches.append(filesWithSameFileName(lastSegment));
|
||||||
if (directoryHandler)
|
if (directoryHandler)
|
||||||
matches.append(pathSegmentsWithSameName(lastSegment));
|
matches.append(pathSegmentsWithSameName(lastSegment));
|
||||||
|
|
||||||
const QStringList matchedFilePaths = bestMatches(matches, originalPath.toUrlishString());
|
const QStringList matchedFilePaths = bestMatches(matches, originalPath.toUrlishString());
|
||||||
if (!matchedFilePaths.empty()) {
|
if (matchedFilePaths.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
const int matchLength = commonPostFixLength(matchedFilePaths.first(), originalPath.toUrlishString());
|
const int matchLength = commonPostFixLength(matchedFilePaths.first(), originalPath.toUrlishString());
|
||||||
FilePaths hits;
|
FilePaths hits;
|
||||||
for (const QString &matchedFilePath : matchedFilePaths) {
|
for (const QString &matchedFilePath : matchedFilePaths) {
|
||||||
if (checkPath(FilePath::fromString(matchedFilePath), matchLength, fileHandler, directoryHandler))
|
if (checkPath(FilePath::fromString(matchedFilePath), matchLength, fileHandler, directoryHandler))
|
||||||
hits.append(FilePath::fromString(matchedFilePath));
|
hits.append(FilePath::fromString(matchedFilePath));
|
||||||
}
|
}
|
||||||
if (!hits.empty())
|
if (hits.isEmpty())
|
||||||
|
return false;
|
||||||
|
|
||||||
return handleSuccess(originalPath, hits, matchLength, "when matching project files");
|
return handleSuccess(originalPath, hits, matchLength, "when matching project files");
|
||||||
}
|
}
|
||||||
|
|
||||||
CacheEntry foundPath = findInSearchPaths(originalPath, fileHandler, directoryHandler);
|
bool FileInProjectFinder::checkSearchPaths(const FilePath &originalPath,
|
||||||
if (!foundPath.paths.isEmpty()) {
|
FileHandler fileHandler,
|
||||||
return handleSuccess(originalPath, foundPath.paths, foundPath.matchLength,
|
DirectoryHandler directoryHandler) const
|
||||||
|
{
|
||||||
|
for (const FilePath &dirPath : m_searchDirectories) {
|
||||||
|
const CacheEntry found = findInSearchPath(dirPath, originalPath,
|
||||||
|
fileHandler, directoryHandler);
|
||||||
|
|
||||||
|
if (!found.paths.isEmpty()) {
|
||||||
|
return handleSuccess(originalPath, found.paths, found.matchLength,
|
||||||
"in search path");
|
"in search path");
|
||||||
}
|
}
|
||||||
|
|
||||||
qCDebug(finderLog) << "FileInProjectFinder: checking absolute path in sysroot ...";
|
|
||||||
|
|
||||||
// check if absolute path is found in sysroot
|
|
||||||
if (!m_sysroot.isEmpty()) {
|
|
||||||
const FilePath sysrootPath = m_sysroot.pathAppended(originalPath.toUrlishString());
|
|
||||||
if (checkPath(sysrootPath, origLength, fileHandler, directoryHandler)) {
|
|
||||||
return handleSuccess(originalPath, {sysrootPath}, origLength, "in sysroot");
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
qCDebug(finderLog) << "FileInProjectFinder: couldn't find file!";
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
FileInProjectFinder::CacheEntry FileInProjectFinder::findInSearchPaths(
|
bool FileInProjectFinder::checkSysroot(const FilePath &originalPath,
|
||||||
const FilePath &filePath, FileHandler fileHandler, DirectoryHandler directoryHandler) const
|
FileHandler fileHandler,
|
||||||
|
DirectoryHandler directoryHandler) const
|
||||||
{
|
{
|
||||||
for (const FilePath &dirPath : m_searchDirectories) {
|
qCDebug(finderLog) << "FileInProjectFinder: checking absolute path in sysroot ...";
|
||||||
const CacheEntry found = findInSearchPath(dirPath, filePath,
|
|
||||||
fileHandler, directoryHandler);
|
if (m_sysroot.isEmpty())
|
||||||
if (!found.paths.isEmpty())
|
return false;
|
||||||
return found;
|
|
||||||
|
const int origLength = originalPath.toFSPathString().length();
|
||||||
|
const FilePath sysrootPath = m_sysroot.pathAppended(originalPath.toUrlishString());
|
||||||
|
|
||||||
|
if (!checkPath(sysrootPath, origLength, fileHandler, directoryHandler))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return handleSuccess(originalPath, {sysrootPath}, origLength, "in sysroot");
|
||||||
}
|
}
|
||||||
|
|
||||||
return CacheEntry();
|
bool FileInProjectFinder::findFileOrDirectory(const FilePath &originalPath,
|
||||||
|
FileHandler fileHandler,
|
||||||
|
DirectoryHandler directoryHandler) const
|
||||||
|
{
|
||||||
|
if (originalPath.isEmpty()) {
|
||||||
|
qCDebug(finderLog) << "FileInProjectFinder: malformed original path, returning";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkRootDirectory(originalPath, directoryHandler))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (checkMappedPath(originalPath, fileHandler, directoryHandler))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (checkCache(originalPath, fileHandler, directoryHandler))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (checkProjectDirectory(originalPath, fileHandler, directoryHandler))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (checkProjectFiles(originalPath, fileHandler, directoryHandler))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (checkSearchPaths(originalPath, fileHandler, directoryHandler))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (checkSysroot(originalPath, fileHandler, directoryHandler))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
qCDebug(finderLog) << "FileInProjectFinder: couldn't find file!";
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static QString chopFirstDir(const QString &dirPath)
|
static QString chopFirstDir(const QString &dirPath)
|
||||||
|
@@ -62,13 +62,30 @@ private:
|
|||||||
mutable QHash<FilePath, std::shared_ptr<QrcParser>> m_parserCache;
|
mutable QHash<FilePath, std::shared_ptr<QrcParser>> m_parserCache;
|
||||||
};
|
};
|
||||||
|
|
||||||
CacheEntry findInSearchPaths(const FilePath &filePath, FileHandler fileHandler,
|
|
||||||
DirectoryHandler directoryHandler) const;
|
|
||||||
static CacheEntry findInSearchPath(const FilePath &searchPath, const FilePath &filePath,
|
static CacheEntry findInSearchPath(const FilePath &searchPath, const FilePath &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 checkRootDirectory(const FilePath &originalPath, DirectoryHandler directoryHandler) const;
|
||||||
|
bool checkMappedPath(const FilePath &originalPath,
|
||||||
|
FileHandler fileHandler,
|
||||||
|
DirectoryHandler directoryHandler) const;
|
||||||
|
bool checkCache(const FilePath &originalPath,
|
||||||
|
FileHandler fileHandler,
|
||||||
|
DirectoryHandler directoryHandler) const;
|
||||||
|
bool checkProjectDirectory(const FilePath &originalPath,
|
||||||
|
FileHandler fileHandler,
|
||||||
|
DirectoryHandler directoryHandler) const;
|
||||||
|
bool checkProjectFiles(const FilePath &originalPath,
|
||||||
|
FileHandler fileHandler,
|
||||||
|
DirectoryHandler directoryHandler) const;
|
||||||
|
bool checkSearchPaths(const FilePath &originalPath,
|
||||||
|
FileHandler fileHandler,
|
||||||
|
DirectoryHandler directoryHandler) const;
|
||||||
|
bool checkSysroot(const FilePath &originalPath,
|
||||||
|
FileHandler fileHandler,
|
||||||
|
DirectoryHandler directoryHandler) const;
|
||||||
bool handleSuccess(const FilePath &originalPath, const FilePaths &found, int confidence,
|
bool handleSuccess(const FilePath &originalPath, const FilePaths &found, int confidence,
|
||||||
const char *where) const;
|
const char *where) const;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user