ui: added automatic include path detection for C/C++ Source Groups
This commit is contained in:
+1
@@ -0,0 +1 @@
|
||||
#include "a.h"
|
||||
+1
@@ -0,0 +1 @@
|
||||
#include "a.h"
|
||||
+1
@@ -0,0 +1 @@
|
||||
#include "../a.h"
|
||||
@@ -128,13 +128,17 @@ bool FilePath::isAbsolute() const
|
||||
FilePath FilePath::getParentDirectory() const
|
||||
{
|
||||
FilePath parentDirectory(m_path->parent_path());
|
||||
parentDirectory.m_checkedIsDirectory = true;
|
||||
parentDirectory.m_isDirectory = true;
|
||||
|
||||
if (m_checkedExists && m_exists)
|
||||
if (!parentDirectory.empty())
|
||||
{
|
||||
parentDirectory.m_checkedExists = true;
|
||||
parentDirectory.m_exists = true;
|
||||
parentDirectory.m_checkedIsDirectory = true;
|
||||
parentDirectory.m_isDirectory = true;
|
||||
|
||||
if (m_checkedExists && m_exists)
|
||||
{
|
||||
parentDirectory.m_checkedExists = true;
|
||||
parentDirectory.m_exists = true;
|
||||
}
|
||||
}
|
||||
|
||||
return parentDirectory;
|
||||
|
||||
@@ -30,7 +30,7 @@ std::vector<FilePath> FileSystem::getFilePathsFromDirectory(
|
||||
}
|
||||
}
|
||||
|
||||
if (boost::filesystem::is_regular_file(*it) && ext.find(it->path().extension().string()) != ext.end())
|
||||
if (boost::filesystem::is_regular_file(*it) && (ext.empty() || ext.find(it->path().extension().string()) != ext.end()))
|
||||
{
|
||||
files.push_back(FilePath(it->path().generic_string()));
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ class FileSystem
|
||||
{
|
||||
public:
|
||||
static std::vector<FilePath> getFilePathsFromDirectory(
|
||||
const FilePath& path, const std::vector<std::string>& extensions);
|
||||
const FilePath& path, const std::vector<std::string>& extensions = {});
|
||||
|
||||
static FileInfo getFileInfoForPath(const FilePath& filePath);
|
||||
|
||||
|
||||
@@ -14,11 +14,12 @@ std::shared_ptr<TextAccess> TextAccess::createFromFile(const FilePath& filePath)
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<TextAccess> TextAccess::createFromString(const std::string& text)
|
||||
std::shared_ptr<TextAccess> TextAccess::createFromString(const std::string& text, const FilePath& filePath)
|
||||
{
|
||||
std::shared_ptr<TextAccess> result(new TextAccess());
|
||||
|
||||
result->m_lines = splitStringByLines(text);
|
||||
result->m_filePath = filePath;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ class TextAccess
|
||||
{
|
||||
public:
|
||||
static std::shared_ptr<TextAccess> createFromFile(const FilePath& filePath);
|
||||
static std::shared_ptr<TextAccess> createFromString(const std::string& text);
|
||||
static std::shared_ptr<TextAccess> createFromString(const std::string& text, const FilePath& filePath = FilePath());
|
||||
|
||||
virtual ~TextAccess();
|
||||
|
||||
|
||||
@@ -51,6 +51,9 @@ namespace utility
|
||||
template<typename T>
|
||||
std::vector<T> toVector(const std::set<T>& d);
|
||||
|
||||
template<typename T>
|
||||
std::set<T>toSet(const std::vector<T>& d);
|
||||
|
||||
template<typename T>
|
||||
void fillVectorWithElements(std::vector<T>& v, const T& arg);
|
||||
|
||||
@@ -191,6 +194,13 @@ std::vector<T> utility::toVector(const std::set<T>& d)
|
||||
return v;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::set<T> utility::toSet(const std::vector<T>& v)
|
||||
{
|
||||
std::set<T> s(v.begin(), v.end());
|
||||
return s;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void utility::fillVectorWithElements(std::vector<T>& v, const T& arg)
|
||||
{
|
||||
|
||||
@@ -78,10 +78,6 @@ add_files(
|
||||
data/parser/cxx/utilityClang.cpp
|
||||
data/parser/cxx/utilityClang.h
|
||||
|
||||
project/IncludeDirective.cpp
|
||||
project/IncludeDirective.h
|
||||
project/IncludeValidation.cpp
|
||||
project/IncludeValidation.h
|
||||
project/SourceGroupCxx.cpp
|
||||
project/SourceGroupCxx.h
|
||||
project/SourceGroupCxxCdb.cpp
|
||||
@@ -93,4 +89,8 @@ add_files(
|
||||
|
||||
utility/CompilationDatabase.cpp
|
||||
utility/CompilationDatabase.h
|
||||
utility/IncludeDirective.cpp
|
||||
utility/IncludeDirective.h
|
||||
utility/IncludeProcessing.cpp
|
||||
utility/IncludeProcessing.h
|
||||
)
|
||||
|
||||
@@ -1,185 +0,0 @@
|
||||
#include "project/IncludeValidation.h"
|
||||
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "project/IncludeDirective.h"
|
||||
#include "utility/file/FilePath.h"
|
||||
#include "utility/text/TextAccess.h"
|
||||
#include "utility/utility.h"
|
||||
#include "utility/utilityString.h"
|
||||
|
||||
std::vector<IncludeDirective> IncludeValidation::getUnresolvedIncludeDirectives(
|
||||
const std::vector<FilePath>& sourceFilePaths,
|
||||
const std::vector<FilePath>& indexedPaths,
|
||||
const std::vector<FilePath>& headerSearchDirectories,
|
||||
size_t quantileCount, std::function<void(float)> progress
|
||||
)
|
||||
{
|
||||
struct IncludeDirectiveComparator
|
||||
{
|
||||
bool operator()(const IncludeDirective& a, const IncludeDirective& b)
|
||||
{
|
||||
return a.getIncludedFile() < b.getIncludedFile();
|
||||
}
|
||||
};
|
||||
|
||||
std::unordered_set<std::string> processedFilePaths;
|
||||
std::set<IncludeDirective, IncludeDirectiveComparator> unresolvedIncludeDirectives;
|
||||
|
||||
quantileCount = std::max<size_t>(1, std::min(quantileCount, sourceFilePaths.size()));
|
||||
|
||||
std::vector<std::vector<FilePath>> quantiles;
|
||||
for (size_t i = 0; i < quantileCount; i++)
|
||||
{
|
||||
quantiles.push_back(std::vector<FilePath>());
|
||||
}
|
||||
for (size_t i = 0; i < sourceFilePaths.size(); i++)
|
||||
{
|
||||
quantiles[i % quantileCount].push_back(sourceFilePaths[i]);
|
||||
}
|
||||
|
||||
OrderedCache<FilePath, FilePath> canonicalPathCache(
|
||||
[](const FilePath& filePath)
|
||||
{
|
||||
return filePath.getCanonical();
|
||||
}
|
||||
);
|
||||
|
||||
for (size_t i = 0; i < quantiles.size(); i++)
|
||||
{
|
||||
const std::vector<FilePath>& quantile = quantiles[i];
|
||||
|
||||
progress(float(i) / quantiles.size());
|
||||
|
||||
std::set<FilePath> unprocessedFilePaths(quantile.begin(), quantile.end());
|
||||
|
||||
while (!unprocessedFilePaths.empty())
|
||||
{
|
||||
std::transform(
|
||||
unprocessedFilePaths.begin(), unprocessedFilePaths.end(),
|
||||
std::inserter(processedFilePaths, processedFilePaths.begin()),
|
||||
[](const FilePath& p){ return p.str(); }
|
||||
);
|
||||
std::set<FilePath> unprocessedFilePathsForNextIteration;
|
||||
|
||||
for (const FilePath& filePath: unprocessedFilePaths)
|
||||
{
|
||||
for (const IncludeDirective& includeDirective: getIncludeDirectives(filePath))
|
||||
{
|
||||
const FilePath resolvedIncludePath =
|
||||
resolveIncludeDirective(includeDirective, headerSearchDirectories, canonicalPathCache).makeCanonical();
|
||||
if (resolvedIncludePath.empty())
|
||||
{
|
||||
unresolvedIncludeDirectives.insert(includeDirective);
|
||||
}
|
||||
else if (processedFilePaths.find(resolvedIncludePath.str()) == processedFilePaths.end())
|
||||
{
|
||||
for (const FilePath& indexedPath: indexedPaths)
|
||||
{
|
||||
if (indexedPath.contains(resolvedIncludePath))
|
||||
{
|
||||
unprocessedFilePathsForNextIteration.insert(resolvedIncludePath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unprocessedFilePaths = unprocessedFilePathsForNextIteration;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<IncludeDirective> ret;
|
||||
|
||||
for (const IncludeDirective& directive: unresolvedIncludeDirectives)
|
||||
{
|
||||
ret.push_back(directive);
|
||||
}
|
||||
|
||||
progress(1.0f);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<IncludeDirective> IncludeValidation::getIncludeDirectives(const FilePath& filePath)
|
||||
{
|
||||
std::vector<IncludeDirective> includeDirectives;
|
||||
|
||||
if (filePath.exists())
|
||||
{
|
||||
std::shared_ptr<TextAccess> textAccess = TextAccess::createFromFile(filePath);
|
||||
const std::vector<std::string> lines = textAccess->getAllLines();
|
||||
for (size_t i = 0; i < lines.size(); i++)
|
||||
{
|
||||
const std::string lineTrimmedToHash = utility::trim(lines[i]);
|
||||
if (utility::isPrefix("#", lineTrimmedToHash))
|
||||
{
|
||||
const std::string lineTrimmedToInclude = utility::trim(lineTrimmedToHash.substr(1));
|
||||
if (utility::isPrefix("include", lineTrimmedToInclude))
|
||||
{
|
||||
std::string includeString = utility::substrBetween(lineTrimmedToInclude, "<", ">");
|
||||
bool usesBrackets = true;
|
||||
if (includeString.empty())
|
||||
{
|
||||
includeString = utility::substrBetween(lineTrimmedToInclude, "\"", "\"");
|
||||
usesBrackets = false;
|
||||
}
|
||||
|
||||
if (!includeString.empty())
|
||||
{
|
||||
// lines are 1 based
|
||||
includeDirectives.push_back(IncludeDirective(FilePath(includeString), filePath, i + 1, usesBrackets));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return includeDirectives;
|
||||
}
|
||||
|
||||
FilePath IncludeValidation::resolveIncludeDirective(
|
||||
const IncludeDirective& includeDirective,
|
||||
const std::vector<FilePath>& headerSearchDirectories,
|
||||
OrderedCache<FilePath, FilePath>& canonicalPathCache
|
||||
)
|
||||
{
|
||||
const FilePath includedFilePath = includeDirective.getIncludedFile();
|
||||
|
||||
{
|
||||
// check for an absolute include path
|
||||
if (includedFilePath.isAbsolute())
|
||||
{
|
||||
const FilePath resolvedIncludePath = canonicalPathCache.getValue(includedFilePath);
|
||||
if (resolvedIncludePath.exists())
|
||||
{
|
||||
return includedFilePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// check for an include path relative to the including path
|
||||
const FilePath resolvedIncludePath = canonicalPathCache.getValue(includeDirective.getIncludingFile().getParentDirectory().concatenate(includedFilePath));
|
||||
if (resolvedIncludePath.exists())
|
||||
{
|
||||
return resolvedIncludePath;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// check for an include path relative to the header search directories
|
||||
for (const FilePath& headerSearchDirectory: headerSearchDirectories)
|
||||
{
|
||||
const FilePath resolvedIncludePath = canonicalPathCache.getValue(headerSearchDirectory.getConcatenated(includedFilePath));
|
||||
if (resolvedIncludePath.exists())
|
||||
{
|
||||
return resolvedIncludePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FilePath();
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
#ifndef INCLUDE_VALIDATION_H
|
||||
#define INCLUDE_VALIDATION_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "utility/OrderedCache.h"
|
||||
|
||||
class FilePath;
|
||||
class IncludeDirective;
|
||||
|
||||
class IncludeValidation
|
||||
{
|
||||
public:
|
||||
static std::vector<IncludeDirective> getUnresolvedIncludeDirectives(
|
||||
const std::vector<FilePath>& sourceFilePaths,
|
||||
const std::vector<FilePath>& indexedPaths,
|
||||
const std::vector<FilePath>& headerSearchDirectories,
|
||||
size_t quantileCount, std::function<void(float)> progress);
|
||||
private:
|
||||
|
||||
static std::vector<IncludeDirective> getIncludeDirectives(const FilePath& filePath);
|
||||
static FilePath resolveIncludeDirective(
|
||||
const IncludeDirective& includeDirective,
|
||||
const std::vector<FilePath>& headerSearchDirectories,
|
||||
OrderedCache<FilePath, FilePath>& canonicalPathCache
|
||||
);
|
||||
};
|
||||
|
||||
#endif // INCLUDE_VALIDATION_H
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "project/IncludeDirective.h"
|
||||
#include "utility/IncludeDirective.h"
|
||||
|
||||
IncludeDirective::IncludeDirective(
|
||||
const FilePath& includedFilePath,
|
||||
@@ -0,0 +1,335 @@
|
||||
#include "utility/IncludeProcessing.h"
|
||||
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "utility/IncludeDirective.h"
|
||||
#include "utility/file/FilePath.h"
|
||||
#include "utility/file/FileSystem.h"
|
||||
#include "utility/text/TextAccess.h"
|
||||
#include "utility/utility.h"
|
||||
#include "utility/utilityString.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
struct IncludeDirectiveComparator
|
||||
{
|
||||
bool operator()(const IncludeDirective& a, const IncludeDirective& b)
|
||||
{
|
||||
return a.getIncludedFile() < b.getIncludedFile();
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<std::vector<FilePath>> splitToQuantiles(
|
||||
const std::set<FilePath>& sourceFilePaths,
|
||||
const size_t desiredQuantileCount)
|
||||
{
|
||||
size_t quantileCount = std::max<size_t>(1, std::min(desiredQuantileCount, sourceFilePaths.size()));
|
||||
|
||||
std::vector<std::vector<FilePath>> quantiles;
|
||||
for (size_t i = 0; i < quantileCount; i++)
|
||||
{
|
||||
quantiles.push_back(std::vector<FilePath>());
|
||||
}
|
||||
|
||||
int i = 0;
|
||||
for (const FilePath& sourceFilePath : sourceFilePaths)
|
||||
{
|
||||
quantiles[i % quantileCount].push_back(sourceFilePath);
|
||||
++i;
|
||||
}
|
||||
|
||||
return quantiles;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<IncludeDirective> IncludeProcessing::getUnresolvedIncludeDirectives(
|
||||
const std::set<FilePath>& sourceFilePaths,
|
||||
const std::set<FilePath>& indexedPaths,
|
||||
const std::set<FilePath>& headerSearchDirectories,
|
||||
const size_t desiredQuantileCount, std::function<void(float)> progress
|
||||
)
|
||||
{
|
||||
std::unordered_set<std::string> processedFilePaths;
|
||||
std::set<IncludeDirective, IncludeDirectiveComparator> unresolvedIncludeDirectives;
|
||||
|
||||
std::vector<std::vector<FilePath>> quantiles = splitToQuantiles(sourceFilePaths, desiredQuantileCount);
|
||||
|
||||
for (size_t i = 0; i < quantiles.size(); i++)
|
||||
{
|
||||
const std::vector<FilePath>& quantile = quantiles[i];
|
||||
|
||||
progress(float(i) / quantiles.size());
|
||||
|
||||
std::set<FilePath> unprocessedFilePaths(quantile.begin(), quantile.end());
|
||||
|
||||
while (!unprocessedFilePaths.empty())
|
||||
{
|
||||
std::transform(
|
||||
unprocessedFilePaths.begin(), unprocessedFilePaths.end(),
|
||||
std::inserter(processedFilePaths, processedFilePaths.begin()),
|
||||
[](const FilePath& p){ return p.str(); }
|
||||
);
|
||||
|
||||
std::set<FilePath> unprocessedFilePathsForNextIteration;
|
||||
|
||||
for (const FilePath& filePath: unprocessedFilePaths)
|
||||
{
|
||||
for (const IncludeDirective& includeDirective: getIncludeDirectives(filePath))
|
||||
{
|
||||
const FilePath resolvedIncludePath = resolveIncludeDirective(includeDirective, headerSearchDirectories).makeCanonical();
|
||||
if (resolvedIncludePath.empty())
|
||||
{
|
||||
unresolvedIncludeDirectives.insert(includeDirective);
|
||||
}
|
||||
else if (processedFilePaths.find(resolvedIncludePath.str()) == processedFilePaths.end())
|
||||
{
|
||||
for (const FilePath& indexedPath: indexedPaths)
|
||||
{
|
||||
if (indexedPath.contains(resolvedIncludePath))
|
||||
{
|
||||
unprocessedFilePathsForNextIteration.insert(resolvedIncludePath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unprocessedFilePaths = unprocessedFilePathsForNextIteration;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<IncludeDirective> ret;
|
||||
|
||||
for (const IncludeDirective& directive: unresolvedIncludeDirectives)
|
||||
{
|
||||
ret.push_back(directive);
|
||||
}
|
||||
|
||||
progress(1.0f);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::set<FilePath> IncludeProcessing::getHeaderSearchDirectories(
|
||||
const std::set<FilePath>& sourceFilePaths,
|
||||
const std::set<FilePath>& searchedPaths,
|
||||
const size_t desiredQuantileCount, std::function<void(float)> progress
|
||||
)
|
||||
{
|
||||
progress(0.0f);
|
||||
|
||||
std::map<std::string, std::set<FilePath>> existingFilePaths;
|
||||
for (const FilePath& searchedPath : searchedPaths)
|
||||
{
|
||||
if (searchedPath.isDirectory())
|
||||
{
|
||||
for (const FilePath& filePath : FileSystem::getFilePathsFromDirectory(searchedPath.getCanonical()))
|
||||
{
|
||||
existingFilePaths[filePath.fileName()].insert(filePath);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
existingFilePaths[searchedPath.fileName()].insert(searchedPath);
|
||||
}
|
||||
}
|
||||
|
||||
std::set<FilePath> headerSearchDirectories;
|
||||
std::unordered_set<std::string> processedFilePaths;
|
||||
std::vector<std::vector<FilePath>> quantiles = splitToQuantiles(sourceFilePaths, desiredQuantileCount);
|
||||
|
||||
for (size_t i = 0; i < quantiles.size(); i++)
|
||||
{
|
||||
const std::vector<FilePath>& quantile = quantiles[i];
|
||||
|
||||
progress(float(i) / quantiles.size());
|
||||
|
||||
std::set<FilePath> unprocessedFilePaths(quantile.begin(), quantile.end());
|
||||
|
||||
while (!unprocessedFilePaths.empty())
|
||||
{
|
||||
std::transform(
|
||||
unprocessedFilePaths.begin(), unprocessedFilePaths.end(),
|
||||
std::inserter(processedFilePaths, processedFilePaths.begin()),
|
||||
[](const FilePath& p) { return p.str(); }
|
||||
);
|
||||
|
||||
std::set<FilePath> unprocessedFilePathsForNextIteration;
|
||||
|
||||
for (const FilePath& unprocessedFilePath : unprocessedFilePaths)
|
||||
{
|
||||
for (const IncludeDirective& includeDirective : getIncludeDirectives(unprocessedFilePath))
|
||||
{
|
||||
const FilePath includedFilePath = includeDirective.getIncludedFile();
|
||||
|
||||
FilePath foundIncludedPath;
|
||||
if (includedFilePath.isAbsolute())
|
||||
{
|
||||
foundIncludedPath = includedFilePath;
|
||||
}
|
||||
else
|
||||
{
|
||||
const FilePath relativeToIncludingPath = includeDirective.getIncludingFile().getParentDirectory().concatenate(includedFilePath);
|
||||
if (relativeToIncludingPath.exists())
|
||||
{
|
||||
foundIncludedPath = relativeToIncludingPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::map<std::string, std::set<FilePath>>::const_iterator it = existingFilePaths.find(includedFilePath.fileName());
|
||||
if (it != existingFilePaths.end())
|
||||
{
|
||||
// TODO: handle the case where a file can be found by two different paths
|
||||
for (FilePath existingFilePath : it->second)
|
||||
{
|
||||
existingFilePath = existingFilePath.getParentDirectory();
|
||||
bool ok = true;
|
||||
{
|
||||
FilePath tempIncludedFilePath = includedFilePath.getParentDirectory();
|
||||
while (!tempIncludedFilePath.empty())
|
||||
{
|
||||
if (tempIncludedFilePath.fileName() == "..")
|
||||
{
|
||||
std::vector<FilePath> subDirectories = FileSystem::getDirectSubDirectories(existingFilePath);
|
||||
if (!subDirectories.empty())
|
||||
{
|
||||
existingFilePath = subDirectories.front();
|
||||
}
|
||||
else
|
||||
{
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
existingFilePath = existingFilePath.getParentDirectory();
|
||||
}
|
||||
tempIncludedFilePath = tempIncludedFilePath.getParentDirectory();
|
||||
}
|
||||
}
|
||||
if (ok)
|
||||
{
|
||||
foundIncludedPath = existingFilePath.getConcatenated(includedFilePath);
|
||||
if (foundIncludedPath.exists())
|
||||
{
|
||||
headerSearchDirectories.insert(existingFilePath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (foundIncludedPath.exists())
|
||||
{
|
||||
if (processedFilePaths.find(foundIncludedPath.str()) == processedFilePaths.end())
|
||||
{
|
||||
for (const FilePath& searchedPath : searchedPaths)
|
||||
{
|
||||
if (searchedPath.contains(foundIncludedPath))
|
||||
{
|
||||
unprocessedFilePathsForNextIteration.insert(foundIncludedPath);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unprocessedFilePaths = unprocessedFilePathsForNextIteration;
|
||||
}
|
||||
}
|
||||
|
||||
progress(1.0f);
|
||||
|
||||
return headerSearchDirectories;
|
||||
}
|
||||
|
||||
std::vector<IncludeDirective> IncludeProcessing::getIncludeDirectives(const FilePath& filePath)
|
||||
{
|
||||
if (filePath.exists())
|
||||
{
|
||||
return getIncludeDirectives(TextAccess::createFromFile(filePath));
|
||||
}
|
||||
return std::vector<IncludeDirective>();
|
||||
}
|
||||
|
||||
std::vector<IncludeDirective> IncludeProcessing::getIncludeDirectives(std::shared_ptr<TextAccess> textAccess)
|
||||
{
|
||||
std::vector<IncludeDirective> includeDirectives;
|
||||
|
||||
const std::vector<std::string> lines = textAccess->getAllLines();
|
||||
for (size_t i = 0; i < lines.size(); i++)
|
||||
{
|
||||
const std::string lineTrimmedToHash = utility::trim(lines[i]);
|
||||
if (utility::isPrefix("#", lineTrimmedToHash))
|
||||
{
|
||||
const std::string lineTrimmedToInclude = utility::trim(lineTrimmedToHash.substr(1));
|
||||
if (utility::isPrefix("include", lineTrimmedToInclude))
|
||||
{
|
||||
std::string includeString = utility::substrBetween(lineTrimmedToInclude, "<", ">");
|
||||
bool usesBrackets = true;
|
||||
if (includeString.empty())
|
||||
{
|
||||
includeString = utility::substrBetween(lineTrimmedToInclude, "\"", "\"");
|
||||
usesBrackets = false;
|
||||
}
|
||||
|
||||
if (!includeString.empty())
|
||||
{
|
||||
// lines are 1 based
|
||||
includeDirectives.push_back(IncludeDirective(FilePath(includeString), textAccess->getFilePath(), i + 1, usesBrackets));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return includeDirectives;
|
||||
}
|
||||
|
||||
FilePath IncludeProcessing::resolveIncludeDirective(
|
||||
const IncludeDirective& includeDirective,
|
||||
const std::set<FilePath>& headerSearchDirectories
|
||||
)
|
||||
{
|
||||
const FilePath includedFilePath = includeDirective.getIncludedFile();
|
||||
|
||||
{
|
||||
// check for an absolute include path
|
||||
if (includedFilePath.isAbsolute())
|
||||
{
|
||||
const FilePath resolvedIncludePath = includedFilePath;
|
||||
if (resolvedIncludePath.exists())
|
||||
{
|
||||
return includedFilePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// check for an include path relative to the including path
|
||||
const FilePath resolvedIncludePath = includeDirective.getIncludingFile().getParentDirectory().concatenate(includedFilePath);
|
||||
if (resolvedIncludePath.exists())
|
||||
{
|
||||
return resolvedIncludePath;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// check for an include path relative to the header search directories
|
||||
for (const FilePath& headerSearchDirectory: headerSearchDirectories)
|
||||
{
|
||||
const FilePath resolvedIncludePath = headerSearchDirectory.getConcatenated(includedFilePath);
|
||||
if (resolvedIncludePath.exists())
|
||||
{
|
||||
return resolvedIncludePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FilePath();
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#ifndef INCLUDE_PROCESSING_H
|
||||
#define INCLUDE_PROCESSING_H
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "utility/OrderedCache.h"
|
||||
|
||||
class FilePath;
|
||||
class IncludeDirective;
|
||||
class TextAccess;
|
||||
|
||||
class IncludeProcessing
|
||||
{
|
||||
public:
|
||||
static std::vector<IncludeDirective> getUnresolvedIncludeDirectives(
|
||||
const std::set<FilePath>& sourceFilePaths,
|
||||
const std::set<FilePath>& indexedPaths,
|
||||
const std::set<FilePath>& headerSearchDirectories,
|
||||
size_t quantileCount, std::function<void(float)> progress
|
||||
);
|
||||
|
||||
static std::set<FilePath> getHeaderSearchDirectories(
|
||||
const std::set<FilePath>& sourceFilePaths,
|
||||
const std::set<FilePath>& searchedPaths,
|
||||
const size_t desiredQuantileCount, std::function<void(float)> progress
|
||||
);
|
||||
|
||||
static std::vector<IncludeDirective> getIncludeDirectives(const FilePath& filePath);
|
||||
|
||||
static std::vector<IncludeDirective> getIncludeDirectives(std::shared_ptr<TextAccess> textAccess);
|
||||
|
||||
private:
|
||||
static FilePath resolveIncludeDirective(
|
||||
const IncludeDirective& includeDirective,
|
||||
const std::set<FilePath>& headerSearchDirectories
|
||||
);
|
||||
|
||||
IncludeProcessing() = delete;
|
||||
};
|
||||
|
||||
#endif // INCLUDE_PROCESSING_H
|
||||
@@ -225,6 +225,8 @@ add_files(
|
||||
qt/window/QtLicenseWindow.h
|
||||
qt/window/QtMainWindow.cpp
|
||||
qt/window/QtMainWindow.h
|
||||
qt/window/QtPathListDialog.cpp
|
||||
qt/window/QtPathListDialog.h
|
||||
qt/window/QtPreferencesWindow.cpp
|
||||
qt/window/QtPreferencesWindow.h
|
||||
qt/window/QtSelectPathsDialog.cpp
|
||||
|
||||
@@ -29,6 +29,11 @@ void QtProgressBar::showProgress(size_t percent)
|
||||
update();
|
||||
}
|
||||
|
||||
size_t QtProgressBar::getProgress() const
|
||||
{
|
||||
return m_percent;
|
||||
}
|
||||
|
||||
void QtProgressBar::showUnknownProgressAnimated()
|
||||
{
|
||||
start();
|
||||
|
||||
@@ -17,6 +17,7 @@ public:
|
||||
QtProgressBar(QWidget* parent = nullptr);
|
||||
|
||||
void showProgress(size_t percent);
|
||||
size_t getProgress() const;
|
||||
|
||||
void showUnknownProgressAnimated();
|
||||
|
||||
|
||||
@@ -58,11 +58,10 @@ void QtDialogView::hideUnknownProgressDialog()
|
||||
|
||||
void QtDialogView::showProgressDialog(const std::string& title, const std::string& message, int progress)
|
||||
{
|
||||
MessageStatus(title + ": " + message + " [" + std::to_string(progress) + "%]", false, true).dispatch();
|
||||
|
||||
m_onQtThread(
|
||||
[=]()
|
||||
{
|
||||
bool sendStatusMessage = true;
|
||||
QtIndexingDialog* window = dynamic_cast<QtIndexingDialog*>(m_windowStack.getTopWindow());
|
||||
if (!window || window->getType() != QtIndexingDialog::DIALOG_PROGRESS)
|
||||
{
|
||||
@@ -71,6 +70,19 @@ void QtDialogView::showProgressDialog(const std::string& title, const std::strin
|
||||
window = createWindow<QtIndexingDialog>();
|
||||
window->setupProgress();
|
||||
}
|
||||
else
|
||||
{
|
||||
sendStatusMessage = (
|
||||
window->getTitle() != title ||
|
||||
window->getMessage() != message ||
|
||||
window->getProgress() != progress
|
||||
);
|
||||
}
|
||||
|
||||
if (sendStatusMessage)
|
||||
{
|
||||
MessageStatus(title + ": " + message + " [" + std::to_string(progress) + "%]", false, true).dispatch();
|
||||
}
|
||||
|
||||
window->updateTitle(title.c_str());
|
||||
window->updateMessage(message.c_str());
|
||||
|
||||
@@ -273,15 +273,29 @@ void QtIndexingDialog::updateMessage(QString message)
|
||||
}
|
||||
}
|
||||
|
||||
void QtIndexingDialog::updateProgress(int progress)
|
||||
std::string QtIndexingDialog::getMessage() const
|
||||
{
|
||||
int percent = std::min(std::max(progress, 0), 100);
|
||||
if (m_messageLabel)
|
||||
{
|
||||
return m_messageLabel->text().toStdString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void QtIndexingDialog::updateProgress(size_t progress)
|
||||
{
|
||||
size_t percent = std::min<size_t>(std::max<size_t>(progress, 0), 100);
|
||||
|
||||
m_progressBar->showProgress(percent);
|
||||
m_percentLabel->setText(QString::number(percent) + "% Progress");
|
||||
setGeometries();
|
||||
}
|
||||
|
||||
size_t QtIndexingDialog::getProgress() const
|
||||
{
|
||||
return m_progressBar->getProgress();
|
||||
}
|
||||
|
||||
void QtIndexingDialog::updateIndexingProgress(size_t fileCount, size_t totalFileCount, std::string sourcePath)
|
||||
{
|
||||
updateMessage(QString::number(fileCount) + "/" + QString::number(totalFileCount) + " File" + (totalFileCount > 1 ? "s" : ""));
|
||||
|
||||
@@ -48,7 +48,9 @@ public:
|
||||
void setupProgress();
|
||||
|
||||
void updateMessage(QString message);
|
||||
void updateProgress(int progress);
|
||||
std::string getMessage() const;
|
||||
void updateProgress(size_t progress);
|
||||
size_t getProgress() const;
|
||||
void updateIndexingProgress(size_t fileCount, size_t totalFileCount, std::string sourcePath);
|
||||
void updateErrorCount(size_t errorCount, size_t fatalCount);
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#include "qt/window/QtPathListDialog.h"
|
||||
|
||||
#include <QLabel>
|
||||
#include "qt/element/QtDirectoryListBox.h"
|
||||
|
||||
QtPathListDialog::QtPathListDialog(const QString& title, const QString& description, QWidget* parent)
|
||||
: QtWindow(parent)
|
||||
, m_title(title)
|
||||
, m_description(description)
|
||||
{
|
||||
}
|
||||
|
||||
QSize QtPathListDialog::sizeHint() const
|
||||
{
|
||||
return QSize(550, 550);
|
||||
}
|
||||
|
||||
void QtPathListDialog::setRelativeRootDirectory(const FilePath& dir)
|
||||
{
|
||||
m_pathList->setRelativeRootDirectory(dir);
|
||||
}
|
||||
|
||||
void QtPathListDialog::setPaths(const std::vector<FilePath>& paths, bool readOnly)
|
||||
{
|
||||
m_pathList->setList(paths, readOnly);
|
||||
}
|
||||
|
||||
std::vector<FilePath> QtPathListDialog::getPaths()
|
||||
{
|
||||
return m_pathList->getList();
|
||||
}
|
||||
|
||||
void QtPathListDialog::populateWindow(QWidget* widget)
|
||||
{
|
||||
QVBoxLayout* layout = new QVBoxLayout();
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
QLabel* description = new QLabel(m_description);
|
||||
description->setObjectName("description");
|
||||
description->setWordWrap(true);
|
||||
layout->addWidget(description);
|
||||
|
||||
m_pathList = new QtDirectoryListBox(this, m_title);
|
||||
layout->addWidget(m_pathList);
|
||||
|
||||
widget->setLayout(layout);
|
||||
}
|
||||
|
||||
void QtPathListDialog::windowReady()
|
||||
{
|
||||
updateNextButton("Save");
|
||||
updateCloseButton("Cancel");
|
||||
|
||||
setPreviousVisible(false);
|
||||
|
||||
updateTitle(m_title);
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
#ifndef QT_PATH_LIST_DIALOG_H
|
||||
#define QT_PATH_LIST_DIALOG_H
|
||||
|
||||
#include "qt/window/QtWindow.h"
|
||||
|
||||
class FilePath;
|
||||
class QtDirectoryListBox;
|
||||
|
||||
class QtPathListDialog
|
||||
: public QtWindow
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QtPathListDialog(const QString& title, const QString& description, QWidget* parent = 0);
|
||||
|
||||
QSize sizeHint() const override;
|
||||
|
||||
void setRelativeRootDirectory(const FilePath& dir);
|
||||
void setPaths(const std::vector<FilePath>& paths, bool readOnly = false);
|
||||
std::vector<FilePath> getPaths();
|
||||
|
||||
protected:
|
||||
void populateWindow(QWidget* widget) override;
|
||||
void windowReady() override;
|
||||
|
||||
QString m_title;
|
||||
QString m_description;
|
||||
|
||||
private:
|
||||
QtDirectoryListBox* m_pathList;
|
||||
};
|
||||
|
||||
#endif // QT_PATH_LIST_DIALOG_H
|
||||
@@ -28,7 +28,6 @@ protected:
|
||||
QString m_description;
|
||||
|
||||
private:
|
||||
|
||||
QPlainTextEdit* m_text;
|
||||
};
|
||||
|
||||
|
||||
@@ -213,6 +213,15 @@ void QtWindow::updateTitle(QString title)
|
||||
}
|
||||
}
|
||||
|
||||
std::string QtWindow::getTitle() const
|
||||
{
|
||||
if (m_title)
|
||||
{
|
||||
return m_title->text().toStdString();
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
void QtWindow::updateSubTitle(QString subTitle)
|
||||
{
|
||||
if (m_subTitle)
|
||||
|
||||
@@ -35,6 +35,7 @@ public:
|
||||
void moveToCenter();
|
||||
|
||||
void updateTitle(QString title);
|
||||
std::string getTitle() const;
|
||||
void updateSubTitle(QString subTitle);
|
||||
|
||||
void updateNextButton(QString text);
|
||||
|
||||
@@ -9,10 +9,11 @@
|
||||
#include "Application.h"
|
||||
#include "component/view/DialogView.h"
|
||||
#include "data/indexer/IndexerCommandCxxCdb.h"
|
||||
#include "project/IncludeDirective.h"
|
||||
#include "project/IncludeValidation.h"
|
||||
#include "utility/IncludeDirective.h"
|
||||
#include "utility/IncludeProcessing.h"
|
||||
#include "qt/element/QtDirectoryListBox.h"
|
||||
#include "qt/view/QtDialogView.h"
|
||||
#include "qt/window/QtPathListDialog.h"
|
||||
#include "qt/window/QtSelectPathsDialog.h"
|
||||
#include "settings/ApplicationSettings.h"
|
||||
#include "settings/SourceGroupSettingsCxxCdb.h"
|
||||
@@ -23,6 +24,7 @@
|
||||
#include "utility/utility.h"
|
||||
#include "utility/utilityFile.h"
|
||||
#include "utility/utilityPathDetection.h"
|
||||
#include "utility/utilityString.h"
|
||||
|
||||
QtProjectWizzardContentPaths::QtProjectWizzardContentPaths(
|
||||
std::shared_ptr<SourceGroupSettings> settings, QtProjectWizzardWindow* window
|
||||
@@ -429,6 +431,8 @@ QtProjectWizzardContentPathsHeaderSearch::QtProjectWizzardContentPathsHeaderSear
|
||||
: QtProjectWizzardContentPaths(settings, window)
|
||||
, m_showValidationResultFunctor(std::bind(
|
||||
&QtProjectWizzardContentPathsHeaderSearch::showValidationResult, this, std::placeholders::_1))
|
||||
, m_showDetectedIncludesResultFunctor(std::bind(
|
||||
&QtProjectWizzardContentPathsHeaderSearch::showDetectedIncludesResult, this, std::placeholders::_1))
|
||||
, m_isCdb(isCDB)
|
||||
{
|
||||
setTitleString(m_isCdb ? "Additional Include Paths" : "Include Paths");
|
||||
@@ -454,11 +458,18 @@ void QtProjectWizzardContentPathsHeaderSearch::populate(QGridLayout* layout, int
|
||||
|
||||
if (!m_isCdb)
|
||||
{
|
||||
QPushButton* button = new QPushButton("validate include directives");
|
||||
button->setObjectName("windowButton");
|
||||
connect(button, &QPushButton::clicked, this, &QtProjectWizzardContentPathsHeaderSearch::validateButtonClicked);
|
||||
|
||||
layout->addWidget(button, row, QtProjectWizzardWindow::BACK_COL, Qt::AlignRight | Qt::AlignTop);
|
||||
{
|
||||
QPushButton* detectionButton = new QPushButton("auto-detect");
|
||||
detectionButton->setObjectName("windowButton");
|
||||
connect(detectionButton, &QPushButton::clicked, this, &QtProjectWizzardContentPathsHeaderSearch::detectIncludesButtonClicked);
|
||||
layout->addWidget(detectionButton, row, QtProjectWizzardWindow::BACK_COL, Qt::AlignLeft | Qt::AlignTop);
|
||||
}
|
||||
{
|
||||
QPushButton* validateionButton = new QPushButton("validate include directives");
|
||||
validateionButton->setObjectName("windowButton");
|
||||
connect(validateionButton, &QPushButton::clicked, this, &QtProjectWizzardContentPathsHeaderSearch::validateIncludesButtonClicked);
|
||||
layout->addWidget(validateionButton, row, QtProjectWizzardWindow::BACK_COL, Qt::AlignRight | Qt::AlignTop);
|
||||
}
|
||||
row++;
|
||||
}
|
||||
}
|
||||
@@ -486,7 +497,30 @@ bool QtProjectWizzardContentPathsHeaderSearch::isScrollAble() const
|
||||
return true;
|
||||
}
|
||||
|
||||
void QtProjectWizzardContentPathsHeaderSearch::validateButtonClicked()
|
||||
void QtProjectWizzardContentPathsHeaderSearch::detectIncludesButtonClicked()
|
||||
{
|
||||
m_window->saveContent();
|
||||
|
||||
m_pathsDialog = std::make_shared<QtPathListDialog>(
|
||||
"Detect Include Paths",
|
||||
"<p>Automatically search the paths provided below for additional include paths that "
|
||||
"can be used to resolve include directives within your source code.</p>"
|
||||
"<p>The indexed paths will be searched by default but you can add further paths if required.</p>"
|
||||
);
|
||||
|
||||
m_pathsDialog->setup();
|
||||
m_pathsDialog->updateNextButton("Next");
|
||||
m_pathsDialog->setCloseVisible(true);
|
||||
|
||||
m_pathsDialog->setRelativeRootDirectory(m_settings->getProjectDirectoryPath());
|
||||
m_pathsDialog->setPaths(m_settings->getSourcePaths(), true);
|
||||
m_pathsDialog->showWindow();
|
||||
|
||||
connect(m_pathsDialog.get(), &QtPathListDialog::finished, this, &QtProjectWizzardContentPathsHeaderSearch::finishedSelectDetectIncludesRootPathsDialog);
|
||||
connect(m_pathsDialog.get(), &QtPathListDialog::canceled, this, &QtProjectWizzardContentPathsHeaderSearch::closedPathsDialog);
|
||||
}
|
||||
|
||||
void QtProjectWizzardContentPathsHeaderSearch::validateIncludesButtonClicked()
|
||||
{
|
||||
// TODO: regard Force Includes here, too!
|
||||
m_window->saveContent();
|
||||
@@ -497,7 +531,7 @@ void QtProjectWizzardContentPathsHeaderSearch::validateButtonClicked()
|
||||
{
|
||||
std::shared_ptr<DialogView> dialogView = Application::getInstance()->getDialogView();
|
||||
|
||||
std::vector<FilePath> sourceFilePaths;
|
||||
std::set<FilePath> sourceFilePaths;
|
||||
std::vector<FilePath> indexedFilePaths;
|
||||
std::vector<FilePath> headerSearchPaths;
|
||||
|
||||
@@ -514,7 +548,7 @@ void QtProjectWizzardContentPathsHeaderSearch::validateButtonClicked()
|
||||
m_settings->getExcludePathsExpandedAndAbsolute(),
|
||||
m_settings->getSourceExtensions()
|
||||
);
|
||||
sourceFilePaths = utility::toVector(fileManager.getAllSourceFilePaths());
|
||||
sourceFilePaths = fileManager.getAllSourceFilePaths();
|
||||
|
||||
headerSearchPaths = ApplicationSettings::getInstance()->getHeaderSearchPathsExpanded();
|
||||
|
||||
@@ -531,10 +565,10 @@ void QtProjectWizzardContentPathsHeaderSearch::validateButtonClicked()
|
||||
dialogView->hideProgressDialog();
|
||||
});
|
||||
|
||||
unresolvedIncludes = IncludeValidation::getUnresolvedIncludeDirectives(
|
||||
unresolvedIncludes = IncludeProcessing::getUnresolvedIncludeDirectives(
|
||||
sourceFilePaths,
|
||||
indexedFilePaths,
|
||||
headerSearchPaths,
|
||||
utility::toSet(indexedFilePaths),
|
||||
utility::toSet(headerSearchPaths),
|
||||
log2(sourceFilePaths.size()),
|
||||
[&](const float progress)
|
||||
{
|
||||
@@ -549,6 +583,147 @@ void QtProjectWizzardContentPathsHeaderSearch::validateButtonClicked()
|
||||
}).detach();
|
||||
}
|
||||
|
||||
|
||||
void QtProjectWizzardContentPathsHeaderSearch::finishedSelectDetectIncludesRootPathsDialog()
|
||||
{
|
||||
// TODO: regard Force Includes here, too!
|
||||
const std::vector<FilePath> searchedPaths = m_settings->makePathsExpandedAndAbsolute(m_pathsDialog->getPaths());
|
||||
|
||||
closedPathsDialog();
|
||||
|
||||
std::thread([=]()
|
||||
{
|
||||
std::set<FilePath> detectedHeaderSearchPaths;
|
||||
{
|
||||
std::shared_ptr<QtDialogView> dialogView = std::dynamic_pointer_cast<QtDialogView>(Application::getInstance()->getDialogView());
|
||||
|
||||
std::set<FilePath> sourceFilePaths;
|
||||
|
||||
{
|
||||
dialogView->setParentWindow(m_window);
|
||||
dialogView->showUnknownProgressDialog("Processing", "Gathering Source Files");
|
||||
ScopedFunctor dialogHider([&dialogView]() {
|
||||
dialogView->hideUnknownProgressDialog();
|
||||
});
|
||||
|
||||
FileManager fileManager;
|
||||
fileManager.update(
|
||||
m_settings->getSourcePathsExpandedAndAbsolute(),
|
||||
m_settings->getExcludePathsExpandedAndAbsolute(),
|
||||
m_settings->getSourceExtensions()
|
||||
);
|
||||
sourceFilePaths = fileManager.getAllSourceFilePaths();
|
||||
}
|
||||
{
|
||||
dialogView->setParentWindow(m_window);
|
||||
ScopedFunctor dialogHider([&dialogView]() {
|
||||
dialogView->hideProgressDialog();
|
||||
});
|
||||
|
||||
detectedHeaderSearchPaths = IncludeProcessing::getHeaderSearchDirectories(
|
||||
sourceFilePaths,
|
||||
utility::toSet(searchedPaths),
|
||||
log2(sourceFilePaths.size()),
|
||||
[&](const float progress)
|
||||
{
|
||||
Application::getInstance()->getDialogView()->showProgressDialog(
|
||||
"Processing", std::to_string(int(progress * sourceFilePaths.size())) + " Files", int(progress * 100.0f)
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
m_showDetectedIncludesResultFunctor(detectedHeaderSearchPaths);
|
||||
}).detach();
|
||||
}
|
||||
|
||||
void QtProjectWizzardContentPathsHeaderSearch::finishedAcceptDetectedIncludePathsDialog()
|
||||
{
|
||||
const std::vector<std::string> detectedPaths = utility::splitToVector(m_filesDialog->getText(), "\n");
|
||||
closedFilesDialog();
|
||||
|
||||
std::vector<std::string> headerSearchPaths = m_list->getStringList();
|
||||
|
||||
headerSearchPaths.reserve(headerSearchPaths.size() + detectedPaths.size());
|
||||
for (const std::string& detectedPath : detectedPaths)
|
||||
{
|
||||
if (!detectedPath.empty())
|
||||
{
|
||||
headerSearchPaths.push_back(detectedPath);
|
||||
}
|
||||
}
|
||||
|
||||
m_list->setStringList(headerSearchPaths);
|
||||
}
|
||||
|
||||
void QtProjectWizzardContentPathsHeaderSearch::closedPathsDialog()
|
||||
{
|
||||
m_pathsDialog->hide();
|
||||
m_pathsDialog.reset();
|
||||
|
||||
window()->raise();
|
||||
}
|
||||
|
||||
void QtProjectWizzardContentPathsHeaderSearch::showDetectedIncludesResult(const std::set<FilePath>& detectedHeaderSearchPaths)
|
||||
{
|
||||
const std::set<FilePath> headerSearchPaths = utility::toSet(m_settings->makePathsExpandedAndAbsolute(m_list->getList()));
|
||||
|
||||
std::vector<FilePath> additionalHeaderSearchPaths;
|
||||
for (const FilePath& detectedHeaderSearchPath : detectedHeaderSearchPaths)
|
||||
{
|
||||
if (headerSearchPaths.find(detectedHeaderSearchPath) == headerSearchPaths.end())
|
||||
{
|
||||
additionalHeaderSearchPaths.push_back(detectedHeaderSearchPath);
|
||||
}
|
||||
}
|
||||
|
||||
if (additionalHeaderSearchPaths.empty())
|
||||
{
|
||||
QMessageBox msgBox;
|
||||
msgBox.setText("<p>No additional include paths have been detected while searching the provided paths.</p>");
|
||||
msgBox.exec();
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string detailedText = "";
|
||||
FilePath relativeRoot = m_list->getRelativeRootDirectory();
|
||||
for (const FilePath& path : additionalHeaderSearchPaths)
|
||||
{
|
||||
if (!relativeRoot.empty())
|
||||
{
|
||||
const FilePath relPath = path.getRelativeTo(relativeRoot);
|
||||
if (relPath.str().size() < path.str().size())
|
||||
{
|
||||
detailedText += relPath.str() + "\n";
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
detailedText += path.str() + "\n";
|
||||
}
|
||||
|
||||
m_filesDialog = std::make_shared<QtTextEditDialog>(
|
||||
"Detected Include Paths",
|
||||
(
|
||||
"<p>The following <b>" + std::to_string(additionalHeaderSearchPaths.size()) + "</b> include paths have been "
|
||||
"detected and will be added to the include paths that are already defined by this Source Group.<b>"
|
||||
).c_str()
|
||||
);
|
||||
|
||||
m_filesDialog->setup();
|
||||
m_filesDialog->setCloseVisible(true);
|
||||
m_filesDialog->updateNextButton("Finish");
|
||||
|
||||
m_filesDialog->setReadOnly(true);
|
||||
m_filesDialog->setText(detailedText);
|
||||
m_filesDialog->showWindow();
|
||||
|
||||
connect(m_filesDialog.get(), &QtTextEditDialog::finished, this, &QtProjectWizzardContentPathsHeaderSearch::finishedAcceptDetectedIncludePathsDialog);
|
||||
connect(m_filesDialog.get(), &QtTextEditDialog::canceled, this, &QtProjectWizzardContentPathsHeaderSearch::closedFilesDialog);
|
||||
}
|
||||
}
|
||||
|
||||
void QtProjectWizzardContentPathsHeaderSearch::showValidationResult(const std::vector<IncludeDirective>& unresolvedIncludes)
|
||||
{
|
||||
if (unresolvedIncludes.empty())
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef QT_PROJECT_WIZZARD_CONTENT_PATHS_H
|
||||
#define QT_PROJECT_WIZZARD_CONTENT_PATHS_H
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "qt/window/project_wizzard/QtProjectWizzardContent.h"
|
||||
#include "utility/path_detector/CombinedPathDetector.h"
|
||||
|
||||
@@ -9,6 +11,7 @@ class QCheckBox;
|
||||
class QComboBox;
|
||||
class QPushButton;
|
||||
class QtDirectoryListBox;
|
||||
class QtPathListDialog;
|
||||
class SourceGroupSettings;
|
||||
class SourceGroupSettingsCxxCdb;
|
||||
|
||||
@@ -119,12 +122,19 @@ public:
|
||||
virtual bool isScrollAble() const override;
|
||||
|
||||
private slots:
|
||||
void validateButtonClicked();
|
||||
void detectIncludesButtonClicked();
|
||||
void validateIncludesButtonClicked();
|
||||
void finishedSelectDetectIncludesRootPathsDialog();
|
||||
void finishedAcceptDetectedIncludePathsDialog();
|
||||
void closedPathsDialog();
|
||||
|
||||
private:
|
||||
void showDetectedIncludesResult(const std::set<FilePath>& detectedHeaderSearchPaths);
|
||||
void showValidationResult(const std::vector<IncludeDirective>& unresolvedIncludes);
|
||||
QtThreadedFunctor<std::set<FilePath>> m_showDetectedIncludesResultFunctor;
|
||||
QtThreadedFunctor<std::vector<IncludeDirective>> m_showValidationResultFunctor;
|
||||
|
||||
std::shared_ptr<QtPathListDialog> m_pathsDialog;
|
||||
const bool m_isCdb;
|
||||
};
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ add_files(
|
||||
|
||||
CommandlineTestSuite.h
|
||||
ConfigManagerTestSuite.h
|
||||
CxxIncludeProcessingTestSuite.h
|
||||
CxxIndexSampleProjectsTestSuite.h
|
||||
CxxParserTestSuite.h
|
||||
CxxTypeNameTestSuite.h
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
#include "cxxtest/TestSuite.h"
|
||||
|
||||
#include "utility/text/TextAccess.h"
|
||||
#include "utility/IncludeDirective.h"
|
||||
#include "utility/IncludeProcessing.h"
|
||||
|
||||
class CxxIncludeProcessingTestSuite: public CxxTest::TestSuite
|
||||
{
|
||||
public:
|
||||
void test_include_detection_finds_include_with_quotes()
|
||||
{
|
||||
std::vector<IncludeDirective> includeDirectives = IncludeProcessing::getIncludeDirectives(TextAccess::createFromString(
|
||||
"#include \"foo.h\"",
|
||||
FilePath("foo.cpp")
|
||||
));
|
||||
|
||||
TS_ASSERT(!includeDirectives.empty());
|
||||
|
||||
if (!includeDirectives.empty())
|
||||
{
|
||||
TS_ASSERT_EQUALS("foo.h", includeDirectives.front().getIncludedFile().str());
|
||||
TS_ASSERT_EQUALS("foo.cpp", includeDirectives.front().getIncludingFile().str());
|
||||
}
|
||||
}
|
||||
|
||||
void test_include_detection_finds_include_with_angle_brackets()
|
||||
{
|
||||
std::vector<IncludeDirective> includeDirectives = IncludeProcessing::getIncludeDirectives(TextAccess::createFromString(
|
||||
"#include <foo.h>",
|
||||
FilePath("foo.cpp")
|
||||
));
|
||||
|
||||
TS_ASSERT(!includeDirectives.empty());
|
||||
|
||||
if (!includeDirectives.empty())
|
||||
{
|
||||
TS_ASSERT_EQUALS("foo.h", includeDirectives.front().getIncludedFile().str());
|
||||
TS_ASSERT_EQUALS("foo.cpp", includeDirectives.front().getIncludingFile().str());
|
||||
}
|
||||
}
|
||||
void test_include_detection_finds_include_with_quotes_and_space_before_keyword()
|
||||
{
|
||||
std::vector<IncludeDirective> includeDirectives = IncludeProcessing::getIncludeDirectives(TextAccess::createFromString(
|
||||
"# include \"foo.h\"",
|
||||
FilePath("foo.cpp")
|
||||
));
|
||||
|
||||
TS_ASSERT(!includeDirectives.empty());
|
||||
|
||||
if (!includeDirectives.empty())
|
||||
{
|
||||
TS_ASSERT_EQUALS("foo.h", includeDirectives.front().getIncludedFile().str());
|
||||
TS_ASSERT_EQUALS("foo.cpp", includeDirectives.front().getIncludingFile().str());
|
||||
}
|
||||
}
|
||||
|
||||
void test_include_detection_does_not_find_include_in_empty_file()
|
||||
{
|
||||
TS_ASSERT(IncludeProcessing::getIncludeDirectives(TextAccess::createFromString("")).empty());
|
||||
}
|
||||
|
||||
void test_include_detection_does_not_find_include_in_file_without_preprocessor_directive()
|
||||
{
|
||||
TS_ASSERT(IncludeProcessing::getIncludeDirectives(TextAccess::createFromString("foo")).empty());
|
||||
}
|
||||
|
||||
void test_include_detection_does_not_find_include_in_file_without_include_preprocessor_directive()
|
||||
{
|
||||
TS_ASSERT(IncludeProcessing::getIncludeDirectives(TextAccess::createFromString("#ifdef xx\n#endif")).empty());
|
||||
}
|
||||
|
||||
void test_header_search_path_detection_does_not_find_path_relative_to_including_file()
|
||||
{
|
||||
std::set<FilePath> headerSearchDirectoies = IncludeProcessing::getHeaderSearchDirectories(
|
||||
{ FilePath("data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_does_not_find_path_relative_to_including_file/a.cpp") },
|
||||
{ FilePath("data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_does_not_find_path_relative_to_including_file") },
|
||||
1, [&](float) {}
|
||||
);
|
||||
|
||||
TS_ASSERT(headerSearchDirectoies.empty());
|
||||
}
|
||||
|
||||
void test_header_search_path_detection_finds_path_inside_sub_directory()
|
||||
{
|
||||
std::set<FilePath> headerSearchDirectoies = IncludeProcessing::getHeaderSearchDirectories(
|
||||
{ FilePath("data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_inside_sub_directory/a.cpp") },
|
||||
{ FilePath("data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_inside_sub_directory") },
|
||||
1, [&](float) {}
|
||||
);
|
||||
|
||||
TS_ASSERT(!headerSearchDirectoies.empty());
|
||||
if (!headerSearchDirectoies.empty())
|
||||
{
|
||||
TS_ASSERT_EQUALS(
|
||||
"CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_inside_sub_directory/include",
|
||||
headerSearchDirectoies.begin()->getRelativeTo(FilePath("data").getAbsolute()).str()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void test_header_search_path_detection_finds_path_relative_to_sub_directory()
|
||||
{
|
||||
std::set<FilePath> headerSearchDirectoies = IncludeProcessing::getHeaderSearchDirectories(
|
||||
{ FilePath("data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_relative_to_sub_directory/a.cpp") },
|
||||
{ FilePath("data/CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_relative_to_sub_directory") },
|
||||
1, [&](float) {}
|
||||
);
|
||||
|
||||
TS_ASSERT(!headerSearchDirectoies.empty());
|
||||
if (!headerSearchDirectoies.empty())
|
||||
{
|
||||
TS_ASSERT_EQUALS(
|
||||
"CxxIncludeProcessingTestSuite/test_header_search_path_detection_finds_path_relative_to_sub_directory/include",
|
||||
headerSearchDirectoies.begin()->getRelativeTo(FilePath("data").getAbsolute()).str()
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -74,6 +74,13 @@ public:
|
||||
TS_ASSERT(path.getParentDirectory().empty());
|
||||
}
|
||||
|
||||
void test_file_path_without_parent_has_empty_parent_directory()
|
||||
{
|
||||
const FilePath path("a.cpp");
|
||||
|
||||
TS_ASSERT(path.getParentDirectory().empty());
|
||||
}
|
||||
|
||||
void test_file_path_is_absolute()
|
||||
{
|
||||
const FilePath path("data/FilePathTestSuite/a.cpp");
|
||||
|
||||
Reference in New Issue
Block a user