#include "QtProjectWizzardContentPaths.h" #include #include #include #include #include #include "DialogView.h" #include "IndexerCommandCxx.h" #include "QtDialogView.h" #include "QtPathListDialog.h" #include "QtSelectPathsDialog.h" #include "SourceGroupCustomCommand.h" #include "SourceGroupCxxEmpty.h" #include "SourceGroupJavaEmpty.h" #include "SourceGroupPythonEmpty.h" #include "ApplicationSettings.h" #include "ResourcePaths.h" #include "SourceGroupSettingsCustomCommand.h" #include "SourceGroupSettingsCxx.h" #include "SourceGroupSettingsCxxCdb.h" #include "SourceGroupSettingsCxxCodeblocks.h" #include "SourceGroupSettingsCxxSonargraph.h" #include "SourceGroupSettingsJavaEmpty.h" #include "SourceGroupSettingsPythonEmpty.h" #include "SourceGroupSettingsWithClasspath.h" #include "SourceGroupSettingsWithIndexedHeaderPaths.h" #include "SourceGroupSettingsWithExcludeFilters.h" #include "SourceGroupSettingsWithSourceExtensions.h" #include "SourceGroupSettingsWithSourcePaths.h" #include "CodeblocksProject.h" #include "FileManager.h" #include "SonargraphProject.h" #include "CompilationDatabase.h" #include "IncludeDirective.h" #include "IncludeProcessing.h" #include "ScopedFunctor.h" #include "utility.h" #include "utilityApp.h" #include "utilityFile.h" #include "utilityPathDetection.h" #include "utilityString.h" #include "Application.h" QtProjectWizzardContentPaths::QtProjectWizzardContentPaths( std::shared_ptr settings, QtProjectWizzardWindow* window, QtPathListBox::SelectionPolicyType selectionPolicy, bool checkMissingPaths ) : QtProjectWizzardContent(window) , m_settings(settings) , m_makePathsRelativeToProjectFileLocation(true) , m_selectionPolicy(selectionPolicy) , m_checkMissingPaths(checkMissingPaths) { } void QtProjectWizzardContentPaths::populate(QGridLayout* layout, int& row) { QLabel* label = createFormLabel(m_titleString); layout->addWidget(label, row, QtProjectWizzardWindow::FRONT_COL, Qt::AlignTop); if (m_helpString.size() > 0) { addHelpButton(m_titleString, m_helpString, layout, row); } m_list = new QtPathListBox(this, m_titleString, m_selectionPolicy); if (m_makePathsRelativeToProjectFileLocation && m_settings) { m_list->setRelativeRootDirectory(m_settings->getProjectDirectoryPath()); } layout->addWidget(m_list, row, QtProjectWizzardWindow::BACK_COL); row++; if (m_showFilesString.size() > 0) { addFilesButton(m_showFilesString, layout, row); row++; } if (m_pathDetector) { addDetection(layout, row); row++; } } bool QtProjectWizzardContentPaths::check() { if (m_checkMissingPaths) { QString missingPaths; std::vector existingPaths; for (const FilePath& path : m_list->getPathsAsDisplayed()) { std::vector expandedPaths(1, path); if (m_settings) { expandedPaths = m_settings->makePathsExpandedAndAbsolute(expandedPaths); } size_t existingCount = 0; for (const FilePath& expandedPath : expandedPaths) { if (!expandedPath.exists()) { missingPaths.append(QString::fromStdWString(expandedPath.wstr() + L"\n")); } else { existingCount++; } } if (!expandedPaths.empty() && expandedPaths.size() == existingCount) { existingPaths.push_back(path); } } if (!missingPaths.isEmpty()) { QMessageBox msgBox; msgBox.setText( QString( "Some provided paths do not exist at \"%1\". Do you want to remove them before continuing?" ).arg(m_titleString) ); msgBox.setDetailedText(missingPaths); QPushButton* removeButton = msgBox.addButton("Remove", QMessageBox::YesRole); QPushButton* keepButton = msgBox.addButton("Keep", QMessageBox::ButtonRole::NoRole); QPushButton* cancelButton = msgBox.addButton("Cancel", QMessageBox::ButtonRole::RejectRole); msgBox.exec(); if (msgBox.clickedButton() == removeButton) { m_list->setPaths(existingPaths); save(); } else if (msgBox.clickedButton() == keepButton) { return true; } else if (msgBox.clickedButton() == cancelButton) { return false; } } } return true; } void QtProjectWizzardContentPaths::setTitleString(const QString& title) { m_titleString = title; } void QtProjectWizzardContentPaths::setHelpString(const QString& help) { m_helpString = help; } void QtProjectWizzardContentPaths::addDetection(QGridLayout* layout, int row) { std::vector detectorNames = m_pathDetector->getWorkingDetectorNames(); if (!detectorNames.size()) { return; } QLabel* label = new QLabel("Auto detection from:"); m_detectorBox = new QComboBox(); for (const std::string& detectorName: detectorNames) { m_detectorBox->addItem(detectorName.c_str()); } QPushButton* button = new QPushButton("detect"); button->setObjectName("windowButton"); connect(button, &QPushButton::clicked, this, &QtProjectWizzardContentPaths::detectionClicked); QHBoxLayout* hlayout = new QHBoxLayout(); hlayout->setContentsMargins(0, 0, 0, 0); hlayout->addWidget(label); hlayout->addWidget(m_detectorBox); hlayout->addWidget(button); QWidget* detectionWidget = new QWidget(); detectionWidget->setLayout(hlayout); layout->addWidget(detectionWidget, row, QtProjectWizzardWindow::BACK_COL, Qt::AlignLeft | Qt::AlignTop); } void QtProjectWizzardContentPaths::detectionClicked() { std::vector paths = m_pathDetector->getPaths(m_detectorBox->currentText().toStdString()); std::vector oldPaths = m_list->getPathsAsDisplayed(); paths = utility::unique(utility::concat(oldPaths, paths)); detectedPaths(paths); } void QtProjectWizzardContentPaths::detectedPaths(const std::vector& paths) { m_list->setPaths(paths); } QtProjectWizzardContentPathsSource::QtProjectWizzardContentPathsSource( std::shared_ptr settings, QtProjectWizzardWindow* window ) : QtProjectWizzardContentPaths(settings, window, QtPathListBox::SELECTION_POLICY_FILES_AND_DIRECTORIES) { m_showFilesString = "show files"; setTitleString("Files & Directories to Index"); setHelpString( "These paths define the files and directories that will be indexed by Sourcetrail. Provide a directory to recursively " "add all contained source and header files.
" "
" "If your project's source code resides in one location, but generated source files are kept at a different location, " "you will also need to add that directory.
" "
" "You can make use of environment variables with ${ENV_VAR}." ); } void QtProjectWizzardContentPathsSource::load() { if (std::shared_ptr pathSettings = std::dynamic_pointer_cast(m_settings)) // FIXME: pass msettings as required type { m_list->setPaths(pathSettings->getSourcePaths()); } } void QtProjectWizzardContentPathsSource::save() { if (std::shared_ptr pathSettings = std::dynamic_pointer_cast(m_settings)) // FIXME: pass msettings as required type { pathSettings->setSourcePaths(m_list->getPathsAsDisplayed()); } } std::vector QtProjectWizzardContentPathsSource::getFilePaths() const { std::set allSourceFilePaths; if (std::shared_ptr settings = std::dynamic_pointer_cast(m_settings)) { allSourceFilePaths = SourceGroupCxxEmpty(settings).getAllSourceFilePaths(); } else if (std::shared_ptr settings = std::dynamic_pointer_cast(m_settings)) { allSourceFilePaths = SourceGroupJavaEmpty(settings).getAllSourceFilePaths(); } else if (std::shared_ptr settings = std::dynamic_pointer_cast(m_settings)) { allSourceFilePaths = SourceGroupPythonEmpty(settings).getAllSourceFilePaths(); } else if (std::shared_ptr settings = std::dynamic_pointer_cast(m_settings)) { allSourceFilePaths = SourceGroupCustomCommand(settings).getAllSourceFilePaths(); } return utility::getAsRelativeIfShorter( utility::toVector(allSourceFilePaths), m_settings->getProjectDirectoryPath() ); } QString QtProjectWizzardContentPathsSource::getFileNamesTitle() const { return "Indexed Files"; } QString QtProjectWizzardContentPathsSource::getFileNamesDescription() const { return " files will be indexed."; } std::vector QtProjectWizzardContentIndexedHeaderPaths::getIndexedPathsDerivedFromSonargraphProject( std::shared_ptr settings) { std::set indexedHeaderPaths; { const FilePath sonargraphProjectPath = settings->getSonargraphProjectPathExpandedAndAbsolute(); if (!sonargraphProjectPath.empty() && sonargraphProjectPath.exists()) { if (std::shared_ptr sonargraphProject = Sonargraph::Project::load( sonargraphProjectPath, settings->getLanguage() )) { for (const FilePath& path : sonargraphProject->getAllSourceFilePathsCanonical()) { indexedHeaderPaths.insert(path.getCanonical().getParentDirectory()); } utility::append(indexedHeaderPaths, sonargraphProject->getAllCxxHeaderSearchPathsCanonical()); } } else { LOG_WARNING("Unable to fetch indexed header paths. The provided Sonargraph project path does not exist."); } } std::vector topLevelPaths; for (const FilePath& path : utility::getTopLevelPaths(indexedHeaderPaths)) { if (path.exists()) { topLevelPaths.push_back(path); } } return topLevelPaths; } std::vector QtProjectWizzardContentIndexedHeaderPaths::getIndexedPathsDerivedFromCodeblocksProject( std::shared_ptr settings) { const FilePath projectPath = settings->getProjectDirectoryPath(); std::set indexedHeaderPaths; { const FilePath codeblocksProjectPath = settings->getCodeblocksProjectPathExpandedAndAbsolute(); if (!codeblocksProjectPath.empty() && codeblocksProjectPath.exists()) { if (std::shared_ptr codeblocksProject = Codeblocks::Project::load(codeblocksProjectPath)) { OrderedCache canonicalDirectoryPathCache([](const FilePath& path) { return path.getCanonical(); }); for (const FilePath& path : codeblocksProject->getAllSourceFilePathsCanonical(settings->getSourceExtensions())) { indexedHeaderPaths.insert(canonicalDirectoryPathCache.getValue(path.getParentDirectory())); } utility::append(indexedHeaderPaths, codeblocksProject->getAllCxxHeaderSearchPathsCanonical()); } } else { LOG_WARNING("Unable to fetch indexed header paths. The provided Sonargraph project path does not exist."); } } std::vector topLevelPaths; for (const FilePath& path : utility::getTopLevelPaths(indexedHeaderPaths)) { if (path.exists()) { topLevelPaths.push_back(path); } } return topLevelPaths; } std::vector QtProjectWizzardContentIndexedHeaderPaths::getIndexedPathsDerivedFromCDB( std::shared_ptr settings) { std::set indexedHeaderPaths; { const FilePath cdbPath = settings->getCompilationDatabasePathExpandedAndAbsolute(); if (!cdbPath.empty() && cdbPath.exists()) { for (const FilePath& path : IndexerCommandCxx::getSourceFilesFromCDB(cdbPath)) { indexedHeaderPaths.insert(path.getCanonical().getParentDirectory()); } for (const FilePath& path : utility::CompilationDatabase(cdbPath).getAllHeaderPaths()) { if (path.exists()) { indexedHeaderPaths.insert(path.getCanonical()); } } } else { LOG_WARNING("Unable to fetch indexed header paths. The provided Compilation Database path does not exist."); } } std::vector topLevelPaths; for (const FilePath& path : utility::getTopLevelPaths(indexedHeaderPaths)) { if (path.exists()) { topLevelPaths.push_back(path); } } return topLevelPaths; } QtProjectWizzardContentIndexedHeaderPaths::QtProjectWizzardContentIndexedHeaderPaths( std::shared_ptr settings, QtProjectWizzardWindow* window, std::string projectKindName ) : QtProjectWizzardContentPaths(settings, window, QtPathListBox::SELECTION_POLICY_FILES_AND_DIRECTORIES) , m_projectKindName(projectKindName) { m_showFilesString = ""; setTitleString("Header Files & Directories to Index"); setHelpString(QString::fromStdString( "The provided " + m_projectKindName + " already specifies which source files are part of your project. But Sourcetrail still " "needs to know which header files to index as part of your project and which to skip. Choosing to skip indexing " "your system headers or external frameworks will significantly improve the overall indexing performance.
" "
" "Use this list to define which header files should be indexed by Sourcetrail. Provide a directory to recursively " "add all contained files.
" "
" "You can make use of environment variables with ${ENV_VAR}.
" "
" "Hint: Just enter the root path of your project if you want Sourcetrail to index all contained headers it " "encounters.
" )); } void QtProjectWizzardContentIndexedHeaderPaths::populate(QGridLayout* layout, int& row) { QtProjectWizzardContentPaths::populate(layout, row); QPushButton* button = new QPushButton(QString::fromStdString("Select from " + m_projectKindName)); button->setObjectName("windowButton"); connect(button, &QPushButton::clicked, this, &QtProjectWizzardContentIndexedHeaderPaths::buttonClicked); layout->addWidget(button, row, QtProjectWizzardWindow::BACK_COL, Qt::AlignRight | Qt::AlignTop); row++; } void QtProjectWizzardContentIndexedHeaderPaths::load() { if (std::shared_ptr cdbSettings = std::dynamic_pointer_cast(m_settings)) { m_list->setPaths(cdbSettings->getIndexedHeaderPaths()); } } void QtProjectWizzardContentIndexedHeaderPaths::save() { if (std::shared_ptr cdbSettings = std::dynamic_pointer_cast(m_settings)) { cdbSettings->setIndexedHeaderPaths(m_list->getPathsAsDisplayed()); } } bool QtProjectWizzardContentIndexedHeaderPaths::check() { if (m_list->getPathsAsDisplayed().empty()) { QMessageBox msgBox; msgBox.setText("You didn't specify any Header Files & Directories to Index."); msgBox.setInformativeText(QString::fromStdString( "Sourcetrail will only index the source files listed in the " + m_projectKindName + " file and none of the included header files." )); msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Ok); return msgBox.exec() == QMessageBox::Ok; } else { return QtProjectWizzardContentPaths::check(); } } void QtProjectWizzardContentIndexedHeaderPaths::buttonClicked() { save(); if (!m_filesDialog) { if (std::shared_ptr sonargraphSettings = std::dynamic_pointer_cast(m_settings)) { const FilePath sonargraphProjectPath = sonargraphSettings->getSonargraphProjectPathExpandedAndAbsolute(); if (!sonargraphProjectPath.exists()) { QMessageBox msgBox; msgBox.setText("The provided Sonargraph project path does not exist."); msgBox.setDetailedText(QString::fromStdWString(sonargraphProjectPath.wstr())); msgBox.exec(); return; } m_filesDialog = std::make_shared( "Select from Include Paths", "The list contains all Include Paths found in the Sonargraph project. Red paths do not exist. Select the " "paths containing the header files you want to index with Sourcetrail."); m_filesDialog->setup(); connect(m_filesDialog.get(), &QtSelectPathsDialog::finished, this, &QtProjectWizzardContentIndexedHeaderPaths::savedFilesDialog); connect(m_filesDialog.get(), &QtSelectPathsDialog::canceled, this, &QtProjectWizzardContentIndexedHeaderPaths::closedFilesDialog); const FilePath projectPath = sonargraphSettings->getProjectDirectoryPath(); dynamic_cast(m_filesDialog.get())->setPathsList( utility::convert( getIndexedPathsDerivedFromSonargraphProject(sonargraphSettings), [&](const FilePath& path) { return utility::getAsRelativeIfShorter(path, projectPath); } ), sonargraphSettings->getIndexedHeaderPaths(), m_settings->getProjectDirectoryPath() ); } else if (std::shared_ptr codeblocksSettings = std::dynamic_pointer_cast(m_settings)) { const FilePath codeblocksProjectPath = codeblocksSettings->getCodeblocksProjectPathExpandedAndAbsolute(); if (!codeblocksProjectPath.exists()) { QMessageBox msgBox; msgBox.setText("The provided Code::Blocks project path does not exist."); msgBox.setDetailedText(QString::fromStdWString(codeblocksProjectPath.wstr())); msgBox.exec(); return; } m_filesDialog = std::make_shared( "Select from Include Paths", "The list contains all Include Paths found in the Code::Blocks project. Red paths do not exist. Select the " "paths containing the header files you want to index with Sourcetrail."); m_filesDialog->setup(); connect(m_filesDialog.get(), &QtSelectPathsDialog::finished, this, &QtProjectWizzardContentIndexedHeaderPaths::savedFilesDialog); connect(m_filesDialog.get(), &QtSelectPathsDialog::canceled, this, &QtProjectWizzardContentIndexedHeaderPaths::closedFilesDialog); const FilePath projectPath = codeblocksSettings->getProjectDirectoryPath(); dynamic_cast(m_filesDialog.get())->setPathsList( utility::convert( getIndexedPathsDerivedFromCodeblocksProject(codeblocksSettings), [&](const FilePath& path) { return utility::getAsRelativeIfShorter(path, projectPath); } ), codeblocksSettings->getIndexedHeaderPaths(), m_settings->getProjectDirectoryPath() ); } else if (std::shared_ptr cdbSettings = std::dynamic_pointer_cast(m_settings)) { const FilePath cdbPath = cdbSettings->getCompilationDatabasePathExpandedAndAbsolute(); if (!cdbPath.exists()) { QMessageBox msgBox; msgBox.setText("The provided Compilation Database path does not exist."); msgBox.setDetailedText(QString::fromStdWString(cdbPath.wstr())); msgBox.exec(); return; } m_filesDialog = std::make_shared( "Select from Include Paths", "The list contains all Include Paths found in the Compilation Database. Red paths do not exist. Select the " "paths containing the header files you want to index with Sourcetrail."); m_filesDialog->setup(); connect(m_filesDialog.get(), &QtSelectPathsDialog::finished, this, &QtProjectWizzardContentIndexedHeaderPaths::savedFilesDialog); connect(m_filesDialog.get(), &QtSelectPathsDialog::canceled, this, &QtProjectWizzardContentIndexedHeaderPaths::closedFilesDialog); const FilePath projectPath = cdbSettings->getProjectDirectoryPath(); dynamic_cast(m_filesDialog.get())->setPathsList( utility::convert( getIndexedPathsDerivedFromCDB(cdbSettings), [&](const FilePath& path) { return utility::getAsRelativeIfShorter(path, projectPath); } ), cdbSettings->getIndexedHeaderPaths(), m_settings->getProjectDirectoryPath() ); } } if (m_filesDialog) { m_filesDialog->showWindow(); m_filesDialog->raise(); } } void QtProjectWizzardContentIndexedHeaderPaths::savedFilesDialog() { m_list->setPaths(dynamic_cast(m_filesDialog.get())->getPathsList()); closedFilesDialog(); } QtProjectWizzardContentPathsExclude::QtProjectWizzardContentPathsExclude( std::shared_ptr settings, QtProjectWizzardWindow* window ) : QtProjectWizzardContentPaths(settings, window, QtPathListBox::SELECTION_POLICY_FILES_AND_DIRECTORIES, false) { setTitleString("Excluded Files & Directories"); setHelpString( "

These paths define the files and directories that will be left out from indexing.

" "

Hints:" "

    " "
  • You can use the wildcard \"*\" which represents characters except \"\\\" or \"/\" (e.g. \"src/*/test.h\" matches \"src/app/test.h\" but does not match \"src/app/widget/test.h\" or \"src/test.h\")
  • " "
  • You can use the wildcard \"**\" which represents arbitrary characters (e.g. \"src**test.h\" matches \"src/app/test.h\" as well as \"src/app/widget/test.h\" or \"src/test.h\")
  • " "
  • You can make use of environment variables with ${ENV_VAR}
  • " "

" ); } void QtProjectWizzardContentPathsExclude::load() { if (std::shared_ptr settings = std::dynamic_pointer_cast(m_settings)) // FIXME: pass msettings as required type { m_list->setPaths(utility::convert(settings->getExcludeFilterStrings(), [](const std::wstring& s) { return FilePath(s); })); } } void QtProjectWizzardContentPathsExclude::save() { if (std::shared_ptr settings = std::dynamic_pointer_cast(m_settings)) // FIXME: pass msettings as required type { settings->setExcludeFilterStrings(utility::toWStrings(m_list->getPathsAsDisplayed())); } } QtProjectWizzardContentPathsHeaderSearch::QtProjectWizzardContentPathsHeaderSearch( std::shared_ptr settings, QtProjectWizzardWindow* window, bool indicateAsAdditional ) : QtProjectWizzardContentPaths(settings, window, QtPathListBox::SELECTION_POLICY_DIRECTORIES_ONLY) , m_showDetectedIncludesResultFunctor(std::bind( &QtProjectWizzardContentPathsHeaderSearch::showDetectedIncludesResult, this, std::placeholders::_1)) , m_showValidationResultFunctor(std::bind( &QtProjectWizzardContentPathsHeaderSearch::showValidationResult, this, std::placeholders::_1)) , m_indicateAsAdditional(indicateAsAdditional) { setTitleString(m_indicateAsAdditional ? "Additional Include Paths" : "Include Paths"); setHelpString( ( (m_indicateAsAdditional ? "Note: Use the Additional Include Paths to add paths that are missing in the " "referenced project file.

" : "" ) + std::string( "Include Paths are used for resolving #include directives in the indexed source and header files. These paths are " "usually passed to the compiler with the '-I' or '-iquote' flags.
" "
" "Add all paths #include directives throughout your project are relative to. If all #include directives are " "specified relative to the project's root directory, please add that root directory here.
" "
" "If your project also includes files from external libraries (e.g. boost), please add these directories as well " "(e.g. add 'path/to/boost_home/include').
" "
" "You can make use of environment variables with ${ENV_VAR}." ) ).c_str() ); } void QtProjectWizzardContentPathsHeaderSearch::populate(QGridLayout* layout, int& row) { QtProjectWizzardContentPaths::populate(layout, row); if (!m_indicateAsAdditional) { { 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++; } } void QtProjectWizzardContentPathsHeaderSearch::load() { std::shared_ptr cxxSettings = std::dynamic_pointer_cast(m_settings); if (cxxSettings) { m_list->setPaths(cxxSettings->getHeaderSearchPaths()); } } void QtProjectWizzardContentPathsHeaderSearch::save() { std::shared_ptr cxxSettings = std::dynamic_pointer_cast(m_settings); if (cxxSettings) { cxxSettings->setHeaderSearchPaths(m_list->getPathsAsDisplayed()); } } bool QtProjectWizzardContentPathsHeaderSearch::isScrollAble() const { return true; } void QtProjectWizzardContentPathsHeaderSearch::detectIncludesButtonClicked() { m_window->saveContent(); m_pathsDialog = std::make_shared( "Detect Include Paths", "

Automatically search for header files in the paths provided below to find missing include paths for " "unresolved include directives in your source code.

" "

The \"Files & Directories to Index\" will be searched by default, but you can add further paths, such " "as directories of third-party libraries, if required.

", QtPathListBox::SELECTION_POLICY_DIRECTORIES_ONLY ); m_pathsDialog->setup(); m_pathsDialog->updateNextButton("Start"); m_pathsDialog->setCloseVisible(true); m_pathsDialog->setRelativeRootDirectory(m_settings->getProjectDirectoryPath()); if (std::shared_ptr pathSettings = std::dynamic_pointer_cast(m_settings)) // FIXME: pass msettings as required type { m_pathsDialog->setPaths(pathSettings->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(); std::thread([&]() { std::shared_ptr extensionSettings = std::dynamic_pointer_cast(m_settings); std::shared_ptr pathSettings = std::dynamic_pointer_cast(m_settings); std::shared_ptr excludeFilterSettings = std::dynamic_pointer_cast(m_settings); if (extensionSettings && pathSettings && excludeFilterSettings) // FIXME: pass msettings as required type { std::vector unresolvedIncludes; { QtDialogView* dialogView = dynamic_cast( Application::getInstance()->getDialogView(DialogView::UseCase::PROJECT_SETUP).get()); std::set sourceFilePaths; std::vector indexedFilePaths; std::vector headerSearchPaths; { dialogView->setParentWindow(m_window); dialogView->showUnknownProgressDialog(L"Processing", L"Gathering Source Files"); ScopedFunctor dialogHider([&dialogView](){ dialogView->hideUnknownProgressDialog(); }); FileManager fileManager; fileManager.update( pathSettings->getSourcePathsExpandedAndAbsolute(), excludeFilterSettings->getExcludeFiltersExpandedAndAbsolute(), extensionSettings->getSourceExtensions() ); sourceFilePaths = fileManager.getAllSourceFilePaths(); indexedFilePaths = pathSettings->getSourcePathsExpandedAndAbsolute(); headerSearchPaths = ApplicationSettings::getInstance()->getHeaderSearchPathsExpanded(); if (std::shared_ptr cxxSettings = std::dynamic_pointer_cast(m_settings)) { utility::append(headerSearchPaths, cxxSettings->getHeaderSearchPathsExpandedAndAbsolute()); } } { dialogView->setParentWindow(m_window); ScopedFunctor dialogHider([&dialogView](){ dialogView->hideProgressDialog(); }); unresolvedIncludes = IncludeProcessing::getUnresolvedIncludeDirectives( sourceFilePaths, utility::toSet(indexedFilePaths), utility::toSet(headerSearchPaths), log2(sourceFilePaths.size()), [&](const float progress) { dialogView->showProgressDialog( L"Processing", std::to_wstring(int(progress * sourceFilePaths.size())) + L" Files", int(progress * 100.0f) ); } ); } } m_showValidationResultFunctor(unresolvedIncludes); } }).detach(); } void QtProjectWizzardContentPathsHeaderSearch::finishedSelectDetectIncludesRootPathsDialog() { // TODO: regard Force Includes here, too! const std::vector searchedPaths = m_settings->makePathsExpandedAndAbsolute(m_pathsDialog->getPaths()); closedPathsDialog(); std::thread([=]() { std::shared_ptr extensionSettings = std::dynamic_pointer_cast(m_settings); std::shared_ptr pathSettings = std::dynamic_pointer_cast(m_settings); std::shared_ptr excludeFilterSettings = std::dynamic_pointer_cast(m_settings); if (extensionSettings && pathSettings && excludeFilterSettings) // FIXME: pass msettings as required type { std::set detectedHeaderSearchPaths; { QtDialogView* dialogView = dynamic_cast( Application::getInstance()->getDialogView(DialogView::UseCase::PROJECT_SETUP).get()); std::set sourceFilePaths; std::vector headerSearchPaths; { dialogView->setParentWindow(m_window); dialogView->showUnknownProgressDialog(L"Processing", L"Gathering Source Files"); ScopedFunctor dialogHider([&dialogView]() { dialogView->hideUnknownProgressDialog(); }); FileManager fileManager; fileManager.update( pathSettings->getSourcePathsExpandedAndAbsolute(), excludeFilterSettings->getExcludeFiltersExpandedAndAbsolute(), extensionSettings->getSourceExtensions() ); sourceFilePaths = fileManager.getAllSourceFilePaths(); headerSearchPaths = ApplicationSettings::getInstance()->getHeaderSearchPathsExpanded(); if (std::shared_ptr cxxSettings = std::dynamic_pointer_cast(m_settings)) { utility::append(headerSearchPaths, cxxSettings->getHeaderSearchPathsExpandedAndAbsolute()); } } { dialogView->setParentWindow(m_window); ScopedFunctor dialogHider([&dialogView]() { dialogView->hideProgressDialog(); }); detectedHeaderSearchPaths = IncludeProcessing::getHeaderSearchDirectories( sourceFilePaths, utility::toSet(searchedPaths), utility::toSet(headerSearchPaths), log2(sourceFilePaths.size()), [&](const float progress) { dialogView->showProgressDialog( L"Processing", std::to_wstring(int(progress * sourceFilePaths.size())) + L" Files", int(progress * 100.0f) ); } ); } } m_showDetectedIncludesResultFunctor(detectedHeaderSearchPaths); } }).detach(); } void QtProjectWizzardContentPathsHeaderSearch::finishedAcceptDetectedIncludePathsDialog() { const std::vector detectedPaths = utility::split>(m_filesDialog->getText(), L"\n"); closedFilesDialog(); std::vector headerSearchPaths = m_list->getPathsAsDisplayed(); headerSearchPaths.reserve(headerSearchPaths.size() + detectedPaths.size()); for (const std::wstring& detectedPath : detectedPaths) { if (!detectedPath.empty()) { headerSearchPaths.push_back(FilePath(detectedPath)); } } m_list->setPaths(headerSearchPaths); } void QtProjectWizzardContentPathsHeaderSearch::closedPathsDialog() { m_pathsDialog->hide(); m_pathsDialog.reset(); window()->raise(); } void QtProjectWizzardContentPathsHeaderSearch::showDetectedIncludesResult(const std::set& detectedHeaderSearchPaths) { const std::set headerSearchPaths = utility::toSet(m_settings->makePathsExpandedAndAbsolute(m_list->getPathsAsDisplayed())); std::vector additionalHeaderSearchPaths; { const FilePath relativeRoot = m_list->getRelativeRootDirectory(); for (const FilePath& detectedHeaderSearchPath : detectedHeaderSearchPaths) { if (headerSearchPaths.find(detectedHeaderSearchPath) == headerSearchPaths.end()) { additionalHeaderSearchPaths.push_back(utility::getAsRelativeIfShorter(detectedHeaderSearchPath, relativeRoot)); } } } if (additionalHeaderSearchPaths.empty()) { QMessageBox msgBox; msgBox.setText("

No additional include paths have been detected while searching the provided paths.

"); msgBox.exec(); } else { std::wstring detailedText = L""; for (const FilePath& path : additionalHeaderSearchPaths) { detailedText += path.wstr() + L"\n"; } m_filesDialog = std::make_shared( "Detected Include Paths", ( "

The following " + std::to_string(additionalHeaderSearchPaths.size()) + " include paths have been " "detected and will be added to the include paths of this Source Group." ).c_str() ); m_filesDialog->setup(); m_filesDialog->setReadOnly(true); m_filesDialog->setCloseVisible(true); m_filesDialog->updateNextButton("Add"); 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& unresolvedIncludes) { if (unresolvedIncludes.empty()) { QMessageBox msgBox; msgBox.setText("

All include directives throughout the indexed files have been resolved.

"); msgBox.exec(); } else { std::map> orderedIncludes; for (const IncludeDirective& unresolvedInclude: unresolvedIncludes) { orderedIncludes[unresolvedInclude.getIncludingFile().wstr()].emplace( unresolvedInclude.getLineNumber(), unresolvedInclude.getDirective()); } std::wstring detailedText = L""; for (const auto& p: orderedIncludes) { detailedText += p.first + L"\n"; for (const auto& p2: p.second) { detailedText += std::to_wstring(p2.first) + L":\t" + p2.second + L"\n"; } detailedText += L"\n"; } m_filesDialog = std::make_shared("Unresolved Include Directives", ("

The indexed files contain " + std::to_string(unresolvedIncludes.size()) + " include directive" + (unresolvedIncludes.size() == 1 ? "" : "s") + " that could not be resolved correctly. Please check the details " "and add the respective header search paths.

" "

Note: This is only a quick pass that does not regard block commenting or conditional preprocessor " "directives. This means that some of the unresolved includes may actually not be required by the indexer.

" ).c_str()); m_filesDialog->setup(); m_filesDialog->setCloseVisible(false); m_filesDialog->setReadOnly(true); m_filesDialog->setText(detailedText); m_filesDialog->showWindow(); connect(m_filesDialog.get(), &QtTextEditDialog::finished, this, &QtProjectWizzardContentPathsHeaderSearch::closedFilesDialog); connect(m_filesDialog.get(), &QtTextEditDialog::canceled, this, &QtProjectWizzardContentPathsHeaderSearch::closedFilesDialog); } } QtProjectWizzardContentPathsHeaderSearchGlobal::QtProjectWizzardContentPathsHeaderSearchGlobal( QtProjectWizzardWindow* window ) : QtProjectWizzardContentPaths(std::shared_ptr(), window, QtPathListBox::SELECTION_POLICY_DIRECTORIES_ONLY) { setTitleString("Global Include Paths"); setHelpString( "The Global Include Paths will be used in all your projects in addition to the project specific Include Paths. " "These paths are usually passed to the compiler with the '-isystem' flag.
" "
" "Use them to add system header paths (See " "Finding System Header Locations or use the auto detection below)." ); m_pathDetector = utility::getCxxHeaderPathDetector(); m_makePathsRelativeToProjectFileLocation = false; } void QtProjectWizzardContentPathsHeaderSearchGlobal::load() { setPaths(ApplicationSettings::getInstance()->getHeaderSearchPaths()); } void QtProjectWizzardContentPathsHeaderSearchGlobal::save() { std::vector paths; for (const FilePath& headerPath : m_list->getPathsAsDisplayed()) { if (headerPath != ResourcePaths::getCxxCompilerHeaderPath()) { paths.push_back(headerPath); } } ApplicationSettings::getInstance()->setHeaderSearchPaths(paths); ApplicationSettings::getInstance()->save(); } bool QtProjectWizzardContentPathsHeaderSearchGlobal::check() { if (utility::getOsType() == OS_WINDOWS) { return QtProjectWizzardContentPaths::check(); } bool hasOtherCompilerConfig = false; for (const FilePath& headerPath : m_list->getPathsAsDisplayed()) { if (headerPath != ResourcePaths::getCxxCompilerHeaderPath() && headerPath.getCanonical().getConcatenated(L"/stdarg.h").exists()) { hasOtherCompilerConfig = true; break; } } if (hasOtherCompilerConfig) { QMessageBox msgBox; msgBox.setText("Multiple Compiler Headers"); msgBox.setInformativeText("Your Global Include Paths contain another path that holds C/C++ compiler headers, " "probably those of your local C/C++ compiler. They are possibly in conflict with the compiler headers of " "Sourcetrail's C/C++ indexer. This can lead to compatiblity errors during indexing. Do you want to remove " "these paths?"); msgBox.addButton("Remove", QMessageBox::ButtonRole::YesRole); msgBox.addButton("Keep", QMessageBox::ButtonRole::NoRole); msgBox.setIcon(QMessageBox::Icon::Question); int ret = msgBox.exec(); if (ret == 0) // QMessageBox::Yes { std::vector paths; for (const FilePath& headerPath : m_list->getPathsAsDisplayed()) { if (headerPath != ResourcePaths::getCxxCompilerHeaderPath() && headerPath.getCanonical().getConcatenated(L"/stdarg.h").exists()) { continue; } paths.push_back(headerPath); } setPaths(paths); } } return QtProjectWizzardContentPaths::check(); } void QtProjectWizzardContentPathsHeaderSearchGlobal::detectedPaths(const std::vector& paths) { std::vector headerPaths; for (const FilePath& headerPath : paths) { if (headerPath != ResourcePaths::getCxxCompilerHeaderPath()) { headerPaths.push_back(headerPath); } } setPaths(headerPaths); } void QtProjectWizzardContentPathsHeaderSearchGlobal::setPaths(const std::vector& paths) { m_list->setPaths({}); m_list->addPaths({ ResourcePaths::getCxxCompilerHeaderPath() }, true); m_list->addPaths(paths); } QtProjectWizzardContentPathsFrameworkSearch::QtProjectWizzardContentPathsFrameworkSearch( std::shared_ptr settings, QtProjectWizzardWindow* window, bool indicateAsAdditional ) : QtProjectWizzardContentPaths(settings, window, QtPathListBox::SELECTION_POLICY_DIRECTORIES_ONLY) { setTitleString(indicateAsAdditional ? "Additional Framework Search Paths" : "Framework Search Paths"); setHelpString( "Framework Search Paths define where MacOS framework containers (.framework), that your project depends on, are " "found. These paths are usually passed to the compiler with the '-iframework' flag.
" "
" "You can make use of environment variables with ${ENV_VAR}." ); } void QtProjectWizzardContentPathsFrameworkSearch::load() { std::shared_ptr cxxSettings = std::dynamic_pointer_cast(m_settings); if (cxxSettings) { m_list->setPaths(cxxSettings->getFrameworkSearchPaths()); } } void QtProjectWizzardContentPathsFrameworkSearch::save() { std::shared_ptr cxxSettings = std::dynamic_pointer_cast(m_settings); if (cxxSettings) { cxxSettings->setFrameworkSearchPaths(m_list->getPathsAsDisplayed()); } } bool QtProjectWizzardContentPathsFrameworkSearch::isScrollAble() const { return true; } QtProjectWizzardContentPathsFrameworkSearchGlobal::QtProjectWizzardContentPathsFrameworkSearchGlobal( QtProjectWizzardWindow* window ) : QtProjectWizzardContentPaths(std::shared_ptr(), window, QtPathListBox::SELECTION_POLICY_DIRECTORIES_ONLY) { setTitleString("Global Framework Search Paths"); setHelpString( "The Global Framework Search Paths will be used in all your projects - in addition to the project specific " "Framework Search Paths.
" "
" "They define where MacOS framework containers (.framework) are found " "(See " "Finding System Header Locations or use the auto detection below)." ); m_pathDetector = utility::getCxxFrameworkPathDetector(); m_makePathsRelativeToProjectFileLocation = false; } void QtProjectWizzardContentPathsFrameworkSearchGlobal::load() { m_list->setPaths(ApplicationSettings::getInstance()->getFrameworkSearchPaths()); } void QtProjectWizzardContentPathsFrameworkSearchGlobal::save() { ApplicationSettings::getInstance()->setFrameworkSearchPaths(m_list->getPathsAsDisplayed()); ApplicationSettings::getInstance()->save(); } QtProjectWizzardContentPathsClassJava::QtProjectWizzardContentPathsClassJava( std::shared_ptr settings, QtProjectWizzardWindow* window ) : QtProjectWizzardContentPaths(settings, window, QtPathListBox::SELECTION_POLICY_FILES_AND_DIRECTORIES) { setTitleString("Class Path"); setHelpString( "Enter all the .jar files your project depends on. If your project depends on uncompiled java code that should " "not be indexed, please add the root directory of those .java files here (the one where all the package names " "are relative to).
" "
" "You can make use of environment variables with ${ENV_VAR}." ); } void QtProjectWizzardContentPathsClassJava::populate(QGridLayout* layout, int& row) { QtProjectWizzardContentPaths::populate(layout, row); QLabel* label = createFormLabel("JRE System Library"); layout->addWidget(label, row, QtProjectWizzardWindow::FRONT_COL, Qt::AlignTop); m_useJreSystemLibraryCheckBox = new QCheckBox("Use JRE System Library", this); layout->addWidget(m_useJreSystemLibraryCheckBox, row, QtProjectWizzardWindow::BACK_COL); row++; } void QtProjectWizzardContentPathsClassJava::load() { std::shared_ptr settings = std::dynamic_pointer_cast(m_settings); if (settings) { m_list->setPaths(settings->getClasspath()); m_useJreSystemLibraryCheckBox->setChecked(settings->getUseJreSystemLibrary()); } } void QtProjectWizzardContentPathsClassJava::save() { std::shared_ptr settings = std::dynamic_pointer_cast(m_settings); if (settings) { settings->setClasspath(m_list->getPathsAsDisplayed()); settings->setUseJreSystemLibrary(m_useJreSystemLibraryCheckBox->isChecked()); } }