ui: added automatic include path detection for C/C++ Source Groups

This commit is contained in:
mlangkabel
2018-01-09 12:13:16 +01:00
parent 978cf4f4ed
commit 686ba504c2
35 changed files with 877 additions and 248 deletions
+9 -5
View File
@@ -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;
+1 -1
View File
@@ -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()));
}
+1 -1
View File
@@ -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);
+2 -1
View File
@@ -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;
}
+1 -1
View File
@@ -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();
+10
View File
@@ -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)
{
+4 -4
View File
@@ -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
)
-185
View File
@@ -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();
}
-29
View File
@@ -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,
+335
View File
@@ -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();
}
+43
View File
@@ -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
+2
View File
@@ -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
+5
View File
@@ -29,6 +29,11 @@ void QtProgressBar::showProgress(size_t percent)
update();
}
size_t QtProgressBar::getProgress() const
{
return m_percent;
}
void QtProgressBar::showUnknownProgressAnimated()
{
start();
+1
View File
@@ -17,6 +17,7 @@ public:
QtProgressBar(QWidget* parent = nullptr);
void showProgress(size_t percent);
size_t getProgress() const;
void showUnknownProgressAnimated();
+14 -2
View File
@@ -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());
+16 -2
View File
@@ -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" : ""));
+3 -1
View File
@@ -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);
}
+34
View File
@@ -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
-1
View File
@@ -28,7 +28,6 @@ protected:
QString m_description;
private:
QPlainTextEdit* m_text;
};
+9
View File
@@ -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)
+1
View File
@@ -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;
};
+1
View File
@@ -11,6 +11,7 @@ add_files(
CommandlineTestSuite.h
ConfigManagerTestSuite.h
CxxIncludeProcessingTestSuite.h
CxxIndexSampleProjectsTestSuite.h
CxxParserTestSuite.h
CxxTypeNameTestSuite.h
+118
View File
@@ -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()
);
}
}
};
+7
View File
@@ -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");