Commit adee8336 authored by Orgad Shaneh's avatar Orgad Shaneh Committed by Orgad Shaneh

C++: Custom directory list for Switch Header/Source

Some projects use separate directories for sources and headers.

An example tree:
*
|-- src
     |-- foo.cpp
|-- include
     |-- foo.h

Allow the user to specify directories for finding out-of-project related
header/source files, in addition to current directory

Task-number: QTCREATORBUG-8883
Change-Id: I57215c8f2feffcc246d0d161798290861bcfcdd4
Reviewed-by: default avatarNikolai Kosjar <nikolai.kosjar@digia.com>
parent d0385537
......@@ -30,6 +30,7 @@
#include "cppfilesettingspage.h"
#include "cpptoolsconstants.h"
#include "cpptoolsplugin.h"
#include <ui_cppfilesettingspage.h>
#include <coreplugin/icore.h>
......@@ -52,6 +53,8 @@
static const char headerSuffixKeyC[] = "HeaderSuffix";
static const char sourceSuffixKeyC[] = "SourceSuffix";
static const char headerSearchPathsKeyC[] = "HeaderSearchPaths";
static const char sourceSearchPathsKeyC[] = "SourceSearchPaths";
static const char licenseTemplatePathKeyC[] = "LicenseTemplate";
const char *licenseTemplateTemplate = QT_TRANSLATE_NOOP("CppTools::Internal::CppFileSettingsWidget",
......@@ -75,6 +78,8 @@ void CppFileSettings::toSettings(QSettings *s) const
s->beginGroup(QLatin1String(Constants::CPPTOOLS_SETTINGSGROUP));
s->setValue(QLatin1String(headerSuffixKeyC), headerSuffix);
s->setValue(QLatin1String(sourceSuffixKeyC), sourceSuffix);
s->setValue(QLatin1String(headerSearchPathsKeyC), headerSearchPaths);
s->setValue(QLatin1String(sourceSearchPathsKeyC), sourceSearchPaths);
s->setValue(QLatin1String(Constants::LOWERCASE_CPPFILES_KEY), lowerCaseFiles);
s->setValue(QLatin1String(licenseTemplatePathKeyC), licenseTemplatePath);
s->endGroup();
......@@ -82,9 +87,22 @@ void CppFileSettings::toSettings(QSettings *s) const
void CppFileSettings::fromSettings(QSettings *s)
{
const QStringList defaultHeaderSearchPaths = QStringList()
<< QLatin1String("include")
<< QLatin1String("Include")
<< QDir::toNativeSeparators(QLatin1String("../include"))
<< QDir::toNativeSeparators(QLatin1String("../Include"));
const QStringList defaultSourceSearchPaths = QStringList()
<< QDir::toNativeSeparators(QLatin1String("../src"))
<< QDir::toNativeSeparators(QLatin1String("../Src"))
<< QLatin1String("..");
s->beginGroup(QLatin1String(Constants::CPPTOOLS_SETTINGSGROUP));
headerSuffix= s->value(QLatin1String(headerSuffixKeyC), QLatin1String("h")).toString();
sourceSuffix = s->value(QLatin1String(sourceSuffixKeyC), QLatin1String("cpp")).toString();
headerSearchPaths = s->value(QLatin1String(headerSearchPathsKeyC), defaultHeaderSearchPaths)
.toStringList();
sourceSearchPaths = s->value(QLatin1String(sourceSearchPathsKeyC), defaultSourceSearchPaths)
.toStringList();
const bool lowerCaseDefault = Constants::lowerCaseFilesDefault;
lowerCaseFiles = s->value(QLatin1String(Constants::LOWERCASE_CPPFILES_KEY), QVariant(lowerCaseDefault)).toBool();
licenseTemplatePath = s->value(QLatin1String(licenseTemplatePathKeyC), QString()).toString();
......@@ -102,6 +120,8 @@ bool CppFileSettings::equals(const CppFileSettings &rhs) const
return lowerCaseFiles == rhs.lowerCaseFiles
&& headerSuffix == rhs.headerSuffix
&& sourceSuffix == rhs.sourceSuffix
&& headerSearchPaths == rhs.headerSearchPaths
&& sourceSearchPaths == rhs.sourceSearchPaths
&& licenseTemplatePath == rhs.licenseTemplatePath;
}
......@@ -252,12 +272,22 @@ void CppFileSettingsWidget::setLicenseTemplatePath(const QString &lp)
m_ui->licenseTemplatePathChooser->setPath(lp);
}
static QStringList trimmedPaths(const QString &paths)
{
QStringList res;
foreach (const QString &path, paths.split(QLatin1Char(','), QString::SkipEmptyParts))
res << path.trimmed();
return res;
}
CppFileSettings CppFileSettingsWidget::settings() const
{
CppFileSettings rc;
rc.lowerCaseFiles = m_ui->lowerCaseFileNamesCheckBox->isChecked();
rc.headerSuffix = m_ui->headerSuffixComboBox->currentText();
rc.sourceSuffix = m_ui->sourceSuffixComboBox->currentText();
rc.headerSearchPaths = trimmedPaths(m_ui->headerSearchPathsEdit->text());
rc.sourceSearchPaths = trimmedPaths(m_ui->sourceSearchPathsEdit->text());
rc.licenseTemplatePath = licenseTemplatePath();
return rc;
}
......@@ -265,8 +295,12 @@ CppFileSettings CppFileSettingsWidget::settings() const
QString CppFileSettingsWidget::searchKeywords() const
{
QString rc;
QTextStream(&rc) << m_ui->headerSuffixLabel->text()
QTextStream(&rc) << m_ui->headersGroupBox->title()
<< ' ' << m_ui->headerSuffixLabel->text()
<< ' ' << m_ui->headerSearchPathsLabel->text()
<< ' ' << m_ui->sourcesGroupBox->title()
<< ' ' << m_ui->sourceSuffixLabel->text()
<< ' ' << m_ui->sourceSearchPathsLabel->text()
<< ' ' << m_ui->lowerCaseFileNamesCheckBox->text()
<< ' ' << m_ui->licenseTemplateLabel->text();
rc.remove(QLatin1Char('&'));
......@@ -284,6 +318,8 @@ void CppFileSettingsWidget::setSettings(const CppFileSettings &s)
m_ui->lowerCaseFileNamesCheckBox->setChecked(s.lowerCaseFiles);
setComboText(m_ui->headerSuffixComboBox, s.headerSuffix);
setComboText(m_ui->sourceSuffixComboBox, s.sourceSuffix);
m_ui->headerSearchPathsEdit->setText(s.headerSearchPaths.join(QLatin1String(",")));
m_ui->sourceSearchPathsEdit->setText(s.sourceSearchPaths.join(QLatin1String(",")));
setLicenseTemplatePath(s.licenseTemplatePath);
}
......@@ -336,6 +372,7 @@ void CppFileSettingsPage::apply()
*m_settings = newSettings;
m_settings->toSettings(Core::ICore::settings());
m_settings->applySuffixesToMimeDB();
CppToolsPlugin::clearHeaderSourceCache();
}
}
}
......
......@@ -50,7 +50,9 @@ struct CppFileSettings
CppFileSettings();
QString headerSuffix;
QStringList headerSearchPaths;
QString sourceSuffix;
QStringList sourceSearchPaths;
bool lowerCaseFiles;
QString licenseTemplatePath;
......
......@@ -6,52 +6,148 @@
<rect>
<x>0</x>
<y>0</y>
<width>291</width>
<height>142</height>
<width>547</width>
<height>305</height>
</rect>
</property>
<layout class="QFormLayout" name="formLayout">
<property name="fieldGrowthPolicy">
<enum>QFormLayout::ExpandingFieldsGrow</enum>
</property>
<item row="0" column="0">
<widget class="QLabel" name="headerSuffixLabel">
<property name="text">
<string>Header suffix:</string>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="headersGroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="headerSuffixComboBox"/>
</item>
<item row="1" column="0">
<widget class="QLabel" name="sourceSuffixLabel">
<property name="text">
<string>Source suffix:</string>
<property name="title">
<string>Headers</string>
</property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0">
<widget class="QLabel" name="headerSuffixLabel">
<property name="text">
<string>&amp;Suffix:</string>
</property>
<property name="buddy">
<cstring>headerSuffixComboBox</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="headerSuffixComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="headerSearchPathsLabel">
<property name="text">
<string>S&amp;earch paths:</string>
</property>
<property name="buddy">
<cstring>headerSearchPathsEdit</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="headerSearchPathsEdit">
<property name="toolTip">
<string>Comma-separated list of header paths.
Paths can be absolute or relative to the directory of the current open document.
These paths are used in addition to current directory on Switch Header/Source.</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="sourceSuffixComboBox"/>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="lowerCaseFileNamesCheckBox">
<property name="text">
<string>Lower case file names</string>
<item>
<widget class="QGroupBox" name="sourcesGroupBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Sources</string>
</property>
<layout class="QFormLayout" name="formLayout_3">
<item row="1" column="0">
<widget class="QLabel" name="sourceSuffixLabel">
<property name="text">
<string>S&amp;uffix:</string>
</property>
<property name="buddy">
<cstring>sourceSuffixComboBox</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="sourceSuffixComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="sourceSearchPathsLabel">
<property name="text">
<string>Se&amp;arch paths:</string>
</property>
<property name="buddy">
<cstring>sourceSearchPathsEdit</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="sourceSearchPathsEdit">
<property name="toolTip">
<string>Comma-separated list of source paths.
Paths can be absolute or relative to the directory of the current open document.
These paths are used in addition to current directory on Switch Header/Source.</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="licenseTemplateLabel">
<item>
<widget class="QCheckBox" name="lowerCaseFileNamesCheckBox">
<property name="text">
<string>License template:</string>
<string>&amp;Lower case file names</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="Utils::PathChooser" name="licenseTemplatePathChooser" native="true"/>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="licenseTemplateLabel">
<property name="text">
<string>License &amp;template:</string>
</property>
<property name="buddy">
<cstring>licenseTemplatePathChooser</cstring>
</property>
</widget>
</item>
<item>
<widget class="Utils::PathChooser" name="licenseTemplatePathChooser" native="true"/>
</item>
</layout>
</item>
<item row="4" column="0" colspan="2">
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
......@@ -65,13 +161,6 @@
</spacer>
</item>
</layout>
<zorder>licenseTemplatePathChooser</zorder>
<zorder>sourceSuffixLabel</zorder>
<zorder>headerSuffixLabel</zorder>
<zorder>lowerCaseFileNamesCheckBox</zorder>
<zorder>headerSuffixComboBox</zorder>
<zorder>sourceSuffixComboBox</zorder>
<zorder>licenseTemplateLabel</zorder>
</widget>
<customwidgets>
<customwidget>
......
/****************************************************************************
**
** Copyright (C) 2013 Orgad Shaneh <orgads@gmail.com>.
** Contact: http://www.qt-project.org/legal
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "cpptoolsplugin.h"
#include "cpptoolsreuse.h"
#include <coreplugin/testdatadir.h>
#include <QDir>
#include <QtTest>
static inline QString _(const QByteArray &ba) { return QString::fromLatin1(ba, ba.size()); }
namespace CppTools {
namespace Internal {
void CppToolsPlugin::test_headersource()
{
QFETCH(QString, sourceFileName);
QFETCH(QString, headerFileName);
bool wasHeader;
Core::Internal::Tests::TestDataDir dataDir(
_(SRCDIR "/../../../tests/cppheadersource/") + _(QTest::currentDataTag()));
const QString sourcePath = dataDir.file(sourceFileName);
const QString headerPath = dataDir.file(headerFileName);
clearHeaderSourceCache();
QCOMPARE(correspondingHeaderOrSource(sourcePath, &wasHeader), headerPath);
QVERIFY(!wasHeader);
clearHeaderSourceCache();
QCOMPARE(correspondingHeaderOrSource(headerPath, &wasHeader), sourcePath);
QVERIFY(wasHeader);
}
void CppToolsPlugin::test_headersource_data()
{
QTest::addColumn<QString>("sourceFileName");
QTest::addColumn<QString>("headerFileName");
QTest::newRow("samedir") << _("foo.cpp") << _("foo.h");
QTest::newRow("includesub") << _("foo.cpp") << _("include/foo.h");
}
} // namespace Internal
} // namespace CppTools
......@@ -107,7 +107,8 @@ equals(TEST, 1) {
cpplocatorfilter_test.cpp \
symbolsearcher_test.cpp \
cpppreprocessor_test.cpp \
cpppreprocessertesthelper.cpp
cpppreprocessertesthelper.cpp \
cppheadersource_test.cpp
HEADERS += \
cpppreprocessertesthelper.h \
......
......@@ -118,6 +118,7 @@ QtcPlugin {
files: [
"cppcodegen_test.cpp",
"cppcompletion_test.cpp",
"cppheadersource_test.cpp",
"cppmodelmanager_test.cpp",
"modelmanagertesthelper.cpp", "modelmanagertesthelper.h",
"cpppointerdeclarationformatter_test.cpp",
......
......@@ -52,6 +52,9 @@
#include <utils/hostosinfo.h>
#include <utils/qtcassert.h>
#ifdef Q_OS_WIN
#include <utils/winutils.h>
#endif
#include <QtPlugin>
#include <QFileInfo>
......@@ -88,6 +91,21 @@ CppToolsPlugin *CppToolsPlugin::instance()
return m_instance;
}
void CppToolsPlugin::clearHeaderSourceCache()
{
m_headerSourceMapping.clear();
}
const QStringList &CppToolsPlugin::headerSearchPaths()
{
return m_instance->m_fileSettings->headerSearchPaths;
}
const QStringList &CppToolsPlugin::sourceSearchPaths()
{
return m_instance->m_fileSettings->sourceSearchPaths;
}
bool CppToolsPlugin::initialize(const QStringList &arguments, QString *error)
{
Q_UNUSED(arguments)
......@@ -231,6 +249,14 @@ static QStringList baseNameWithAllSuffixes(const QString &baseName, const QStrin
return result;
}
static QStringList baseDirWithAllDirectories(const QDir &baseDir, const QStringList &directories)
{
QStringList result;
foreach (const QString &dir, directories)
result << QDir::cleanPath(baseDir.absoluteFilePath(dir));
return result;
}
static int commonStringLength(const QString &s1, const QString &s2)
{
int length = qMin(s1.length(), s2.length());
......@@ -307,15 +333,28 @@ QString correspondingHeaderOrSource(const QString &fileName, bool *wasHeader)
}
const QDir absoluteDir = fi.absoluteDir();
// Try to find a file in the same directory first
foreach (const QString &candidateFileName, candidateFileNames) {
const QFileInfo candidateFi(absoluteDir, candidateFileName);
if (candidateFi.isFile()) {
m_headerSourceMapping[fi.absoluteFilePath()] = candidateFi.absoluteFilePath();
if (!isHeader || !baseName.endsWith(privateHeaderSuffix))
m_headerSourceMapping[candidateFi.absoluteFilePath()] = fi.absoluteFilePath();
return candidateFi.absoluteFilePath();
QStringList candidateDirs(absoluteDir.absolutePath());
// If directory is not root, try matching against its siblings
const QStringList searchPaths = isHeader ? m_instance->sourceSearchPaths()
: m_instance->headerSearchPaths();
candidateDirs += baseDirWithAllDirectories(absoluteDir, searchPaths);
// Try to find a file in the same or sibling directories first
foreach (const QString &candidateDir, candidateDirs) {
foreach (const QString &candidateFileName, candidateFileNames) {
const QString candidateFilePath = candidateDir + QLatin1Char('/') + candidateFileName;
#ifdef Q_OS_WIN
const QString normalized = Utils::normalizePathName(candidateFilePath);
#else
const QString normalized = candidateFilePath;
#endif
const QFileInfo candidateFi(normalized);
if (candidateFi.isFile()) {
m_headerSourceMapping[fi.absoluteFilePath()] = candidateFi.absoluteFilePath();
if (!isHeader || !baseName.endsWith(privateHeaderSuffix))
m_headerSourceMapping[candidateFi.absoluteFilePath()] = fi.absoluteFilePath();
return candidateFi.absoluteFilePath();
}
}
}
......
......@@ -60,6 +60,9 @@ public:
~CppToolsPlugin();
static CppToolsPlugin *instance();
static const QStringList &headerSearchPaths();
static const QStringList &sourceSearchPaths();
static void clearHeaderSourceCache();
bool initialize(const QStringList &arguments, QString *errorMessage);
void extensionsInitialized();
......@@ -211,6 +214,9 @@ private slots:
void test_builtinsymbolsearcher();
void test_builtinsymbolsearcher_data();
void test_headersource_data();
void test_headersource();
private:
void test_completion();
#endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment